aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/libertas
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/libertas')
-rw-r--r--drivers/net/wireless/libertas/11d.c698
-rw-r--r--drivers/net/wireless/libertas/11d.h104
-rw-r--r--drivers/net/wireless/libertas/Kconfig45
-rw-r--r--drivers/net/wireless/libertas/Makefile12
-rw-r--r--drivers/net/wireless/libertas/README36
-rw-r--r--drivers/net/wireless/libertas/assoc.c1834
-rw-r--r--drivers/net/wireless/libertas/assoc.h29
-rw-r--r--drivers/net/wireless/libertas/cfg.c2218
-rw-r--r--drivers/net/wireless/libertas/cfg.h20
-rw-r--r--drivers/net/wireless/libertas/cmd.c1572
-rw-r--r--drivers/net/wireless/libertas/cmd.h125
-rw-r--r--drivers/net/wireless/libertas/cmdresp.c387
-rw-r--r--drivers/net/wireless/libertas/debugfs.c305
-rw-r--r--drivers/net/wireless/libertas/decl.h105
-rw-r--r--drivers/net/wireless/libertas/defs.h146
-rw-r--r--drivers/net/wireless/libertas/dev.h456
-rw-r--r--drivers/net/wireless/libertas/ethtool.c134
-rw-r--r--drivers/net/wireless/libertas/firmware.c227
-rw-r--r--drivers/net/wireless/libertas/host.h986
-rw-r--r--drivers/net/wireless/libertas/hostcmd.h775
-rw-r--r--drivers/net/wireless/libertas/if_cs.c422
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c862
-rw-r--r--drivers/net/wireless/libertas/if_sdio.h7
-rw-r--r--drivers/net/wireless/libertas/if_spi.c805
-rw-r--r--drivers/net/wireless/libertas/if_spi.h76
-rw-r--r--drivers/net/wireless/libertas/if_usb.c422
-rw-r--r--drivers/net/wireless/libertas/if_usb.h15
-rw-r--r--drivers/net/wireless/libertas/main.c1434
-rw-r--r--drivers/net/wireless/libertas/mesh.c1188
-rw-r--r--drivers/net/wireless/libertas/mesh.h77
-rw-r--r--drivers/net/wireless/libertas/persistcfg.c453
-rw-r--r--drivers/net/wireless/libertas/radiotap.h4
-rw-r--r--drivers/net/wireless/libertas/rx.c239
-rw-r--r--drivers/net/wireless/libertas/scan.c1209
-rw-r--r--drivers/net/wireless/libertas/scan.h33
-rw-r--r--drivers/net/wireless/libertas/tx.c65
-rw-r--r--drivers/net/wireless/libertas/types.h242
-rw-r--r--drivers/net/wireless/libertas/wext.c2266
-rw-r--r--drivers/net/wireless/libertas/wext.h10
39 files changed, 8057 insertions, 11986 deletions
diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c
deleted file mode 100644
index 4bc46a60ae2..00000000000
--- a/drivers/net/wireless/libertas/11d.c
+++ /dev/null
@@ -1,698 +0,0 @@
-/**
- * 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 lbs_region_2_code(u8 *region)
-{
- u8 i;
-
- for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++)
- region[i] = toupper(region[i]);
-
- for (i = 0; i < ARRAY_SIZE(region_code_mapping); 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 *lbs_code_2_region(u8 code)
-{
- u8 i;
-
- for (i = 0; i < ARRAY_SIZE(region_code_mapping); 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 lbs_get_chan_11d(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 = ARRAY_SIZE(channel_freq_power_UN_BG);
-
- for (i = 0; i < cfp_no; i++) {
- if ((cfp + i)->channel == firstchan) {
- lbs_deb_11d("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 lbs_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_deb_hex(LBS_DEB_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_deb_11d("found chan %d\n", chan);
- return 1;
- }
- }
-
- lbs_deb_11d("chan %d not found\n", chan);
- return 0;
-}
-
-u32 lbs_chan_2_freq(u8 chan)
-{
- struct chan_freq_power *cf;
- u16 i;
- u32 freq = 0;
-
- cf = channel_freq_power_UN_BG;
-
- for (i = 0; i < ARRAY_SIZE(channel_freq_power_UN_BG); 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 lbs_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_deb_11d("nrchan %d\n", nr_chan);
- lbs_deb_hex(LBS_DEB_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_deb_11d("nr_subband=%x\n", domaininfo->nr_subband);
- lbs_deb_hex(LBS_DEB_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 lbs_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_deb_11d("region_chan is NULL\n");
- return;
- }
-
- cfp = region_chan->CFP;
- if (cfp == NULL) {
- lbs_deb_11d("cfp is NULL \n");
- return;
- }
-
- parsed_region_chan->band = region_chan->band;
- parsed_region_chan->region = region_chan->region;
- memcpy(parsed_region_chan->countrycode,
- lbs_code_2_region(region_chan->region), COUNTRY_CODE_LEN);
-
- lbs_deb_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_deb_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_deb_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 lbs_region_chan_supported_11d(u8 region, u8 chan)
-{
- struct chan_freq_power *cfp;
- int cfp_no;
- u8 idx;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- cfp = lbs_get_region_cfp_table(region, &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) {
- ret = 0;
- } else {
- ret = 1;
- }
- goto done;
- }
- }
-
- /*chan is not in the region table */
-
-done:
- lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
- return ret;
-}
-
-/**
- * @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;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- /*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_deb_hex(LBS_DEB_11D, "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 */
- goto done;
- }
-
- /*Step1: check region_code */
- parsed_region_chan->region = region =
- lbs_region_2_code(countryinfo->countrycode);
-
- lbs_deb_11d("regioncode=%x\n", (u8) parsed_region_chan->region);
- lbs_deb_hex(LBS_DEB_11D, "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_deb_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 (!lbs_get_chan_11d(firstchan, i, &curchan)) {
- /* Chan is not found in UN table */
- lbs_deb_11d("chan is not supported: %d \n", i);
- break;
- }
-
- lastchan = curchan;
-
- if (lbs_region_chan_supported_11d(region, 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_deb_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_deb_11d("nrchan=%x\n", parsed_region_chan->nr_chan);
- lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (u8 *) parsed_region_chan,
- 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx);
-
-done:
- lbs_deb_enter(LBS_DEB_11D);
- 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 lbs_get_scan_type_11d(u8 chan,
- struct parsed_region_chan_11d * parsed_region_chan)
-{
- u8 scan_type = CMD_SCAN_TYPE_PASSIVE;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- if (lbs_channel_known_11d(chan, parsed_region_chan)) {
- lbs_deb_11d("found, do active scan\n");
- scan_type = CMD_SCAN_TYPE_ACTIVE;
- } else {
- lbs_deb_11d("not found, do passive scan\n");
- }
-
- lbs_deb_leave_args(LBS_DEB_11D, "ret scan_type %d", scan_type);
- return scan_type;
-
-}
-
-void lbs_init_11d(struct lbs_private *priv)
-{
- priv->enable11d = 0;
- memset(&(priv->parsed_region_chan), 0,
- sizeof(struct parsed_region_chan_11d));
- return;
-}
-
-/**
- * @brief This function sets DOMAIN INFO to FW
- * @param priv pointer to struct lbs_private
- * @return 0; -1
-*/
-static int set_domain_info_11d(struct lbs_private *priv)
-{
- int ret;
-
- if (!priv->enable11d) {
- lbs_deb_11d("dnld domain Info with 11d disabled\n");
- return 0;
- }
-
- ret = lbs_prepare_and_send_command(priv, CMD_802_11D_DOMAIN_INFO,
- CMD_ACT_SET,
- CMD_OPTION_WAITFORRSP, 0, NULL);
- if (ret)
- lbs_deb_11d("fail to dnld domain info\n");
-
- return ret;
-}
-
-/**
- * @brief This function setups scan channels
- * @param priv pointer to struct lbs_private
- * @param band band
- * @return 0
-*/
-int lbs_set_universaltable(struct lbs_private *priv, u8 band)
-{
- u16 size = sizeof(struct chan_freq_power);
- u16 i = 0;
-
- memset(priv->universal_channel, 0,
- sizeof(priv->universal_channel));
-
- priv->universal_channel[i].nrcfp =
- sizeof(channel_freq_power_UN_BG) / size;
- lbs_deb_11d("BG-band nrcfp %d\n",
- priv->universal_channel[i].nrcfp);
-
- priv->universal_channel[i].CFP = channel_freq_power_UN_BG;
- priv->universal_channel[i].valid = 1;
- priv->universal_channel[i].region = UNIVERSAL_REGION_CODE;
- priv->universal_channel[i].band = band;
- i++;
-
- return 0;
-}
-
-/**
- * @brief This function implements command CMD_802_11D_DOMAIN_INFO
- * @param priv pointer to struct lbs_private
- * @param cmd pointer to cmd buffer
- * @param cmdno cmd ID
- * @param cmdOption cmd action
- * @return 0
-*/
-int lbs_cmd_802_11d_domain_info(struct lbs_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;
- u8 nr_subband = priv->domainreg.nr_subband;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- lbs_deb_11d("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_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd,
- le16_to_cpu(cmd->size));
- goto done;
- }
-
- domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
- memcpy(domain->countrycode, priv->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, priv->domainreg.subband,
- nr_subband * sizeof(struct ieeetypes_subbandset));
-
- cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
- le16_to_cpu(domain->header.len) +
- sizeof(struct mrvlietypesheader) +
- S_DS_GEN);
- } else {
- cmd->size =
- cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
- }
-
- lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, le16_to_cpu(cmd->size));
-
-done:
- lbs_deb_enter(LBS_DEB_11D);
- return 0;
-}
-
-/**
- * @brief This function parses countryinfo from AP and download country info to FW
- * @param priv pointer to struct lbs_private
- * @param resp pointer to command response buffer
- * @return 0; -1
- */
-int lbs_ret_802_11d_domain_info(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;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp,
- (int)le16_to_cpu(resp->size));
-
- nr_subband = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) /
- sizeof(struct ieeetypes_subbandset);
-
- lbs_deb_11d("domain info resp: nr_subband %d\n", nr_subband);
-
- if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) {
- lbs_deb_11d("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_deb_11d("Invalid action:%d\n", domaininfo->action);
- ret = -1;
- break;
- }
-
- lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
- return ret;
-}
-
-/**
- * @brief This function parses countryinfo from AP and download country info to FW
- * @param priv pointer to struct lbs_private
- * @return 0; -1
- */
-int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv,
- struct bss_descriptor * bss)
-{
- int ret;
-
- lbs_deb_enter(LBS_DEB_11D);
- if (priv->enable11d) {
- memset(&priv->parsed_region_chan, 0,
- sizeof(struct parsed_region_chan_11d));
- ret = parse_domain_info_11d(&bss->countryinfo, 0,
- &priv->parsed_region_chan);
-
- if (ret == -1) {
- lbs_deb_11d("error parsing domain_info from AP\n");
- goto done;
- }
-
- memset(&priv->domainreg, 0,
- sizeof(struct lbs_802_11d_domain_reg));
- generate_domain_info_11d(&priv->parsed_region_chan,
- &priv->domainreg);
-
- ret = set_domain_info_11d(priv);
-
- if (ret) {
- lbs_deb_11d("error setting domain info\n");
- goto done;
- }
- }
- ret = 0;
-
-done:
- lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
- return ret;
-}
-
-/**
- * @brief This function generates 11D info from user specified regioncode and download to FW
- * @param priv pointer to struct lbs_private
- * @return 0; -1
- */
-int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv)
-{
- int ret;
- struct region_channel *region_chan;
- u8 j;
-
- lbs_deb_enter(LBS_DEB_11D);
- lbs_deb_11d("curbssparams.band %d\n", priv->curbssparams.band);
-
- if (priv->enable11d) {
- /* update parsed_region_chan_11; dnld domaininf to FW */
-
- for (j = 0; j < ARRAY_SIZE(priv->region_channel); j++) {
- region_chan = &priv->region_channel[j];
-
- lbs_deb_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 != priv->curbssparams.band)
- continue;
- break;
- }
-
- if (j >= ARRAY_SIZE(priv->region_channel)) {
- lbs_deb_11d("region_chan not found, band %d\n",
- priv->curbssparams.band);
- ret = -1;
- goto done;
- }
-
- memset(&priv->parsed_region_chan, 0,
- sizeof(struct parsed_region_chan_11d));
- lbs_generate_parsed_region_chan_11d(region_chan,
- &priv->
- parsed_region_chan);
-
- memset(&priv->domainreg, 0,
- sizeof(struct lbs_802_11d_domain_reg));
- generate_domain_info_11d(&priv->parsed_region_chan,
- &priv->domainreg);
-
- ret = set_domain_info_11d(priv);
-
- if (ret) {
- lbs_deb_11d("error setting domain info\n");
- goto done;
- }
-
- }
- ret = 0;
-
-done:
- lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
- return ret;
-}
diff --git a/drivers/net/wireless/libertas/11d.h b/drivers/net/wireless/libertas/11d.h
deleted file mode 100644
index 4f4f47f0f87..00000000000
--- a/drivers/net/wireless/libertas/11d.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * This header file contains data structures and
- * function declarations of 802.11d
- */
-#ifndef _LBS_11D_
-#define _LBS_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 {
- __le16 action;
- struct mrvlietypes_domainparamset domain;
-} __attribute__ ((packed));
-
-/** domain regulatory information */
-struct lbs_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;
-};
-
-struct lbs_private;
-
-u8 lbs_get_scan_type_11d(u8 chan,
- struct parsed_region_chan_11d *parsed_region_chan);
-
-u32 lbs_chan_2_freq(u8 chan);
-
-void lbs_init_11d(struct lbs_private *priv);
-
-int lbs_set_universaltable(struct lbs_private *priv, u8 band);
-
-int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
- struct cmd_ds_command *cmd, u16 cmdno,
- u16 cmdOption);
-
-int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp);
-
-struct bss_descriptor;
-int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv,
- struct bss_descriptor * bss);
-
-int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv);
-
-#endif
diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/libertas/Kconfig
new file mode 100644
index 00000000000..0485c995757
--- /dev/null
+++ b/drivers/net/wireless/libertas/Kconfig
@@ -0,0 +1,45 @@
+config LIBERTAS
+ tristate "Marvell 8xxx Libertas WLAN driver support"
+ depends on CFG80211
+ select WIRELESS_EXT
+ select WEXT_SPY
+ select LIB80211
+ select FW_LOADER
+ ---help---
+ A library for Marvell Libertas 8xxx devices.
+
+config LIBERTAS_USB
+ tristate "Marvell Libertas 8388 USB 802.11b/g cards"
+ depends on LIBERTAS && USB
+ ---help---
+ A driver for Marvell Libertas 8388 USB devices.
+
+config LIBERTAS_CS
+ tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards"
+ depends on LIBERTAS && PCMCIA
+ ---help---
+ A driver for Marvell Libertas 8385 CompactFlash devices.
+
+config LIBERTAS_SDIO
+ tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards"
+ depends on LIBERTAS && MMC
+ ---help---
+ A driver for Marvell Libertas 8385/8686/8688 SDIO devices.
+
+config LIBERTAS_SPI
+ tristate "Marvell Libertas 8686 SPI 802.11b/g cards"
+ depends on LIBERTAS && SPI
+ ---help---
+ A driver for Marvell Libertas 8686 SPI devices.
+
+config LIBERTAS_DEBUG
+ bool "Enable full debugging output in the Libertas module."
+ depends on LIBERTAS
+ ---help---
+ Debugging support.
+
+config LIBERTAS_MESH
+ bool "Enable mesh support"
+ depends on LIBERTAS
+ help
+ This enables Libertas' MESH support, used by e.g. the OLPC people.
diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile
index 0b691858450..eac72f7bd34 100644
--- a/drivers/net/wireless/libertas/Makefile
+++ b/drivers/net/wireless/libertas/Makefile
@@ -1,5 +1,13 @@
-libertas-objs := main.o wext.o rx.o tx.o cmd.o cmdresp.o scan.o 11d.o \
- debugfs.o persistcfg.o ethtool.o assoc.o
+libertas-y += cfg.o
+libertas-y += cmd.o
+libertas-y += cmdresp.o
+libertas-y += debugfs.o
+libertas-y += ethtool.o
+libertas-y += main.o
+libertas-y += rx.o
+libertas-y += tx.o
+libertas-y += firmware.o
+libertas-$(CONFIG_LIBERTAS_MESH) += mesh.o
usb8xxx-objs += if_usb.o
libertas_cs-objs += if_cs.o
diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
index d860fc37575..1a554a685e9 100644
--- a/drivers/net/wireless/libertas/README
+++ b/drivers/net/wireless/libertas/README
@@ -1,5 +1,5 @@
================================================================================
- README for USB8388
+ README for Libertas
(c) Copyright © 2003-2006, Marvell International Ltd.
All Rights Reserved
@@ -8,9 +8,8 @@
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.
+ is available along with the File in the license.txt file 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
@@ -70,9 +69,9 @@ 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").
+ hexadecimal (its possible to precede preceding the number with a "0x").
- Path: /debugfs/libertas_wireless/ethX/registers/
+ Path: /sys/kernel/debug/libertas_wireless/ethX/registers/
Usage:
echo "0xa123" > rdmac ; cat rdmac
@@ -84,7 +83,7 @@ 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
+ be specified in hexadecimal (its possible to precede the number
with a "0x").
Usage:
@@ -95,7 +94,7 @@ wrrf
sleepparams
This command is used to set the sleepclock configurations
- Path: /debugfs/libertas_wireless/ethX/
+ Path: /sys/kernel/debug/libertas_wireless/ethX/
Usage:
cat sleepparams: reads the current sleepclock configuration
@@ -115,7 +114,7 @@ subscribed_events
The subscribed_events directory contains the interface for the
subscribed events API.
- Path: /debugfs/libertas_wireless/ethX/subscribed_events/
+ Path: /sys/kernel/debug/libertas_wireless/ethX/subscribed_events/
Each event is represented by a filename. Each filename consists of the
following three fields:
@@ -165,7 +164,7 @@ subscribed_events
extscan
This command is used to do a specific scan.
- Path: /debugfs/libertas_wireless/ethX/
+ Path: /sys/kernel/debug/libertas_wireless/ethX/
Usage: echo "SSID" > extscan
@@ -179,7 +178,7 @@ getscantable
Display the current contents of the driver scan table (ie. get the
scan results).
- Path: /debugfs/libertas_wireless/ethX/
+ Path: /sys/kernel/debug/libertas_wireless/ethX/
Usage:
cat getscantable
@@ -188,7 +187,7 @@ setuserscan
Initiate a customized scan and retrieve the results
- Path: /debugfs/libertas_wireless/ethX/
+ Path: /sys/kernel/debug/libertas_wireless/ethX/
Usage:
echo "[ARGS]" > setuserscan
@@ -226,4 +225,15 @@ 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.
-==============================================================================
+hostsleep
+ This command is used to enable/disable host sleep.
+ Note: Host sleep parameters should be configured using
+ "ethtool -s ethX wol X" command before enabling host sleep.
+
+ Path: /sys/kernel/debug/libertas_wireless/ethX/
+
+ Usage:
+ cat hostsleep: reads the current hostsleep state
+ echo "1" > hostsleep : enable host sleep.
+ echo "0" > hostsleep : disable host sleep
+
diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c
deleted file mode 100644
index a0e440cd896..00000000000
--- a/drivers/net/wireless/libertas/assoc.c
+++ /dev/null
@@ -1,1834 +0,0 @@
-/* Copyright (C) 2006, Red Hat, Inc. */
-
-#include <linux/types.h>
-#include <linux/etherdevice.h>
-#include <linux/ieee80211.h>
-#include <linux/if_arp.h>
-#include <net/lib80211.h>
-
-#include "assoc.h"
-#include "decl.h"
-#include "host.h"
-#include "scan.h"
-#include "cmd.h"
-
-static int lbs_adhoc_post(struct lbs_private *priv, struct cmd_header *resp);
-
-static const u8 bssid_any[ETH_ALEN] __attribute__ ((aligned (2))) =
- { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-static const u8 bssid_off[ETH_ALEN] __attribute__ ((aligned (2))) =
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-
-/* The firmware needs certain bits masked out of the beacon-derviced capability
- * field when associating/joining to BSSs.
- */
-#define CAPINFO_MASK (~(0xda00))
-
-
-/**
- * @brief This function finds common rates between rates and card rates.
- *
- * It will fill common rates in rates 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 priv A pointer to struct lbs_private structure
- * @param rates the buffer which keeps input and output
- * @param rates_size the size of rate1 buffer; new size of buffer on return
- *
- * @return 0 on success, or -1 on error
- */
-static int get_common_rates(struct lbs_private *priv,
- u8 *rates,
- u16 *rates_size)
-{
- u8 *card_rates = lbs_bg_rates;
- size_t num_card_rates = sizeof(lbs_bg_rates);
- int ret = 0, i, j;
- u8 tmp[30];
- size_t tmp_size = 0;
-
- /* For each rate in card_rates that exists in rate1, copy to tmp */
- for (i = 0; card_rates[i] && (i < num_card_rates); i++) {
- for (j = 0; rates[j] && (j < *rates_size); j++) {
- if (rates[j] == card_rates[i])
- tmp[tmp_size++] = card_rates[i];
- }
- }
-
- lbs_deb_hex(LBS_DEB_JOIN, "AP rates ", rates, *rates_size);
- lbs_deb_hex(LBS_DEB_JOIN, "card rates ", card_rates, num_card_rates);
- lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size);
- lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate);
-
- if (!priv->enablehwauto) {
- for (i = 0; i < tmp_size; i++) {
- if (tmp[i] == priv->cur_rate)
- goto done;
- }
- lbs_pr_alert("Previously set fixed data rate %#x isn't "
- "compatible with the network.\n", priv->cur_rate);
- ret = -1;
- goto done;
- }
- ret = 0;
-
-done:
- memset(rates, 0, *rates_size);
- *rates_size = min_t(int, tmp_size, *rates_size);
- memcpy(rates, tmp, *rates_size);
- return ret;
-}
-
-
-/**
- * @brief Sets the MSB on basic rates as the firmware requires
- *
- * Scan through an array and set the MSB for basic data rates.
- *
- * @param rates buffer of data rates
- * @param len size of buffer
- */
-static void lbs_set_basic_rate_flags(u8 *rates, size_t len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- if (rates[i] == 0x02 || rates[i] == 0x04 ||
- rates[i] == 0x0b || rates[i] == 0x16)
- rates[i] |= 0x80;
- }
-}
-
-
-/**
- * @brief Associate to a specific BSS discovered in a scan
- *
- * @param priv A pointer to struct lbs_private structure
- * @param assoc_req The association request describing the BSS to associate with
- *
- * @return 0-success, otherwise fail
- */
-static int lbs_associate(struct lbs_private *priv,
- struct assoc_request *assoc_req)
-{
- int ret;
- u8 preamble = RADIO_PREAMBLE_LONG;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- ret = lbs_prepare_and_send_command(priv, CMD_802_11_AUTHENTICATE,
- 0, CMD_OPTION_WAITFORRSP,
- 0, assoc_req->bss.bssid);
- if (ret)
- goto out;
-
- /* Use short preamble only when both the BSS and firmware support it */
- if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
- (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE))
- preamble = RADIO_PREAMBLE_SHORT;
-
- ret = lbs_set_radio(priv, preamble, 1);
- if (ret)
- goto out;
-
- ret = lbs_prepare_and_send_command(priv, CMD_802_11_ASSOCIATE,
- 0, CMD_OPTION_WAITFORRSP, 0, assoc_req);
-
-out:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-/**
- * @brief Join an adhoc network found in a previous scan
- *
- * @param priv A pointer to struct lbs_private structure
- * @param assoc_req The association request describing the BSS to join
- *
- * @return 0 on success, error on failure
- */
-static int lbs_adhoc_join(struct lbs_private *priv,
- struct assoc_request *assoc_req)
-{
- struct cmd_ds_802_11_ad_hoc_join cmd;
- struct bss_descriptor *bss = &assoc_req->bss;
- u8 preamble = RADIO_PREAMBLE_LONG;
- DECLARE_SSID_BUF(ssid);
- u16 ratesize = 0;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- lbs_deb_join("current SSID '%s', ssid length %u\n",
- print_ssid(ssid, priv->curbssparams.ssid,
- priv->curbssparams.ssid_len),
- priv->curbssparams.ssid_len);
- lbs_deb_join("requested ssid '%s', ssid length %u\n",
- print_ssid(ssid, bss->ssid, bss->ssid_len),
- bss->ssid_len);
-
- /* check if the requested SSID is already joined */
- if (priv->curbssparams.ssid_len &&
- !lbs_ssid_cmp(priv->curbssparams.ssid,
- priv->curbssparams.ssid_len,
- bss->ssid, bss->ssid_len) &&
- (priv->mode == IW_MODE_ADHOC) &&
- (priv->connect_status == LBS_CONNECTED)) {
- union iwreq_data wrqu;
-
- lbs_deb_join("ADHOC_J_CMD: New ad-hoc SSID is the same as "
- "current, not attempting to re-join");
-
- /* Send the re-association event though, because the association
- * request really was successful, even if just a null-op.
- */
- memset(&wrqu, 0, sizeof(wrqu));
- memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid,
- ETH_ALEN);
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
- goto out;
- }
-
- /* Use short preamble only when both the BSS and firmware support it */
- if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
- (bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) {
- lbs_deb_join("AdhocJoin: Short preamble\n");
- preamble = RADIO_PREAMBLE_SHORT;
- }
-
- ret = lbs_set_radio(priv, preamble, 1);
- if (ret)
- goto out;
-
- lbs_deb_join("AdhocJoin: channel = %d\n", assoc_req->channel);
- lbs_deb_join("AdhocJoin: band = %c\n", assoc_req->band);
-
- priv->adhoccreate = 0;
- priv->curbssparams.channel = bss->channel;
-
- /* Build the join command */
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- cmd.bss.type = CMD_BSS_TYPE_IBSS;
- cmd.bss.beaconperiod = cpu_to_le16(bss->beaconperiod);
-
- memcpy(&cmd.bss.bssid, &bss->bssid, ETH_ALEN);
- memcpy(&cmd.bss.ssid, &bss->ssid, bss->ssid_len);
-
- memcpy(&cmd.bss.phyparamset, &bss->phyparamset,
- sizeof(union ieeetypes_phyparamset));
-
- memcpy(&cmd.bss.ssparamset, &bss->ssparamset,
- sizeof(union IEEEtypes_ssparamset));
-
- cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK);
- lbs_deb_join("ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n",
- bss->capability, CAPINFO_MASK);
-
- /* information on BSSID descriptor passed to FW */
- lbs_deb_join("ADHOC_J_CMD: BSSID = %pM, SSID = '%s'\n",
- cmd.bss.bssid, cmd.bss.ssid);
-
- /* Only v8 and below support setting these */
- if (priv->fwrelease < 0x09000000) {
- /* failtimeout */
- cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
- /* probedelay */
- cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
- }
-
- /* Copy Data rates from the rates recorded in scan response */
- memset(cmd.bss.rates, 0, sizeof(cmd.bss.rates));
- ratesize = min_t(u16, sizeof(cmd.bss.rates), MAX_RATES);
- memcpy(cmd.bss.rates, bss->rates, ratesize);
- if (get_common_rates(priv, cmd.bss.rates, &ratesize)) {
- lbs_deb_join("ADHOC_JOIN: get_common_rates returned error.\n");
- ret = -1;
- goto out;
- }
-
- /* Copy the ad-hoc creation rates into Current BSS state structure */
- memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
- memcpy(&priv->curbssparams.rates, cmd.bss.rates, ratesize);
-
- /* Set MSB on basic rates as the firmware requires, but _after_
- * copying to current bss rates.
- */
- lbs_set_basic_rate_flags(cmd.bss.rates, ratesize);
-
- cmd.bss.ssparamset.ibssparamset.atimwindow = cpu_to_le16(bss->atimwindow);
-
- if (assoc_req->secinfo.wep_enabled) {
- u16 tmp = le16_to_cpu(cmd.bss.capability);
- tmp |= WLAN_CAPABILITY_PRIVACY;
- cmd.bss.capability = cpu_to_le16(tmp);
- }
-
- if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
- __le32 local_ps_mode = cpu_to_le32(LBS802_11POWERMODECAM);
-
- /* wake up first */
- ret = lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
- CMD_ACT_SET, 0, 0,
- &local_ps_mode);
- if (ret) {
- ret = -1;
- goto out;
- }
- }
-
- if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
- ret = -1;
- goto out;
- }
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd);
- if (ret == 0)
- ret = lbs_adhoc_post(priv, (struct cmd_header *) &cmd);
-
-out:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-/**
- * @brief Start an Adhoc Network
- *
- * @param priv A pointer to struct lbs_private structure
- * @param assoc_req The association request describing the BSS to start
- *
- * @return 0 on success, error on failure
- */
-static int lbs_adhoc_start(struct lbs_private *priv,
- struct assoc_request *assoc_req)
-{
- struct cmd_ds_802_11_ad_hoc_start cmd;
- u8 preamble = RADIO_PREAMBLE_LONG;
- size_t ratesize = 0;
- u16 tmpcap = 0;
- int ret = 0;
- DECLARE_SSID_BUF(ssid);
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- if (priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) {
- lbs_deb_join("ADHOC_START: Will use short preamble\n");
- preamble = RADIO_PREAMBLE_SHORT;
- }
-
- ret = lbs_set_radio(priv, preamble, 1);
- if (ret)
- goto out;
-
- /* Build the start command */
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- memcpy(cmd.ssid, assoc_req->ssid, assoc_req->ssid_len);
-
- lbs_deb_join("ADHOC_START: SSID '%s', ssid length %u\n",
- print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len),
- assoc_req->ssid_len);
-
- cmd.bsstype = CMD_BSS_TYPE_IBSS;
-
- if (priv->beacon_period == 0)
- priv->beacon_period = MRVDRV_BEACON_INTERVAL;
- cmd.beaconperiod = cpu_to_le16(priv->beacon_period);
-
- WARN_ON(!assoc_req->channel);
-
- /* set Physical parameter set */
- cmd.phyparamset.dsparamset.elementid = WLAN_EID_DS_PARAMS;
- cmd.phyparamset.dsparamset.len = 1;
- cmd.phyparamset.dsparamset.currentchan = assoc_req->channel;
-
- /* set IBSS parameter set */
- cmd.ssparamset.ibssparamset.elementid = WLAN_EID_IBSS_PARAMS;
- cmd.ssparamset.ibssparamset.len = 2;
- cmd.ssparamset.ibssparamset.atimwindow = 0;
-
- /* set capability info */
- tmpcap = WLAN_CAPABILITY_IBSS;
- if (assoc_req->secinfo.wep_enabled) {
- lbs_deb_join("ADHOC_START: WEP enabled, setting privacy on\n");
- tmpcap |= WLAN_CAPABILITY_PRIVACY;
- } else
- lbs_deb_join("ADHOC_START: WEP disabled, setting privacy off\n");
-
- cmd.capability = cpu_to_le16(tmpcap);
-
- /* Only v8 and below support setting probe delay */
- if (priv->fwrelease < 0x09000000)
- cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
-
- ratesize = min(sizeof(cmd.rates), sizeof(lbs_bg_rates));
- memcpy(cmd.rates, lbs_bg_rates, ratesize);
-
- /* Copy the ad-hoc creating rates into Current BSS state structure */
- memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
- memcpy(&priv->curbssparams.rates, &cmd.rates, ratesize);
-
- /* Set MSB on basic rates as the firmware requires, but _after_
- * copying to current bss rates.
- */
- lbs_set_basic_rate_flags(cmd.rates, ratesize);
-
- lbs_deb_join("ADHOC_START: rates=%02x %02x %02x %02x\n",
- cmd.rates[0], cmd.rates[1], cmd.rates[2], cmd.rates[3]);
-
- if (lbs_create_dnld_countryinfo_11d(priv)) {
- lbs_deb_join("ADHOC_START: dnld_countryinfo_11d failed\n");
- ret = -1;
- goto out;
- }
-
- lbs_deb_join("ADHOC_START: Starting Ad-Hoc BSS on channel %d, band %d\n",
- assoc_req->channel, assoc_req->band);
-
- priv->adhoccreate = 1;
- priv->mode = IW_MODE_ADHOC;
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd);
- if (ret == 0)
- ret = lbs_adhoc_post(priv, (struct cmd_header *) &cmd);
-
-out:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-/**
- * @brief Stop and Ad-Hoc network and exit Ad-Hoc mode
- *
- * @param priv A pointer to struct lbs_private structure
- * @return 0 on success, or an error
- */
-int lbs_adhoc_stop(struct lbs_private *priv)
-{
- struct cmd_ds_802_11_ad_hoc_stop cmd;
- int ret;
-
- lbs_deb_enter(LBS_DEB_JOIN);
-
- memset(&cmd, 0, sizeof (cmd));
- cmd.hdr.size = cpu_to_le16 (sizeof (cmd));
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd);
-
- /* Clean up everything even if there was an error */
- lbs_mac_event_disconnected(priv);
-
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-static inline int match_bss_no_security(struct lbs_802_11_security *secinfo,
- struct bss_descriptor *match_bss)
-{
- if (!secinfo->wep_enabled && !secinfo->WPAenabled
- && !secinfo->WPA2enabled
- && match_bss->wpa_ie[0] != WLAN_EID_GENERIC
- && match_bss->rsn_ie[0] != WLAN_EID_RSN
- && !(match_bss->capability & WLAN_CAPABILITY_PRIVACY))
- return 1;
- else
- return 0;
-}
-
-static inline int match_bss_static_wep(struct lbs_802_11_security *secinfo,
- struct bss_descriptor *match_bss)
-{
- if (secinfo->wep_enabled && !secinfo->WPAenabled
- && !secinfo->WPA2enabled
- && (match_bss->capability & WLAN_CAPABILITY_PRIVACY))
- return 1;
- else
- return 0;
-}
-
-static inline int match_bss_wpa(struct lbs_802_11_security *secinfo,
- struct bss_descriptor *match_bss)
-{
- if (!secinfo->wep_enabled && secinfo->WPAenabled
- && (match_bss->wpa_ie[0] == WLAN_EID_GENERIC)
- /* privacy bit may NOT be set in some APs like LinkSys WRT54G
- && (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */
- )
- return 1;
- else
- return 0;
-}
-
-static inline int match_bss_wpa2(struct lbs_802_11_security *secinfo,
- struct bss_descriptor *match_bss)
-{
- if (!secinfo->wep_enabled && secinfo->WPA2enabled &&
- (match_bss->rsn_ie[0] == WLAN_EID_RSN)
- /* privacy bit may NOT be set in some APs like LinkSys WRT54G
- (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */
- )
- return 1;
- else
- return 0;
-}
-
-static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo,
- struct bss_descriptor *match_bss)
-{
- if (!secinfo->wep_enabled && !secinfo->WPAenabled
- && !secinfo->WPA2enabled
- && (match_bss->wpa_ie[0] != WLAN_EID_GENERIC)
- && (match_bss->rsn_ie[0] != WLAN_EID_RSN)
- && (match_bss->capability & WLAN_CAPABILITY_PRIVACY))
- return 1;
- else
- return 0;
-}
-
-/**
- * @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 priv A pointer to struct lbs_private
- * @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(struct lbs_private *priv,
- struct bss_descriptor *bss, uint8_t mode)
-{
- int matched = 0;
-
- lbs_deb_enter(LBS_DEB_SCAN);
-
- if (bss->mode != mode)
- goto done;
-
- matched = match_bss_no_security(&priv->secinfo, bss);
- if (matched)
- goto done;
- matched = match_bss_static_wep(&priv->secinfo, bss);
- if (matched)
- goto done;
- matched = match_bss_wpa(&priv->secinfo, bss);
- if (matched) {
- lbs_deb_scan("is_network_compatible() WPA: wpa_ie 0x%x "
- "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s "
- "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0],
- priv->secinfo.wep_enabled ? "e" : "d",
- priv->secinfo.WPAenabled ? "e" : "d",
- priv->secinfo.WPA2enabled ? "e" : "d",
- (bss->capability & WLAN_CAPABILITY_PRIVACY));
- goto done;
- }
- matched = match_bss_wpa2(&priv->secinfo, bss);
- if (matched) {
- lbs_deb_scan("is_network_compatible() WPA2: wpa_ie 0x%x "
- "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s "
- "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0],
- priv->secinfo.wep_enabled ? "e" : "d",
- priv->secinfo.WPAenabled ? "e" : "d",
- priv->secinfo.WPA2enabled ? "e" : "d",
- (bss->capability & WLAN_CAPABILITY_PRIVACY));
- goto done;
- }
- matched = match_bss_dynamic_wep(&priv->secinfo, bss);
- if (matched) {
- lbs_deb_scan("is_network_compatible() dynamic WEP: "
- "wpa_ie 0x%x wpa2_ie 0x%x privacy 0x%x\n",
- bss->wpa_ie[0], bss->rsn_ie[0],
- (bss->capability & WLAN_CAPABILITY_PRIVACY));
- goto done;
- }
-
- /* bss security settings don't match those configured on card */
- lbs_deb_scan("is_network_compatible() FAILED: wpa_ie 0x%x "
- "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s privacy 0x%x\n",
- bss->wpa_ie[0], bss->rsn_ie[0],
- priv->secinfo.wep_enabled ? "e" : "d",
- priv->secinfo.WPAenabled ? "e" : "d",
- priv->secinfo.WPA2enabled ? "e" : "d",
- (bss->capability & WLAN_CAPABILITY_PRIVACY));
-
-done:
- lbs_deb_leave_args(LBS_DEB_SCAN, "matched: %d", matched);
- return matched;
-}
-
-/**
- * @brief This function finds a specific compatible BSSID in the scan list
- *
- * Used in association code
- *
- * @param priv A pointer to struct lbs_private
- * @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)
- */
-static struct bss_descriptor *lbs_find_bssid_in_list(struct lbs_private *priv,
- uint8_t *bssid, uint8_t mode)
-{
- struct bss_descriptor *iter_bss;
- struct bss_descriptor *found_bss = NULL;
-
- lbs_deb_enter(LBS_DEB_SCAN);
-
- if (!bssid)
- goto out;
-
- lbs_deb_hex(LBS_DEB_SCAN, "looking for", bssid, ETH_ALEN);
-
- /* Look through the scan table for a compatible match. 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
- */
- mutex_lock(&priv->lock);
- list_for_each_entry(iter_bss, &priv->network_list, list) {
- if (compare_ether_addr(iter_bss->bssid, bssid))
- continue; /* bssid doesn't match */
- switch (mode) {
- case IW_MODE_INFRA:
- case IW_MODE_ADHOC:
- if (!is_network_compatible(priv, iter_bss, mode))
- break;
- found_bss = iter_bss;
- break;
- default:
- found_bss = iter_bss;
- break;
- }
- }
- mutex_unlock(&priv->lock);
-
-out:
- lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss);
- return found_bss;
-}
-
-/**
- * @brief This function finds ssid in ssid list.
- *
- * Used in association code
- *
- * @param priv A pointer to struct lbs_private
- * @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
- */
-static struct bss_descriptor *lbs_find_ssid_in_list(struct lbs_private *priv,
- uint8_t *ssid, uint8_t ssid_len,
- uint8_t *bssid, uint8_t mode,
- int channel)
-{
- u32 bestrssi = 0;
- struct bss_descriptor *iter_bss = NULL;
- struct bss_descriptor *found_bss = NULL;
- struct bss_descriptor *tmp_oldest = NULL;
-
- lbs_deb_enter(LBS_DEB_SCAN);
-
- mutex_lock(&priv->lock);
-
- list_for_each_entry(iter_bss, &priv->network_list, list) {
- if (!tmp_oldest ||
- (iter_bss->last_scanned < tmp_oldest->last_scanned))
- tmp_oldest = iter_bss;
-
- if (lbs_ssid_cmp(iter_bss->ssid, iter_bss->ssid_len,
- ssid, ssid_len) != 0)
- continue; /* ssid doesn't match */
- if (bssid && compare_ether_addr(iter_bss->bssid, bssid) != 0)
- continue; /* bssid doesn't match */
- if ((channel > 0) && (iter_bss->channel != channel))
- continue; /* channel doesn't match */
-
- switch (mode) {
- case IW_MODE_INFRA:
- case IW_MODE_ADHOC:
- if (!is_network_compatible(priv, iter_bss, mode))
- break;
-
- if (bssid) {
- /* Found requested BSSID */
- found_bss = iter_bss;
- goto out;
- }
-
- if (SCAN_RSSI(iter_bss->rssi) > bestrssi) {
- bestrssi = SCAN_RSSI(iter_bss->rssi);
- found_bss = iter_bss;
- }
- break;
- case IW_MODE_AUTO:
- default:
- if (SCAN_RSSI(iter_bss->rssi) > bestrssi) {
- bestrssi = SCAN_RSSI(iter_bss->rssi);
- found_bss = iter_bss;
- }
- break;
- }
- }
-
-out:
- mutex_unlock(&priv->lock);
- lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss);
- return found_bss;
-}
-
-static int assoc_helper_essid(struct lbs_private *priv,
- struct assoc_request * assoc_req)
-{
- int ret = 0;
- struct bss_descriptor * bss;
- int channel = -1;
- DECLARE_SSID_BUF(ssid);
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- /* FIXME: take channel into account when picking SSIDs if a channel
- * is set.
- */
-
- if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
- channel = assoc_req->channel;
-
- lbs_deb_assoc("SSID '%s' requested\n",
- print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len));
- if (assoc_req->mode == IW_MODE_INFRA) {
- lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
- assoc_req->ssid_len);
-
- bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
- assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel);
- if (bss != NULL) {
- memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
- ret = lbs_associate(priv, assoc_req);
- } else {
- lbs_deb_assoc("SSID not found; cannot associate\n");
- }
- } 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
- */
- lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
- assoc_req->ssid_len);
-
- /* Search for the requested SSID in the scan table */
- bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
- assoc_req->ssid_len, NULL, IW_MODE_ADHOC, channel);
- if (bss != NULL) {
- lbs_deb_assoc("SSID found, will join\n");
- memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
- lbs_adhoc_join(priv, assoc_req);
- } else {
- /* else send START command */
- lbs_deb_assoc("SSID not found, creating adhoc network\n");
- memcpy(&assoc_req->bss.ssid, &assoc_req->ssid,
- IW_ESSID_MAX_SIZE);
- assoc_req->bss.ssid_len = assoc_req->ssid_len;
- lbs_adhoc_start(priv, assoc_req);
- }
- }
-
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-
-static int assoc_helper_bssid(struct lbs_private *priv,
- struct assoc_request * assoc_req)
-{
- int ret = 0;
- struct bss_descriptor * bss;
-
- lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %pM", assoc_req->bssid);
-
- /* Search for index position in list for requested MAC */
- bss = lbs_find_bssid_in_list(priv, assoc_req->bssid,
- assoc_req->mode);
- if (bss == NULL) {
- lbs_deb_assoc("ASSOC: WAP: BSSID %pM not found, "
- "cannot associate.\n", assoc_req->bssid);
- goto out;
- }
-
- memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
- if (assoc_req->mode == IW_MODE_INFRA) {
- ret = lbs_associate(priv, assoc_req);
- lbs_deb_assoc("ASSOC: lbs_associate(bssid) returned %d\n", ret);
- } else if (assoc_req->mode == IW_MODE_ADHOC) {
- lbs_adhoc_join(priv, assoc_req);
- }
-
-out:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-
-static int assoc_helper_associate(struct lbs_private *priv,
- struct assoc_request * assoc_req)
-{
- int ret = 0, done = 0;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- /* If we're given and 'any' BSSID, try associating based on SSID */
-
- if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
- if (compare_ether_addr(bssid_any, assoc_req->bssid)
- && compare_ether_addr(bssid_off, assoc_req->bssid)) {
- ret = assoc_helper_bssid(priv, assoc_req);
- done = 1;
- }
- }
-
- if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
- ret = assoc_helper_essid(priv, assoc_req);
- }
-
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-
-static int assoc_helper_mode(struct lbs_private *priv,
- struct assoc_request * assoc_req)
-{
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- if (assoc_req->mode == priv->mode)
- goto done;
-
- if (assoc_req->mode == IW_MODE_INFRA) {
- if (priv->psstate != PS_STATE_FULL_POWER)
- lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
- priv->psmode = LBS802_11POWERMODECAM;
- }
-
- priv->mode = assoc_req->mode;
- ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, assoc_req->mode);
-
-done:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-static int assoc_helper_channel(struct lbs_private *priv,
- struct assoc_request * assoc_req)
-{
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- ret = lbs_update_channel(priv);
- if (ret) {
- lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
- goto done;
- }
-
- if (assoc_req->channel == priv->curbssparams.channel)
- goto done;
-
- if (priv->mesh_dev) {
- /* Change mesh channel first; 21.p21 firmware won't let
- you change channel otherwise (even though it'll return
- an error to this */
- lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
- assoc_req->channel);
- }
-
- lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
- priv->curbssparams.channel, assoc_req->channel);
-
- ret = lbs_set_channel(priv, assoc_req->channel);
- if (ret < 0)
- lbs_deb_assoc("ASSOC: channel: error setting channel.\n");
-
- /* FIXME: shouldn't need to grab the channel _again_ after setting
- * it since the firmware is supposed to return the new channel, but
- * whatever... */
- ret = lbs_update_channel(priv);
- if (ret) {
- lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
- goto done;
- }
-
- if (assoc_req->channel != priv->curbssparams.channel) {
- lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n",
- assoc_req->channel);
- goto restore_mesh;
- }
-
- if ( assoc_req->secinfo.wep_enabled
- && (assoc_req->wep_keys[0].len
- || assoc_req->wep_keys[1].len
- || assoc_req->wep_keys[2].len
- || assoc_req->wep_keys[3].len)) {
- /* Make sure WEP keys are re-sent to firmware */
- set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
- }
-
- /* Must restart/rejoin adhoc networks after channel change */
- set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
-
- restore_mesh:
- if (priv->mesh_dev)
- lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->curbssparams.channel);
-
- done:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-
-static int assoc_helper_wep_keys(struct lbs_private *priv,
- struct assoc_request *assoc_req)
-{
- int i;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- /* 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 = lbs_cmd_802_11_set_wep(priv, CMD_ACT_ADD, assoc_req);
- else
- ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_REMOVE, assoc_req);
-
- if (ret)
- goto out;
-
- /* enable/disable the MAC's WEP packet filter */
- if (assoc_req->secinfo.wep_enabled)
- priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE;
- else
- priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE;
-
- lbs_set_mac_control(priv);
-
- mutex_lock(&priv->lock);
-
- /* Copy WEP keys into priv wep key fields */
- for (i = 0; i < 4; i++) {
- memcpy(&priv->wep_keys[i], &assoc_req->wep_keys[i],
- sizeof(struct enc_key));
- }
- priv->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
-
- mutex_unlock(&priv->lock);
-
-out:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-static int assoc_helper_secinfo(struct lbs_private *priv,
- struct assoc_request * assoc_req)
-{
- int ret = 0;
- uint16_t do_wpa;
- uint16_t rsn = 0;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- memcpy(&priv->secinfo, &assoc_req->secinfo,
- sizeof(struct lbs_802_11_security));
-
- lbs_set_mac_control(priv);
-
- /* If RSN is already enabled, don't try to enable it again, since
- * ENABLE_RSN resets internal state machines and will clobber the
- * 4-way WPA handshake.
- */
-
- /* Get RSN enabled/disabled */
- ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_GET, &rsn);
- if (ret) {
- lbs_deb_assoc("Failed to get RSN status: %d\n", ret);
- goto out;
- }
-
- /* Don't re-enable RSN if it's already enabled */
- do_wpa = assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled;
- if (do_wpa == rsn)
- goto out;
-
- /* Set RSN enabled/disabled */
- ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_SET, &do_wpa);
-
-out:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-
-static int assoc_helper_wpa_keys(struct lbs_private *priv,
- struct assoc_request * assoc_req)
-{
- int ret = 0;
- unsigned int flags = assoc_req->flags;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- /* Work around older firmware bug where WPA unicast and multicast
- * keys must be set independently. Seen in SDIO parts with firmware
- * version 5.0.11p0.
- */
-
- if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
- clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
- ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
- assoc_req->flags = flags;
- }
-
- if (ret)
- goto out;
-
- if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
- clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
-
- ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
- assoc_req->flags = flags;
- }
-
-out:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-
-static int assoc_helper_wpa_ie(struct lbs_private *priv,
- struct assoc_request * assoc_req)
-{
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
- memcpy(&priv->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
- priv->wpa_ie_len = assoc_req->wpa_ie_len;
- } else {
- memset(&priv->wpa_ie, 0, MAX_WPA_IE_LEN);
- priv->wpa_ie_len = 0;
- }
-
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-
-static int should_deauth_infrastructure(struct lbs_private *priv,
- struct assoc_request * assoc_req)
-{
- int ret = 0;
-
- if (priv->connect_status != LBS_CONNECTED)
- return 0;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
- if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
- lbs_deb_assoc("Deauthenticating due to new SSID\n");
- ret = 1;
- goto out;
- }
-
- if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
- if (priv->secinfo.auth_mode != assoc_req->secinfo.auth_mode) {
- lbs_deb_assoc("Deauthenticating due to new security\n");
- ret = 1;
- goto out;
- }
- }
-
- if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
- lbs_deb_assoc("Deauthenticating due to new BSSID\n");
- ret = 1;
- goto out;
- }
-
- if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
- lbs_deb_assoc("Deauthenticating due to channel switch\n");
- ret = 1;
- goto out;
- }
-
- /* FIXME: deal with 'auto' mode somehow */
- if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
- if (assoc_req->mode != IW_MODE_INFRA) {
- lbs_deb_assoc("Deauthenticating due to leaving "
- "infra mode\n");
- ret = 1;
- goto out;
- }
- }
-
-out:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-
-static int should_stop_adhoc(struct lbs_private *priv,
- struct assoc_request * assoc_req)
-{
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- if (priv->connect_status != LBS_CONNECTED)
- return 0;
-
- if (lbs_ssid_cmp(priv->curbssparams.ssid,
- priv->curbssparams.ssid_len,
- assoc_req->ssid, assoc_req->ssid_len) != 0)
- 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;
- }
-
- if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
- if (assoc_req->channel != priv->curbssparams.channel)
- return 1;
- }
-
- lbs_deb_leave(LBS_DEB_ASSOC);
- return 0;
-}
-
-
-/**
- * @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 priv A pointer to struct lbs_private
- *
- * @return index in BSSID list
- */
-static struct bss_descriptor *lbs_find_best_ssid_in_list(
- struct lbs_private *priv, uint8_t mode)
-{
- uint8_t bestrssi = 0;
- struct bss_descriptor *iter_bss;
- struct bss_descriptor *best_bss = NULL;
-
- lbs_deb_enter(LBS_DEB_SCAN);
-
- mutex_lock(&priv->lock);
-
- list_for_each_entry(iter_bss, &priv->network_list, list) {
- switch (mode) {
- case IW_MODE_INFRA:
- case IW_MODE_ADHOC:
- if (!is_network_compatible(priv, iter_bss, mode))
- break;
- if (SCAN_RSSI(iter_bss->rssi) <= bestrssi)
- break;
- bestrssi = SCAN_RSSI(iter_bss->rssi);
- best_bss = iter_bss;
- break;
- case IW_MODE_AUTO:
- default:
- if (SCAN_RSSI(iter_bss->rssi) <= bestrssi)
- break;
- bestrssi = SCAN_RSSI(iter_bss->rssi);
- best_bss = iter_bss;
- break;
- }
- }
-
- mutex_unlock(&priv->lock);
- lbs_deb_leave_args(LBS_DEB_SCAN, "best_bss %p", best_bss);
- return best_bss;
-}
-
-/**
- * @brief Find the best AP
- *
- * Used from association worker.
- *
- * @param priv A pointer to struct lbs_private structure
- * @param pSSID A pointer to AP's ssid
- *
- * @return 0--success, otherwise--fail
- */
-static int lbs_find_best_network_ssid(struct lbs_private *priv,
- uint8_t *out_ssid, uint8_t *out_ssid_len, uint8_t preferred_mode,
- uint8_t *out_mode)
-{
- int ret = -1;
- struct bss_descriptor *found;
-
- lbs_deb_enter(LBS_DEB_SCAN);
-
- priv->scan_ssid_len = 0;
- lbs_scan_networks(priv, 1);
- if (priv->surpriseremoved)
- goto out;
-
- found = lbs_find_best_ssid_in_list(priv, preferred_mode);
- if (found && (found->ssid_len > 0)) {
- memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE);
- *out_ssid_len = found->ssid_len;
- *out_mode = found->mode;
- ret = 0;
- }
-
-out:
- lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
- return ret;
-}
-
-
-void lbs_association_worker(struct work_struct *work)
-{
- struct lbs_private *priv = container_of(work, struct lbs_private,
- assoc_work.work);
- struct assoc_request * assoc_req = NULL;
- int ret = 0;
- int find_any_ssid = 0;
- DECLARE_SSID_BUF(ssid);
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- mutex_lock(&priv->lock);
- assoc_req = priv->pending_assoc_req;
- priv->pending_assoc_req = NULL;
- priv->in_progress_assoc_req = assoc_req;
- mutex_unlock(&priv->lock);
-
- if (!assoc_req)
- goto done;
-
- lbs_deb_assoc(
- "Association Request:\n"
- " flags: 0x%08lx\n"
- " SSID: '%s'\n"
- " chann: %d\n"
- " band: %d\n"
- " mode: %d\n"
- " BSSID: %pM\n"
- " secinfo: %s%s%s\n"
- " auth_mode: %d\n",
- assoc_req->flags,
- print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len),
- assoc_req->channel, assoc_req->band, assoc_req->mode,
- assoc_req->bssid,
- assoc_req->secinfo.WPAenabled ? " WPA" : "",
- assoc_req->secinfo.WPA2enabled ? " WPA2" : "",
- assoc_req->secinfo.wep_enabled ? " WEP" : "",
- assoc_req->secinfo.auth_mode);
-
- /* If 'any' SSID was specified, find an SSID to associate with */
- if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
- && !assoc_req->ssid_len)
- 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 (compare_ether_addr(assoc_req->bssid, bssid_any)
- && compare_ether_addr(assoc_req->bssid, bssid_off))
- find_any_ssid = 0;
- }
-
- if (find_any_ssid) {
- u8 new_mode = assoc_req->mode;
-
- ret = lbs_find_best_network_ssid(priv, assoc_req->ssid,
- &assoc_req->ssid_len, assoc_req->mode, &new_mode);
- if (ret) {
- lbs_deb_assoc("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 (priv->mode == IW_MODE_INFRA) {
- if (should_deauth_infrastructure(priv, assoc_req)) {
- ret = lbs_cmd_80211_deauthenticate(priv,
- priv->curbssparams.bssid,
- WLAN_REASON_DEAUTH_LEAVING);
- if (ret) {
- lbs_deb_assoc("Deauthentication due to new "
- "configuration request failed: %d\n",
- ret);
- }
- }
- } else if (priv->mode == IW_MODE_ADHOC) {
- if (should_stop_adhoc(priv, assoc_req)) {
- ret = lbs_adhoc_stop(priv);
- if (ret) {
- lbs_deb_assoc("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)
- goto out;
- }
-
- if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
- ret = assoc_helper_channel(priv, assoc_req);
- if (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)
- goto out;
- }
-
- if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
- ret = assoc_helper_secinfo(priv, assoc_req);
- if (ret)
- goto out;
- }
-
- if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
- ret = assoc_helper_wpa_ie(priv, assoc_req);
- if (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)
- 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_deb_assoc("ASSOC: association unsuccessful: %d\n",
- ret);
- success = 0;
- }
-
- if (priv->connect_status != LBS_CONNECTED) {
- lbs_deb_assoc("ASSOC: association unsuccessful, "
- "not connected\n");
- success = 0;
- }
-
- if (success) {
- lbs_deb_assoc("associated to %pM\n",
- priv->curbssparams.bssid);
- lbs_prepare_and_send_command(priv,
- CMD_802_11_RSSI,
- 0, CMD_OPTION_WAITFORRSP, 0, NULL);
- } else {
- ret = -1;
- }
- }
-
-out:
- if (ret) {
- lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n",
- ret);
- }
-
- mutex_lock(&priv->lock);
- priv->in_progress_assoc_req = NULL;
- mutex_unlock(&priv->lock);
- kfree(assoc_req);
-
-done:
- lbs_deb_leave(LBS_DEB_ASSOC);
-}
-
-
-/*
- * Caller MUST hold any necessary locks
- */
-struct assoc_request *lbs_get_association_request(struct lbs_private *priv)
-{
- struct assoc_request * assoc_req;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
- if (!priv->pending_assoc_req) {
- priv->pending_assoc_req = kzalloc(sizeof(struct assoc_request),
- GFP_KERNEL);
- if (!priv->pending_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 = priv->pending_assoc_req;
- if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
- memcpy(&assoc_req->ssid, &priv->curbssparams.ssid,
- IW_ESSID_MAX_SIZE);
- assoc_req->ssid_len = priv->curbssparams.ssid_len;
- }
-
- if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
- assoc_req->channel = priv->curbssparams.channel;
-
- if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags))
- assoc_req->band = priv->curbssparams.band;
-
- if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
- assoc_req->mode = priv->mode;
-
- if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
- memcpy(&assoc_req->bssid, priv->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], &priv->wep_keys[i],
- sizeof(struct enc_key));
- }
- }
-
- if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags))
- assoc_req->wep_tx_keyidx = priv->wep_tx_keyidx;
-
- if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
- memcpy(&assoc_req->wpa_mcast_key, &priv->wpa_mcast_key,
- sizeof(struct enc_key));
- }
-
- if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
- memcpy(&assoc_req->wpa_unicast_key, &priv->wpa_unicast_key,
- sizeof(struct enc_key));
- }
-
- if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
- memcpy(&assoc_req->secinfo, &priv->secinfo,
- sizeof(struct lbs_802_11_security));
- }
-
- if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
- memcpy(&assoc_req->wpa_ie, &priv->wpa_ie,
- MAX_WPA_IE_LEN);
- assoc_req->wpa_ie_len = priv->wpa_ie_len;
- }
-
- lbs_deb_leave(LBS_DEB_ASSOC);
- return assoc_req;
-}
-
-
-/**
- * @brief This function prepares command of authenticate.
- *
- * @param priv A pointer to struct lbs_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 lbs_cmd_80211_authenticate(struct lbs_private *priv,
- struct cmd_ds_command *cmd,
- void *pdata_buf)
-{
- struct cmd_ds_802_11_authenticate *pauthenticate = &cmd->params.auth;
- int ret = -1;
- u8 *bssid = pdata_buf;
-
- lbs_deb_enter(LBS_DEB_JOIN);
-
- 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 (priv->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_deb_join("AUTH_CMD: invalid auth alg 0x%X\n",
- priv->secinfo.auth_mode);
- goto out;
- }
-
- memcpy(pauthenticate->macaddr, bssid, ETH_ALEN);
-
- lbs_deb_join("AUTH_CMD: BSSID %pM, auth 0x%x\n",
- bssid, pauthenticate->authtype);
- ret = 0;
-
-out:
- lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
- return ret;
-}
-
-/**
- * @brief Deauthenticate from a specific BSS
- *
- * @param priv A pointer to struct lbs_private structure
- * @param bssid The specific BSS to deauthenticate from
- * @param reason The 802.11 sec. 7.3.1.7 Reason Code for deauthenticating
- *
- * @return 0 on success, error on failure
- */
-int lbs_cmd_80211_deauthenticate(struct lbs_private *priv, u8 bssid[ETH_ALEN],
- u16 reason)
-{
- struct cmd_ds_802_11_deauthenticate cmd;
- int ret;
-
- lbs_deb_enter(LBS_DEB_JOIN);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- memcpy(cmd.macaddr, &bssid[0], ETH_ALEN);
- cmd.reasoncode = cpu_to_le16(reason);
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd);
-
- /* Clean up everything even if there was an error; can't assume that
- * we're still authenticated to the AP after trying to deauth.
- */
- lbs_mac_event_disconnected(priv);
-
- lbs_deb_leave(LBS_DEB_JOIN);
- return ret;
-}
-
-int lbs_cmd_80211_associate(struct lbs_private *priv,
- struct cmd_ds_command *cmd, void *pdata_buf)
-{
- struct cmd_ds_802_11_associate *passo = &cmd->params.associate;
- int ret = 0;
- struct assoc_request *assoc_req = pdata_buf;
- struct bss_descriptor *bss = &assoc_req->bss;
- u8 *pos;
- u16 tmpcap, tmplen;
- struct mrvlietypes_ssidparamset *ssid;
- struct mrvlietypes_phyparamset *phy;
- struct mrvlietypes_ssparamset *ss;
- struct mrvlietypes_ratesparamset *rates;
- struct mrvlietypes_rsnparamset *rsn;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- pos = (u8 *) passo;
-
- if (!priv) {
- ret = -1;
- goto done;
- }
-
- cmd->command = cpu_to_le16(CMD_802_11_ASSOCIATE);
-
- memcpy(passo->peerstaaddr, bss->bssid, sizeof(passo->peerstaaddr));
- pos += sizeof(passo->peerstaaddr);
-
- /* set the listen interval */
- passo->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL);
-
- pos += sizeof(passo->capability);
- 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);
- tmplen = bss->ssid_len;
- ssid->header.len = cpu_to_le16(tmplen);
- memcpy(ssid->ssid, bss->ssid, tmplen);
- pos += sizeof(ssid->header) + tmplen;
-
- phy = (struct mrvlietypes_phyparamset *) pos;
- phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
- tmplen = sizeof(phy->fh_ds.dsparamset);
- phy->header.len = cpu_to_le16(tmplen);
- memcpy(&phy->fh_ds.dsparamset,
- &bss->phyparamset.dsparamset.currentchan,
- tmplen);
- pos += sizeof(phy->header) + tmplen;
-
- ss = (struct mrvlietypes_ssparamset *) pos;
- ss->header.type = cpu_to_le16(TLV_TYPE_CF);
- tmplen = sizeof(ss->cf_ibss.cfparamset);
- ss->header.len = cpu_to_le16(tmplen);
- pos += sizeof(ss->header) + tmplen;
-
- rates = (struct mrvlietypes_ratesparamset *) pos;
- rates->header.type = cpu_to_le16(TLV_TYPE_RATES);
- memcpy(&rates->rates, &bss->rates, MAX_RATES);
- tmplen = MAX_RATES;
- if (get_common_rates(priv, rates->rates, &tmplen)) {
- ret = -1;
- goto done;
- }
- pos += sizeof(rates->header) + tmplen;
- rates->header.len = cpu_to_le16(tmplen);
- lbs_deb_assoc("ASSOC_CMD: num rates %u\n", tmplen);
-
- /* Copy the infra. association rates into Current BSS state structure */
- memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
- memcpy(&priv->curbssparams.rates, &rates->rates, tmplen);
-
- /* Set MSB on basic rates as the firmware requires, but _after_
- * copying to current bss rates.
- */
- lbs_set_basic_rate_flags(rates->rates, tmplen);
-
- if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
- rsn = (struct mrvlietypes_rsnparamset *) pos;
- /* WPA_IE or WPA2_IE */
- rsn->header.type = cpu_to_le16((u16) assoc_req->wpa_ie[0]);
- tmplen = (u16) assoc_req->wpa_ie[1];
- rsn->header.len = cpu_to_le16(tmplen);
- memcpy(rsn->rsnie, &assoc_req->wpa_ie[2], tmplen);
- lbs_deb_hex(LBS_DEB_JOIN, "ASSOC_CMD: RSN IE", (u8 *) rsn,
- sizeof(rsn->header) + tmplen);
- pos += sizeof(rsn->header) + tmplen;
- }
-
- /* update curbssparams */
- priv->curbssparams.channel = bss->phyparamset.dsparamset.currentchan;
-
- if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
- ret = -1;
- goto done;
- }
-
- cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN);
-
- /* set the capability info */
- tmpcap = (bss->capability & CAPINFO_MASK);
- if (bss->mode == IW_MODE_INFRA)
- tmpcap |= WLAN_CAPABILITY_ESS;
- passo->capability = cpu_to_le16(tmpcap);
- lbs_deb_assoc("ASSOC_CMD: capability 0x%04x\n", tmpcap);
-
-done:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-int lbs_ret_80211_associate(struct lbs_private *priv,
- struct cmd_ds_command *resp)
-{
- int ret = 0;
- union iwreq_data wrqu;
- struct ieeetypes_assocrsp *passocrsp;
- struct bss_descriptor *bss;
- u16 status_code;
-
- lbs_deb_enter(LBS_DEB_ASSOC);
-
- if (!priv->in_progress_assoc_req) {
- lbs_deb_assoc("ASSOC_RESP: no in-progress assoc request\n");
- ret = -1;
- goto done;
- }
- bss = &priv->in_progress_assoc_req->bss;
-
- passocrsp = (struct ieeetypes_assocrsp *) &resp->params;
-
- /*
- * Older FW versions map the IEEE 802.11 Status Code in the association
- * response to the following values returned in passocrsp->statuscode:
- *
- * IEEE Status Code Marvell Status Code
- * 0 -> 0x0000 ASSOC_RESULT_SUCCESS
- * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
- * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
- * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
- * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
- * others -> 0x0003 ASSOC_RESULT_REFUSED
- *
- * Other response codes:
- * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused)
- * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for
- * association response from the AP)
- */
-
- status_code = le16_to_cpu(passocrsp->statuscode);
- switch (status_code) {
- case 0x00:
- break;
- case 0x01:
- lbs_deb_assoc("ASSOC_RESP: invalid parameters\n");
- break;
- case 0x02:
- lbs_deb_assoc("ASSOC_RESP: internal timer "
- "expired while waiting for the AP\n");
- break;
- case 0x03:
- lbs_deb_assoc("ASSOC_RESP: association "
- "refused by AP\n");
- break;
- case 0x04:
- lbs_deb_assoc("ASSOC_RESP: authentication "
- "refused by AP\n");
- break;
- default:
- lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x "
- " unknown\n", status_code);
- break;
- }
-
- if (status_code) {
- lbs_mac_event_disconnected(priv);
- ret = -1;
- goto done;
- }
-
- lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_RESP", (void *)&resp->params,
- le16_to_cpu(resp->size) - S_DS_GEN);
-
- /* Send a Media Connected event, according to the Spec */
- priv->connect_status = LBS_CONNECTED;
-
- /* Update current SSID and BSSID */
- memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
- priv->curbssparams.ssid_len = bss->ssid_len;
- memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
-
- priv->SNR[TYPE_RXPD][TYPE_AVG] = 0;
- priv->NF[TYPE_RXPD][TYPE_AVG] = 0;
-
- memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR));
- memset(priv->rawNF, 0x00, sizeof(priv->rawNF));
- priv->nextSNRNF = 0;
- priv->numSNRNF = 0;
-
- netif_carrier_on(priv->dev);
- if (!priv->tx_pending_len)
- netif_wake_queue(priv->dev);
-
- memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
-
-done:
- lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
- return ret;
-}
-
-static int lbs_adhoc_post(struct lbs_private *priv, struct cmd_header *resp)
-{
- 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 *adhoc_resp;
- union iwreq_data wrqu;
- struct bss_descriptor *bss;
- DECLARE_SSID_BUF(ssid);
-
- lbs_deb_enter(LBS_DEB_JOIN);
-
- adhoc_resp = (struct cmd_ds_802_11_ad_hoc_result *) resp;
-
- if (!priv->in_progress_assoc_req) {
- lbs_deb_join("ADHOC_RESP: no in-progress association "
- "request\n");
- ret = -1;
- goto done;
- }
- bss = &priv->in_progress_assoc_req->bss;
-
- /*
- * Join result code 0 --> SUCCESS
- */
- if (result) {
- lbs_deb_join("ADHOC_RESP: failed (result 0x%X)\n", result);
- if (priv->connect_status == LBS_CONNECTED)
- lbs_mac_event_disconnected(priv);
- ret = -1;
- goto done;
- }
-
- /* Send a Media Connected event, according to the Spec */
- priv->connect_status = LBS_CONNECTED;
-
- if (command == CMD_RET(CMD_802_11_AD_HOC_START)) {
- /* Update the created network descriptor with the new BSSID */
- memcpy(bss->bssid, adhoc_resp->bssid, ETH_ALEN);
- }
-
- /* Set the BSSID from the joined/started descriptor */
- memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
-
- /* Set the new SSID to current SSID */
- memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
- priv->curbssparams.ssid_len = bss->ssid_len;
-
- netif_carrier_on(priv->dev);
- if (!priv->tx_pending_len)
- netif_wake_queue(priv->dev);
-
- memset(&wrqu, 0, sizeof(wrqu));
- memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
-
- lbs_deb_join("ADHOC_RESP: Joined/started '%s', BSSID %pM, channel %d\n",
- print_ssid(ssid, bss->ssid, bss->ssid_len),
- priv->curbssparams.bssid,
- priv->curbssparams.channel);
-
-done:
- lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
- return ret;
-}
-
diff --git a/drivers/net/wireless/libertas/assoc.h b/drivers/net/wireless/libertas/assoc.h
deleted file mode 100644
index 8b7336dd02a..00000000000
--- a/drivers/net/wireless/libertas/assoc.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright (C) 2006, Red Hat, Inc. */
-
-#ifndef _LBS_ASSOC_H_
-#define _LBS_ASSOC_H_
-
-#include "dev.h"
-
-void lbs_association_worker(struct work_struct *work);
-struct assoc_request *lbs_get_association_request(struct lbs_private *priv);
-
-struct cmd_ds_command;
-int lbs_cmd_80211_authenticate(struct lbs_private *priv,
- struct cmd_ds_command *cmd,
- void *pdata_buf);
-
-int lbs_adhoc_stop(struct lbs_private *priv);
-
-int lbs_cmd_80211_deauthenticate(struct lbs_private *priv,
- u8 bssid[ETH_ALEN], u16 reason);
-int lbs_cmd_80211_associate(struct lbs_private *priv,
- struct cmd_ds_command *cmd,
- void *pdata_buf);
-
-int lbs_ret_80211_ad_hoc_start(struct lbs_private *priv,
- struct cmd_ds_command *resp);
-int lbs_ret_80211_associate(struct lbs_private *priv,
- struct cmd_ds_command *resp);
-
-#endif /* _LBS_ASSOC_H */
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
new file mode 100644
index 00000000000..47a998d8f99
--- /dev/null
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -0,0 +1,2218 @@
+/*
+ * Implement cfg80211 ("iw") support.
+ *
+ * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany
+ * Holger Schurig <hs4233@mail.mn-solutions.de>
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/hardirq.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <asm/unaligned.h>
+
+#include "decl.h"
+#include "cfg.h"
+#include "cmd.h"
+#include "mesh.h"
+
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_channel lbs_2ghz_channels[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0),
+};
+
+#define RATETAB_ENT(_rate, _hw_value, _flags) { \
+ .bitrate = (_rate), \
+ .hw_value = (_hw_value), \
+ .flags = (_flags), \
+}
+
+
+/* Table 6 in section 3.2.1.1 */
+static struct ieee80211_rate lbs_rates[] = {
+ RATETAB_ENT(10, 0, 0),
+ RATETAB_ENT(20, 1, 0),
+ RATETAB_ENT(55, 2, 0),
+ RATETAB_ENT(110, 3, 0),
+ RATETAB_ENT(60, 9, 0),
+ RATETAB_ENT(90, 6, 0),
+ RATETAB_ENT(120, 7, 0),
+ RATETAB_ENT(180, 8, 0),
+ RATETAB_ENT(240, 9, 0),
+ RATETAB_ENT(360, 10, 0),
+ RATETAB_ENT(480, 11, 0),
+ RATETAB_ENT(540, 12, 0),
+};
+
+static struct ieee80211_supported_band lbs_band_2ghz = {
+ .channels = lbs_2ghz_channels,
+ .n_channels = ARRAY_SIZE(lbs_2ghz_channels),
+ .bitrates = lbs_rates,
+ .n_bitrates = ARRAY_SIZE(lbs_rates),
+};
+
+
+static const u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+};
+
+/* Time to stay on the channel */
+#define LBS_DWELL_PASSIVE 100
+#define LBS_DWELL_ACTIVE 40
+
+
+/***************************************************************************
+ * Misc utility functions
+ *
+ * TLVs are Marvell specific. They are very similar to IEs, they have the
+ * same structure: type, length, data*. The only difference: for IEs, the
+ * type and length are u8, but for TLVs they're __le16.
+ */
+
+/*
+ * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1
+ * in the firmware spec
+ */
+static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type)
+{
+ int ret = -ENOTSUPP;
+
+ switch (auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ ret = auth_type;
+ break;
+ case NL80211_AUTHTYPE_AUTOMATIC:
+ ret = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ ret = 0x80;
+ break;
+ default:
+ /* silence compiler */
+ break;
+ }
+ return ret;
+}
+
+
+/*
+ * Various firmware commands need the list of supported rates, but with
+ * the hight-bit set for basic rates
+ */
+static int lbs_add_rates(u8 *rates)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) {
+ u8 rate = lbs_rates[i].bitrate / 5;
+ if (rate == 0x02 || rate == 0x04 ||
+ rate == 0x0b || rate == 0x16)
+ rate |= 0x80;
+ rates[i] = rate;
+ }
+ return ARRAY_SIZE(lbs_rates);
+}
+
+
+/***************************************************************************
+ * TLV utility functions
+ *
+ * TLVs are Marvell specific. They are very similar to IEs, they have the
+ * same structure: type, length, data*. The only difference: for IEs, the
+ * type and length are u8, but for TLVs they're __le16.
+ */
+
+
+/*
+ * Add ssid TLV
+ */
+#define LBS_MAX_SSID_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + IEEE80211_MAX_SSID_LEN)
+
+static int lbs_add_ssid_tlv(u8 *tlv, const u8 *ssid, int ssid_len)
+{
+ struct mrvl_ie_ssid_param_set *ssid_tlv = (void *)tlv;
+
+ /*
+ * TLV-ID SSID 00 00
+ * length 06 00
+ * ssid 4d 4e 54 45 53 54
+ */
+ ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID);
+ ssid_tlv->header.len = cpu_to_le16(ssid_len);
+ memcpy(ssid_tlv->ssid, ssid, ssid_len);
+ return sizeof(ssid_tlv->header) + ssid_len;
+}
+
+
+/*
+ * Add channel list TLV (section 8.4.2)
+ *
+ * Actual channel data comes from priv->wdev->wiphy->channels.
+ */
+#define LBS_MAX_CHANNEL_LIST_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + (LBS_SCAN_BEFORE_NAP * sizeof(struct chanscanparamset)))
+
+static int lbs_add_channel_list_tlv(struct lbs_private *priv, u8 *tlv,
+ int last_channel, int active_scan)
+{
+ int chanscanparamsize = sizeof(struct chanscanparamset) *
+ (last_channel - priv->scan_channel);
+
+ struct mrvl_ie_header *header = (void *) tlv;
+
+ /*
+ * TLV-ID CHANLIST 01 01
+ * length 0e 00
+ * channel 00 01 00 00 00 64 00
+ * radio type 00
+ * channel 01
+ * scan type 00
+ * min scan time 00 00
+ * max scan time 64 00
+ * channel 2 00 02 00 00 00 64 00
+ *
+ */
+
+ header->type = cpu_to_le16(TLV_TYPE_CHANLIST);
+ header->len = cpu_to_le16(chanscanparamsize);
+ tlv += sizeof(struct mrvl_ie_header);
+
+ /* lbs_deb_scan("scan: channels %d to %d\n", priv->scan_channel,
+ last_channel); */
+ memset(tlv, 0, chanscanparamsize);
+
+ while (priv->scan_channel < last_channel) {
+ struct chanscanparamset *param = (void *) tlv;
+
+ param->radiotype = CMD_SCAN_RADIO_TYPE_BG;
+ param->channumber =
+ priv->scan_req->channels[priv->scan_channel]->hw_value;
+ if (active_scan) {
+ param->maxscantime = cpu_to_le16(LBS_DWELL_ACTIVE);
+ } else {
+ param->chanscanmode.passivescan = 1;
+ param->maxscantime = cpu_to_le16(LBS_DWELL_PASSIVE);
+ }
+ tlv += sizeof(struct chanscanparamset);
+ priv->scan_channel++;
+ }
+ return sizeof(struct mrvl_ie_header) + chanscanparamsize;
+}
+
+
+/*
+ * Add rates TLV
+ *
+ * The rates are in lbs_bg_rates[], but for the 802.11b
+ * rates the high bit is set. We add this TLV only because
+ * there's a firmware which otherwise doesn't report all
+ * APs in range.
+ */
+#define LBS_MAX_RATES_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + (ARRAY_SIZE(lbs_rates)))
+
+/* Adds a TLV with all rates the hardware supports */
+static int lbs_add_supported_rates_tlv(u8 *tlv)
+{
+ size_t i;
+ struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv;
+
+ /*
+ * TLV-ID RATES 01 00
+ * length 0e 00
+ * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c
+ */
+ rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
+ tlv += sizeof(rate_tlv->header);
+ i = lbs_add_rates(tlv);
+ tlv += i;
+ rate_tlv->header.len = cpu_to_le16(i);
+ return sizeof(rate_tlv->header) + i;
+}
+
+/* Add common rates from a TLV and return the new end of the TLV */
+static u8 *
+add_ie_rates(u8 *tlv, const u8 *ie, int *nrates)
+{
+ int hw, ap, ap_max = ie[1];
+ u8 hw_rate;
+
+ /* Advance past IE header */
+ ie += 2;
+
+ lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max);
+
+ for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
+ hw_rate = lbs_rates[hw].bitrate / 5;
+ for (ap = 0; ap < ap_max; ap++) {
+ if (hw_rate == (ie[ap] & 0x7f)) {
+ *tlv++ = ie[ap];
+ *nrates = *nrates + 1;
+ }
+ }
+ }
+ return tlv;
+}
+
+/*
+ * Adds a TLV with all rates the hardware *and* BSS supports.
+ */
+static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
+{
+ struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv;
+ const u8 *rates_eid, *ext_rates_eid;
+ int n = 0;
+
+ rcu_read_lock();
+ rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+ ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
+
+ /*
+ * 01 00 TLV_TYPE_RATES
+ * 04 00 len
+ * 82 84 8b 96 rates
+ */
+ rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
+ tlv += sizeof(rate_tlv->header);
+
+ /* Add basic rates */
+ if (rates_eid) {
+ tlv = add_ie_rates(tlv, rates_eid, &n);
+
+ /* Add extended rates, if any */
+ if (ext_rates_eid)
+ tlv = add_ie_rates(tlv, ext_rates_eid, &n);
+ } else {
+ lbs_deb_assoc("assoc: bss had no basic rate IE\n");
+ /* Fallback: add basic 802.11b rates */
+ *tlv++ = 0x82;
+ *tlv++ = 0x84;
+ *tlv++ = 0x8b;
+ *tlv++ = 0x96;
+ n = 4;
+ }
+ rcu_read_unlock();
+
+ rate_tlv->header.len = cpu_to_le16(n);
+ return sizeof(rate_tlv->header) + n;
+}
+
+
+/*
+ * Add auth type TLV.
+ *
+ * This is only needed for newer firmware (V9 and up).
+ */
+#define LBS_MAX_AUTH_TYPE_TLV_SIZE \
+ sizeof(struct mrvl_ie_auth_type)
+
+static int lbs_add_auth_type_tlv(u8 *tlv, enum nl80211_auth_type auth_type)
+{
+ struct mrvl_ie_auth_type *auth = (void *) tlv;
+
+ /*
+ * 1f 01 TLV_TYPE_AUTH_TYPE
+ * 01 00 len
+ * 01 auth type
+ */
+ auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+ auth->header.len = cpu_to_le16(sizeof(*auth)-sizeof(auth->header));
+ auth->auth = cpu_to_le16(lbs_auth_to_authtype(auth_type));
+ return sizeof(*auth);
+}
+
+
+/*
+ * Add channel (phy ds) TLV
+ */
+#define LBS_MAX_CHANNEL_TLV_SIZE \
+ sizeof(struct mrvl_ie_header)
+
+static int lbs_add_channel_tlv(u8 *tlv, u8 channel)
+{
+ struct mrvl_ie_ds_param_set *ds = (void *) tlv;
+
+ /*
+ * 03 00 TLV_TYPE_PHY_DS
+ * 01 00 len
+ * 06 channel
+ */
+ ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
+ ds->header.len = cpu_to_le16(sizeof(*ds)-sizeof(ds->header));
+ ds->channel = channel;
+ return sizeof(*ds);
+}
+
+
+/*
+ * Add (empty) CF param TLV of the form:
+ */
+#define LBS_MAX_CF_PARAM_TLV_SIZE \
+ sizeof(struct mrvl_ie_header)
+
+static int lbs_add_cf_param_tlv(u8 *tlv)
+{
+ struct mrvl_ie_cf_param_set *cf = (void *)tlv;
+
+ /*
+ * 04 00 TLV_TYPE_CF
+ * 06 00 len
+ * 00 cfpcnt
+ * 00 cfpperiod
+ * 00 00 cfpmaxduration
+ * 00 00 cfpdurationremaining
+ */
+ cf->header.type = cpu_to_le16(TLV_TYPE_CF);
+ cf->header.len = cpu_to_le16(sizeof(*cf)-sizeof(cf->header));
+ return sizeof(*cf);
+}
+
+/*
+ * Add WPA TLV
+ */
+#define LBS_MAX_WPA_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + 128 /* TODO: I guessed the size */)
+
+static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len)
+{
+ size_t tlv_len;
+
+ /*
+ * We need just convert an IE to an TLV. IEs use u8 for the header,
+ * u8 type
+ * u8 len
+ * u8[] data
+ * but TLVs use __le16 instead:
+ * __le16 type
+ * __le16 len
+ * u8[] data
+ */
+ *tlv++ = *ie++;
+ *tlv++ = 0;
+ tlv_len = *tlv++ = *ie++;
+ *tlv++ = 0;
+ while (tlv_len--)
+ *tlv++ = *ie++;
+ /* the TLV is two bytes larger than the IE */
+ return ie_len + 2;
+}
+
+/*
+ * Set Channel
+ */
+
+static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = -ENOTSUPP;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d",
+ chandef->chan->center_freq,
+ cfg80211_get_chandef_type(chandef));
+
+ if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT)
+ goto out;
+
+ ret = lbs_set_channel(priv, chandef->chan->hw_value);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy,
+ struct net_device *netdev,
+ struct ieee80211_channel *channel)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = -ENOTSUPP;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d",
+ netdev_name(netdev), channel->center_freq);
+
+ if (netdev != priv->mesh_dev)
+ goto out;
+
+ ret = lbs_mesh_set_channel(priv, channel->hw_value);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+/*
+ * Scanning
+ */
+
+/*
+ * When scanning, the firmware doesn't send a nul packet with the power-safe
+ * bit to the AP. So we cannot stay away from our current channel too long,
+ * otherwise we loose data. So take a "nap" while scanning every other
+ * while.
+ */
+#define LBS_SCAN_BEFORE_NAP 4
+
+
+/*
+ * When the firmware reports back a scan-result, it gives us an "u8 rssi",
+ * which isn't really an RSSI, as it becomes larger when moving away from
+ * the AP. Anyway, we need to convert that into mBm.
+ */
+#define LBS_SCAN_RSSI_TO_MBM(rssi) \
+ ((-(int)rssi + 3)*100)
+
+static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
+ struct cmd_header *resp)
+{
+ struct cfg80211_bss *bss;
+ struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp;
+ int bsssize;
+ const u8 *pos;
+ const u8 *tsfdesc;
+ int tsfsize;
+ int i;
+ int ret = -EILSEQ;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ bsssize = get_unaligned_le16(&scanresp->bssdescriptsize);
+
+ lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n",
+ scanresp->nr_sets, bsssize, le16_to_cpu(resp->size));
+
+ if (scanresp->nr_sets == 0) {
+ ret = 0;
+ goto done;
+ }
+
+ /*
+ * The general layout of the scan response is described in chapter
+ * 5.7.1. Basically we have a common part, then any number of BSS
+ * descriptor sections. Finally we have section with the same number
+ * of TSFs.
+ *
+ * cmd_ds_802_11_scan_rsp
+ * cmd_header
+ * pos_size
+ * nr_sets
+ * bssdesc 1
+ * bssid
+ * rssi
+ * timestamp
+ * intvl
+ * capa
+ * IEs
+ * bssdesc 2
+ * bssdesc n
+ * MrvlIEtypes_TsfFimestamp_t
+ * TSF for BSS 1
+ * TSF for BSS 2
+ * TSF for BSS n
+ */
+
+ pos = scanresp->bssdesc_and_tlvbuffer;
+
+ lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer,
+ scanresp->bssdescriptsize);
+
+ tsfdesc = pos + bsssize;
+ tsfsize = 4 + 8 * scanresp->nr_sets;
+ lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize);
+
+ /* Validity check: we expect a Marvell-Local TLV */
+ i = get_unaligned_le16(tsfdesc);
+ tsfdesc += 2;
+ if (i != TLV_TYPE_TSFTIMESTAMP) {
+ lbs_deb_scan("scan response: invalid TSF Timestamp %d\n", i);
+ goto done;
+ }
+
+ /*
+ * Validity check: the TLV holds TSF values with 8 bytes each, so
+ * the size in the TLV must match the nr_sets value
+ */
+ i = get_unaligned_le16(tsfdesc);
+ tsfdesc += 2;
+ if (i / 8 != scanresp->nr_sets) {
+ lbs_deb_scan("scan response: invalid number of TSF timestamp "
+ "sets (expected %d got %d)\n", scanresp->nr_sets,
+ i / 8);
+ goto done;
+ }
+
+ for (i = 0; i < scanresp->nr_sets; i++) {
+ const u8 *bssid;
+ const u8 *ie;
+ int left;
+ int ielen;
+ int rssi;
+ u16 intvl;
+ u16 capa;
+ int chan_no = -1;
+ const u8 *ssid = NULL;
+ u8 ssid_len = 0;
+ DECLARE_SSID_BUF(ssid_buf);
+
+ int len = get_unaligned_le16(pos);
+ pos += 2;
+
+ /* BSSID */
+ bssid = pos;
+ pos += ETH_ALEN;
+ /* RSSI */
+ rssi = *pos++;
+ /* Packet time stamp */
+ pos += 8;
+ /* Beacon interval */
+ intvl = get_unaligned_le16(pos);
+ pos += 2;
+ /* Capabilities */
+ capa = get_unaligned_le16(pos);
+ pos += 2;
+
+ /* To find out the channel, we must parse the IEs */
+ ie = pos;
+ /*
+ * 6+1+8+2+2: size of BSSID, RSSI, time stamp, beacon
+ * interval, capabilities
+ */
+ ielen = left = len - (6 + 1 + 8 + 2 + 2);
+ while (left >= 2) {
+ u8 id, elen;
+ id = *pos++;
+ elen = *pos++;
+ left -= 2;
+ if (elen > left) {
+ lbs_deb_scan("scan response: invalid IE fmt\n");
+ goto done;
+ }
+
+ if (id == WLAN_EID_DS_PARAMS)
+ chan_no = *pos;
+ if (id == WLAN_EID_SSID) {
+ ssid = pos;
+ ssid_len = elen;
+ }
+ left -= elen;
+ pos += elen;
+ }
+
+ /* No channel, no luck */
+ if (chan_no != -1) {
+ struct wiphy *wiphy = priv->wdev->wiphy;
+ int freq = ieee80211_channel_to_frequency(chan_no,
+ IEEE80211_BAND_2GHZ);
+ struct ieee80211_channel *channel =
+ ieee80211_get_channel(wiphy, freq);
+
+ lbs_deb_scan("scan: %pM, capa %04x, chan %2d, %s, "
+ "%d dBm\n",
+ bssid, capa, chan_no,
+ print_ssid(ssid_buf, ssid, ssid_len),
+ LBS_SCAN_RSSI_TO_MBM(rssi)/100);
+
+ if (channel &&
+ !(channel->flags & IEEE80211_CHAN_DISABLED)) {
+ bss = cfg80211_inform_bss(wiphy, channel,
+ bssid, get_unaligned_le64(tsfdesc),
+ capa, intvl, ie, ielen,
+ LBS_SCAN_RSSI_TO_MBM(rssi),
+ GFP_KERNEL);
+ cfg80211_put_bss(wiphy, bss);
+ }
+ } else
+ lbs_deb_scan("scan response: missing BSS channel IE\n");
+
+ tsfdesc += 8;
+ }
+ ret = 0;
+
+ done:
+ lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
+ return ret;
+}
+
+
+/*
+ * Our scan command contains a TLV, consting of a SSID TLV, a channel list
+ * TLV and a rates TLV. Determine the maximum size of them:
+ */
+#define LBS_SCAN_MAX_CMD_SIZE \
+ (sizeof(struct cmd_ds_802_11_scan) \
+ + LBS_MAX_SSID_TLV_SIZE \
+ + LBS_MAX_CHANNEL_LIST_TLV_SIZE \
+ + LBS_MAX_RATES_TLV_SIZE)
+
+/*
+ * Assumes priv->scan_req is initialized and valid
+ * Assumes priv->scan_channel is initialized
+ */
+static void lbs_scan_worker(struct work_struct *work)
+{
+ struct lbs_private *priv =
+ container_of(work, struct lbs_private, scan_work.work);
+ struct cmd_ds_802_11_scan *scan_cmd;
+ u8 *tlv; /* pointer into our current, growing TLV storage area */
+ int last_channel;
+ int running, carrier;
+
+ lbs_deb_enter(LBS_DEB_SCAN);
+
+ scan_cmd = kzalloc(LBS_SCAN_MAX_CMD_SIZE, GFP_KERNEL);
+ if (scan_cmd == NULL)
+ goto out_no_scan_cmd;
+
+ /* prepare fixed part of scan command */
+ scan_cmd->bsstype = CMD_BSS_TYPE_ANY;
+
+ /* stop network while we're away from our main channel */
+ running = !netif_queue_stopped(priv->dev);
+ carrier = netif_carrier_ok(priv->dev);
+ if (running)
+ netif_stop_queue(priv->dev);
+ if (carrier)
+ netif_carrier_off(priv->dev);
+
+ /* prepare fixed part of scan command */
+ tlv = scan_cmd->tlvbuffer;
+
+ /* add SSID TLV */
+ if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0)
+ tlv += lbs_add_ssid_tlv(tlv,
+ priv->scan_req->ssids[0].ssid,
+ priv->scan_req->ssids[0].ssid_len);
+
+ /* add channel TLVs */
+ last_channel = priv->scan_channel + LBS_SCAN_BEFORE_NAP;
+ if (last_channel > priv->scan_req->n_channels)
+ last_channel = priv->scan_req->n_channels;
+ tlv += lbs_add_channel_list_tlv(priv, tlv, last_channel,
+ priv->scan_req->n_ssids);
+
+ /* add rates TLV */
+ tlv += lbs_add_supported_rates_tlv(tlv);
+
+ if (priv->scan_channel < priv->scan_req->n_channels) {
+ cancel_delayed_work(&priv->scan_work);
+ if (netif_running(priv->dev))
+ queue_delayed_work(priv->work_thread, &priv->scan_work,
+ msecs_to_jiffies(300));
+ }
+
+ /* This is the final data we are about to send */
+ scan_cmd->hdr.size = cpu_to_le16(tlv - (u8 *)scan_cmd);
+ lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd,
+ sizeof(*scan_cmd));
+ lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer,
+ tlv - scan_cmd->tlvbuffer);
+
+ __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr,
+ le16_to_cpu(scan_cmd->hdr.size),
+ lbs_ret_scan, 0);
+
+ if (priv->scan_channel >= priv->scan_req->n_channels) {
+ /* Mark scan done */
+ cancel_delayed_work(&priv->scan_work);
+ lbs_scan_done(priv);
+ }
+
+ /* Restart network */
+ if (carrier)
+ netif_carrier_on(priv->dev);
+ if (running && !priv->tx_pending_len)
+ netif_wake_queue(priv->dev);
+
+ kfree(scan_cmd);
+
+ /* Wake up anything waiting on scan completion */
+ if (priv->scan_req == NULL) {
+ lbs_deb_scan("scan: waking up waiters\n");
+ wake_up_all(&priv->scan_q);
+ }
+
+ out_no_scan_cmd:
+ lbs_deb_leave(LBS_DEB_SCAN);
+}
+
+static void _internal_start_scan(struct lbs_private *priv, bool internal,
+ struct cfg80211_scan_request *request)
+{
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n",
+ request->n_ssids, request->n_channels, request->ie_len);
+
+ priv->scan_channel = 0;
+ priv->scan_req = request;
+ priv->internal_scan = internal;
+
+ queue_delayed_work(priv->work_thread, &priv->scan_work,
+ msecs_to_jiffies(50));
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+/*
+ * Clean up priv->scan_req. Should be used to handle the allocation details.
+ */
+void lbs_scan_done(struct lbs_private *priv)
+{
+ WARN_ON(!priv->scan_req);
+
+ if (priv->internal_scan)
+ kfree(priv->scan_req);
+ else
+ cfg80211_scan_done(priv->scan_req, false);
+
+ priv->scan_req = NULL;
+}
+
+static int lbs_cfg_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (priv->scan_req || delayed_work_pending(&priv->scan_work)) {
+ /* old scan request not yet processed */
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ _internal_start_scan(priv, false, request);
+
+ if (priv->surpriseremoved)
+ ret = -EIO;
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+
+/*
+ * Events
+ */
+
+void lbs_send_disconnect_notification(struct lbs_private *priv)
+{
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ cfg80211_disconnected(priv->dev,
+ 0,
+ NULL, 0,
+ GFP_KERNEL);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event)
+{
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ cfg80211_michael_mic_failure(priv->dev,
+ priv->assoc_bss,
+ event == MACREG_INT_CODE_MIC_ERR_MULTICAST ?
+ NL80211_KEYTYPE_GROUP :
+ NL80211_KEYTYPE_PAIRWISE,
+ -1,
+ NULL,
+ GFP_KERNEL);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+
+
+
+/*
+ * Connect/disconnect
+ */
+
+
+/*
+ * This removes all WEP keys
+ */
+static int lbs_remove_wep_keys(struct lbs_private *priv)
+{
+ struct cmd_ds_802_11_set_wep cmd;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.keyindex = cpu_to_le16(priv->wep_tx_key);
+ cmd.action = cpu_to_le16(CMD_ACT_REMOVE);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return ret;
+}
+
+/*
+ * Set WEP keys
+ */
+static int lbs_set_wep_keys(struct lbs_private *priv)
+{
+ struct cmd_ds_802_11_set_wep cmd;
+ int i;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ /*
+ * command 13 00
+ * size 50 00
+ * sequence xx xx
+ * result 00 00
+ * action 02 00 ACT_ADD
+ * transmit key 00 00
+ * type for key 1 01 WEP40
+ * type for key 2 00
+ * type for key 3 00
+ * type for key 4 00
+ * key 1 39 39 39 39 39 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * key 2 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * key 3 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * key 4 00 00 00 00 00 00 00 00
+ */
+ if (priv->wep_key_len[0] || priv->wep_key_len[1] ||
+ priv->wep_key_len[2] || priv->wep_key_len[3]) {
+ /* Only set wep keys if we have at least one of them */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.keyindex = cpu_to_le16(priv->wep_tx_key);
+ cmd.action = cpu_to_le16(CMD_ACT_ADD);
+
+ for (i = 0; i < 4; i++) {
+ switch (priv->wep_key_len[i]) {
+ case WLAN_KEY_LEN_WEP40:
+ cmd.keytype[i] = CMD_TYPE_WEP_40_BIT;
+ break;
+ case WLAN_KEY_LEN_WEP104:
+ cmd.keytype[i] = CMD_TYPE_WEP_104_BIT;
+ break;
+ default:
+ cmd.keytype[i] = 0;
+ break;
+ }
+ memcpy(cmd.keymaterial[i], priv->wep_key[i],
+ priv->wep_key_len[i]);
+ }
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd);
+ } else {
+ /* Otherwise remove all wep keys */
+ ret = lbs_remove_wep_keys(priv);
+ }
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return ret;
+}
+
+
+/*
+ * Enable/Disable RSN status
+ */
+static int lbs_enable_rsn(struct lbs_private *priv, int enable)
+{
+ struct cmd_ds_802_11_enable_rsn cmd;
+ int ret;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", enable);
+
+ /*
+ * cmd 2f 00
+ * size 0c 00
+ * sequence xx xx
+ * result 00 00
+ * action 01 00 ACT_SET
+ * enable 01 00
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+ cmd.enable = cpu_to_le16(enable);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return ret;
+}
+
+
+/*
+ * Set WPA/WPA key material
+ */
+
+/*
+ * like "struct cmd_ds_802_11_key_material", but with cmd_header. Once we
+ * get rid of WEXT, this should go into host.h
+ */
+
+struct cmd_key_material {
+ struct cmd_header hdr;
+
+ __le16 action;
+ struct MrvlIEtype_keyParamSet param;
+} __packed;
+
+static int lbs_set_key_material(struct lbs_private *priv,
+ int key_type, int key_info,
+ const u8 *key, u16 key_len)
+{
+ struct cmd_key_material cmd;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ /*
+ * Example for WPA (TKIP):
+ *
+ * cmd 5e 00
+ * size 34 00
+ * sequence xx xx
+ * result 00 00
+ * action 01 00
+ * TLV type 00 01 key param
+ * length 00 26
+ * key type 01 00 TKIP
+ * key info 06 00 UNICAST | ENABLED
+ * key len 20 00
+ * key 32 bytes
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+ cmd.param.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
+ cmd.param.length = cpu_to_le16(sizeof(cmd.param) - 4);
+ cmd.param.keytypeid = cpu_to_le16(key_type);
+ cmd.param.keyinfo = cpu_to_le16(key_info);
+ cmd.param.keylen = cpu_to_le16(key_len);
+ if (key && key_len)
+ memcpy(cmd.param.key, key, key_len);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return ret;
+}
+
+
+/*
+ * Sets the auth type (open, shared, etc) in the firmware. That
+ * we use CMD_802_11_AUTHENTICATE is misleading, this firmware
+ * command doesn't send an authentication frame at all, it just
+ * stores the auth_type.
+ */
+static int lbs_set_authtype(struct lbs_private *priv,
+ struct cfg80211_connect_params *sme)
+{
+ struct cmd_ds_802_11_authenticate cmd;
+ int ret;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", sme->auth_type);
+
+ /*
+ * cmd 11 00
+ * size 19 00
+ * sequence xx xx
+ * result 00 00
+ * BSS id 00 13 19 80 da 30
+ * auth type 00
+ * reserved 00 00 00 00 00 00 00 00 00 00
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ if (sme->bssid)
+ memcpy(cmd.bssid, sme->bssid, ETH_ALEN);
+ /* convert auth_type */
+ ret = lbs_auth_to_authtype(sme->auth_type);
+ if (ret < 0)
+ goto done;
+
+ cmd.authtype = ret;
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd);
+
+ done:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+/*
+ * Create association request
+ */
+#define LBS_ASSOC_MAX_CMD_SIZE \
+ (sizeof(struct cmd_ds_802_11_associate) \
+ - 512 /* cmd_ds_802_11_associate.iebuf */ \
+ + LBS_MAX_SSID_TLV_SIZE \
+ + LBS_MAX_CHANNEL_TLV_SIZE \
+ + LBS_MAX_CF_PARAM_TLV_SIZE \
+ + LBS_MAX_AUTH_TYPE_TLV_SIZE \
+ + LBS_MAX_WPA_TLV_SIZE)
+
+static int lbs_associate(struct lbs_private *priv,
+ struct cfg80211_bss *bss,
+ struct cfg80211_connect_params *sme)
+{
+ struct cmd_ds_802_11_associate_response *resp;
+ struct cmd_ds_802_11_associate *cmd = kzalloc(LBS_ASSOC_MAX_CMD_SIZE,
+ GFP_KERNEL);
+ const u8 *ssid_eid;
+ size_t len, resp_ie_len;
+ int status;
+ int ret;
+ u8 *pos = &(cmd->iebuf[0]);
+ u8 *tmp;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ /*
+ * cmd 50 00
+ * length 34 00
+ * sequence xx xx
+ * result 00 00
+ * BSS id 00 13 19 80 da 30
+ * capabilities 11 00
+ * listen interval 0a 00
+ * beacon interval 00 00
+ * DTIM period 00
+ * TLVs xx (up to 512 bytes)
+ */
+ cmd->hdr.command = cpu_to_le16(CMD_802_11_ASSOCIATE);
+
+ /* Fill in static fields */
+ memcpy(cmd->bssid, bss->bssid, ETH_ALEN);
+ cmd->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL);
+ cmd->capability = cpu_to_le16(bss->capability);
+
+ /* add SSID TLV */
+ rcu_read_lock();
+ ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
+ if (ssid_eid)
+ pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]);
+ else
+ lbs_deb_assoc("no SSID\n");
+ rcu_read_unlock();
+
+ /* add DS param TLV */
+ if (bss->channel)
+ pos += lbs_add_channel_tlv(pos, bss->channel->hw_value);
+ else
+ lbs_deb_assoc("no channel\n");
+
+ /* add (empty) CF param TLV */
+ pos += lbs_add_cf_param_tlv(pos);
+
+ /* add rates TLV */
+ tmp = pos + 4; /* skip Marvell IE header */
+ pos += lbs_add_common_rates_tlv(pos, bss);
+ lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp);
+
+ /* add auth type TLV */
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) >= 9)
+ pos += lbs_add_auth_type_tlv(pos, sme->auth_type);
+
+ /* add WPA/WPA2 TLV */
+ if (sme->ie && sme->ie_len)
+ pos += lbs_add_wpa_tlv(pos, sme->ie, sme->ie_len);
+
+ len = (sizeof(*cmd) - sizeof(cmd->iebuf)) +
+ (u16)(pos - (u8 *) &cmd->iebuf);
+ cmd->hdr.size = cpu_to_le16(len);
+
+ lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_CMD", (u8 *) cmd,
+ le16_to_cpu(cmd->hdr.size));
+
+ /* store for later use */
+ memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_ASSOCIATE, cmd);
+ if (ret)
+ goto done;
+
+ /* generate connect message to cfg80211 */
+
+ resp = (void *) cmd; /* recast for easier field access */
+ status = le16_to_cpu(resp->statuscode);
+
+ /* Older FW versions map the IEEE 802.11 Status Code in the association
+ * response to the following values returned in resp->statuscode:
+ *
+ * IEEE Status Code Marvell Status Code
+ * 0 -> 0x0000 ASSOC_RESULT_SUCCESS
+ * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * others -> 0x0003 ASSOC_RESULT_REFUSED
+ *
+ * Other response codes:
+ * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused)
+ * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for
+ * association response from the AP)
+ */
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) {
+ switch (status) {
+ case 0:
+ break;
+ case 1:
+ lbs_deb_assoc("invalid association parameters\n");
+ status = WLAN_STATUS_CAPS_UNSUPPORTED;
+ break;
+ case 2:
+ lbs_deb_assoc("timer expired while waiting for AP\n");
+ status = WLAN_STATUS_AUTH_TIMEOUT;
+ break;
+ case 3:
+ lbs_deb_assoc("association refused by AP\n");
+ status = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ break;
+ case 4:
+ lbs_deb_assoc("authentication refused by AP\n");
+ status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ break;
+ default:
+ lbs_deb_assoc("association failure %d\n", status);
+ /* v5 OLPC firmware does return the AP status code if
+ * it's not one of the values above. Let that through.
+ */
+ break;
+ }
+ }
+
+ lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, "
+ "aid 0x%04x\n", status, le16_to_cpu(resp->statuscode),
+ le16_to_cpu(resp->capability), le16_to_cpu(resp->aid));
+
+ resp_ie_len = le16_to_cpu(resp->hdr.size)
+ - sizeof(resp->hdr)
+ - 6;
+ cfg80211_connect_result(priv->dev,
+ priv->assoc_bss,
+ sme->ie, sme->ie_len,
+ resp->iebuf, resp_ie_len,
+ status,
+ GFP_KERNEL);
+
+ if (status == 0) {
+ /* TODO: get rid of priv->connect_status */
+ priv->connect_status = LBS_CONNECTED;
+ netif_carrier_on(priv->dev);
+ if (!priv->tx_pending_len)
+ netif_tx_wake_all_queues(priv->dev);
+ }
+
+ kfree(cmd);
+done:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+static struct cfg80211_scan_request *
+_new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme)
+{
+ struct cfg80211_scan_request *creq = NULL;
+ int i, n_channels = ieee80211_get_num_supported_channels(wiphy);
+ enum ieee80211_band band;
+
+ creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
+ n_channels * sizeof(void *),
+ GFP_ATOMIC);
+ if (!creq)
+ return NULL;
+
+ /* SSIDs come after channels */
+ creq->ssids = (void *)&creq->channels[n_channels];
+ creq->n_channels = n_channels;
+ creq->n_ssids = 1;
+
+ /* Scan all available channels */
+ i = 0;
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ int j;
+
+ if (!wiphy->bands[band])
+ continue;
+
+ for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
+ /* ignore disabled channels */
+ if (wiphy->bands[band]->channels[j].flags &
+ IEEE80211_CHAN_DISABLED)
+ continue;
+
+ creq->channels[i] = &wiphy->bands[band]->channels[j];
+ i++;
+ }
+ }
+ if (i) {
+ /* Set real number of channels specified in creq->channels[] */
+ creq->n_channels = i;
+
+ /* Scan for the SSID we're going to connect to */
+ memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len);
+ creq->ssids[0].ssid_len = sme->ssid_len;
+ } else {
+ /* No channels found... */
+ kfree(creq);
+ creq = NULL;
+ }
+
+ return creq;
+}
+
+static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ struct cfg80211_bss *bss = NULL;
+ int ret = 0;
+ u8 preamble = RADIO_PREAMBLE_SHORT;
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (!sme->bssid) {
+ struct cfg80211_scan_request *creq;
+
+ /*
+ * Scan for the requested network after waiting for existing
+ * scans to finish.
+ */
+ lbs_deb_assoc("assoc: waiting for existing scans\n");
+ wait_event_interruptible_timeout(priv->scan_q,
+ (priv->scan_req == NULL),
+ (15 * HZ));
+
+ creq = _new_connect_scan_req(wiphy, sme);
+ if (!creq) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ lbs_deb_assoc("assoc: scanning for compatible AP\n");
+ _internal_start_scan(priv, true, creq);
+
+ lbs_deb_assoc("assoc: waiting for scan to complete\n");
+ wait_event_interruptible_timeout(priv->scan_q,
+ (priv->scan_req == NULL),
+ (15 * HZ));
+ lbs_deb_assoc("assoc: scanning competed\n");
+ }
+
+ /* Find the BSS we want using available scan results */
+ bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
+ sme->ssid, sme->ssid_len,
+ WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
+ if (!bss) {
+ wiphy_err(wiphy, "assoc: bss %pM not in scan results\n",
+ sme->bssid);
+ ret = -ENOENT;
+ goto done;
+ }
+ lbs_deb_assoc("trying %pM\n", bss->bssid);
+ lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n",
+ sme->crypto.cipher_group,
+ sme->key_idx, sme->key_len);
+
+ /* As this is a new connection, clear locally stored WEP keys */
+ priv->wep_tx_key = 0;
+ memset(priv->wep_key, 0, sizeof(priv->wep_key));
+ memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len));
+
+ /* set/remove WEP keys */
+ switch (sme->crypto.cipher_group) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ /* Store provided WEP keys in priv-> */
+ priv->wep_tx_key = sme->key_idx;
+ priv->wep_key_len[sme->key_idx] = sme->key_len;
+ memcpy(priv->wep_key[sme->key_idx], sme->key, sme->key_len);
+ /* Set WEP keys and WEP mode */
+ lbs_set_wep_keys(priv);
+ priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE;
+ lbs_set_mac_control(priv);
+ /* No RSN mode for WEP */
+ lbs_enable_rsn(priv, 0);
+ break;
+ case 0: /* there's no WLAN_CIPHER_SUITE_NONE definition */
+ /*
+ * If we don't have no WEP, no WPA and no WPA2,
+ * we remove all keys like in the WPA/WPA2 setup,
+ * we just don't set RSN.
+ *
+ * Therefore: fall-through
+ */
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ /* Remove WEP keys and WEP mode */
+ lbs_remove_wep_keys(priv);
+ priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE;
+ lbs_set_mac_control(priv);
+
+ /* clear the WPA/WPA2 keys */
+ lbs_set_key_material(priv,
+ KEY_TYPE_ID_WEP, /* doesn't matter */
+ KEY_INFO_WPA_UNICAST,
+ NULL, 0);
+ lbs_set_key_material(priv,
+ KEY_TYPE_ID_WEP, /* doesn't matter */
+ KEY_INFO_WPA_MCAST,
+ NULL, 0);
+ /* RSN mode for WPA/WPA2 */
+ lbs_enable_rsn(priv, sme->crypto.cipher_group != 0);
+ break;
+ default:
+ wiphy_err(wiphy, "unsupported cipher group 0x%x\n",
+ sme->crypto.cipher_group);
+ ret = -ENOTSUPP;
+ goto done;
+ }
+
+ ret = lbs_set_authtype(priv, sme);
+ if (ret == -ENOTSUPP) {
+ wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type);
+ goto done;
+ }
+
+ lbs_set_radio(priv, preamble, 1);
+
+ /* Do the actual association */
+ ret = lbs_associate(priv, bss, sme);
+
+ done:
+ if (bss)
+ cfg80211_put_bss(wiphy, bss);
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+int lbs_disconnect(struct lbs_private *priv, u16 reason)
+{
+ struct cmd_ds_802_11_deauthenticate cmd;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ /* Mildly ugly to use a locally store my own BSSID ... */
+ memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN);
+ cmd.reasoncode = cpu_to_le16(reason);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd);
+ if (ret)
+ return ret;
+
+ cfg80211_disconnected(priv->dev,
+ reason,
+ NULL, 0,
+ GFP_KERNEL);
+ priv->connect_status = LBS_DISCONNECTED;
+
+ return 0;
+}
+
+static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);
+
+ /* store for lbs_cfg_ret_disconnect() */
+ priv->disassoc_reason = reason_code;
+
+ return lbs_disconnect(priv, reason_code);
+}
+
+static int lbs_cfg_set_default_key(struct wiphy *wiphy,
+ struct net_device *netdev,
+ u8 key_index, bool unicast,
+ bool multicast)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+
+ if (netdev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (key_index != priv->wep_tx_key) {
+ lbs_deb_assoc("set_default_key: to %d\n", key_index);
+ priv->wep_tx_key = key_index;
+ lbs_set_wep_keys(priv);
+ }
+
+ return 0;
+}
+
+
+static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 idx, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ u16 key_info;
+ u16 key_type;
+ int ret = 0;
+
+ if (netdev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n",
+ params->cipher, mac_addr);
+ lbs_deb_assoc("add_key: key index %d, key len %d\n",
+ idx, params->key_len);
+ if (params->key_len)
+ lbs_deb_hex(LBS_DEB_CFG80211, "KEY",
+ params->key, params->key_len);
+
+ lbs_deb_assoc("add_key: seq len %d\n", params->seq_len);
+ if (params->seq_len)
+ lbs_deb_hex(LBS_DEB_CFG80211, "SEQ",
+ params->seq, params->seq_len);
+
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ /* actually compare if something has changed ... */
+ if ((priv->wep_key_len[idx] != params->key_len) ||
+ memcmp(priv->wep_key[idx],
+ params->key, params->key_len) != 0) {
+ priv->wep_key_len[idx] = params->key_len;
+ memcpy(priv->wep_key[idx],
+ params->key, params->key_len);
+ lbs_set_wep_keys(priv);
+ }
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ key_info = KEY_INFO_WPA_ENABLED | ((idx == 0)
+ ? KEY_INFO_WPA_UNICAST
+ : KEY_INFO_WPA_MCAST);
+ key_type = (params->cipher == WLAN_CIPHER_SUITE_TKIP)
+ ? KEY_TYPE_ID_TKIP
+ : KEY_TYPE_ID_AES;
+ lbs_set_key_material(priv,
+ key_type,
+ key_info,
+ params->key, params->key_len);
+ break;
+ default:
+ wiphy_err(wiphy, "unhandled cipher 0x%x\n", params->cipher);
+ ret = -ENOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+
+static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev,
+ u8 key_index, bool pairwise, const u8 *mac_addr)
+{
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ lbs_deb_assoc("del_key: key_idx %d, mac_addr %pM\n",
+ key_index, mac_addr);
+
+#ifdef TODO
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ /*
+ * I think can keep this a NO-OP, because:
+
+ * - we clear all keys whenever we do lbs_cfg_connect() anyway
+ * - neither "iw" nor "wpa_supplicant" won't call this during
+ * an ongoing connection
+ * - TODO: but I have to check if this is still true when
+ * I set the AP to periodic re-keying
+ * - we've not kzallec() something when we've added a key at
+ * lbs_cfg_connect() or lbs_cfg_add_key().
+ *
+ * This causes lbs_cfg_del_key() only called at disconnect time,
+ * where we'd just waste time deleting a key that is not going
+ * to be used anyway.
+ */
+ if (key_index < 3 && priv->wep_key_len[key_index]) {
+ priv->wep_key_len[key_index] = 0;
+ lbs_set_wep_keys(priv);
+ }
+#endif
+
+ return 0;
+}
+
+
+/*
+ * Get station
+ */
+
+static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ s8 signal, noise;
+ int ret;
+ size_t i;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ sinfo->filled |= STATION_INFO_TX_BYTES |
+ STATION_INFO_TX_PACKETS |
+ STATION_INFO_RX_BYTES |
+ STATION_INFO_RX_PACKETS;
+ sinfo->tx_bytes = priv->dev->stats.tx_bytes;
+ sinfo->tx_packets = priv->dev->stats.tx_packets;
+ sinfo->rx_bytes = priv->dev->stats.rx_bytes;
+ sinfo->rx_packets = priv->dev->stats.rx_packets;
+
+ /* Get current RSSI */
+ ret = lbs_get_rssi(priv, &signal, &noise);
+ if (ret == 0) {
+ sinfo->signal = signal;
+ sinfo->filled |= STATION_INFO_SIGNAL;
+ }
+
+ /* Convert priv->cur_rate from hw_value to NL80211 value */
+ for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) {
+ if (priv->cur_rate == lbs_rates[i].hw_value) {
+ sinfo->txrate.legacy = lbs_rates[i].bitrate;
+ sinfo->filled |= STATION_INFO_TX_BITRATE;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+
+
+/*
+ * Change interface
+ */
+
+static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = 0;
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (priv->iface_running)
+ ret = lbs_set_iface_type(priv, type);
+
+ if (!ret)
+ priv->wdev->iftype = type;
+
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+/*
+ * IBSS (Ad-Hoc)
+ */
+
+/*
+ * The firmware needs the following bits masked out of the beacon-derived
+ * capability field when associating/joining to a BSS:
+ * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused)
+ */
+#define CAPINFO_MASK (~(0xda00))
+
+
+static void lbs_join_post(struct lbs_private *priv,
+ struct cfg80211_ibss_params *params,
+ u8 *bssid, u16 capability)
+{
+ u8 fake_ie[2 + IEEE80211_MAX_SSID_LEN + /* ssid */
+ 2 + 4 + /* basic rates */
+ 2 + 1 + /* DS parameter */
+ 2 + 2 + /* atim */
+ 2 + 8]; /* extended rates */
+ u8 *fake = fake_ie;
+ struct cfg80211_bss *bss;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ /*
+ * For cfg80211_inform_bss, we'll need a fake IE, as we can't get
+ * the real IE from the firmware. So we fabricate a fake IE based on
+ * what the firmware actually sends (sniffed with wireshark).
+ */
+ /* Fake SSID IE */
+ *fake++ = WLAN_EID_SSID;
+ *fake++ = params->ssid_len;
+ memcpy(fake, params->ssid, params->ssid_len);
+ fake += params->ssid_len;
+ /* Fake supported basic rates IE */
+ *fake++ = WLAN_EID_SUPP_RATES;
+ *fake++ = 4;
+ *fake++ = 0x82;
+ *fake++ = 0x84;
+ *fake++ = 0x8b;
+ *fake++ = 0x96;
+ /* Fake DS channel IE */
+ *fake++ = WLAN_EID_DS_PARAMS;
+ *fake++ = 1;
+ *fake++ = params->chandef.chan->hw_value;
+ /* Fake IBSS params IE */
+ *fake++ = WLAN_EID_IBSS_PARAMS;
+ *fake++ = 2;
+ *fake++ = 0; /* ATIM=0 */
+ *fake++ = 0;
+ /* Fake extended rates IE, TODO: don't add this for 802.11b only,
+ * but I don't know how this could be checked */
+ *fake++ = WLAN_EID_EXT_SUPP_RATES;
+ *fake++ = 8;
+ *fake++ = 0x0c;
+ *fake++ = 0x12;
+ *fake++ = 0x18;
+ *fake++ = 0x24;
+ *fake++ = 0x30;
+ *fake++ = 0x48;
+ *fake++ = 0x60;
+ *fake++ = 0x6c;
+ lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie);
+
+ bss = cfg80211_inform_bss(priv->wdev->wiphy,
+ params->chandef.chan,
+ bssid,
+ 0,
+ capability,
+ params->beacon_interval,
+ fake_ie, fake - fake_ie,
+ 0, GFP_KERNEL);
+ cfg80211_put_bss(priv->wdev->wiphy, bss);
+
+ memcpy(priv->wdev->ssid, params->ssid, params->ssid_len);
+ priv->wdev->ssid_len = params->ssid_len;
+
+ cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan,
+ GFP_KERNEL);
+
+ /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */
+ priv->connect_status = LBS_CONNECTED;
+ netif_carrier_on(priv->dev);
+ if (!priv->tx_pending_len)
+ netif_wake_queue(priv->dev);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+static int lbs_ibss_join_existing(struct lbs_private *priv,
+ struct cfg80211_ibss_params *params,
+ struct cfg80211_bss *bss)
+{
+ const u8 *rates_eid;
+ struct cmd_ds_802_11_ad_hoc_join cmd;
+ u8 preamble = RADIO_PREAMBLE_SHORT;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ /* TODO: set preamble based on scan result */
+ ret = lbs_set_radio(priv, preamble, 1);
+ if (ret)
+ goto out;
+
+ /*
+ * Example CMD_802_11_AD_HOC_JOIN command:
+ *
+ * command 2c 00 CMD_802_11_AD_HOC_JOIN
+ * size 65 00
+ * sequence xx xx
+ * result 00 00
+ * bssid 02 27 27 97 2f 96
+ * ssid 49 42 53 53 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * type 02 CMD_BSS_TYPE_IBSS
+ * beacon period 64 00
+ * dtim period 00
+ * timestamp 00 00 00 00 00 00 00 00
+ * localtime 00 00 00 00 00 00 00 00
+ * IE DS 03
+ * IE DS len 01
+ * IE DS channel 01
+ * reserveed 00 00 00 00
+ * IE IBSS 06
+ * IE IBSS len 02
+ * IE IBSS atim 00 00
+ * reserved 00 00 00 00
+ * capability 02 00
+ * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c 00
+ * fail timeout ff 00
+ * probe delay 00 00
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ memcpy(cmd.bss.bssid, bss->bssid, ETH_ALEN);
+ memcpy(cmd.bss.ssid, params->ssid, params->ssid_len);
+ cmd.bss.type = CMD_BSS_TYPE_IBSS;
+ cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval);
+ cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS;
+ cmd.bss.ds.header.len = 1;
+ cmd.bss.ds.channel = params->chandef.chan->hw_value;
+ cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS;
+ cmd.bss.ibss.header.len = 2;
+ cmd.bss.ibss.atimwindow = 0;
+ cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK);
+
+ /* set rates to the intersection of our rates and the rates in the
+ bss */
+ rcu_read_lock();
+ rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+ if (!rates_eid) {
+ lbs_add_rates(cmd.bss.rates);
+ } else {
+ int hw, i;
+ u8 rates_max = rates_eid[1];
+ u8 *rates = cmd.bss.rates;
+ for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
+ u8 hw_rate = lbs_rates[hw].bitrate / 5;
+ for (i = 0; i < rates_max; i++) {
+ if (hw_rate == (rates_eid[i+2] & 0x7f)) {
+ u8 rate = rates_eid[i+2];
+ if (rate == 0x02 || rate == 0x04 ||
+ rate == 0x0b || rate == 0x16)
+ rate |= 0x80;
+ *rates++ = rate;
+ }
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ /* Only v8 and below support setting this */
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) {
+ cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
+ cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
+ }
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd);
+ if (ret)
+ goto out;
+
+ /*
+ * This is a sample response to CMD_802_11_AD_HOC_JOIN:
+ *
+ * response 2c 80
+ * size 09 00
+ * sequence xx xx
+ * result 00 00
+ * reserved 00
+ */
+ lbs_join_post(priv, params, bss->bssid, bss->capability);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+static int lbs_ibss_start_new(struct lbs_private *priv,
+ struct cfg80211_ibss_params *params)
+{
+ struct cmd_ds_802_11_ad_hoc_start cmd;
+ struct cmd_ds_802_11_ad_hoc_result *resp =
+ (struct cmd_ds_802_11_ad_hoc_result *) &cmd;
+ u8 preamble = RADIO_PREAMBLE_SHORT;
+ int ret = 0;
+ u16 capability;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ ret = lbs_set_radio(priv, preamble, 1);
+ if (ret)
+ goto out;
+
+ /*
+ * Example CMD_802_11_AD_HOC_START command:
+ *
+ * command 2b 00 CMD_802_11_AD_HOC_START
+ * size b1 00
+ * sequence xx xx
+ * result 00 00
+ * ssid 54 45 53 54 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * 00 00 00 00 00 00 00 00
+ * bss type 02
+ * beacon period 64 00
+ * dtim period 00
+ * IE IBSS 06
+ * IE IBSS len 02
+ * IE IBSS atim 00 00
+ * reserved 00 00 00 00
+ * IE DS 03
+ * IE DS len 01
+ * IE DS channel 01
+ * reserved 00 00 00 00
+ * probe delay 00 00
+ * capability 02 00
+ * rates 82 84 8b 96 (basic rates with have bit 7 set)
+ * 0c 12 18 24 30 48 60 6c
+ * padding 100 bytes
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ memcpy(cmd.ssid, params->ssid, params->ssid_len);
+ cmd.bsstype = CMD_BSS_TYPE_IBSS;
+ cmd.beaconperiod = cpu_to_le16(params->beacon_interval);
+ cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS;
+ cmd.ibss.header.len = 2;
+ cmd.ibss.atimwindow = 0;
+ cmd.ds.header.id = WLAN_EID_DS_PARAMS;
+ cmd.ds.header.len = 1;
+ cmd.ds.channel = params->chandef.chan->hw_value;
+ /* Only v8 and below support setting probe delay */
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8)
+ cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
+ /* TODO: mix in WLAN_CAPABILITY_PRIVACY */
+ capability = WLAN_CAPABILITY_IBSS;
+ cmd.capability = cpu_to_le16(capability);
+ lbs_add_rates(cmd.rates);
+
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd);
+ if (ret)
+ goto out;
+
+ /*
+ * This is a sample response to CMD_802_11_AD_HOC_JOIN:
+ *
+ * response 2b 80
+ * size 14 00
+ * sequence xx xx
+ * result 00 00
+ * reserved 00
+ * bssid 02 2b 7b 0f 86 0e
+ */
+ lbs_join_post(priv, params, resp->bssid, capability);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ibss_params *params)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = 0;
+ struct cfg80211_bss *bss;
+ DECLARE_SSID_BUF(ssid_buf);
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (!params->chandef.chan) {
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ ret = lbs_set_channel(priv, params->chandef.chan->hw_value);
+ if (ret)
+ goto out;
+
+ /* Search if someone is beaconing. This assumes that the
+ * bss list is populated already */
+ bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid,
+ params->ssid, params->ssid_len,
+ WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
+
+ if (bss) {
+ ret = lbs_ibss_join_existing(priv, params, bss);
+ cfg80211_put_bss(wiphy, bss);
+ } else
+ ret = lbs_ibss_start_new(priv, params);
+
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ struct cmd_ds_802_11_ad_hoc_stop cmd;
+ int ret = 0;
+
+ if (dev == priv->mesh_dev)
+ return -EOPNOTSUPP;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd);
+
+ /* TODO: consider doing this at MACREG_INT_CODE_ADHOC_BCN_LOST time */
+ lbs_mac_event_disconnected(priv);
+
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+
+/*
+ * Initialization
+ */
+
+static struct cfg80211_ops lbs_cfg80211_ops = {
+ .set_monitor_channel = lbs_cfg_set_monitor_channel,
+ .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel,
+ .scan = lbs_cfg_scan,
+ .connect = lbs_cfg_connect,
+ .disconnect = lbs_cfg_disconnect,
+ .add_key = lbs_cfg_add_key,
+ .del_key = lbs_cfg_del_key,
+ .set_default_key = lbs_cfg_set_default_key,
+ .get_station = lbs_cfg_get_station,
+ .change_virtual_intf = lbs_change_intf,
+ .join_ibss = lbs_join_ibss,
+ .leave_ibss = lbs_leave_ibss,
+};
+
+
+/*
+ * At this time lbs_private *priv doesn't even exist, so we just allocate
+ * memory and don't initialize the wiphy further. This is postponed until we
+ * can talk to the firmware and happens at registration time in
+ * lbs_cfg_wiphy_register().
+ */
+struct wireless_dev *lbs_cfg_alloc(struct device *dev)
+{
+ int ret = 0;
+ struct wireless_dev *wdev;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ if (!wdev)
+ return ERR_PTR(-ENOMEM);
+
+ wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private));
+ if (!wdev->wiphy) {
+ dev_err(dev, "cannot allocate wiphy\n");
+ ret = -ENOMEM;
+ goto err_wiphy_new;
+ }
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return wdev;
+
+ err_wiphy_new:
+ kfree(wdev);
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ERR_PTR(ret);
+}
+
+
+static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv)
+{
+ struct region_code_mapping {
+ const char *cn;
+ int code;
+ };
+
+ /* Section 5.17.2 */
+ static const struct region_code_mapping regmap[] = {
+ {"US ", 0x10}, /* US FCC */
+ {"CA ", 0x20}, /* Canada */
+ {"EU ", 0x30}, /* ETSI */
+ {"ES ", 0x31}, /* Spain */
+ {"FR ", 0x32}, /* France */
+ {"JP ", 0x40}, /* Japan */
+ };
+ size_t i;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ for (i = 0; i < ARRAY_SIZE(regmap); i++)
+ if (regmap[i].code == priv->regioncode) {
+ regulatory_hint(priv->wdev->wiphy, regmap[i].cn);
+ break;
+ }
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+static void lbs_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain "
+ "callback for domain %c%c\n", request->alpha2[0],
+ request->alpha2[1]);
+
+ memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2));
+ if (lbs_iface_active(priv))
+ lbs_set_11d_domain_info(priv);
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+}
+
+/*
+ * This function get's called after lbs_setup_firmware() determined the
+ * firmware capabities. So we can setup the wiphy according to our
+ * hardware/firmware.
+ */
+int lbs_cfg_register(struct lbs_private *priv)
+{
+ struct wireless_dev *wdev = priv->wdev;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ wdev->wiphy->max_scan_ssids = 1;
+ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+ wdev->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC);
+ if (lbs_rtap_supported(priv))
+ wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
+ if (lbs_mesh_activated(priv))
+ wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
+
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz;
+
+ /*
+ * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have
+ * never seen a firmware without WPA
+ */
+ wdev->wiphy->cipher_suites = cipher_suites;
+ wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+ wdev->wiphy->reg_notifier = lbs_reg_notifier;
+
+ ret = wiphy_register(wdev->wiphy);
+ if (ret < 0)
+ pr_err("cannot register wiphy device\n");
+
+ priv->wiphy_registered = true;
+
+ ret = register_netdev(priv->dev);
+ if (ret)
+ pr_err("cannot register network device\n");
+
+ INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker);
+
+ lbs_cfg_set_regulatory_hint(priv);
+
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+void lbs_scan_deinit(struct lbs_private *priv)
+{
+ lbs_deb_enter(LBS_DEB_CFG80211);
+ cancel_delayed_work_sync(&priv->scan_work);
+}
+
+
+void lbs_cfg_free(struct lbs_private *priv)
+{
+ struct wireless_dev *wdev = priv->wdev;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (!wdev)
+ return;
+
+ if (priv->wiphy_registered)
+ wiphy_unregister(wdev->wiphy);
+
+ if (wdev->wiphy)
+ wiphy_free(wdev->wiphy);
+
+ kfree(wdev);
+}
diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h
new file mode 100644
index 00000000000..10995f59fe3
--- /dev/null
+++ b/drivers/net/wireless/libertas/cfg.h
@@ -0,0 +1,20 @@
+#ifndef __LBS_CFG80211_H__
+#define __LBS_CFG80211_H__
+
+struct device;
+struct lbs_private;
+struct regulatory_request;
+struct wiphy;
+
+struct wireless_dev *lbs_cfg_alloc(struct device *dev);
+int lbs_cfg_register(struct lbs_private *priv);
+void lbs_cfg_free(struct lbs_private *priv);
+
+void lbs_send_disconnect_notification(struct lbs_private *priv);
+void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event);
+
+void lbs_scan_done(struct lbs_private *priv);
+void lbs_scan_deinit(struct lbs_private *priv);
+int lbs_disconnect(struct lbs_private *priv, u16 reason);
+
+#endif
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index 8c3605cdc64..aaa297315c4 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -1,32 +1,31 @@
-/**
- * This file contains the handling of command.
- * It prepares command and sends it to firmware when it is ready.
- */
+/*
+ * 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 <net/lib80211.h>
+#include <linux/hardirq.h>
#include <linux/kfifo.h>
-#include "host.h"
-#include "hostcmd.h"
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/if_arp.h>
+#include <linux/export.h>
+
#include "decl.h"
-#include "defs.h"
-#include "dev.h"
-#include "assoc.h"
-#include "wext.h"
+#include "cfg.h"
#include "cmd.h"
-static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
-
+#define CAL_NF(nf) ((s32)(-(s32)(nf)))
+#define CAL_RSSI(snr, nf) ((s32)((s32)(snr) + CAL_NF(nf)))
/**
- * @brief Simple callback that copies response back into command
+ * lbs_cmd_copyback - Simple callback that copies response back into command
*
- * @param priv A pointer to struct lbs_private structure
- * @param extra A pointer to the original command structure for which
- * 'resp' is a response
- * @param resp A pointer to the command response
+ * @priv: A pointer to &struct lbs_private structure
+ * @extra: A pointer to the original command structure for which
+ * 'resp' is a response
+ * @resp: A pointer to the command response
*
- * @return 0 on success, error on failure
+ * returns: 0 on success, error on failure
*/
int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
struct cmd_header *resp)
@@ -41,15 +40,15 @@ int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
EXPORT_SYMBOL_GPL(lbs_cmd_copyback);
/**
- * @brief Simple callback that ignores the result. Use this if
- * you just want to send a command to the hardware, but don't
+ * lbs_cmd_async_callback - Simple callback that ignores the result.
+ * Use this if you just want to send a command to the hardware, but don't
* care for the result.
*
- * @param priv ignored
- * @param extra ignored
- * @param resp ignored
+ * @priv: ignored
+ * @extra: ignored
+ * @resp: ignored
*
- * @return 0 for success
+ * returns: 0 for success
*/
static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra,
struct cmd_header *resp)
@@ -59,16 +58,19 @@ static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra,
/**
- * @brief Checks whether a command is allowed in Power Save mode
+ * is_command_allowed_in_ps - tests if a command is allowed in Power Save mode
*
- * @param command the command ID
- * @return 1 if allowed, 0 if not allowed
+ * @cmd: the command ID
+ *
+ * returns: 1 if allowed, 0 if not allowed
*/
static u8 is_command_allowed_in_ps(u16 cmd)
{
switch (cmd) {
case CMD_802_11_RSSI:
return 1;
+ case CMD_802_11_HOST_SLEEP_CFG:
+ return 1;
default:
break;
}
@@ -76,11 +78,12 @@ static u8 is_command_allowed_in_ps(u16 cmd)
}
/**
- * @brief Updates the hardware details like MAC address and regulatory region
+ * lbs_update_hw_spec - Updates the hardware details like MAC address
+ * and regulatory region
*
- * @param priv A pointer to struct lbs_private structure
+ * @priv: A pointer to &struct lbs_private structure
*
- * @return 0 on success, error on failure
+ * returns: 0 on success, error on failure
*/
int lbs_update_hw_spec(struct lbs_private *priv)
{
@@ -109,7 +112,7 @@ int lbs_update_hw_spec(struct lbs_private *priv)
* CF card firmware 5.0.16p0: cap 0x00000303
* USB dongle firmware 5.110.17p2: cap 0x00000303
*/
- lbs_pr_info("%pM, fw %u.%u.%up%u, cap 0x%08x\n",
+ netdev_info(priv->dev, "%pM, fw %u.%u.%up%u, cap 0x%08x\n",
cmd.permanentaddr,
priv->fwrelease >> 24 & 0xff,
priv->fwrelease >> 16 & 0xff,
@@ -122,8 +125,14 @@ int lbs_update_hw_spec(struct lbs_private *priv)
/* Clamp region code to 8-bit since FW spec indicates that it should
* only ever be 8-bit, even though the field size is 16-bit. Some firmware
* returns non-zero high 8 bits here.
+ *
+ * Firmware version 4.0.102 used in CF8381 has region code shifted. We
+ * need to check for this problem and handle it properly.
*/
- priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4)
+ priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF;
+ else
+ priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
/* use the region code to search for the index */
@@ -134,24 +143,19 @@ int lbs_update_hw_spec(struct lbs_private *priv)
/* if it's unidentified region code, use the default (USA) */
if (i >= MRVDRV_MAX_REGION_CODE) {
priv->regioncode = 0x10;
- lbs_pr_info("unidentified region code; using the default (USA)\n");
+ netdev_info(priv->dev,
+ "unidentified region code; using the default (USA)\n");
}
if (priv->current_addr[0] == 0xff)
memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN);
- memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN);
- if (priv->mesh_dev)
- memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN);
-
- if (lbs_set_regiontable(priv, priv->regioncode, 0)) {
- ret = -1;
- goto out;
- }
-
- if (lbs_set_universaltable(priv, 0)) {
- ret = -1;
- goto out;
+ if (!priv->copied_hwaddr) {
+ memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN);
+ if (priv->mesh_dev)
+ memcpy(priv->mesh_dev->dev_addr,
+ priv->current_addr, ETH_ALEN);
+ priv->copied_hwaddr = 1;
}
out:
@@ -159,12 +163,37 @@ out:
return ret;
}
+static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy,
+ struct cmd_header *resp)
+{
+ lbs_deb_enter(LBS_DEB_CMD);
+ if (priv->is_host_sleep_activated) {
+ priv->is_host_sleep_configured = 0;
+ if (priv->psstate == PS_STATE_FULL_POWER) {
+ priv->is_host_sleep_activated = 0;
+ wake_up_interruptible(&priv->host_sleep_q);
+ }
+ } else {
+ priv->is_host_sleep_configured = 1;
+ }
+ lbs_deb_leave(LBS_DEB_CMD);
+ return 0;
+}
+
int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
struct wol_config *p_wol_config)
{
struct cmd_ds_host_sleep cmd_config;
int ret;
+ /*
+ * Certain firmware versions do not support EHS_REMOVE_WAKEUP command
+ * and the card will return a failure. Since we need to be
+ * able to reset the mask, in those cases we set a 0 mask instead.
+ */
+ if (criteria == EHS_REMOVE_WAKEUP && !priv->ehs_remove_supported)
+ criteria = 0;
+
cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config));
cmd_config.criteria = cpu_to_le32(criteria);
cmd_config.gpio = priv->wol_gpio;
@@ -176,86 +205,65 @@ int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
else
cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE;
- ret = lbs_cmd_with_response(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config);
+ ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr,
+ le16_to_cpu(cmd_config.hdr.size),
+ lbs_ret_host_sleep_cfg, 0);
if (!ret) {
- if (criteria) {
- lbs_deb_cmd("Set WOL criteria to %x\n", criteria);
- priv->wol_criteria = criteria;
- } else
+ if (p_wol_config)
memcpy((uint8_t *) p_wol_config,
(uint8_t *)&cmd_config.wol_conf,
sizeof(struct wol_config));
} else {
- lbs_pr_info("HOST_SLEEP_CFG failed %d\n", ret);
+ netdev_info(priv->dev, "HOST_SLEEP_CFG failed %d\n", ret);
}
return ret;
}
EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg);
-static int lbs_cmd_802_11_ps_mode(struct cmd_ds_command *cmd,
- u16 cmd_action)
-{
- struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- 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 (cmd_action) {
- case CMD_SUBCMD_ENTER_PS:
- lbs_deb_cmd("PS command:" "SubCode- Enter PS\n");
-
- psm->locallisteninterval = 0;
- psm->nullpktinterval = 0;
- psm->multipledtim =
- cpu_to_le16(MRVDRV_DEFAULT_MULTIPLE_DTIM);
- break;
-
- case CMD_SUBCMD_EXIT_PS:
- lbs_deb_cmd("PS command:" "SubCode- Exit PS\n");
- break;
-
- case CMD_SUBCMD_SLEEP_CONFIRMED:
- lbs_deb_cmd("PS command: SubCode- sleep confirm\n");
- break;
-
- default:
- break;
- }
-
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
-
-int lbs_cmd_802_11_inactivity_timeout(struct lbs_private *priv,
- uint16_t cmd_action, uint16_t *timeout)
+/**
+ * lbs_set_ps_mode - Sets the Power Save mode
+ *
+ * @priv: A pointer to &struct lbs_private structure
+ * @cmd_action: The Power Save operation (PS_MODE_ACTION_ENTER_PS or
+ * PS_MODE_ACTION_EXIT_PS)
+ * @block: Whether to block on a response or not
+ *
+ * returns: 0 on success, error on failure
+ */
+int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block)
{
- struct cmd_ds_802_11_inactivity_timeout cmd;
- int ret;
+ struct cmd_ds_802_11_ps_mode cmd;
+ int ret = 0;
lbs_deb_enter(LBS_DEB_CMD);
- cmd.hdr.command = cpu_to_le16(CMD_802_11_INACTIVITY_TIMEOUT);
+ memset(&cmd, 0, sizeof(cmd));
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
cmd.action = cpu_to_le16(cmd_action);
- if (cmd_action == CMD_ACT_SET)
- cmd.timeout = cpu_to_le16(*timeout);
- else
- cmd.timeout = 0;
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_INACTIVITY_TIMEOUT, &cmd);
+ if (cmd_action == PS_MODE_ACTION_ENTER_PS) {
+ lbs_deb_cmd("PS_MODE: action ENTER_PS\n");
+ cmd.multipledtim = cpu_to_le16(1); /* Default DTIM multiple */
+ } else if (cmd_action == PS_MODE_ACTION_EXIT_PS) {
+ lbs_deb_cmd("PS_MODE: action EXIT_PS\n");
+ } else {
+ /* We don't handle CONFIRM_SLEEP here because it needs to
+ * be fastpathed to the firmware.
+ */
+ lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action);
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
- if (!ret)
- *timeout = le16_to_cpu(cmd.timeout);
+ if (block)
+ ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd);
+ else
+ lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd));
+out:
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return 0;
+ return ret;
}
int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
@@ -300,205 +308,130 @@ int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
return 0;
}
-int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
- struct assoc_request *assoc)
+static int lbs_wait_for_ds_awake(struct lbs_private *priv)
{
- struct cmd_ds_802_11_set_wep cmd;
int ret = 0;
lbs_deb_enter(LBS_DEB_CMD);
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.command = cpu_to_le16(CMD_802_11_SET_WEP);
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- cmd.action = cpu_to_le16(cmd_action);
-
- if (cmd_action == CMD_ACT_ADD) {
- int i;
-
- /* default tx key index */
- cmd.keyindex = cpu_to_le16(assoc->wep_tx_keyidx &
- CMD_WEP_KEY_INDEX_MASK);
-
- /* Copy key types and material to host command structure */
- for (i = 0; i < 4; i++) {
- struct enc_key *pkey = &assoc->wep_keys[i];
-
- switch (pkey->len) {
- case KEY_LEN_WEP_40:
- cmd.keytype[i] = CMD_TYPE_WEP_40_BIT;
- memmove(cmd.keymaterial[i], pkey->key, pkey->len);
- lbs_deb_cmd("SET_WEP: add key %d (40 bit)\n", i);
- break;
- case KEY_LEN_WEP_104:
- cmd.keytype[i] = CMD_TYPE_WEP_104_BIT;
- memmove(cmd.keymaterial[i], pkey->key, pkey->len);
- lbs_deb_cmd("SET_WEP: add key %d (104 bit)\n", i);
- break;
- case 0:
- break;
- default:
- lbs_deb_cmd("SET_WEP: invalid key %d, length %d\n",
- i, pkey->len);
- ret = -1;
- goto done;
- break;
- }
+ if (priv->is_deep_sleep) {
+ if (!wait_event_interruptible_timeout(priv->ds_awake_q,
+ !priv->is_deep_sleep, (10 * HZ))) {
+ netdev_err(priv->dev, "ds_awake_q: timer expired\n");
+ ret = -1;
}
- } else if (cmd_action == CMD_ACT_REMOVE) {
- /* ACT_REMOVE clears _all_ WEP keys */
-
- /* default tx key index */
- cmd.keyindex = cpu_to_le16(priv->wep_tx_keyidx &
- CMD_WEP_KEY_INDEX_MASK);
- lbs_deb_cmd("SET_WEP: remove key %d\n", priv->wep_tx_keyidx);
}
- ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd);
-done:
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
-int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
- uint16_t *enable)
+int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
{
- struct cmd_ds_802_11_enable_rsn cmd;
- int ret;
+ int ret = 0;
lbs_deb_enter(LBS_DEB_CMD);
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(cmd_action);
-
- if (cmd_action == CMD_ACT_GET)
- cmd.enable = 0;
- else {
- if (*enable)
- cmd.enable = cpu_to_le16(CMD_ENABLE_RSN);
- else
- cmd.enable = cpu_to_le16(CMD_DISABLE_RSN);
- lbs_deb_cmd("ENABLE_RSN: %d\n", *enable);
+ if (deep_sleep) {
+ if (priv->is_deep_sleep != 1) {
+ lbs_deb_cmd("deep sleep: sleep\n");
+ BUG_ON(!priv->enter_deep_sleep);
+ ret = priv->enter_deep_sleep(priv);
+ if (!ret) {
+ netif_stop_queue(priv->dev);
+ netif_carrier_off(priv->dev);
+ }
+ } else {
+ netdev_err(priv->dev, "deep sleep: already enabled\n");
+ }
+ } else {
+ if (priv->is_deep_sleep) {
+ lbs_deb_cmd("deep sleep: wakeup\n");
+ BUG_ON(!priv->exit_deep_sleep);
+ ret = priv->exit_deep_sleep(priv);
+ if (!ret) {
+ ret = lbs_wait_for_ds_awake(priv);
+ if (ret)
+ netdev_err(priv->dev,
+ "deep sleep: wakeup failed\n");
+ }
+ }
}
- ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd);
- if (!ret && cmd_action == CMD_ACT_GET)
- *enable = le16_to_cpu(cmd.enable);
-
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
-static void set_one_wpa_key(struct MrvlIEtype_keyParamSet *keyparam,
- struct enc_key *key)
+static int lbs_ret_host_sleep_activate(struct lbs_private *priv,
+ unsigned long dummy,
+ struct cmd_header *cmd)
{
- lbs_deb_enter(LBS_DEB_CMD);
-
- if (key->flags & KEY_INFO_WPA_ENABLED)
- keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED);
- if (key->flags & KEY_INFO_WPA_UNICAST)
- keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
- if (key->flags & KEY_INFO_WPA_MCAST)
- keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
-
- keyparam->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
- keyparam->keytypeid = cpu_to_le16(key->type);
- keyparam->keylen = cpu_to_le16(key->len);
- memcpy(keyparam->key, key->key, key->len);
-
- /* Length field doesn't include the {type,length} header */
- keyparam->length = cpu_to_le16(sizeof(*keyparam) - 4);
- lbs_deb_leave(LBS_DEB_CMD);
+ lbs_deb_enter(LBS_DEB_FW);
+ priv->is_host_sleep_activated = 1;
+ wake_up_interruptible(&priv->host_sleep_q);
+ lbs_deb_leave(LBS_DEB_FW);
+ return 0;
}
-int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
- struct assoc_request *assoc)
+int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep)
{
- struct cmd_ds_802_11_key_material cmd;
+ struct cmd_header cmd;
int ret = 0;
- int index = 0;
+ uint32_t criteria = EHS_REMOVE_WAKEUP;
lbs_deb_enter(LBS_DEB_CMD);
- cmd.action = cpu_to_le16(cmd_action);
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- if (cmd_action == CMD_ACT_GET) {
- cmd.hdr.size = cpu_to_le16(S_DS_GEN + 2);
- } else {
- memset(cmd.keyParamSet, 0, sizeof(cmd.keyParamSet));
-
- if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc->flags)) {
- set_one_wpa_key(&cmd.keyParamSet[index],
- &assoc->wpa_unicast_key);
- index++;
- }
-
- if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc->flags)) {
- set_one_wpa_key(&cmd.keyParamSet[index],
- &assoc->wpa_mcast_key);
- index++;
- }
+ if (host_sleep) {
+ if (priv->is_host_sleep_activated != 1) {
+ memset(&cmd, 0, sizeof(cmd));
+ ret = lbs_host_sleep_cfg(priv, priv->wol_criteria,
+ (struct wol_config *)NULL);
+ if (ret) {
+ netdev_info(priv->dev,
+ "Host sleep configuration failed: %d\n",
+ ret);
+ return ret;
+ }
+ if (priv->psstate == PS_STATE_FULL_POWER) {
+ ret = __lbs_cmd(priv,
+ CMD_802_11_HOST_SLEEP_ACTIVATE,
+ &cmd,
+ sizeof(cmd),
+ lbs_ret_host_sleep_activate, 0);
+ if (ret)
+ netdev_info(priv->dev,
+ "HOST_SLEEP_ACTIVATE failed: %d\n",
+ ret);
+ }
- /* The common header and as many keys as we included */
- cmd.hdr.size = cpu_to_le16(offsetof(typeof(cmd),
- keyParamSet[index]));
- }
- ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd);
- /* Copy the returned key to driver private data */
- if (!ret && cmd_action == CMD_ACT_GET) {
- void *buf_ptr = cmd.keyParamSet;
- void *resp_end = &(&cmd)[1];
-
- while (buf_ptr < resp_end) {
- struct MrvlIEtype_keyParamSet *keyparam = buf_ptr;
- struct enc_key *key;
- uint16_t param_set_len = le16_to_cpu(keyparam->length);
- uint16_t key_len = le16_to_cpu(keyparam->keylen);
- uint16_t key_flags = le16_to_cpu(keyparam->keyinfo);
- uint16_t key_type = le16_to_cpu(keyparam->keytypeid);
- void *end;
-
- end = (void *)keyparam + sizeof(keyparam->type)
- + sizeof(keyparam->length) + param_set_len;
-
- /* Make sure we don't access past the end of the IEs */
- if (end > resp_end)
- break;
-
- if (key_flags & KEY_INFO_WPA_UNICAST)
- key = &priv->wpa_unicast_key;
- else if (key_flags & KEY_INFO_WPA_MCAST)
- key = &priv->wpa_mcast_key;
- else
- break;
-
- /* Copy returned key into driver */
- memset(key, 0, sizeof(struct enc_key));
- if (key_len > sizeof(key->key))
- break;
- key->type = key_type;
- key->flags = key_flags;
- key->len = key_len;
- memcpy(key->key, keyparam->key, key->len);
-
- buf_ptr = end + 1;
+ if (!wait_event_interruptible_timeout(
+ priv->host_sleep_q,
+ priv->is_host_sleep_activated,
+ (10 * HZ))) {
+ netdev_err(priv->dev,
+ "host_sleep_q: timer expired\n");
+ ret = -1;
+ }
+ } else {
+ netdev_err(priv->dev, "host sleep: already enabled\n");
}
+ } else {
+ if (priv->is_host_sleep_activated)
+ ret = lbs_host_sleep_cfg(priv, criteria,
+ (struct wol_config *)NULL);
}
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
/**
- * @brief Set an SNMP MIB value
+ * lbs_set_snmp_mib - Set an SNMP MIB value
*
- * @param priv A pointer to struct lbs_private structure
- * @param oid The OID to set in the firmware
- * @param val Value to set the OID to
+ * @priv: A pointer to &struct lbs_private structure
+ * @oid: The OID to set in the firmware
+ * @val: Value to set the OID to
*
- * @return 0 on success, error on failure
+ * returns: 0 on success, error on failure
*/
int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val)
{
@@ -515,7 +448,7 @@ int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val)
switch (oid) {
case SNMP_MIB_OID_BSS_TYPE:
cmd.bufsize = cpu_to_le16(sizeof(u8));
- cmd.value[0] = (val == IW_MODE_ADHOC) ? 2 : 1;
+ cmd.value[0] = val;
break;
case SNMP_MIB_OID_11D_ENABLE:
case SNMP_MIB_OID_FRAG_THRESHOLD:
@@ -542,13 +475,13 @@ out:
}
/**
- * @brief Get an SNMP MIB value
+ * lbs_get_snmp_mib - Get an SNMP MIB value
*
- * @param priv A pointer to struct lbs_private structure
- * @param oid The OID to retrieve from the firmware
- * @param out_val Location for the returned value
+ * @priv: A pointer to &struct lbs_private structure
+ * @oid: The OID to retrieve from the firmware
+ * @out_val: Location for the returned value
*
- * @return 0 on success, error on failure
+ * returns: 0 on success, error on failure
*/
int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val)
{
@@ -568,13 +501,7 @@ int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val)
switch (le16_to_cpu(cmd.bufsize)) {
case sizeof(u8):
- if (oid == SNMP_MIB_OID_BSS_TYPE) {
- if (cmd.value[0] == 2)
- *out_val = IW_MODE_ADHOC;
- else
- *out_val = IW_MODE_INFRA;
- } else
- *out_val = cmd.value[0];
+ *out_val = cmd.value[0];
break;
case sizeof(u16):
*out_val = le16_to_cpu(*((__le16 *)(&cmd.value)));
@@ -591,14 +518,14 @@ out:
}
/**
- * @brief Get the min, max, and current TX power
+ * lbs_get_tx_power - Get the min, max, and current TX power
*
- * @param priv A pointer to struct lbs_private structure
- * @param curlevel Current power level in dBm
- * @param minlevel Minimum supported power level in dBm (optional)
- * @param maxlevel Maximum supported power level in dBm (optional)
+ * @priv: A pointer to &struct lbs_private structure
+ * @curlevel: Current power level in dBm
+ * @minlevel: Minimum supported power level in dBm (optional)
+ * @maxlevel: Maximum supported power level in dBm (optional)
*
- * @return 0 on success, error on failure
+ * returns: 0 on success, error on failure
*/
int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
s16 *maxlevel)
@@ -626,12 +553,12 @@ int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
}
/**
- * @brief Set the TX power
+ * lbs_set_tx_power - Set the TX power
*
- * @param priv A pointer to struct lbs_private structure
- * @param dbm The desired power level in dBm
+ * @priv: A pointer to &struct lbs_private structure
+ * @dbm: The desired power level in dBm
*
- * @return 0 on success, error on failure
+ * returns: 0 on success, error on failure
*/
int lbs_set_tx_power(struct lbs_private *priv, s16 dbm)
{
@@ -653,138 +580,46 @@ int lbs_set_tx_power(struct lbs_private *priv, s16 dbm)
return ret;
}
-static int lbs_cmd_802_11_monitor_mode(struct cmd_ds_command *cmd,
- u16 cmd_action, void *pdata_buf)
-{
- struct cmd_ds_802_11_monitor_mode *monitor = &cmd->params.monitor;
-
- cmd->command = cpu_to_le16(CMD_802_11_MONITOR_MODE);
- cmd->size =
- cpu_to_le16(sizeof(struct cmd_ds_802_11_monitor_mode) +
- S_DS_GEN);
-
- monitor->action = cpu_to_le16(cmd_action);
- if (cmd_action == CMD_ACT_SET) {
- monitor->mode =
- cpu_to_le16((u16) (*(u32 *) pdata_buf));
- }
-
- return 0;
-}
-
-static __le16 lbs_rate_to_fw_bitmap(int rate, int lower_rates_ok)
-{
-/* Bit Rate
-* 15:13 Reserved
-* 12 54 Mbps
-* 11 48 Mbps
-* 10 36 Mbps
-* 9 24 Mbps
-* 8 18 Mbps
-* 7 12 Mbps
-* 6 9 Mbps
-* 5 6 Mbps
-* 4 Reserved
-* 3 11 Mbps
-* 2 5.5 Mbps
-* 1 2 Mbps
-* 0 1 Mbps
-**/
-
- uint16_t ratemask;
- int i = lbs_data_rate_to_fw_index(rate);
- if (lower_rates_ok)
- ratemask = (0x1fef >> (12 - i));
- else
- ratemask = (1 << i);
- return cpu_to_le16(ratemask);
-}
-
-int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
- uint16_t cmd_action)
-{
- struct cmd_ds_802_11_rate_adapt_rateset cmd;
- int ret;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- if (!priv->cur_rate && !priv->enablehwauto)
- return -EINVAL;
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- cmd.action = cpu_to_le16(cmd_action);
- cmd.enablehwauto = cpu_to_le16(priv->enablehwauto);
- cmd.bitmap = lbs_rate_to_fw_bitmap(priv->cur_rate, priv->enablehwauto);
- ret = lbs_cmd_with_response(priv, CMD_802_11_RATE_ADAPT_RATESET, &cmd);
- if (!ret && cmd_action == CMD_ACT_GET) {
- priv->ratebitmap = le16_to_cpu(cmd.bitmap);
- priv->enablehwauto = le16_to_cpu(cmd.enablehwauto);
- }
-
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return ret;
-}
-EXPORT_SYMBOL_GPL(lbs_cmd_802_11_rate_adapt_rateset);
-
/**
- * @brief Set the data rate
+ * lbs_set_monitor_mode - Enable or disable monitor mode
+ * (only implemented on OLPC usb8388 FW)
*
- * @param priv A pointer to struct lbs_private structure
- * @param rate The desired data rate, or 0 to clear a locked rate
+ * @priv: A pointer to &struct lbs_private structure
+ * @enable: 1 to enable monitor mode, 0 to disable
*
- * @return 0 on success, error on failure
+ * returns: 0 on success, error on failure
*/
-int lbs_set_data_rate(struct lbs_private *priv, u8 rate)
+int lbs_set_monitor_mode(struct lbs_private *priv, int enable)
{
- struct cmd_ds_802_11_data_rate cmd;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CMD);
+ struct cmd_ds_802_11_monitor_mode cmd;
+ int ret;
memset(&cmd, 0, sizeof(cmd));
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+ if (enable)
+ cmd.mode = cpu_to_le16(0x1);
- if (rate > 0) {
- cmd.action = cpu_to_le16(CMD_ACT_SET_TX_FIX_RATE);
- cmd.rates[0] = lbs_data_rate_to_fw_index(rate);
- if (cmd.rates[0] == 0) {
- lbs_deb_cmd("DATA_RATE: invalid requested rate of"
- " 0x%02X\n", rate);
- ret = 0;
- goto out;
- }
- lbs_deb_cmd("DATA_RATE: set fixed 0x%02X\n", cmd.rates[0]);
- } else {
- cmd.action = cpu_to_le16(CMD_ACT_SET_TX_AUTO);
- lbs_deb_cmd("DATA_RATE: setting auto\n");
- }
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_DATA_RATE, &cmd);
- if (ret)
- goto out;
-
- lbs_deb_hex(LBS_DEB_CMD, "DATA_RATE_RESP", (u8 *) &cmd, sizeof (cmd));
+ lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable);
- /* FIXME: get actual rates FW can do if this command actually returns
- * all data rates supported.
- */
- priv->cur_rate = lbs_fw_index_to_data_rate(cmd.rates[0]);
- lbs_deb_cmd("DATA_RATE: current rate is 0x%02x\n", priv->cur_rate);
+ ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd);
+ if (ret == 0) {
+ priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP :
+ ARPHRD_ETHER;
+ }
-out:
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ lbs_deb_leave(LBS_DEB_CMD);
return ret;
}
/**
- * @brief Get the radio channel
+ * lbs_get_channel - Get the radio channel
*
- * @param priv A pointer to struct lbs_private structure
+ * @priv: A pointer to &struct lbs_private structure
*
- * @return The channel on success, error on failure
+ * returns: The channel on success, error on failure
*/
-int lbs_get_channel(struct lbs_private *priv)
+static int lbs_get_channel(struct lbs_private *priv)
{
struct cmd_ds_802_11_rf_channel cmd;
int ret = 0;
@@ -816,7 +651,7 @@ int lbs_update_channel(struct lbs_private *priv)
ret = lbs_get_channel(priv);
if (ret > 0) {
- priv->curbssparams.channel = ret;
+ priv->channel = ret;
ret = 0;
}
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
@@ -824,18 +659,18 @@ int lbs_update_channel(struct lbs_private *priv)
}
/**
- * @brief Set the radio channel
+ * lbs_set_channel - Set the radio channel
*
- * @param priv A pointer to struct lbs_private structure
- * @param channel The desired channel, or 0 to clear a locked channel
+ * @priv: A pointer to &struct lbs_private structure
+ * @channel: The desired channel, or 0 to clear a locked channel
*
- * @return 0 on success, error on failure
+ * returns: 0 on success, error on failure
*/
int lbs_set_channel(struct lbs_private *priv, u8 channel)
{
struct cmd_ds_802_11_rf_channel cmd;
#ifdef DEBUG
- u8 old_channel = priv->curbssparams.channel;
+ u8 old_channel = priv->channel;
#endif
int ret = 0;
@@ -850,285 +685,256 @@ int lbs_set_channel(struct lbs_private *priv, u8 channel)
if (ret)
goto out;
- priv->curbssparams.channel = (uint8_t) le16_to_cpu(cmd.channel);
+ priv->channel = (uint8_t) le16_to_cpu(cmd.channel);
lbs_deb_cmd("channel switch from %d to %d\n", old_channel,
- priv->curbssparams.channel);
+ priv->channel);
out:
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
-static int lbs_cmd_802_11_rssi(struct lbs_private *priv,
- struct cmd_ds_command *cmd)
+/**
+ * lbs_get_rssi - Get current RSSI and noise floor
+ *
+ * @priv: A pointer to &struct lbs_private structure
+ * @rssi: On successful return, signal level in mBm
+ * @nf: On successful return, Noise floor
+ *
+ * returns: The channel on success, error on failure
+ */
+int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf)
{
+ struct cmd_ds_802_11_rssi cmd;
+ int ret = 0;
lbs_deb_enter(LBS_DEB_CMD);
- 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 = cpu_to_le16(DEFAULT_BCN_AVG_FACTOR);
-
- /* reset Beacon SNR/NF/RSSI values */
- priv->SNR[TYPE_BEACON][TYPE_NOAVG] = 0;
- priv->SNR[TYPE_BEACON][TYPE_AVG] = 0;
- priv->NF[TYPE_BEACON][TYPE_NOAVG] = 0;
- priv->NF[TYPE_BEACON][TYPE_AVG] = 0;
- priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0;
- priv->RSSI[TYPE_BEACON][TYPE_AVG] = 0;
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
+ BUG_ON(rssi == NULL);
+ BUG_ON(nf == NULL);
-static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr,
- u8 cmd_action, void *pdata_buf)
-{
- struct lbs_offset_value *offval;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ /* Average SNR over last 8 beacons */
+ cmd.n_or_snr = cpu_to_le16(8);
- lbs_deb_enter(LBS_DEB_CMD);
+ ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd);
+ if (ret == 0) {
+ *nf = CAL_NF(le16_to_cpu(cmd.nf));
+ *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf));
+ }
- offval = (struct lbs_offset_value *)pdata_buf;
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
- switch (le16_to_cpu(cmdptr->command)) {
- case CMD_MAC_REG_ACCESS:
- {
- struct cmd_ds_mac_reg_access *macreg;
+/**
+ * lbs_set_11d_domain_info - Send regulatory and 802.11d domain information
+ * to the firmware
+ *
+ * @priv: pointer to &struct lbs_private
+ *
+ * returns: 0 on success, error code on failure
+*/
+int lbs_set_11d_domain_info(struct lbs_private *priv)
+{
+ struct wiphy *wiphy = priv->wdev->wiphy;
+ struct ieee80211_supported_band **bands = wiphy->bands;
+ struct cmd_ds_802_11d_domain_info cmd;
+ struct mrvl_ie_domain_param_set *domain = &cmd.domain;
+ struct ieee80211_country_ie_triplet *t;
+ enum ieee80211_band band;
+ struct ieee80211_channel *ch;
+ u8 num_triplet = 0;
+ u8 num_parsed_chan = 0;
+ u8 first_channel = 0, next_chan = 0, max_pwr = 0;
+ u8 i, flag = 0;
+ size_t triplet_size;
+ int ret = 0;
- 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;
+ lbs_deb_enter(LBS_DEB_11D);
+ if (!priv->country_code[0])
+ goto out;
- macreg->action = cpu_to_le16(cmd_action);
- macreg->offset = cpu_to_le16((u16) offval->offset);
- macreg->value = cpu_to_le32(offval->value);
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
- break;
- }
+ lbs_deb_11d("Setting country code '%c%c'\n",
+ priv->country_code[0], priv->country_code[1]);
- case CMD_BBP_REG_ACCESS:
- {
- struct cmd_ds_bbp_reg_access *bbpreg;
+ domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
- 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;
+ /* Set country code */
+ domain->country_code[0] = priv->country_code[0];
+ domain->country_code[1] = priv->country_code[1];
+ domain->country_code[2] = ' ';
- bbpreg->action = cpu_to_le16(cmd_action);
- bbpreg->offset = cpu_to_le16((u16) offval->offset);
- bbpreg->value = (u8) offval->value;
+ /* Now set up the channel triplets; firmware is somewhat picky here
+ * and doesn't validate channel numbers and spans; hence it would
+ * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since
+ * the last 3 aren't valid channels, the driver is responsible for
+ * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20)
+ * etc.
+ */
+ for (band = 0;
+ (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS);
+ band++) {
+
+ if (!bands[band])
+ continue;
+
+ for (i = 0;
+ (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS);
+ i++) {
+ ch = &bands[band]->channels[i];
+ if (ch->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ if (!flag) {
+ flag = 1;
+ next_chan = first_channel = (u32) ch->hw_value;
+ max_pwr = ch->max_power;
+ num_parsed_chan = 1;
+ continue;
+ }
- break;
+ if ((ch->hw_value == next_chan + 1) &&
+ (ch->max_power == max_pwr)) {
+ /* Consolidate adjacent channels */
+ next_chan++;
+ num_parsed_chan++;
+ } else {
+ /* Add this triplet */
+ lbs_deb_11d("11D triplet (%d, %d, %d)\n",
+ first_channel, num_parsed_chan,
+ max_pwr);
+ t = &domain->triplet[num_triplet];
+ t->chans.first_channel = first_channel;
+ t->chans.num_channels = num_parsed_chan;
+ t->chans.max_power = max_pwr;
+ num_triplet++;
+ flag = 0;
+ }
}
- 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;
+ if (flag) {
+ /* Add last triplet */
+ lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel,
+ num_parsed_chan, max_pwr);
+ t = &domain->triplet[num_triplet];
+ t->chans.first_channel = first_channel;
+ t->chans.num_channels = num_parsed_chan;
+ t->chans.max_power = max_pwr;
+ num_triplet++;
}
-
- default:
- break;
- }
-
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
-
-static int lbs_cmd_bt_access(struct cmd_ds_command *cmd,
- u16 cmd_action, void *pdata_buf)
-{
- struct cmd_ds_bt_access *bt_access = &cmd->params.bt;
- lbs_deb_enter_args(LBS_DEB_CMD, "action %d", 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_deb_hex(LBS_DEB_MESH, "BT_ADD: blinded MAC addr", bt_access->addr1, 6);
- break;
- case CMD_ACT_BT_ACCESS_DEL:
- memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN);
- lbs_deb_hex(LBS_DEB_MESH, "BT_DEL: blinded MAC addr", 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;
- case CMD_ACT_BT_ACCESS_SET_INVERT:
- bt_access->id = cpu_to_le32(*(u32 *) pdata_buf);
- break;
- case CMD_ACT_BT_ACCESS_GET_INVERT:
- break;
- default:
- break;
}
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
-static int lbs_cmd_fwt_access(struct cmd_ds_command *cmd,
- u16 cmd_action, void *pdata_buf)
-{
- struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt;
- lbs_deb_enter_args(LBS_DEB_CMD, "action %d", 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));
+ lbs_deb_11d("# triplets %d\n", num_triplet);
- fwt_access->action = cpu_to_le16(cmd_action);
+ /* Set command header sizes */
+ triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet);
+ domain->header.len = cpu_to_le16(sizeof(domain->country_code) +
+ triplet_size);
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
-
-int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
- struct cmd_ds_mesh_access *cmd)
-{
- int ret;
+ lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set",
+ (u8 *) &cmd.domain.country_code,
+ le16_to_cpu(domain->header.len));
- lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) +
+ sizeof(cmd.action) +
+ sizeof(cmd.domain.header) +
+ sizeof(cmd.domain.country_code) +
+ triplet_size);
- cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
- cmd->hdr.size = cpu_to_le16(sizeof(*cmd));
- cmd->hdr.result = 0;
+ ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd);
- cmd->action = cpu_to_le16(cmd_action);
-
- ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);
-
- lbs_deb_leave(LBS_DEB_CMD);
+out:
+ lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
return ret;
}
-static int __lbs_mesh_config_send(struct lbs_private *priv,
- struct cmd_ds_mesh_config *cmd,
- uint16_t action, uint16_t type)
+/**
+ * lbs_get_reg - Read a MAC, Baseband, or RF register
+ *
+ * @priv: pointer to &struct lbs_private
+ * @reg: register command, one of CMD_MAC_REG_ACCESS,
+ * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS
+ * @offset: byte offset of the register to get
+ * @value: on success, the value of the register at 'offset'
+ *
+ * returns: 0 on success, error code on failure
+*/
+int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value)
{
- int ret;
+ struct cmd_ds_reg_access cmd;
+ int ret = 0;
lbs_deb_enter(LBS_DEB_CMD);
- cmd->hdr.command = cpu_to_le16(CMD_MESH_CONFIG);
- cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
- cmd->hdr.result = 0;
-
- cmd->type = cpu_to_le16(type);
- cmd->action = cpu_to_le16(action);
-
- ret = lbs_cmd_with_response(priv, CMD_MESH_CONFIG, cmd);
+ BUG_ON(value == NULL);
- lbs_deb_leave(LBS_DEB_CMD);
- return ret;
-}
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_GET);
+ cmd.offset = cpu_to_le16(offset);
-int lbs_mesh_config_send(struct lbs_private *priv,
- struct cmd_ds_mesh_config *cmd,
- uint16_t action, uint16_t type)
-{
- int ret;
+ if (reg != CMD_MAC_REG_ACCESS &&
+ reg != CMD_BBP_REG_ACCESS &&
+ reg != CMD_RF_REG_ACCESS) {
+ ret = -EINVAL;
+ goto out;
+ }
- if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
- return -EOPNOTSUPP;
+ ret = lbs_cmd_with_response(priv, reg, &cmd);
+ if (!ret) {
+ if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS)
+ *value = cmd.value.bbp_rf;
+ else if (reg == CMD_MAC_REG_ACCESS)
+ *value = le32_to_cpu(cmd.value.mac);
+ }
- ret = __lbs_mesh_config_send(priv, cmd, action, type);
+out:
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
-/* This function is the CMD_MESH_CONFIG legacy function. It only handles the
- * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG
- * are all handled by preparing a struct cmd_ds_mesh_config and passing it to
- * lbs_mesh_config_send.
- */
-int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
+/**
+ * lbs_set_reg - Write a MAC, Baseband, or RF register
+ *
+ * @priv: pointer to &struct lbs_private
+ * @reg: register command, one of CMD_MAC_REG_ACCESS,
+ * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS
+ * @offset: byte offset of the register to set
+ * @value: the value to write to the register at 'offset'
+ *
+ * returns: 0 on success, error code on failure
+*/
+int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value)
{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_meshie *ie;
- DECLARE_SSID_BUF(ssid);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.channel = cpu_to_le16(chan);
- ie = (struct mrvl_meshie *)cmd.data;
-
- switch (action) {
- case CMD_ACT_MESH_CONFIG_START:
- ie->id = WLAN_EID_GENERIC;
- ie->val.oui[0] = 0x00;
- ie->val.oui[1] = 0x50;
- ie->val.oui[2] = 0x43;
- ie->val.type = MARVELL_MESH_IE_TYPE;
- ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
- ie->val.version = MARVELL_MESH_IE_VERSION;
- ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
- ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
- ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
- ie->val.mesh_id_len = priv->mesh_ssid_len;
- memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len);
- ie->len = sizeof(struct mrvl_meshie_val) -
- IW_ESSID_MAX_SIZE + priv->mesh_ssid_len;
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
- break;
- case CMD_ACT_MESH_CONFIG_STOP:
- break;
- default:
- return -1;
- }
- lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
- action, priv->mesh_tlv, chan,
- print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len));
+ struct cmd_ds_reg_access cmd;
+ int ret = 0;
- return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
-}
+ lbs_deb_enter(LBS_DEB_CMD);
-static int lbs_cmd_bcn_ctrl(struct lbs_private * priv,
- struct cmd_ds_command *cmd,
- u16 cmd_action)
-{
- struct cmd_ds_802_11_beacon_control
- *bcn_ctrl = &cmd->params.bcn_ctrl;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+ cmd.offset = cpu_to_le16(offset);
- lbs_deb_enter(LBS_DEB_CMD);
- cmd->size =
- cpu_to_le16(sizeof(struct cmd_ds_802_11_beacon_control)
- + S_DS_GEN);
- cmd->command = cpu_to_le16(CMD_802_11_BEACON_CTRL);
+ if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS)
+ cmd.value.bbp_rf = (u8) (value & 0xFF);
+ else if (reg == CMD_MAC_REG_ACCESS)
+ cmd.value.mac = cpu_to_le32(value);
+ else {
+ ret = -EINVAL;
+ goto out;
+ }
- bcn_ctrl->action = cpu_to_le16(cmd_action);
- bcn_ctrl->beacon_enable = cpu_to_le16(priv->beacon_enable);
- bcn_ctrl->beacon_period = cpu_to_le16(priv->beacon_period);
+ ret = lbs_cmd_with_response(priv, reg, &cmd);
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
+out:
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
}
static void lbs_queue_cmd(struct lbs_private *priv,
@@ -1151,14 +957,17 @@ static void lbs_queue_cmd(struct lbs_private *priv,
/* Exit_PS command needs to be queued in the header always. */
if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) {
- struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf[1];
+ struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf;
- if (psm->action == cpu_to_le16(CMD_SUBCMD_EXIT_PS)) {
+ if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) {
if (priv->psstate != PS_STATE_FULL_POWER)
addtail = 0;
}
}
+ if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM)
+ addtail = 0;
+
spin_lock_irqsave(&priv->driver_lock, flags);
if (addtail)
@@ -1190,16 +999,16 @@ static void lbs_submit_command(struct lbs_private *priv,
cmd = cmdnode->cmdbuf;
spin_lock_irqsave(&priv->driver_lock, flags);
+ priv->seqnum++;
+ cmd->seqnum = cpu_to_le16(priv->seqnum);
priv->cur_cmd = cmdnode;
- priv->cur_cmd_retcode = 0;
spin_unlock_irqrestore(&priv->driver_lock, flags);
cmdsize = le16_to_cpu(cmd->size);
command = le16_to_cpu(cmd->command);
/* These commands take longer */
- if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE ||
- command == CMD_802_11_AUTHENTICATE)
+ if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE)
timeo = 5 * HZ;
lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n",
@@ -1209,19 +1018,29 @@ static void lbs_submit_command(struct lbs_private *priv,
ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize);
if (ret) {
- lbs_pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret);
- /* Let the timer kick in and retry, and potentially reset
- the whole thing if the condition persists */
- timeo = HZ/4;
+ netdev_info(priv->dev, "DNLD_CMD: hw_host_to_card failed: %d\n",
+ ret);
+ /* Reset dnld state machine, report failure */
+ priv->dnld_sent = DNLD_RES_RECEIVED;
+ lbs_complete_command(priv, cmdnode, ret);
}
- /* Setup the timer after transmit command */
- mod_timer(&priv->command_timer, jiffies + timeo);
+ if (command == CMD_802_11_DEEP_SLEEP) {
+ if (priv->is_auto_deep_sleep_enabled) {
+ priv->wakeup_dev_required = 1;
+ priv->dnld_sent = 0;
+ }
+ priv->is_deep_sleep = 1;
+ lbs_complete_command(priv, cmdnode, 0);
+ } else {
+ /* Setup the timer after transmit command */
+ mod_timer(&priv->command_timer, jiffies + timeo);
+ }
lbs_deb_leave(LBS_DEB_HOST);
}
-/**
+/*
* This function inserts command node to cmdfreeq
* after cleans it. Requires priv->driver_lock held.
*/
@@ -1253,19 +1072,34 @@ static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
spin_unlock_irqrestore(&priv->driver_lock, flags);
}
-void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
- int result)
+void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
+ int result)
{
- if (cmd == priv->cur_cmd)
- priv->cur_cmd_retcode = result;
+ /*
+ * Normally, commands are removed from cmdpendingq before being
+ * submitted. However, we can arrive here on alternative codepaths
+ * where the command is still pending. Make sure the command really
+ * isn't part of a list at this point.
+ */
+ list_del_init(&cmd->list);
cmd->result = result;
cmd->cmdwaitqwoken = 1;
- wake_up_interruptible(&cmd->cmdwait_q);
+ wake_up(&cmd->cmdwait_q);
if (!cmd->callback || cmd->callback == lbs_cmd_async_callback)
__lbs_cleanup_and_insert_cmd(priv, cmd);
priv->cur_cmd = NULL;
+ wake_up(&priv->waitq);
+}
+
+void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
+ int result)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ __lbs_complete_command(priv, cmd, result);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
}
int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
@@ -1282,9 +1116,6 @@ int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
if (priv->fwrelease < 0x09000000) {
switch (preamble) {
case RADIO_PREAMBLE_SHORT:
- if (!(priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE))
- goto out;
- /* Fall through */
case RADIO_PREAMBLE_AUTO:
case RADIO_PREAMBLE_LONG:
cmd.control = cpu_to_le16(preamble);
@@ -1328,211 +1159,29 @@ void lbs_set_mac_control(struct lbs_private *priv)
lbs_deb_leave(LBS_DEB_CMD);
}
-/**
- * @brief This function prepare the command before send to firmware.
- *
- * @param priv A pointer to struct lbs_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 lbs_prepare_and_send_command(struct lbs_private *priv,
- u16 cmd_no,
- u16 cmd_action,
- u16 wait_option, u32 cmd_oid, void *pdata_buf)
+int lbs_set_mac_control_sync(struct lbs_private *priv)
{
+ struct cmd_ds_mac_control cmd;
int ret = 0;
- struct cmd_ctrl_node *cmdnode;
- struct cmd_ds_command *cmdptr;
- unsigned long flags;
-
- lbs_deb_enter(LBS_DEB_HOST);
-
- if (!priv) {
- lbs_deb_host("PREP_CMD: priv is NULL\n");
- ret = -1;
- goto done;
- }
-
- if (priv->surpriseremoved) {
- lbs_deb_host("PREP_CMD: card removed\n");
- ret = -1;
- goto done;
- }
-
- cmdnode = lbs_get_cmd_ctrl_node(priv);
-
- if (cmdnode == NULL) {
- lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
-
- /* Wake up main thread to execute next command */
- wake_up_interruptible(&priv->waitq);
- ret = -1;
- goto done;
- }
-
- cmdnode->callback = NULL;
- cmdnode->callback_arg = (unsigned long)pdata_buf;
-
- cmdptr = (struct cmd_ds_command *)cmdnode->cmdbuf;
-
- lbs_deb_host("PREP_CMD: command 0x%04x\n", cmd_no);
-
- /* Set sequence number, command and INT option */
- priv->seqnum++;
- cmdptr->seqnum = cpu_to_le16(priv->seqnum);
-
- cmdptr->command = cpu_to_le16(cmd_no);
- cmdptr->result = 0;
-
- switch (cmd_no) {
- case CMD_802_11_PS_MODE:
- ret = lbs_cmd_802_11_ps_mode(cmdptr, cmd_action);
- break;
-
- case CMD_802_11_ASSOCIATE:
- case CMD_802_11_REASSOCIATE:
- ret = lbs_cmd_80211_associate(priv, cmdptr, pdata_buf);
- break;
- case CMD_802_11_AUTHENTICATE:
- ret = lbs_cmd_80211_authenticate(priv, cmdptr, pdata_buf);
- break;
-
- case CMD_MAC_REG_ACCESS:
- case CMD_BBP_REG_ACCESS:
- case CMD_RF_REG_ACCESS:
- ret = lbs_cmd_reg_access(cmdptr, cmd_action, pdata_buf);
- break;
-
- case CMD_802_11_MONITOR_MODE:
- ret = lbs_cmd_802_11_monitor_mode(cmdptr,
- cmd_action, pdata_buf);
- break;
-
- case CMD_802_11_RSSI:
- ret = lbs_cmd_802_11_rssi(priv, cmdptr);
- 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 = lbs_cmd_802_11d_domain_info(priv, cmdptr,
- cmd_no, cmd_action);
- 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(le16_to_cpu(gpio->header.len)
- + S_DS_GEN
- + ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN);
- gpio->header.len = gpio->header.len;
-
- ret = 0;
- break;
- }
-
- case CMD_BT_ACCESS:
- ret = lbs_cmd_bt_access(cmdptr, cmd_action, pdata_buf);
- break;
-
- case CMD_FWT_ACCESS:
- ret = lbs_cmd_fwt_access(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_BEACON_CTRL:
- ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
- break;
- default:
- lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no);
- ret = -1;
- break;
- }
-
- /* return error, since the command preparation failed */
- if (ret != 0) {
- lbs_deb_host("PREP_CMD: command preparation failed\n");
- lbs_cleanup_and_insert_cmd(priv, cmdnode);
- ret = -1;
- goto done;
- }
-
- cmdnode->cmdwaitqwoken = 0;
-
- lbs_queue_cmd(priv, cmdnode);
- wake_up_interruptible(&priv->waitq);
-
- if (wait_option & CMD_OPTION_WAITFORRSP) {
- lbs_deb_host("PREP_CMD: wait for response\n");
- might_sleep();
- wait_event_interruptible(cmdnode->cmdwait_q,
- cmdnode->cmdwaitqwoken);
- }
+ lbs_deb_enter(LBS_DEB_CMD);
- spin_lock_irqsave(&priv->driver_lock, flags);
- if (priv->cur_cmd_retcode) {
- lbs_deb_host("PREP_CMD: command failed with return code %d\n",
- priv->cur_cmd_retcode);
- priv->cur_cmd_retcode = 0;
- ret = -1;
- }
- spin_unlock_irqrestore(&priv->driver_lock, flags);
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(priv->mac_control);
+ cmd.reserved = 0;
+ ret = lbs_cmd_with_response(priv, CMD_MAC_CONTROL, &cmd);
-done:
- lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
+ lbs_deb_leave(LBS_DEB_CMD);
return ret;
}
/**
- * @brief This function allocates the command buffer and link
- * it to command free queue.
+ * lbs_allocate_cmd_buffer - allocates the command buffer and links
+ * it to command free queue
+ *
+ * @priv: A pointer to &struct lbs_private structure
*
- * @param priv A pointer to struct lbs_private structure
- * @return 0 or -1
+ * returns: 0 for success or -1 on error
*/
int lbs_allocate_cmd_buffer(struct lbs_private *priv)
{
@@ -1574,10 +1223,11 @@ done:
}
/**
- * @brief This function frees the command buffer.
+ * lbs_free_cmd_buffer - free the command buffer
*
- * @param priv A pointer to struct lbs_private structure
- * @return 0 or -1
+ * @priv: A pointer to &struct lbs_private structure
+ *
+ * returns: 0 for success
*/
int lbs_free_cmd_buffer(struct lbs_private *priv)
{
@@ -1614,13 +1264,15 @@ done:
}
/**
- * @brief This function gets a free command node if available in
- * command free queue.
+ * lbs_get_free_cmd_node - gets a free command node if available in
+ * command free queue
+ *
+ * @priv: A pointer to &struct lbs_private structure
*
- * @param priv A pointer to struct lbs_private structure
- * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL
+ * returns: A pointer to &cmd_ctrl_node structure on success
+ * or %NULL on error
*/
-static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv)
+static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv)
{
struct cmd_ctrl_node *tempnode;
unsigned long flags;
@@ -1635,7 +1287,7 @@ static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv)
if (!list_empty(&priv->cmdfreeq)) {
tempnode = list_first_entry(&priv->cmdfreeq,
struct cmd_ctrl_node, list);
- list_del(&tempnode->list);
+ list_del_init(&tempnode->list);
} else {
lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
tempnode = NULL;
@@ -1648,12 +1300,12 @@ static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv)
}
/**
- * @brief This function executes next command in command
- * pending queue. It will put firmware back to PS mode
- * if applicable.
+ * lbs_execute_next_command - execute next command in command
+ * pending queue. Will put firmware back to PS mode if applicable.
*
- * @param priv A pointer to struct lbs_private structure
- * @return 0 or -1
+ * @priv: A pointer to &struct lbs_private structure
+ *
+ * returns: 0 on success or -1 on error
*/
int lbs_execute_next_command(struct lbs_private *priv)
{
@@ -1670,7 +1322,8 @@ int lbs_execute_next_command(struct lbs_private *priv)
spin_lock_irqsave(&priv->driver_lock, flags);
if (priv->cur_cmd) {
- lbs_pr_alert( "EXEC_NEXT_CMD: already processing command!\n");
+ netdev_alert(priv->dev,
+ "EXEC_NEXT_CMD: already processing command!\n");
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = -1;
goto done;
@@ -1703,10 +1356,10 @@ int lbs_execute_next_command(struct lbs_private *priv)
/*
* 1. Non-PS command:
* Queue it. set needtowakeup to TRUE if current state
- * is SLEEP, otherwise call lbs_ps_wakeup to send Exit_PS.
- * 2. PS command but not Exit_PS:
+ * is SLEEP, otherwise call send EXIT_PS.
+ * 2. PS command but not EXIT_PS:
* Ignore it.
- * 3. PS command Exit_PS:
+ * 3. PS command EXIT_PS:
* Set needtowakeup to TRUE if current state is SLEEP,
* otherwise send this command down to firmware
* immediately.
@@ -1720,8 +1373,11 @@ int lbs_execute_next_command(struct lbs_private *priv)
/* w/ new scheme, it will not reach here.
since it is blocked in main_thread. */
priv->needtowakeup = 1;
- } else
- lbs_ps_wakeup(priv, 0);
+ } else {
+ lbs_set_ps_mode(priv,
+ PS_MODE_ACTION_EXIT_PS,
+ false);
+ }
ret = 0;
goto done;
@@ -1736,13 +1392,10 @@ int lbs_execute_next_command(struct lbs_private *priv)
"EXEC_NEXT_CMD: PS cmd, action 0x%02x\n",
psm->action);
if (psm->action !=
- cpu_to_le16(CMD_SUBCMD_EXIT_PS)) {
+ cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) {
lbs_deb_host(
"EXEC_NEXT_CMD: ignore ENTER_PS cmd\n");
- list_del(&cmdnode->list);
- spin_lock_irqsave(&priv->driver_lock, flags);
lbs_complete_command(priv, cmdnode, 0);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = 0;
goto done;
@@ -1752,10 +1405,7 @@ int lbs_execute_next_command(struct lbs_private *priv)
(priv->psstate == PS_STATE_PRE_SLEEP)) {
lbs_deb_host(
"EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n");
- list_del(&cmdnode->list);
- spin_lock_irqsave(&priv->driver_lock, flags);
lbs_complete_command(priv, cmdnode, 0);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
priv->needtowakeup = 1;
ret = 0;
@@ -1766,7 +1416,9 @@ int lbs_execute_next_command(struct lbs_private *priv)
"EXEC_NEXT_CMD: sending EXIT_PS\n");
}
}
- list_del(&cmdnode->list);
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ list_del_init(&cmdnode->list);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
le16_to_cpu(cmd->command));
lbs_submit_command(priv, cmdnode);
@@ -1775,10 +1427,19 @@ int lbs_execute_next_command(struct lbs_private *priv)
* check if in power save mode, if yes, put the device back
* to PS mode
*/
+#ifdef TODO
+ /*
+ * This was the old code for libertas+wext. Someone that
+ * understands this beast should re-code it in a sane way.
+ *
+ * I actually don't understand why this is related to WPA
+ * and to connection status, shouldn't powering should be
+ * independ of such things?
+ */
if ((priv->psmode != LBS802_11POWERMODECAM) &&
(priv->psstate == PS_STATE_FULL_POWER) &&
((priv->connect_status == LBS_CONNECTED) ||
- (priv->mesh_connect_status == LBS_CONNECTED))) {
+ lbs_mesh_connected(priv))) {
if (priv->secinfo.WPAenabled ||
priv->secinfo.WPA2enabled) {
/* check for valid WPA group keys */
@@ -1787,15 +1448,19 @@ int lbs_execute_next_command(struct lbs_private *priv)
lbs_deb_host(
"EXEC_NEXT_CMD: WPA enabled and GTK_SET"
" go back to PS_SLEEP");
- lbs_ps_sleep(priv, 0);
+ lbs_set_ps_mode(priv,
+ PS_MODE_ACTION_ENTER_PS,
+ false);
}
} else {
lbs_deb_host(
"EXEC_NEXT_CMD: cmdpendingq empty, "
"go back to PS_SLEEP");
- lbs_ps_sleep(priv, 0);
+ lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS,
+ false);
}
}
+#endif
}
ret = 0;
@@ -1804,30 +1469,6 @@ done:
return ret;
}
-void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str)
-{
- union iwreq_data iwrq;
- u8 buf[50];
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- 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_deb_wext("event indication string %s\n", (char *)buf);
- lbs_deb_wext("event indication length %d\n", iwrq.data.length);
- lbs_deb_wext("sending wireless event IWEVCUSTOM for %s\n", str);
-
- wireless_send_event(priv->dev, IWEVCUSTOM, &iwrq, buf);
-
- lbs_deb_leave(LBS_DEB_WEXT);
-}
-
static void lbs_send_confirmsleep(struct lbs_private *priv)
{
unsigned long flags;
@@ -1840,7 +1481,7 @@ static void lbs_send_confirmsleep(struct lbs_private *priv)
ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep,
sizeof(confirm_sleep));
if (ret) {
- lbs_pr_alert("confirm_sleep failed\n");
+ netdev_alert(priv->dev, "confirm_sleep failed\n");
goto out;
}
@@ -1849,8 +1490,13 @@ static void lbs_send_confirmsleep(struct lbs_private *priv)
/* We don't get a response on the sleep-confirmation */
priv->dnld_sent = DNLD_RES_RECEIVED;
+ if (priv->is_host_sleep_configured) {
+ priv->is_host_sleep_activated = 1;
+ wake_up_interruptible(&priv->host_sleep_q);
+ }
+
/* If nothing to do, go back to sleep (?) */
- if (!__kfifo_len(priv->event_fifo) && !priv->resp_len[priv->resp_idx])
+ if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx])
priv->psstate = PS_STATE_SLEEP;
spin_unlock_irqrestore(&priv->driver_lock, flags);
@@ -1859,50 +1505,13 @@ out:
lbs_deb_leave(LBS_DEB_HOST);
}
-void lbs_ps_sleep(struct lbs_private *priv, int wait_option)
-{
- lbs_deb_enter(LBS_DEB_HOST);
-
- /*
- * PS is currently supported only in Infrastructure mode
- * Remove this check if it is to be supported in IBSS mode also
- */
-
- lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
- CMD_SUBCMD_ENTER_PS, wait_option, 0, NULL);
-
- lbs_deb_leave(LBS_DEB_HOST);
-}
-
/**
- * @brief This function sends Exit_PS command to firmware.
+ * lbs_ps_confirm_sleep - checks condition and prepares to
+ * send sleep confirm command to firmware if ok
*
- * @param priv A pointer to struct lbs_private structure
- * @param wait_option wait response or not
- * @return n/a
- */
-void lbs_ps_wakeup(struct lbs_private *priv, int wait_option)
-{
- __le32 Localpsmode;
-
- lbs_deb_enter(LBS_DEB_HOST);
-
- Localpsmode = cpu_to_le32(LBS802_11POWERMODECAM);
-
- lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
- CMD_SUBCMD_EXIT_PS,
- wait_option, 0, &Localpsmode);
-
- lbs_deb_leave(LBS_DEB_HOST);
-}
-
-/**
- * @brief This function checks condition and prepares to
- * send sleep confirm command to firmware if ok.
+ * @priv: A pointer to &struct lbs_private structure
*
- * @param priv A pointer to struct lbs_private structure
- * @param psmode Power Saving mode
- * @return n/a
+ * returns: n/a
*/
void lbs_ps_confirm_sleep(struct lbs_private *priv)
{
@@ -1924,7 +1533,7 @@ void lbs_ps_confirm_sleep(struct lbs_private *priv)
}
/* Pending events or command responses? */
- if (__kfifo_len(priv->event_fifo) || priv->resp_len[priv->resp_idx]) {
+ if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) {
allowed = 0;
lbs_deb_host("pending events or command responses\n");
}
@@ -1942,16 +1551,16 @@ void lbs_ps_confirm_sleep(struct lbs_private *priv)
/**
- * @brief Configures the transmission power control functionality.
+ * lbs_set_tpc_cfg - Configures the transmission power control functionality
*
- * @param priv A pointer to struct lbs_private structure
- * @param enable Transmission power control enable
- * @param p0 Power level when link quality is good (dBm).
- * @param p1 Power level when link quality is fair (dBm).
- * @param p2 Power level when link quality is poor (dBm).
- * @param usesnr Use Signal to Noise Ratio in TPC
+ * @priv: A pointer to &struct lbs_private structure
+ * @enable: Transmission power control enable
+ * @p0: Power level when link quality is good (dBm).
+ * @p1: Power level when link quality is fair (dBm).
+ * @p2: Power level when link quality is poor (dBm).
+ * @usesnr: Use Signal to Noise Ratio in TPC
*
- * @return 0 on success
+ * returns: 0 on success
*/
int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
int8_t p2, int usesnr)
@@ -1974,15 +1583,15 @@ int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
}
/**
- * @brief Configures the power adaptation settings.
+ * lbs_set_power_adapt_cfg - Configures the power adaptation settings
*
- * @param priv A pointer to struct lbs_private structure
- * @param enable Power adaptation enable
- * @param p0 Power level for 1, 2, 5.5 and 11 Mbps (dBm).
- * @param p1 Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm).
- * @param p2 Power level for 48 and 54 Mbps (dBm).
+ * @priv: A pointer to &struct lbs_private structure
+ * @enable: Power adaptation enable
+ * @p0: Power level for 1, 2, 5.5 and 11 Mbps (dBm).
+ * @p1: Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm).
+ * @p2: Power level for 48 and 54 Mbps (dBm).
*
- * @return 0 on Success
+ * returns: 0 on Success
*/
int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
@@ -2005,7 +1614,7 @@ int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
}
-static struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
+struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
unsigned long callback_arg)
@@ -2020,12 +1629,23 @@ static struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
goto done;
}
- cmdnode = lbs_get_cmd_ctrl_node(priv);
+ /* No commands are allowed in Deep Sleep until we toggle the GPIO
+ * to wake up the card and it has signaled that it's ready.
+ */
+ if (!priv->is_auto_deep_sleep_enabled) {
+ if (priv->is_deep_sleep) {
+ lbs_deb_cmd("command not allowed in deep sleep\n");
+ cmdnode = ERR_PTR(-EBUSY);
+ goto done;
+ }
+ }
+
+ cmdnode = lbs_get_free_cmd_node(priv);
if (cmdnode == NULL) {
lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
/* Wake up main thread to execute next command */
- wake_up_interruptible(&priv->waitq);
+ wake_up(&priv->waitq);
cmdnode = ERR_PTR(-ENOBUFS);
goto done;
}
@@ -2036,18 +1656,16 @@ static struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
/* Copy the incoming command to the buffer */
memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size);
- /* Set sequence number, clean result, move to buffer */
- priv->seqnum++;
+ /* Set command, clean result, move to buffer */
cmdnode->cmdbuf->command = cpu_to_le16(command);
cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size);
- cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum);
cmdnode->cmdbuf->result = 0;
lbs_deb_host("PREP_CMD: command 0x%04x\n", command);
cmdnode->cmdwaitqwoken = 0;
lbs_queue_cmd(priv, cmdnode);
- wake_up_interruptible(&priv->waitq);
+ wake_up(&priv->waitq);
done:
lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode);
@@ -2082,12 +1700,18 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command,
}
might_sleep();
- wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
+
+ /*
+ * Be careful with signals here. A signal may be received as the system
+ * goes into suspend or resume. We do not want this to interrupt the
+ * command, so we perform an uninterruptible sleep.
+ */
+ wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
spin_lock_irqsave(&priv->driver_lock, flags);
ret = cmdnode->result;
if (ret)
- lbs_pr_info("PREP_CMD: command 0x%04x failed: %d\n",
+ netdev_info(priv->dev, "PREP_CMD: command 0x%04x failed: %d\n",
command, ret);
__lbs_cleanup_and_insert_cmd(priv, cmdnode);
@@ -2098,5 +1722,3 @@ done:
return ret;
}
EXPORT_SYMBOL_GPL(__lbs_cmd);
-
-
diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h
index 392e578ca09..4279e8ab95f 100644
--- a/drivers/net/wireless/libertas/cmd.h
+++ b/drivers/net/wireless/libertas/cmd.h
@@ -3,11 +3,32 @@
#ifndef _LBS_CMD_H_
#define _LBS_CMD_H_
-#include "hostcmd.h"
+#include <net/cfg80211.h>
+
+#include "host.h"
#include "dev.h"
+
+/* Command & response transfer between host and card */
+
+struct cmd_ctrl_node {
+ struct list_head list;
+ int result;
+ /* command response */
+ int (*callback)(struct lbs_private *,
+ unsigned long,
+ struct cmd_header *);
+ unsigned long callback_arg;
+ /* command data */
+ struct cmd_header *cmdbuf;
+ /* wait queue */
+ u16 cmdwaitqwoken;
+ wait_queue_head_t cmdwait_q;
+};
+
+
/* lbs_cmd() infers the size of the buffer to copy data back into, from
- the size of the target of the pointer. Since the command to be sent
+ the size of the target of the pointer. Since the command to be sent
may often be smaller, that size is set in cmd->size by the caller.*/
#define lbs_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \
uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \
@@ -26,62 +47,94 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command,
int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
unsigned long callback_arg);
-int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
- int8_t p1, int8_t p2);
+struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
+ uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
+ int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
+ unsigned long callback_arg);
-int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
- int8_t p2, int usesnr);
+int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
+ struct cmd_header *resp);
-int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
- int8_t p1, int8_t p2);
+int lbs_allocate_cmd_buffer(struct lbs_private *priv);
+int lbs_free_cmd_buffer(struct lbs_private *priv);
-int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
- int8_t p2, int usesnr);
+int lbs_execute_next_command(struct lbs_private *priv);
+void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
+ int result);
+void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
+ int result);
+int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len);
-int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
- struct cmd_header *resp);
-int lbs_update_hw_spec(struct lbs_private *priv);
+/* From cmdresp.c */
-int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
- struct cmd_ds_mesh_access *cmd);
+void lbs_mac_event_disconnected(struct lbs_private *priv);
-int lbs_set_data_rate(struct lbs_private *priv, u8 rate);
-int lbs_get_channel(struct lbs_private *priv);
+
+/* Events */
+
+int lbs_process_event(struct lbs_private *priv, u32 event);
+
+
+/* Actual commands */
+
+int lbs_update_hw_spec(struct lbs_private *priv);
+
int lbs_set_channel(struct lbs_private *priv, u8 channel);
-int lbs_mesh_config_send(struct lbs_private *priv,
- struct cmd_ds_mesh_config *cmd,
- uint16_t action, uint16_t type);
-int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan);
+int lbs_update_channel(struct lbs_private *priv);
int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
struct wol_config *p_wol_config);
-int lbs_suspend(struct lbs_private *priv);
-void lbs_resume(struct lbs_private *priv);
-int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
- uint16_t cmd_action);
-int lbs_cmd_802_11_inactivity_timeout(struct lbs_private *priv,
- uint16_t cmd_action, uint16_t *timeout);
int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
struct sleep_params *sp);
-int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
- struct assoc_request *assoc);
-int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
- uint16_t *enable);
-int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
- struct assoc_request *assoc);
-int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
- s16 *maxlevel);
-int lbs_set_tx_power(struct lbs_private *priv, s16 dbm);
+void lbs_ps_confirm_sleep(struct lbs_private *priv);
int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on);
+void lbs_set_mac_control(struct lbs_private *priv);
+int lbs_set_mac_control_sync(struct lbs_private *priv);
+
+int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
+ s16 *maxlevel);
+
int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val);
int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val);
+
+/* Commands only used in wext.c, assoc. and scan.c */
+
+int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
+ int8_t p1, int8_t p2);
+
+int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
+ int8_t p2, int usesnr);
+
+int lbs_set_data_rate(struct lbs_private *priv, u8 rate);
+
+int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
+ uint16_t cmd_action);
+
+int lbs_set_tx_power(struct lbs_private *priv, s16 dbm);
+
+int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep);
+
+int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep);
+
+int lbs_set_monitor_mode(struct lbs_private *priv, int enable);
+
+int lbs_get_rssi(struct lbs_private *priv, s8 *snr, s8 *nf);
+
+int lbs_set_11d_domain_info(struct lbs_private *priv);
+
+int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value);
+
+int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value);
+
+int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block);
+
#endif /* _LBS_CMD_H */
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
index bcf2a9756fb..65f18f1e869 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -1,47 +1,42 @@
-/**
- * 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>
+/*
+ * This file contains the handling of command
+ * responses as well as events generated by firmware.
+ */
-#include <net/iw_handler.h>
+#include <linux/hardirq.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/unaligned.h>
+#include <net/cfg80211.h>
-#include "host.h"
-#include "decl.h"
-#include "defs.h"
-#include "dev.h"
-#include "assoc.h"
-#include "wext.h"
+#include "cfg.h"
+#include "cmd.h"
/**
- * @brief This function handles disconnect event. it
- * reports disconnect to upper layer, clean tx/rx packets,
- * reset link state etc.
+ * lbs_mac_event_disconnected - handles disconnect event. It
+ * reports disconnect to upper layer, clean tx/rx packets,
+ * reset link state etc.
+ *
+ * @priv: A pointer to struct lbs_private structure
*
- * @param priv A pointer to struct lbs_private structure
- * @return n/a
+ * returns: n/a
*/
void lbs_mac_event_disconnected(struct lbs_private *priv)
{
- union iwreq_data wrqu;
-
if (priv->connect_status != LBS_CONNECTED)
return;
lbs_deb_enter(LBS_DEB_ASSOC);
- 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->dev, SIOCGIWAP, &wrqu, NULL);
+
+ if (priv->wdev->iftype == NL80211_IFTYPE_STATION)
+ lbs_send_disconnect_notification(priv);
/* report disconnect to upper layer */
netif_stop_queue(priv->dev);
@@ -52,237 +47,16 @@ void lbs_mac_event_disconnected(struct lbs_private *priv)
priv->currenttxskb = NULL;
priv->tx_pending_len = 0;
- /* reset SNR/NF/RSSI values */
- memset(priv->SNR, 0x00, sizeof(priv->SNR));
- memset(priv->NF, 0x00, sizeof(priv->NF));
- memset(priv->RSSI, 0x00, sizeof(priv->RSSI));
- memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR));
- memset(priv->rawNF, 0x00, sizeof(priv->rawNF));
- priv->nextSNRNF = 0;
- priv->numSNRNF = 0;
priv->connect_status = LBS_DISCONNECTED;
- /* Clear out associated SSID and BSSID since connection is
- * no longer valid.
- */
- memset(&priv->curbssparams.bssid, 0, ETH_ALEN);
- memset(&priv->curbssparams.ssid, 0, IW_ESSID_MAX_SIZE);
- priv->curbssparams.ssid_len = 0;
-
if (priv->psstate != PS_STATE_FULL_POWER) {
/* make firmware to exit PS mode */
lbs_deb_cmd("disconnected, so exit PS mode\n");
- lbs_ps_wakeup(priv, 0);
+ lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false);
}
lbs_deb_leave(LBS_DEB_ASSOC);
}
-/**
- * @brief This function handles MIC failure event.
- *
- * @param priv A pointer to struct lbs_private structure
- * @para event the event id
- * @return n/a
- */
-static void handle_mic_failureevent(struct lbs_private *priv, u32 event)
-{
- char buf[50];
-
- lbs_deb_enter(LBS_DEB_CMD);
- 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 ");
- }
-
- lbs_send_iwevcustom_event(priv, buf);
- lbs_deb_leave(LBS_DEB_CMD);
-}
-
-static int lbs_ret_reg_access(struct lbs_private *priv,
- u16 type, struct cmd_ds_command *resp)
-{
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- switch (type) {
- case CMD_RET(CMD_MAC_REG_ACCESS):
- {
- struct cmd_ds_mac_reg_access *reg = &resp->params.macreg;
-
- priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
- priv->offsetvalue.value = le32_to_cpu(reg->value);
- break;
- }
-
- case CMD_RET(CMD_BBP_REG_ACCESS):
- {
- struct cmd_ds_bbp_reg_access *reg = &resp->params.bbpreg;
-
- priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
- priv->offsetvalue.value = reg->value;
- break;
- }
-
- case CMD_RET(CMD_RF_REG_ACCESS):
- {
- struct cmd_ds_rf_reg_access *reg = &resp->params.rfreg;
-
- priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
- priv->offsetvalue.value = reg->value;
- break;
- }
-
- default:
- ret = -1;
- }
-
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return ret;
-}
-
-static int lbs_ret_802_11_rssi(struct lbs_private *priv,
- struct cmd_ds_command *resp)
-{
- struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- /* store the non average value */
- priv->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR);
- priv->NF[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->noisefloor);
-
- priv->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR);
- priv->NF[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgnoisefloor);
-
- priv->RSSI[TYPE_BEACON][TYPE_NOAVG] =
- CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
- priv->NF[TYPE_BEACON][TYPE_NOAVG]);
-
- priv->RSSI[TYPE_BEACON][TYPE_AVG] =
- CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE,
- priv->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE);
-
- lbs_deb_cmd("RSSI: beacon %d, avg %d\n",
- priv->RSSI[TYPE_BEACON][TYPE_NOAVG],
- priv->RSSI[TYPE_BEACON][TYPE_AVG]);
-
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
-
-static int lbs_ret_802_11_bcn_ctrl(struct lbs_private * priv,
- struct cmd_ds_command *resp)
-{
- struct cmd_ds_802_11_beacon_control *bcn_ctrl =
- &resp->params.bcn_ctrl;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- if (bcn_ctrl->action == CMD_ACT_GET) {
- priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable);
- priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period);
- }
-
- lbs_deb_enter(LBS_DEB_CMD);
- return 0;
-}
-
-static inline int handle_cmd_response(struct lbs_private *priv,
- struct cmd_header *cmd_response)
-{
- struct cmd_ds_command *resp = (struct cmd_ds_command *) cmd_response;
- int ret = 0;
- unsigned long flags;
- uint16_t respcmd = le16_to_cpu(resp->command);
-
- lbs_deb_enter(LBS_DEB_HOST);
-
- switch (respcmd) {
- case CMD_RET(CMD_MAC_REG_ACCESS):
- case CMD_RET(CMD_BBP_REG_ACCESS):
- case CMD_RET(CMD_RF_REG_ACCESS):
- ret = lbs_ret_reg_access(priv, respcmd, resp);
- break;
-
- case CMD_RET_802_11_ASSOCIATE:
- case CMD_RET(CMD_802_11_ASSOCIATE):
- case CMD_RET(CMD_802_11_REASSOCIATE):
- ret = lbs_ret_80211_associate(priv, resp);
- break;
-
- case CMD_RET(CMD_802_11_SET_AFC):
- case CMD_RET(CMD_802_11_GET_AFC):
- spin_lock_irqsave(&priv->driver_lock, flags);
- memmove((void *)priv->cur_cmd->callback_arg, &resp->params.afc,
- sizeof(struct cmd_ds_802_11_afc));
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
- break;
-
- case CMD_RET(CMD_802_11_AUTHENTICATE):
- case CMD_RET(CMD_802_11_BEACON_STOP):
- break;
-
- case CMD_RET(CMD_802_11_RSSI):
- ret = lbs_ret_802_11_rssi(priv, resp);
- break;
-
- case CMD_RET(CMD_802_11D_DOMAIN_INFO):
- ret = lbs_ret_802_11d_domain_info(resp);
- break;
-
- case CMD_RET(CMD_802_11_TPC_CFG):
- spin_lock_irqsave(&priv->driver_lock, flags);
- memmove((void *)priv->cur_cmd->callback_arg, &resp->params.tpccfg,
- sizeof(struct cmd_ds_802_11_tpc_cfg));
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- break;
- case CMD_RET(CMD_802_11_LED_GPIO_CTRL):
- spin_lock_irqsave(&priv->driver_lock, flags);
- memmove((void *)priv->cur_cmd->callback_arg, &resp->params.ledgpio,
- sizeof(struct cmd_ds_802_11_led_ctrl));
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- break;
-
- case CMD_RET(CMD_GET_TSF):
- spin_lock_irqsave(&priv->driver_lock, flags);
- memcpy((void *)priv->cur_cmd->callback_arg,
- &resp->params.gettsf.tsfvalue, sizeof(u64));
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- break;
- case CMD_RET(CMD_BT_ACCESS):
- spin_lock_irqsave(&priv->driver_lock, flags);
- if (priv->cur_cmd->callback_arg)
- memcpy((void *)priv->cur_cmd->callback_arg,
- &resp->params.bt.addr1, 2 * ETH_ALEN);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- break;
- case CMD_RET(CMD_FWT_ACCESS):
- spin_lock_irqsave(&priv->driver_lock, flags);
- if (priv->cur_cmd->callback_arg)
- memcpy((void *)priv->cur_cmd->callback_arg, &resp->params.fwt,
- sizeof(resp->params.fwt));
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- break;
- case CMD_RET(CMD_802_11_BEACON_CTRL):
- ret = lbs_ret_802_11_bcn_ctrl(priv, resp);
- break;
-
- default:
- lbs_pr_err("CMD_RESP: unknown cmd response 0x%04x\n",
- le16_to_cpu(resp->command));
- break;
- }
- lbs_deb_leave(LBS_DEB_HOST);
- return ret;
-}
-
int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
{
uint16_t respcmd, curcmd;
@@ -313,15 +87,18 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len);
if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) {
- lbs_pr_info("Received CMD_RESP with invalid sequence %d (expected %d)\n",
- le16_to_cpu(resp->seqnum), le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum));
+ netdev_info(priv->dev,
+ "Received CMD_RESP with invalid sequence %d (expected %d)\n",
+ le16_to_cpu(resp->seqnum),
+ le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum));
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = -1;
goto done;
}
if (respcmd != CMD_RET(curcmd) &&
respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) {
- lbs_pr_info("Invalid CMD_RESP %x to command %x!\n", respcmd, curcmd);
+ netdev_info(priv->dev, "Invalid CMD_RESP %x to command %x!\n",
+ respcmd, curcmd);
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = -1;
goto done;
@@ -330,7 +107,8 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
if (resp->result == cpu_to_le16(0x0004)) {
/* 0x0004 means -EAGAIN. Drop the response, let it time out
and be resubmitted */
- lbs_pr_info("Firmware returns DEFER to command %x. Will let it time out...\n",
+ netdev_info(priv->dev,
+ "Firmware returns DEFER to command %x. Will let it time out...\n",
le16_to_cpu(resp->command));
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = -1;
@@ -340,14 +118,6 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
/* Now we got response from FW, cancel the command timer */
del_timer(&priv->command_timer);
priv->cmd_timed_out = 0;
- if (priv->nr_retries) {
- lbs_pr_info("Received result %x to command %x after %d retries\n",
- result, curcmd, priv->nr_retries);
- priv->nr_retries = 0;
- }
-
- /* Store the response code to cur_cmd_retcode. */
- priv->cur_cmd_retcode = result;
if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) {
struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1];
@@ -365,10 +135,10 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
* ad-hoc mode. It takes place in
* lbs_execute_next_command().
*/
- if (priv->mode == IW_MODE_ADHOC &&
- action == CMD_SUBCMD_ENTER_PS)
+ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR &&
+ action == PS_MODE_ACTION_ENTER_PS)
priv->psmode = LBS802_11POWERMODECAM;
- } else if (action == CMD_SUBCMD_ENTER_PS) {
+ } else if (action == PS_MODE_ACTION_ENTER_PS) {
priv->needtowakeup = 0;
priv->psstate = PS_STATE_AWAKE;
@@ -383,11 +153,12 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
spin_unlock_irqrestore(&priv->driver_lock, flags);
mutex_unlock(&priv->lock);
- lbs_ps_wakeup(priv, 0);
+ lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS,
+ false);
mutex_lock(&priv->lock);
spin_lock_irqsave(&priv->driver_lock, flags);
}
- } else if (action == CMD_SUBCMD_EXIT_PS) {
+ } else if (action == PS_MODE_ACTION_EXIT_PS) {
priv->needtowakeup = 0;
priv->psstate = PS_STATE_FULL_POWER;
lbs_deb_host("CMD_RESP: EXIT_PS command response\n");
@@ -395,7 +166,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
lbs_deb_host("CMD_RESP: PS action 0x%X\n", action);
}
- lbs_complete_command(priv, priv->cur_cmd, result);
+ __lbs_complete_command(priv, priv->cur_cmd, result);
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = 0;
@@ -416,7 +187,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
break;
}
- lbs_complete_command(priv, priv->cur_cmd, result);
+ __lbs_complete_command(priv, priv->cur_cmd, result);
spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = -1;
@@ -428,14 +199,13 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
if (priv->cur_cmd && priv->cur_cmd->callback) {
ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg,
resp);
- } else
- ret = handle_cmd_response(priv, resp);
+ }
spin_lock_irqsave(&priv->driver_lock, flags);
if (priv->cur_cmd) {
/* Clean up and Put current command back to cmdfreeq */
- lbs_complete_command(priv, priv->cur_cmd, result);
+ __lbs_complete_command(priv, priv->cur_cmd, result);
}
spin_unlock_irqrestore(&priv->driver_lock, flags);
@@ -445,32 +215,10 @@ done:
return ret;
}
-static int lbs_send_confirmwake(struct lbs_private *priv)
-{
- struct cmd_header cmd;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_HOST);
-
- cmd.command = cpu_to_le16(CMD_802_11_WAKEUP_CONFIRM);
- cmd.size = cpu_to_le16(sizeof(cmd));
- cmd.seqnum = cpu_to_le16(++priv->seqnum);
- cmd.result = 0;
-
- lbs_deb_hex(LBS_DEB_HOST, "wake confirm", (u8 *) &cmd,
- sizeof(cmd));
-
- ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &cmd, sizeof(cmd));
- if (ret)
- lbs_pr_alert("SEND_WAKEC_CMD: Host to Card failed for Confirm Wake\n");
-
- lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
- return ret;
-}
-
int lbs_process_event(struct lbs_private *priv, u32 event)
{
int ret = 0;
+ struct cmd_header cmd;
lbs_deb_enter(LBS_DEB_CMD);
@@ -500,7 +248,7 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
/* handle unexpected PS SLEEP event */
if (priv->psstate == PS_STATE_FULL_POWER) {
lbs_deb_cmd(
- "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n");
+ "EVENT: in FULL POWER mode, ignoring PS_SLEEP\n");
break;
}
priv->psstate = PS_STATE_PRE_SLEEP;
@@ -511,7 +259,22 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
case MACREG_INT_CODE_HOST_AWAKE:
lbs_deb_cmd("EVENT: host awake\n");
- lbs_send_confirmwake(priv);
+ if (priv->reset_deep_sleep_wakeup)
+ priv->reset_deep_sleep_wakeup(priv);
+ priv->is_deep_sleep = 0;
+ lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd,
+ sizeof(cmd));
+ priv->is_host_sleep_activated = 0;
+ wake_up_interruptible(&priv->host_sleep_q);
+ break;
+
+ case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
+ if (priv->reset_deep_sleep_wakeup)
+ priv->reset_deep_sleep_wakeup(priv);
+ lbs_deb_cmd("EVENT: ds awake\n");
+ priv->is_deep_sleep = 0;
+ priv->wakeup_dev_required = 0;
+ wake_up_interruptible(&priv->ds_awake_q);
break;
case MACREG_INT_CODE_PS_AWAKE:
@@ -533,18 +296,18 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
* in lbs_ps_wakeup()
*/
lbs_deb_cmd("waking up ...\n");
- lbs_ps_wakeup(priv, 0);
+ lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false);
}
break;
case MACREG_INT_CODE_MIC_ERR_UNICAST:
lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n");
- handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST);
+ lbs_send_mic_failureevent(priv, event);
break;
case MACREG_INT_CODE_MIC_ERR_MULTICAST:
lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n");
- handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST);
+ lbs_send_mic_failureevent(priv, event);
break;
case MACREG_INT_CODE_MIB_CHANGED:
@@ -557,40 +320,28 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
lbs_deb_cmd("EVENT: ADHOC beacon lost\n");
break;
case MACREG_INT_CODE_RSSI_LOW:
- lbs_pr_alert("EVENT: rssi low\n");
+ netdev_alert(priv->dev, "EVENT: rssi low\n");
break;
case MACREG_INT_CODE_SNR_LOW:
- lbs_pr_alert("EVENT: snr low\n");
+ netdev_alert(priv->dev, "EVENT: snr low\n");
break;
case MACREG_INT_CODE_MAX_FAIL:
- lbs_pr_alert("EVENT: max fail\n");
+ netdev_alert(priv->dev, "EVENT: max fail\n");
break;
case MACREG_INT_CODE_RSSI_HIGH:
- lbs_pr_alert("EVENT: rssi high\n");
+ netdev_alert(priv->dev, "EVENT: rssi high\n");
break;
case MACREG_INT_CODE_SNR_HIGH:
- lbs_pr_alert("EVENT: snr high\n");
+ netdev_alert(priv->dev, "EVENT: snr high\n");
break;
case MACREG_INT_CODE_MESH_AUTO_STARTED:
- /* Ignore spurious autostart events if autostart is disabled */
- if (!priv->mesh_autostart_enabled) {
- lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n");
- break;
- }
- lbs_pr_info("EVENT: MESH_AUTO_STARTED\n");
- priv->mesh_connect_status = LBS_CONNECTED;
- if (priv->mesh_open) {
- netif_carrier_on(priv->mesh_dev);
- if (!priv->tx_pending_len)
- netif_wake_queue(priv->mesh_dev);
- }
- priv->mode = IW_MODE_ADHOC;
- schedule_work(&priv->sync_channel);
+ /* Ignore spurious autostart events */
+ netdev_info(priv->dev, "EVENT: MESH_AUTO_STARTED (ignoring)\n");
break;
default:
- lbs_pr_alert("EVENT: unknown event id %d\n", event);
+ netdev_alert(priv->dev, "EVENT: unknown event id %d\n", event);
break;
}
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
index 50e28a0cdfe..cc6a0a586f0 100644
--- a/drivers/net/wireless/libertas/debugfs.c
+++ b/drivers/net/wireless/libertas/debugfs.c
@@ -1,17 +1,15 @@
-#include <linux/module.h>
#include <linux/dcache.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/hardirq.h>
#include <linux/mm.h>
#include <linux/string.h>
-#include <net/iw_handler.h>
-#include <net/lib80211.h>
+#include <linux/slab.h>
+#include <linux/export.h>
-#include "dev.h"
#include "decl.h"
-#include "host.h"
-#include "debugfs.h"
#include "cmd.h"
+#include "debugfs.h"
static struct dentry *lbs_dir;
static char *szStates[] = {
@@ -23,12 +21,6 @@ static char *szStates[] = {
static void lbs_debug_init(struct lbs_private *priv);
#endif
-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)
{
@@ -45,6 +37,8 @@ static ssize_t lbs_dev_info(struct file *file, char __user *userbuf,
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
ssize_t res;
+ if (!buf)
+ return -ENOMEM;
pos += snprintf(buf+pos, len-pos, "state = %s\n",
szStates[priv->connect_status]);
@@ -57,49 +51,6 @@ static ssize_t lbs_dev_info(struct file *file, char __user *userbuf,
return res;
}
-
-static ssize_t lbs_getscantable(struct file *file, char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- struct lbs_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;
- DECLARE_SSID_BUF(ssid);
- struct bss_descriptor * iter_bss;
-
- pos += snprintf(buf+pos, len-pos,
- "# | ch | rssi | bssid | cap | Qual | SSID \n");
-
- mutex_lock(&priv->lock);
- list_for_each_entry (iter_bss, &priv->network_list, list) {
- u16 ibss = (iter_bss->capability & WLAN_CAPABILITY_IBSS);
- u16 privacy = (iter_bss->capability & WLAN_CAPABILITY_PRIVACY);
- u16 spectrum_mgmt = (iter_bss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT);
-
- pos += snprintf(buf+pos, len-pos, "%02u| %03d | %04d | %pM |",
- numscansdone, iter_bss->channel, iter_bss->rssi,
- iter_bss->bssid);
- pos += snprintf(buf+pos, len-pos, " %04x-", iter_bss->capability);
- pos += snprintf(buf+pos, len-pos, "%c%c%c |",
- ibss ? 'A' : 'I', privacy ? 'P' : ' ',
- spectrum_mgmt ? 'S' : ' ');
- pos += snprintf(buf+pos, len-pos, " %04d |", SCAN_RSSI(iter_bss->rssi));
- pos += snprintf(buf+pos, len-pos, " %s\n",
- print_ssid(ssid, iter_bss->ssid,
- iter_bss->ssid_len));
-
- numscansdone++;
- }
- mutex_unlock(&priv->lock);
-
- res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
-
- free_page(addr);
- return res;
-}
-
static ssize_t lbs_sleepparams_write(struct file *file,
const char __user *user_buf, size_t count,
loff_t *ppos)
@@ -110,6 +61,8 @@ static ssize_t lbs_sleepparams_write(struct file *file,
int p1, p2, p3, p4, p5, p6;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
buf_size = min(count, len - 1);
if (copy_from_user(buf, user_buf, buf_size)) {
@@ -148,6 +101,8 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
struct sleep_params sp;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp);
if (ret)
@@ -165,6 +120,71 @@ out_unlock:
return ret;
}
+static ssize_t lbs_host_sleep_write(struct file *file,
+ const char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct lbs_private *priv = file->private_data;
+ ssize_t buf_size, ret;
+ int host_sleep;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
+
+ buf_size = min(count, len - 1);
+ if (copy_from_user(buf, user_buf, buf_size)) {
+ ret = -EFAULT;
+ goto out_unlock;
+ }
+ ret = sscanf(buf, "%d", &host_sleep);
+ if (ret != 1) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (host_sleep == 0)
+ ret = lbs_set_host_sleep(priv, 0);
+ else if (host_sleep == 1) {
+ if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
+ netdev_info(priv->dev,
+ "wake parameters not configured\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ ret = lbs_set_host_sleep(priv, 1);
+ } else {
+ netdev_err(priv->dev, "invalid option\n");
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ ret = count;
+
+out_unlock:
+ free_page(addr);
+ return ret;
+}
+
+static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct lbs_private *priv = file->private_data;
+ ssize_t ret;
+ size_t pos = 0;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
+
+ pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated);
+
+ ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+ free_page(addr);
+ return ret;
+}
+
/*
* When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might
* get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the
@@ -183,12 +203,12 @@ out_unlock:
*/
static void *lbs_tlv_find(uint16_t tlv_type, const uint8_t *tlv, uint16_t size)
{
- struct mrvlietypesheader *tlv_h;
+ struct mrvl_ie_header *tlv_h;
uint16_t length;
ssize_t pos = 0;
while (pos < size) {
- tlv_h = (struct mrvlietypesheader *) tlv;
+ tlv_h = (struct mrvl_ie_header *) tlv;
if (!tlv_h->len)
return NULL;
if (tlv_h->type == cpu_to_le16(tlv_type))
@@ -206,7 +226,7 @@ static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
size_t count, loff_t *ppos)
{
struct cmd_ds_802_11_subscribe_event *subscribed;
- struct mrvlietypes_thresholds *got;
+ struct mrvl_ie_thresholds *got;
struct lbs_private *priv = file->private_data;
ssize_t ret = 0;
size_t pos = 0;
@@ -259,7 +279,7 @@ static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
loff_t *ppos)
{
struct cmd_ds_802_11_subscribe_event *events;
- struct mrvlietypes_thresholds *tlv;
+ struct mrvl_ie_thresholds *tlv;
struct lbs_private *priv = file->private_data;
ssize_t buf_size;
int value, freq, new_mask;
@@ -423,28 +443,26 @@ static ssize_t lbs_bcnmiss_write(struct file *file, const char __user *userbuf,
}
-
static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct lbs_private *priv = file->private_data;
- struct lbs_offset_value offval;
ssize_t pos = 0;
int ret;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ u32 val = 0;
- offval.offset = priv->mac_offset;
- offval.value = 0;
+ if (!buf)
+ return -ENOMEM;
- ret = lbs_prepare_and_send_command(priv,
- CMD_MAC_REG_ACCESS, 0,
- CMD_OPTION_WAITFORRSP, 0, &offval);
+ ret = lbs_get_reg(priv, CMD_MAC_REG_ACCESS, priv->mac_offset, &val);
mdelay(10);
- pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n",
- priv->mac_offset, priv->offsetvalue.value);
-
- ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ if (!ret) {
+ pos = snprintf(buf, len, "MAC[0x%x] = 0x%08x\n",
+ priv->mac_offset, val);
+ ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ }
free_page(addr);
return ret;
}
@@ -457,13 +475,15 @@ static ssize_t lbs_rdmac_write(struct file *file,
ssize_t res, buf_size;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
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);
+ priv->mac_offset = simple_strtoul(buf, NULL, 16);
res = count;
out_unlock:
free_page(addr);
@@ -478,9 +498,10 @@ static ssize_t lbs_wrmac_write(struct file *file,
struct lbs_private *priv = file->private_data;
ssize_t res, buf_size;
u32 offset, value;
- struct lbs_offset_value offval;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
buf_size = min(count, len - 1);
if (copy_from_user(buf, userbuf, buf_size)) {
@@ -493,14 +514,11 @@ static ssize_t lbs_wrmac_write(struct file *file,
goto out_unlock;
}
- offval.offset = offset;
- offval.value = value;
- res = lbs_prepare_and_send_command(priv,
- CMD_MAC_REG_ACCESS, 1,
- CMD_OPTION_WAITFORRSP, 0, &offval);
+ res = lbs_set_reg(priv, CMD_MAC_REG_ACCESS, offset, value);
mdelay(10);
- res = count;
+ if (!res)
+ res = count;
out_unlock:
free_page(addr);
return res;
@@ -510,23 +528,22 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct lbs_private *priv = file->private_data;
- struct lbs_offset_value offval;
ssize_t pos = 0;
int ret;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ u32 val;
- offval.offset = priv->bbp_offset;
- offval.value = 0;
+ if (!buf)
+ return -ENOMEM;
- ret = lbs_prepare_and_send_command(priv,
- CMD_BBP_REG_ACCESS, 0,
- CMD_OPTION_WAITFORRSP, 0, &offval);
+ ret = lbs_get_reg(priv, CMD_BBP_REG_ACCESS, priv->bbp_offset, &val);
mdelay(10);
- pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n",
- priv->bbp_offset, priv->offsetvalue.value);
-
- ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ if (!ret) {
+ pos = snprintf(buf, len, "BBP[0x%x] = 0x%08x\n",
+ priv->bbp_offset, val);
+ ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ }
free_page(addr);
return ret;
@@ -540,13 +557,15 @@ static ssize_t lbs_rdbbp_write(struct file *file,
ssize_t res, buf_size;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
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);
+ priv->bbp_offset = simple_strtoul(buf, NULL, 16);
res = count;
out_unlock:
free_page(addr);
@@ -561,9 +580,10 @@ static ssize_t lbs_wrbbp_write(struct file *file,
struct lbs_private *priv = file->private_data;
ssize_t res, buf_size;
u32 offset, value;
- struct lbs_offset_value offval;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
buf_size = min(count, len - 1);
if (copy_from_user(buf, userbuf, buf_size)) {
@@ -576,14 +596,11 @@ static ssize_t lbs_wrbbp_write(struct file *file,
goto out_unlock;
}
- offval.offset = offset;
- offval.value = value;
- res = lbs_prepare_and_send_command(priv,
- CMD_BBP_REG_ACCESS, 1,
- CMD_OPTION_WAITFORRSP, 0, &offval);
+ res = lbs_set_reg(priv, CMD_BBP_REG_ACCESS, offset, value);
mdelay(10);
- res = count;
+ if (!res)
+ res = count;
out_unlock:
free_page(addr);
return res;
@@ -593,23 +610,22 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct lbs_private *priv = file->private_data;
- struct lbs_offset_value offval;
ssize_t pos = 0;
int ret;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ u32 val;
- offval.offset = priv->rf_offset;
- offval.value = 0;
+ if (!buf)
+ return -ENOMEM;
- ret = lbs_prepare_and_send_command(priv,
- CMD_RF_REG_ACCESS, 0,
- CMD_OPTION_WAITFORRSP, 0, &offval);
+ ret = lbs_get_reg(priv, CMD_RF_REG_ACCESS, priv->rf_offset, &val);
mdelay(10);
- pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n",
- priv->rf_offset, priv->offsetvalue.value);
-
- ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ if (!ret) {
+ pos = snprintf(buf, len, "RF[0x%x] = 0x%08x\n",
+ priv->rf_offset, val);
+ ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ }
free_page(addr);
return ret;
@@ -623,6 +639,8 @@ static ssize_t lbs_rdrf_write(struct file *file,
ssize_t res, buf_size;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
buf_size = min(count, len - 1);
if (copy_from_user(buf, userbuf, buf_size)) {
@@ -644,9 +662,10 @@ static ssize_t lbs_wrrf_write(struct file *file,
struct lbs_private *priv = file->private_data;
ssize_t res, buf_size;
u32 offset, value;
- struct lbs_offset_value offval;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
buf_size = min(count, len - 1);
if (copy_from_user(buf, userbuf, buf_size)) {
@@ -659,14 +678,11 @@ static ssize_t lbs_wrrf_write(struct file *file,
goto out_unlock;
}
- offval.offset = offset;
- offval.value = value;
- res = lbs_prepare_and_send_command(priv,
- CMD_RF_REG_ACCESS, 1,
- CMD_OPTION_WAITFORRSP, 0, &offval);
+ res = lbs_set_reg(priv, CMD_RF_REG_ACCESS, offset, value);
mdelay(10);
- res = count;
+ if (!res)
+ res = count;
out_unlock:
free_page(addr);
return res;
@@ -674,23 +690,24 @@ out_unlock:
#define FOPS(fread, fwrite) { \
.owner = THIS_MODULE, \
- .open = open_file_generic, \
+ .open = simple_open, \
.read = (fread), \
.write = (fwrite), \
+ .llseek = generic_file_llseek, \
}
struct lbs_debugfs_files {
const char *name;
- int perm;
+ umode_t perm;
struct file_operations fops;
};
static const struct lbs_debugfs_files debugfs_files[] = {
{ "info", 0444, FOPS(lbs_dev_info, write_file_dummy), },
- { "getscantable", 0444, FOPS(lbs_getscantable,
- write_file_dummy), },
{ "sleepparams", 0644, FOPS(lbs_sleepparams_read,
lbs_sleepparams_write), },
+ { "hostsleep", 0644, FOPS(lbs_host_sleep_read,
+ lbs_host_sleep_write), },
};
static const struct lbs_debugfs_files debugfs_events_files[] = {
@@ -721,15 +738,12 @@ void lbs_debugfs_init(void)
{
if (!lbs_dir)
lbs_dir = debugfs_create_dir("lbs_wireless", NULL);
-
- return;
}
void lbs_debugfs_remove(void)
{
if (lbs_dir)
debugfs_remove(lbs_dir);
- return;
}
void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev)
@@ -832,15 +846,14 @@ static struct debug_data items[] = {
static int num_of_items = ARRAY_SIZE(items);
/**
- * @brief proc read function
+ * lbs_debugfs_read - 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
+ * @file: file to read
+ * @userbuf: pointer to buffer
+ * @count: number of bytes to read
+ * @ppos: read data starting position
+ *
+ * returns: amount of data read or negative error code
*/
static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
@@ -853,10 +866,12 @@ static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf,
struct debug_data *d;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
+ if (!buf)
+ return -ENOMEM;
p = buf;
- d = (struct debug_data *)file->private_data;
+ d = file->private_data;
for (i = 0; i < num_of_items; i++) {
if (d[i].size == 1)
@@ -878,13 +893,14 @@ static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf,
}
/**
- * @brief proc write function
+ * lbs_debugfs_write - proc write function
+ *
+ * @f: file pointer
+ * @buf: pointer to data buffer
+ * @cnt: data number to write
+ * @ppos: file position
*
- * @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
+ * returns: amount of data written
*/
static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
size_t cnt, loff_t *ppos)
@@ -895,9 +911,12 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
char *p0;
char *p1;
char *p2;
- struct debug_data *d = (struct debug_data *)f->private_data;
+ struct debug_data *d = f->private_data;
- pdata = kmalloc(cnt, GFP_KERNEL);
+ if (cnt == 0)
+ return 0;
+
+ pdata = kmalloc(cnt + 1, GFP_KERNEL);
if (pdata == NULL)
return 0;
@@ -906,6 +925,7 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
kfree(pdata);
return 0;
}
+ pdata[cnt] = '\0';
p0 = pdata;
for (i = 0; i < num_of_items; i++) {
@@ -940,17 +960,18 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
static const struct file_operations lbs_debug_fops = {
.owner = THIS_MODULE,
- .open = open_file_generic,
+ .open = simple_open,
.write = lbs_debugfs_write,
.read = lbs_debugfs_read,
+ .llseek = default_llseek,
};
/**
- * @brief create debug proc file
+ * lbs_debug_init - create debug proc file
+ *
+ * @priv: pointer to &struct lbs_private
*
- * @param priv pointer struct lbs_private
- * @param dev pointer net_device
- * @return N/A
+ * returns: N/A
*/
static void lbs_debug_init(struct lbs_private *priv)
{
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
index 0b84bdca072..84a3aa7ac57 100644
--- a/drivers/net/wireless/libertas/decl.h
+++ b/drivers/net/wireless/libertas/decl.h
@@ -1,77 +1,82 @@
-/**
- * This file contains declaration referring to
- * functions defined in other source files
- */
+
+/*
+ * This file contains declaration referring to
+ * functions defined in other source files
+ */
#ifndef _LBS_DECL_H_
#define _LBS_DECL_H_
-#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/firmware.h>
+#include <linux/nl80211.h>
+
+/* Should be terminated by a NULL entry */
+struct lbs_fw_table {
+ int model;
+ const char *helper;
+ const char *fwname;
+};
-#include "defs.h"
+struct lbs_private;
+typedef void (*lbs_fw_cb)(struct lbs_private *priv, int ret,
+ const struct firmware *helper, const struct firmware *mainfw);
-/** Function Prototype Declaration */
struct lbs_private;
struct sk_buff;
struct net_device;
-struct cmd_ctrl_node;
struct cmd_ds_command;
-void lbs_set_mac_control(struct lbs_private *priv);
-
-void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count);
-
-int lbs_free_cmd_buffer(struct lbs_private *priv);
-
-int lbs_prepare_and_send_command(struct lbs_private *priv,
- u16 cmd_no,
- u16 cmd_action,
- u16 wait_option, u32 cmd_oid, void *pdata_buf);
-int lbs_allocate_cmd_buffer(struct lbs_private *priv);
-int lbs_execute_next_command(struct lbs_private *priv);
-int lbs_process_event(struct lbs_private *priv, u32 event);
-void lbs_queue_event(struct lbs_private *priv, u32 event);
-void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
+/* ethtool.c */
+extern const struct ethtool_ops lbs_ethtool_ops;
-u32 lbs_fw_index_to_data_rate(u8 index);
-u8 lbs_data_rate_to_fw_index(u32 rate);
-/** The proc fs interface */
-int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len);
-void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
- int result);
-int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev);
-int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band);
+/* tx.c */
+void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count);
+netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+/* rx.c */
int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *);
-void lbs_ps_sleep(struct lbs_private *priv, int wait_option);
-void lbs_ps_confirm_sleep(struct lbs_private *priv);
-void lbs_ps_wakeup(struct lbs_private *priv, int wait_option);
-
-struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
- struct lbs_private *priv,
- u8 band,
- u16 channel);
-
-void lbs_mac_event_disconnected(struct lbs_private *priv);
-
-void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str);
-
-/* persistcfg.c */
-void lbs_persist_config_init(struct net_device *net);
-void lbs_persist_config_remove(struct net_device *net);
/* main.c */
-struct chan_freq_power *lbs_get_region_cfp_table(u8 region,
- int *cfp_no);
struct lbs_private *lbs_add_card(void *card, struct device *dmdev);
void lbs_remove_card(struct lbs_private *priv);
int lbs_start_card(struct lbs_private *priv);
void lbs_stop_card(struct lbs_private *priv);
void lbs_host_to_card_done(struct lbs_private *priv);
-int lbs_update_channel(struct lbs_private *priv);
+int lbs_start_iface(struct lbs_private *priv);
+int lbs_stop_iface(struct lbs_private *priv);
+int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type);
+
+int lbs_rtap_supported(struct lbs_private *priv);
+
+int lbs_set_mac_address(struct net_device *dev, void *addr);
+void lbs_set_multicast_list(struct net_device *dev);
+void lbs_update_mcast(struct lbs_private *priv);
+
+int lbs_suspend(struct lbs_private *priv);
+int lbs_resume(struct lbs_private *priv);
+
+void lbs_queue_event(struct lbs_private *priv, u32 event);
+void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
+
+int lbs_enter_auto_deep_sleep(struct lbs_private *priv);
+int lbs_exit_auto_deep_sleep(struct lbs_private *priv);
+
+u32 lbs_fw_index_to_data_rate(u8 index);
+u8 lbs_data_rate_to_fw_index(u32 rate);
+
+int lbs_get_firmware(struct device *dev, u32 card_model,
+ const struct lbs_fw_table *fw_table,
+ const struct firmware **helper,
+ const struct firmware **mainfw);
+int lbs_get_firmware_async(struct lbs_private *priv, struct device *device,
+ u32 card_model, const struct lbs_fw_table *fw_table,
+ lbs_fw_cb callback);
+void lbs_wait_for_firmware_load(struct lbs_private *priv);
#endif
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h
index e8dfde39abf..407784aca62 100644
--- a/drivers/net/wireless/libertas/defs.h
+++ b/drivers/net/wireless/libertas/defs.h
@@ -1,7 +1,7 @@
-/**
- * This header file contains global constant/enum definitions,
- * global variable declaration.
- */
+/*
+ * This header file contains global constant/enum definitions,
+ * global variable declaration.
+ */
#ifndef _LBS_DEFS_H_
#define _LBS_DEFS_H_
@@ -42,6 +42,7 @@
#define LBS_DEB_SDIO 0x00400000
#define LBS_DEB_SYSFS 0x00800000
#define LBS_DEB_SPI 0x01000000
+#define LBS_DEB_CFG80211 0x02000000
extern unsigned int lbs_debug;
@@ -86,16 +87,11 @@ do { if ((lbs_debug & (grp)) == (grp)) \
#define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args)
#define lbs_deb_sysfs(fmt, args...) LBS_DEB_LL(LBS_DEB_SYSFS, " sysfs", fmt, ##args)
#define lbs_deb_spi(fmt, args...) LBS_DEB_LL(LBS_DEB_SPI, " spi", fmt, ##args)
-
-#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)
+#define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args)
#ifdef DEBUG
-static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len)
+static inline void lbs_deb_hex(unsigned int grp, const char *prompt,
+ const u8 *buf, int len)
{
int i = 0;
@@ -121,19 +117,19 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
-/** Buffer Constants */
+/* 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.
-*/
+ * 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 LBS_NUM_CMD_BUFFERS 10
@@ -164,17 +160,12 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
#define WOL_RESULT_NOSPC_ERR 1
#define WOL_RESULT_EEXIST_ERR 2
-/** Misc constants */
+/* 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
@@ -186,7 +177,8 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
#define MARVELL_MESH_IE_LENGTH 9
-/* Values used to populate the struct mrvl_mesh_ie. The only time you need this
+/*
+ * Values used to populate the struct mrvl_mesh_ie. The only time you need this
* is when enabling the mesh using CMD_MESH_CONFIG.
*/
#define MARVELL_MESH_IE_TYPE 4
@@ -196,7 +188,7 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
#define MARVELL_MESH_METRIC_ID 0
#define MARVELL_MESH_CAPABILITY 0
-/** INT status Bit Definition*/
+/* INT status Bit Definition */
#define MRVDRV_TX_DNLD_RDY 0x0001
#define MRVDRV_RX_UPLD_RDY 0x0002
#define MRVDRV_CMD_DNLD_RDY 0x0004
@@ -211,43 +203,63 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
#define TPC_DEFAULT_P1 10
#define TPC_DEFAULT_P2 13
-/** TxPD status */
+/* TxPD status */
-/* Station firmware use TxPD status field to report final Tx transmit
-* result, Bit masks are used to present combined situations.
-*/
+/*
+ * 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.
+/* 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 */
+/* Mesh interface ID */
+#define MESH_IFACE_ID 0x0001
+/* Mesh id should be in bits 14-13-12 */
+#define MESH_IFACE_BIT_OFFSET 0x000c
+/* Mesh enable bit in FW capability */
+#define MESH_CAPINFO_ENABLE_MASK (1<<16)
+
+/* FW definition from Marvell v4 */
+#define MRVL_FW_V4 (0x04)
+/* FW definition from Marvell v5 */
+#define MRVL_FW_V5 (0x05)
+/* FW definition from Marvell v10 */
+#define MRVL_FW_V10 (0x0a)
+/* FW major revision definition */
+#define MRVL_FW_MAJOR_REV(x) ((x)>>24)
+
+/* 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.
+/* 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
-*/
+/* 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 */
+/* RTS/FRAG related defines */
#define MRVDRV_RTS_MIN_VALUE 0
#define MRVDRV_RTS_MAX_VALUE 2347
#define MRVDRV_FRAG_MIN_VALUE 256
@@ -283,54 +295,40 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
#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 MAX_RATES 14
#define MAX_LEDS 8
-/** Global Variable Declaration */
+/* Global Variable Declaration */
extern const char lbs_driver_version[];
extern u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE];
-extern u8 lbs_bg_rates[MAX_RATES];
-/** ENUM definition*/
-/** SNRNF_TYPE */
+/* ENUM definition */
+/* SNRNF_TYPE */
enum SNRNF_TYPE {
TYPE_BEACON = 0,
TYPE_RXPD,
MAX_TYPE_B
};
-/** SNRNF_DATA*/
+/* SNRNF_DATA */
enum SNRNF_DATA {
TYPE_NOAVG = 0,
TYPE_AVG,
MAX_TYPE_AVG
};
-/** LBS_802_11_POWER_MODE */
+/* LBS_802_11_POWER_MODE */
enum LBS_802_11_POWER_MODE {
LBS802_11POWERMODECAM,
LBS802_11POWERMODEMAX_PSP,
LBS802_11POWERMODEFAST_PSP,
- /*not a real mode, defined as an upper bound */
+ /* not a real mode, defined as an upper bound */
LBS802_11POWEMODEMAX
};
-/** PS_STATE */
+/* PS_STATE */
enum PS_STATE {
PS_STATE_FULL_POWER,
PS_STATE_AWAKE,
@@ -338,7 +336,7 @@ enum PS_STATE {
PS_STATE_SLEEP
};
-/** DNLD_STATE */
+/* DNLD_STATE */
enum DNLD_STATE {
DNLD_RES_RECEIVED,
DNLD_DATA_SENT,
@@ -346,19 +344,19 @@ enum DNLD_STATE {
DNLD_BOOTCMD_SENT,
};
-/** LBS_MEDIA_STATE */
+/* LBS_MEDIA_STATE */
enum LBS_MEDIA_STATE {
LBS_CONNECTED,
LBS_DISCONNECTED
};
-/** LBS_802_11_PRIVACY_FILTER */
+/* LBS_802_11_PRIVACY_FILTER */
enum LBS_802_11_PRIVACY_FILTER {
LBS802_11PRIVFILTERACCEPTALL,
LBS802_11PRIVFILTER8021XWEP
};
-/** mv_ms_type */
+/* mv_ms_type */
enum mv_ms_type {
MVMS_DAT = 0,
MVMS_CMD = 1,
@@ -366,14 +364,14 @@ enum mv_ms_type {
MVMS_EVENT
};
-/** KEY_TYPE_ID */
+/* 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) */
+/* KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */
enum KEY_INFO_WPA {
KEY_INFO_WPA_MCAST = 0x01,
KEY_INFO_WPA_UNICAST = 0x02,
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index 27e81fd97c9..6bd1608992b 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -1,82 +1,18 @@
-/**
- * This file contains definitions and data structures specific
- * to Marvell 802.11 NIC. It contains the Device Information
- * structure struct lbs_private..
- */
+/*
+ * This file contains definitions and data structures specific
+ * to Marvell 802.11 NIC. It contains the Device Information
+ * structure struct lbs_private..
+ */
#ifndef _LBS_DEV_H_
#define _LBS_DEV_H_
-#include <linux/netdevice.h>
-#include <linux/wireless.h>
-#include <linux/ethtool.h>
-#include <linux/debugfs.h>
-
#include "defs.h"
-#include "hostcmd.h"
-
-extern struct ethtool_ops lbs_ethtool_ops;
-
-#define MAX_BSSID_PER_CHANNEL 16
+#include "decl.h"
+#include "host.h"
-#define NR_TX_QUEUE 3
+#include <linux/kfifo.h>
-/* 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 lbs_802_11_security {
- u8 WPAenabled;
- u8 WPA2enabled;
- u8 wep_enabled;
- u8 auth_mode;
- u32 key_mgmt;
-};
-
-/** Current Basic Service Set State Structure */
-struct current_bss_params {
- /** bssid */
- u8 bssid[ETH_ALEN];
- /** ssid */
- u8 ssid[IW_ESSID_MAX_SIZE + 1];
- u8 ssid_len;
-
- /** band */
- u8 band;
- /** channel */
- u8 channel;
- /** zero-terminated array of supported data rates */
- u8 rates[MAX_RATES + 1];
-};
-
-/** sleep_params */
+/* sleep_params */
struct sleep_params {
uint16_t sp_error;
uint16_t sp_offset;
@@ -98,94 +34,101 @@ struct lbs_mesh_stats {
u32 tx_failed_cnt; /* Tx: Failed transmissions */
};
-/** Private structure for the MV device */
+/* Private structure for the MV device */
struct lbs_private {
- int mesh_open;
- int infra_open;
- int mesh_autostart_enabled;
- char name[DEV_NAME_LEN];
-
- void *card;
+ /* Basic networking */
struct net_device *dev;
+ u32 connect_status;
+ struct work_struct mcast_work;
+ u32 nr_of_multicastmacaddr;
+ u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
- struct net_device *mesh_dev; /* Virtual device */
- struct net_device *rtap_net_dev;
+ /* CFG80211 */
+ struct wireless_dev *wdev;
+ bool wiphy_registered;
+ struct cfg80211_scan_request *scan_req;
+ u8 assoc_bss[ETH_ALEN];
+ u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+ u8 disassoc_reason;
- struct iw_statistics wstats;
+ /* Mesh */
+ struct net_device *mesh_dev; /* Virtual device */
+#ifdef CONFIG_LIBERTAS_MESH
struct lbs_mesh_stats mstats;
+ uint16_t mesh_tlv;
+ u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1];
+ u8 mesh_ssid_len;
+ u8 mesh_channel;
+#endif
+
+ /* Debugfs */
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];
+ /* Hardware debugging */
u32 mac_offset;
u32 bbp_offset;
u32 rf_offset;
- /* 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;
-
- /** thread to service interrupts */
- struct task_struct *main_thread;
- wait_queue_head_t waitq;
- struct workqueue_struct *work_thread;
-
- struct work_struct mcast_work;
-
- /** Scanning */
- struct delayed_work scan_work;
- struct delayed_work assoc_work;
- struct work_struct sync_channel;
- /* remember which channel was scanned last, != 0 if currently scanning */
- int scan_channel;
- u8 scan_ssid[IW_ESSID_MAX_SIZE + 1];
- u8 scan_ssid_len;
+ /* Power management */
+ u16 psmode;
+ u32 psstate;
+ u8 needtowakeup;
- /** Hardware access */
+ /* Deep sleep */
+ int is_deep_sleep;
+ int deep_sleep_required;
+ int is_auto_deep_sleep_enabled;
+ int wakeup_dev_required;
+ int is_activity_detected;
+ int auto_deep_sleep_timeout; /* in ms */
+ wait_queue_head_t ds_awake_q;
+ struct timer_list auto_deepsleep_timer;
+
+ /* Host sleep*/
+ int is_host_sleep_configured;
+ int is_host_sleep_activated;
+ wait_queue_head_t host_sleep_q;
+
+ /* Hardware access */
+ void *card;
+ bool iface_running;
+ u8 fw_ready;
+ u8 surpriseremoved;
+ u8 setup_fw_on_resume;
int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
void (*reset_card) (struct lbs_private *priv);
+ int (*power_save) (struct lbs_private *priv);
+ int (*power_restore) (struct lbs_private *priv);
+ int (*enter_deep_sleep) (struct lbs_private *priv);
+ int (*exit_deep_sleep) (struct lbs_private *priv);
+ int (*reset_deep_sleep_wakeup) (struct lbs_private *priv);
- /* Wake On LAN */
- uint32_t wol_criteria;
- uint8_t wol_gpio;
- uint8_t wol_gap;
-
- /** Wlan adapter data structure*/
- /** STATUS variables */
+ /* Adapter info (from EEPROM) */
u32 fwrelease;
u32 fwcapinfo;
+ u16 regioncode;
+ u8 current_addr[ETH_ALEN];
+ u8 copied_hwaddr;
- struct mutex lock;
-
- /* TX packet ready to be sent... */
- int tx_pending_len; /* -1 while building packet */
-
- u8 tx_pending_buf[LBS_UPLD_SIZE];
- /* protected by hard_start_xmit serialization */
-
- /** command-related variables */
+ /* Command download */
+ u8 dnld_sent;
+ /* bit0 1/0=data_sent/data_tx_done,
+ bit1 1/0=cmd_sent/cmd_tx_done,
+ all other bits reserved 0 */
u16 seqnum;
-
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;
+ struct list_head cmdfreeq; /* free command buffers */
+ struct list_head cmdpendingq; /* pending command buffers */
+ struct timer_list command_timer;
+ int cmd_timed_out;
/* Command responses sent from the hardware to the driver */
u8 resp_idx;
@@ -193,217 +136,76 @@ struct lbs_private {
u32 resp_len[2];
/* Events sent from hardware to driver */
- struct kfifo *event_fifo;
-
- /* nickname */
- u8 nodename[16];
-
- /** spin locks */
- spinlock_t driver_lock;
-
- /** Timers */
- struct timer_list command_timer;
- int nr_retries;
- int cmd_timed_out;
-
- /** current ssid/bssid related parameters*/
- struct current_bss_params curbssparams;
+ struct kfifo event_fifo;
- uint16_t mesh_tlv;
- u8 mesh_ssid[IW_ESSID_MAX_SIZE + 1];
- u8 mesh_ssid_len;
-
- /* IW_MODE_* */
- u8 mode;
-
- /* Scan results list */
- struct list_head network_list;
- struct list_head network_free_list;
- struct bss_descriptor *networks;
-
- u16 beacon_period;
- u8 beacon_enable;
- u8 adhoccreate;
-
- /** capability Info used in Association, start, join */
- u16 capability;
-
- /** MAC address information */
- u8 current_addr[ETH_ALEN];
- u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
- u32 nr_of_multicastmacaddr;
+ /* thread to service interrupts */
+ struct task_struct *main_thread;
+ wait_queue_head_t waitq;
+ struct workqueue_struct *work_thread;
- /** 802.11 statistics */
-// struct cmd_DS_802_11_GET_STAT wlan802_11Stat;
+ /* Encryption stuff */
+ u8 authtype_auto;
+ u8 wep_tx_key;
+ u8 wep_key[4][WLAN_KEY_LEN_WEP104];
+ u8 wep_key_len[4];
- uint16_t enablehwauto;
- uint16_t ratebitmap;
+ /* Wake On LAN */
+ uint32_t wol_criteria;
+ uint8_t wol_gpio;
+ uint8_t wol_gap;
+ bool ehs_remove_supported;
+ /* Transmitting */
+ int tx_pending_len; /* -1 while building packet */
+ u8 tx_pending_buf[LBS_UPLD_SIZE];
+ /* protected by hard_start_xmit serialization */
u8 txretrycount;
-
- /** Tx-related variables (for single packet tx) */
struct sk_buff *currenttxskb;
+ struct timer_list tx_lockup_timer;
+
+ /* Locks */
+ struct mutex lock;
+ spinlock_t driver_lock;
- /** NIC Operation characteristics */
+ /* NIC/link operation characteristics */
u16 mac_control;
- u32 connect_status;
- u32 mesh_connect_status;
- u16 regioncode;
+ u8 radio_on;
+ u8 cur_rate;
+ u8 channel;
s16 txpower_cur;
s16 txpower_min;
s16 txpower_max;
- /** POWER MANAGEMENT AND PnP SUPPORT */
- u8 surpriseremoved;
-
- u16 psmode; /* Wlan802_11PowermodeCAM=disable
- Wlan802_11PowermodeMAX_PSP=enable */
- u32 psstate;
- char ps_supported;
- u8 needtowakeup;
-
- struct assoc_request * pending_assoc_req;
- struct assoc_request * in_progress_assoc_req;
-
- /** Encryption parameter */
- struct lbs_802_11_security secinfo;
-
- /** WEP keys */
- struct enc_key wep_keys[4];
- u16 wep_tx_keyidx;
-
- /** WPA keys */
- struct enc_key wpa_mcast_key;
- struct enc_key wpa_unicast_key;
-
-/*
- * In theory, the IE is limited to the IE length, 255,
- * but in practice 64 bytes are enough.
- */
-#define MAX_WPA_IE_LEN 64
-
- /** WPA Information Elements*/
- u8 wpa_ie[MAX_WPA_IE_LEN];
- u8 wpa_ie_len;
-
- /** Requested Signal Strength*/
- 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;
-
- u8 radio_on;
-
- /** data rate stuff */
- u8 cur_rate;
-
- /** 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 lbs_802_11d_domain_reg domainreg;
- struct parsed_region_chan_11d parsed_region_chan;
-
- /** FSM variable for 11d support */
- u32 enable11d;
-
- /** MISCELLANEOUS */
- struct lbs_offset_value offsetvalue;
-
- u32 monitormode;
- u8 fw_ready;
+ /* Scanning */
+ struct delayed_work scan_work;
+ int scan_channel;
+ /* Queue of things waiting for scan completion */
+ wait_queue_head_t scan_q;
+ /* Whether the scan was initiated internally and not by cfg80211 */
+ bool internal_scan;
+
+ /* Firmware load */
+ u32 fw_model;
+ wait_queue_head_t fw_waitq;
+ struct device *fw_device;
+ const struct firmware *helper_fw;
+ const struct lbs_fw_table *fw_table;
+ const struct lbs_fw_table *fw_iter;
+ lbs_fw_cb fw_callback;
};
extern struct cmd_confirm_sleep confirm_sleep;
-/**
- * @brief Structure used to store information for each beacon/probe response
- */
-struct bss_descriptor {
- u8 bssid[ETH_ALEN];
-
- u8 ssid[IW_ESSID_MAX_SIZE + 1];
- u8 ssid_len;
-
- u16 capability;
- u32 rssi;
- u32 channel;
- u16 beaconperiod;
- u32 atimwindow;
-
- /* IW_MODE_AUTO, IW_MODE_ADHOC, IW_MODE_INFRA */
- u8 mode;
-
- /* zero-terminated array of supported data rates */
- u8 rates[MAX_RATES + 1];
-
- unsigned long last_scanned;
+/* Check if there is an interface active. */
+static inline int lbs_iface_active(struct lbs_private *priv)
+{
+ int r;
- union ieeetypes_phyparamset phyparamset;
- union IEEEtypes_ssparamset ssparamset;
-
- 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;
-
- u8 mesh;
-
- struct list_head list;
-};
+ r = netif_running(priv->dev);
+ if (priv->mesh_dev)
+ r |= netif_running(priv->mesh_dev);
-/** 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_BAND 3
-#define ASSOC_FLAG_MODE 4
-#define ASSOC_FLAG_BSSID 5
-#define ASSOC_FLAG_WEP_KEYS 6
-#define ASSOC_FLAG_WEP_TX_KEYIDX 7
-#define ASSOC_FLAG_WPA_MCAST_KEY 8
-#define ASSOC_FLAG_WPA_UCAST_KEY 9
-#define ASSOC_FLAG_SECINFO 10
-#define ASSOC_FLAG_WPA_IE 11
- unsigned long flags;
-
- u8 ssid[IW_ESSID_MAX_SIZE + 1];
- u8 ssid_len;
- u8 channel;
- u8 band;
- u8 mode;
- u8 bssid[ETH_ALEN] __attribute__ ((aligned (2)));
-
- /** WEP keys */
- struct enc_key wep_keys[4];
- u16 wep_tx_keyidx;
-
- /** WPA keys */
- struct enc_key wpa_mcast_key;
- struct enc_key wpa_unicast_key;
-
- struct lbs_802_11_security secinfo;
-
- /** WPA Information Elements*/
- u8 wpa_ie[MAX_WPA_IE_LEN];
- u8 wpa_ie_len;
-
- /* BSS to associate with for infrastructure of Ad-Hoc join */
- struct bss_descriptor bss;
-};
+ return r;
+}
#endif
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c
index b118a35ec60..f955b2d66ed 100644
--- a/drivers/net/wireless/libertas/ethtool.c
+++ b/drivers/net/wireless/libertas/ethtool.c
@@ -1,40 +1,30 @@
+#include <linux/hardirq.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/delay.h>
-#include "host.h"
#include "decl.h"
-#include "defs.h"
-#include "dev.h"
-#include "wext.h"
#include "cmd.h"
+#include "mesh.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",
- "tx_failed_cnt"
-};
static void lbs_ethtool_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct lbs_private *priv = dev->ml_priv;
- snprintf(info->fw_version, 32, "%u.%u.%u.p%u",
+ snprintf(info->fw_version, sizeof(info->fw_version),
+ "%u.%u.%u.p%u",
priv->fwrelease >> 24 & 0xff,
priv->fwrelease >> 16 & 0xff,
priv->fwrelease >> 8 & 0xff,
priv->fwrelease & 0xff);
- strcpy(info->driver, "libertas");
- strcpy(info->version, lbs_driver_version);
+ strlcpy(info->driver, "libertas", sizeof(info->driver));
+ strlcpy(info->version, lbs_driver_version, sizeof(info->version));
}
-/* All 8388 parts have 16KiB EEPROM size at the time of writing.
+/*
+ * All 8388 parts have 16KiB EEPROM size at the time of writing.
* In case that changes this needs fixing.
*/
#define LBS_EEPROM_LEN 16384
@@ -73,86 +63,16 @@ out:
return ret;
}
-static void lbs_ethtool_get_stats(struct net_device *dev,
- struct ethtool_stats *stats, uint64_t *data)
-{
- struct lbs_private *priv = dev->ml_priv;
- struct cmd_ds_mesh_access mesh_access;
- int ret;
-
- lbs_deb_enter(LBS_DEB_ETHTOOL);
-
- /* Get Mesh Statistics */
- ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access);
-
- if (ret) {
- memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t)));
- return;
- }
-
- priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
- priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
- priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
- priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
- priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
- priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
- priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
- priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);
-
- 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;
- data[7] = priv->mstats.tx_failed_cnt;
-
- lbs_deb_enter(LBS_DEB_ETHTOOL);
-}
-
-static int lbs_ethtool_get_sset_count(struct net_device *dev, int sset)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- if (sset == ETH_SS_STATS && dev == priv->mesh_dev)
- return MESH_STATS_NUM;
-
- return -EOPNOTSUPP;
-}
-
-static void lbs_ethtool_get_strings(struct net_device *dev,
- uint32_t stringset, uint8_t *s)
-{
- int i;
-
- lbs_deb_enter(LBS_DEB_ETHTOOL);
-
- 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;
- }
- lbs_deb_enter(LBS_DEB_ETHTOOL);
-}
-
static void lbs_ethtool_get_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
struct lbs_private *priv = dev->ml_priv;
- if (priv->wol_criteria == 0xffffffff) {
- /* Interface driver didn't configure wake */
- wol->supported = wol->wolopts = 0;
- return;
- }
-
wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY;
+ if (priv->wol_criteria == EHS_REMOVE_WAKEUP)
+ return;
+
if (priv->wol_criteria & EHS_WAKE_ON_UNICAST_DATA)
wol->wolopts |= WAKE_UCAST;
if (priv->wol_criteria & EHS_WAKE_ON_MULTICAST_DATA)
@@ -167,29 +87,33 @@ static int lbs_ethtool_set_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
struct lbs_private *priv = dev->ml_priv;
- uint32_t criteria = 0;
-
- if (priv->wol_criteria == 0xffffffff && wol->wolopts)
- return -EOPNOTSUPP;
if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY))
return -EOPNOTSUPP;
- if (wol->wolopts & WAKE_UCAST) criteria |= EHS_WAKE_ON_UNICAST_DATA;
- if (wol->wolopts & WAKE_MCAST) criteria |= EHS_WAKE_ON_MULTICAST_DATA;
- if (wol->wolopts & WAKE_BCAST) criteria |= EHS_WAKE_ON_BROADCAST_DATA;
- if (wol->wolopts & WAKE_PHY) criteria |= EHS_WAKE_ON_MAC_EVENT;
-
- return lbs_host_sleep_cfg(priv, criteria, (struct wol_config *)NULL);
+ priv->wol_criteria = 0;
+ if (wol->wolopts & WAKE_UCAST)
+ priv->wol_criteria |= EHS_WAKE_ON_UNICAST_DATA;
+ if (wol->wolopts & WAKE_MCAST)
+ priv->wol_criteria |= EHS_WAKE_ON_MULTICAST_DATA;
+ if (wol->wolopts & WAKE_BCAST)
+ priv->wol_criteria |= EHS_WAKE_ON_BROADCAST_DATA;
+ if (wol->wolopts & WAKE_PHY)
+ priv->wol_criteria |= EHS_WAKE_ON_MAC_EVENT;
+ if (wol->wolopts == 0)
+ priv->wol_criteria |= EHS_REMOVE_WAKEUP;
+ return 0;
}
-struct ethtool_ops lbs_ethtool_ops = {
+const struct ethtool_ops lbs_ethtool_ops = {
.get_drvinfo = lbs_ethtool_get_drvinfo,
.get_eeprom = lbs_ethtool_get_eeprom,
.get_eeprom_len = lbs_ethtool_get_eeprom_len,
- .get_sset_count = lbs_ethtool_get_sset_count,
- .get_ethtool_stats = lbs_ethtool_get_stats,
- .get_strings = lbs_ethtool_get_strings,
+#ifdef CONFIG_LIBERTAS_MESH
+ .get_sset_count = lbs_mesh_ethtool_get_sset_count,
+ .get_ethtool_stats = lbs_mesh_ethtool_get_stats,
+ .get_strings = lbs_mesh_ethtool_get_strings,
+#endif
.get_wol = lbs_ethtool_get_wol,
.set_wol = lbs_ethtool_set_wol,
};
diff --git a/drivers/net/wireless/libertas/firmware.c b/drivers/net/wireless/libertas/firmware.c
new file mode 100644
index 00000000000..51b92b5df11
--- /dev/null
+++ b/drivers/net/wireless/libertas/firmware.c
@@ -0,0 +1,227 @@
+/*
+ * Firmware loading and handling functions.
+ */
+
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include "dev.h"
+#include "decl.h"
+
+static void load_next_firmware_from_table(struct lbs_private *private);
+
+static void lbs_fw_loaded(struct lbs_private *priv, int ret,
+ const struct firmware *helper, const struct firmware *mainfw)
+{
+ unsigned long flags;
+
+ lbs_deb_fw("firmware load complete, code %d\n", ret);
+
+ /* User must free helper/mainfw */
+ priv->fw_callback(priv, ret, helper, mainfw);
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ priv->fw_callback = NULL;
+ wake_up(&priv->fw_waitq);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+}
+
+static void do_load_firmware(struct lbs_private *priv, const char *name,
+ void (*cb)(const struct firmware *fw, void *context))
+{
+ int ret;
+
+ lbs_deb_fw("Requesting %s\n", name);
+ ret = request_firmware_nowait(THIS_MODULE, true, name,
+ priv->fw_device, GFP_KERNEL, priv, cb);
+ if (ret) {
+ lbs_deb_fw("request_firmware_nowait error %d\n", ret);
+ lbs_fw_loaded(priv, ret, NULL, NULL);
+ }
+}
+
+static void main_firmware_cb(const struct firmware *firmware, void *context)
+{
+ struct lbs_private *priv = context;
+
+ if (!firmware) {
+ /* Failed to find firmware: try next table entry */
+ load_next_firmware_from_table(priv);
+ return;
+ }
+
+ /* Firmware found! */
+ lbs_fw_loaded(priv, 0, priv->helper_fw, firmware);
+ if (priv->helper_fw) {
+ release_firmware (priv->helper_fw);
+ priv->helper_fw = NULL;
+ }
+ release_firmware (firmware);
+}
+
+static void helper_firmware_cb(const struct firmware *firmware, void *context)
+{
+ struct lbs_private *priv = context;
+
+ if (!firmware) {
+ /* Failed to find firmware: try next table entry */
+ load_next_firmware_from_table(priv);
+ return;
+ }
+
+ /* Firmware found! */
+ if (priv->fw_iter->fwname) {
+ priv->helper_fw = firmware;
+ do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb);
+ } else {
+ /* No main firmware needed for this helper --> success! */
+ lbs_fw_loaded(priv, 0, firmware, NULL);
+ }
+}
+
+static void load_next_firmware_from_table(struct lbs_private *priv)
+{
+ const struct lbs_fw_table *iter;
+
+ if (!priv->fw_iter)
+ iter = priv->fw_table;
+ else
+ iter = ++priv->fw_iter;
+
+ if (priv->helper_fw) {
+ release_firmware(priv->helper_fw);
+ priv->helper_fw = NULL;
+ }
+
+next:
+ if (!iter->helper) {
+ /* End of table hit. */
+ lbs_fw_loaded(priv, -ENOENT, NULL, NULL);
+ return;
+ }
+
+ if (iter->model != priv->fw_model) {
+ iter++;
+ goto next;
+ }
+
+ priv->fw_iter = iter;
+ do_load_firmware(priv, iter->helper, helper_firmware_cb);
+}
+
+void lbs_wait_for_firmware_load(struct lbs_private *priv)
+{
+ wait_event(priv->fw_waitq, priv->fw_callback == NULL);
+}
+
+/**
+ * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load
+ * either a helper firmware and a main firmware (2-stage), or just the helper.
+ *
+ * @priv: Pointer to lbs_private instance
+ * @dev: A pointer to &device structure
+ * @card_model: Bus-specific card model ID used to filter firmware table
+ * elements
+ * @fw_table: Table of firmware file names and device model numbers
+ * terminated by an entry with a NULL helper name
+ * @callback: User callback to invoke when firmware load succeeds or fails.
+ */
+int lbs_get_firmware_async(struct lbs_private *priv, struct device *device,
+ u32 card_model, const struct lbs_fw_table *fw_table,
+ lbs_fw_cb callback)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ if (priv->fw_callback) {
+ lbs_deb_fw("firmware load already in progress\n");
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ return -EBUSY;
+ }
+
+ priv->fw_device = device;
+ priv->fw_callback = callback;
+ priv->fw_table = fw_table;
+ priv->fw_iter = NULL;
+ priv->fw_model = card_model;
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ lbs_deb_fw("Starting async firmware load\n");
+ load_next_firmware_from_table(priv);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lbs_get_firmware_async);
+
+/**
+ * lbs_get_firmware - Retrieves two-stage firmware
+ *
+ * @dev: A pointer to &device structure
+ * @card_model: Bus-specific card model ID used to filter firmware table
+ * elements
+ * @fw_table: Table of firmware file names and device model numbers
+ * terminated by an entry with a NULL helper name
+ * @helper: On success, the helper firmware; caller must free
+ * @mainfw: On success, the main firmware; caller must free
+ *
+ * Deprecated: use lbs_get_firmware_async() instead.
+ *
+ * returns: 0 on success, non-zero on failure
+ */
+int lbs_get_firmware(struct device *dev, u32 card_model,
+ const struct lbs_fw_table *fw_table,
+ const struct firmware **helper,
+ const struct firmware **mainfw)
+{
+ const struct lbs_fw_table *iter;
+ int ret;
+
+ BUG_ON(helper == NULL);
+ BUG_ON(mainfw == NULL);
+
+ /* Search for firmware to use from the table. */
+ iter = fw_table;
+ while (iter && iter->helper) {
+ if (iter->model != card_model)
+ goto next;
+
+ if (*helper == NULL) {
+ ret = request_firmware(helper, iter->helper, dev);
+ if (ret)
+ goto next;
+
+ /* If the device has one-stage firmware (ie cf8305) and
+ * we've got it then we don't need to bother with the
+ * main firmware.
+ */
+ if (iter->fwname == NULL)
+ return 0;
+ }
+
+ if (*mainfw == NULL) {
+ ret = request_firmware(mainfw, iter->fwname, dev);
+ if (ret) {
+ /* Clear the helper to ensure we don't have
+ * mismatched firmware pairs.
+ */
+ release_firmware(*helper);
+ *helper = NULL;
+ }
+ }
+
+ if (*helper && *mainfw)
+ return 0;
+
+ next:
+ iter++;
+ }
+
+ /* Failed */
+ release_firmware(*helper);
+ *helper = NULL;
+ release_firmware(*mainfw);
+ *mainfw = NULL;
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(lbs_get_firmware);
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
index d4457ef808a..96726f79a1d 100644
--- a/drivers/net/wireless/libertas/host.h
+++ b/drivers/net/wireless/libertas/host.h
@@ -1,198 +1,183 @@
-/**
- * This file contains definitions of WLAN commands.
- */
+/*
+ * This file function prototypes, data structure
+ * and definitions for all the host/station commands
+ */
#ifndef _LBS_HOST_H_
#define _LBS_HOST_H_
-/** PUBLIC DEFINITIONS */
-#define DEFAULT_AD_HOC_CHANNEL 6
-#define DEFAULT_AD_HOC_CHANNEL_A 36
+#include "types.h"
+#include "defs.h"
+
+#define DEFAULT_AD_HOC_CHANNEL 6
-#define CMD_OPTION_WAITFORRSP 0x0002
+#define CMD_OPTION_WAITFORRSP 0x0002
-/** Host command IDs */
+/* Host command IDs */
-/* Return command are almost always the same as the host command, but with
+/*
+ * Return command are almost always the same as the host command, but with
* bit 15 set high. There are a few exceptions, though...
*/
-#define CMD_RET(cmd) (0x8000 | cmd)
+#define CMD_RET(cmd) (0x8000 | cmd)
/* Return command convention exceptions: */
-#define CMD_RET_802_11_ASSOCIATE 0x8012
+#define CMD_RET_802_11_ASSOCIATE 0x8012
/* Command codes */
-#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_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_SET_AFC 0x003c
-#define CMD_802_11_GET_AFC 0x003d
-#define CMD_802_11_AD_HOC_STOP 0x0040
-#define CMD_802_11_HOST_SLEEP_CFG 0x0043
-#define CMD_802_11_WAKEUP_CONFIRM 0x0044
-#define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045
-#define CMD_802_11_BEACON_STOP 0x0049
-#define CMD_802_11_MAC_ADDRESS 0x004d
-#define CMD_802_11_LED_GPIO_CTRL 0x004e
-#define CMD_802_11_EEPROM_ACCESS 0x0059
-#define CMD_802_11_BAND_CONFIG 0x0058
-#define CMD_GSPI_BUS_CONFIG 0x005a
-#define CMD_802_11D_DOMAIN_INFO 0x005b
-#define CMD_802_11_KEY_MATERIAL 0x005e
-#define CMD_802_11_SLEEP_PARAMS 0x0066
-#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067
-#define CMD_802_11_SLEEP_PERIOD 0x0068
-#define CMD_802_11_TPC_CFG 0x0072
-#define CMD_802_11_PA_CFG 0x0073
-#define CMD_802_11_FW_WAKE_METHOD 0x0074
-#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_FWT_ACCESS 0x0095
-#define CMD_802_11_MONITOR_MODE 0x0098
-#define CMD_MESH_ACCESS 0x009b
-#define CMD_MESH_CONFIG 0x00a3
-#define CMD_SET_BOOT2_VER 0x00a5
-#define CMD_802_11_BEACON_CTRL 0x00b0
+#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_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_SET_AFC 0x003c
+#define CMD_802_11_GET_AFC 0x003d
+#define CMD_802_11_DEEP_SLEEP 0x003e
+#define CMD_802_11_AD_HOC_STOP 0x0040
+#define CMD_802_11_HOST_SLEEP_CFG 0x0043
+#define CMD_802_11_WAKEUP_CONFIRM 0x0044
+#define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045
+#define CMD_802_11_BEACON_STOP 0x0049
+#define CMD_802_11_MAC_ADDRESS 0x004d
+#define CMD_802_11_LED_GPIO_CTRL 0x004e
+#define CMD_802_11_BAND_CONFIG 0x0058
+#define CMD_GSPI_BUS_CONFIG 0x005a
+#define CMD_802_11D_DOMAIN_INFO 0x005b
+#define CMD_802_11_KEY_MATERIAL 0x005e
+#define CMD_802_11_SLEEP_PARAMS 0x0066
+#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067
+#define CMD_802_11_SLEEP_PERIOD 0x0068
+#define CMD_802_11_TPC_CFG 0x0072
+#define CMD_802_11_PA_CFG 0x0073
+#define CMD_802_11_FW_WAKE_METHOD 0x0074
+#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_FWT_ACCESS 0x0095
+#define CMD_802_11_MONITOR_MODE 0x0098
+#define CMD_MESH_ACCESS 0x009b
+#define CMD_MESH_CONFIG_OLD 0x00a3
+#define CMD_MESH_CONFIG 0x00ac
+#define CMD_SET_BOOT2_VER 0x00a5
+#define CMD_FUNC_INIT 0x00a9
+#define CMD_FUNC_SHUTDOWN 0x00aa
+#define CMD_802_11_BEACON_CTRL 0x00b0
/* 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
+#define PS_MODE_ACTION_ENTER_PS 0x0030
+#define PS_MODE_ACTION_EXIT_PS 0x0031
+#define PS_MODE_ACTION_SLEEP_CONFIRMED 0x0034
-#define CMD_ENABLE_RSN 0x0001
-#define CMD_DISABLE_RSN 0x0000
+#define CMD_ENABLE_RSN 0x0001
+#define CMD_DISABLE_RSN 0x0000
-#define CMD_ACT_GET 0x0000
-#define CMD_ACT_SET 0x0001
-#define CMD_ACT_GET_AES 0x0002
-#define CMD_ACT_SET_AES 0x0003
-#define CMD_ACT_REMOVE_AES 0x0004
+#define CMD_ACT_GET 0x0000
+#define CMD_ACT_SET 0x0001
/* 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 0x01
-#define CMD_TYPE_WEP_104_BIT 0x02
+#define CMD_ACT_ADD 0x0002
+#define CMD_ACT_REMOVE 0x0004
-#define CMD_NUM_OF_WEP_KEYS 4
+#define CMD_TYPE_WEP_40_BIT 0x01
+#define CMD_TYPE_WEP_104_BIT 0x02
-#define CMD_WEP_KEY_INDEX_MASK 0x3fff
+#define CMD_NUM_OF_WEP_KEYS 4
-/* Define action or option for CMD_802_11_RESET */
-#define CMD_ACT_HALT 0x0003
+#define CMD_WEP_KEY_INDEX_MASK 0x3fff
/* 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 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_TYPE_ACTIVE 0x0000
+#define CMD_SCAN_TYPE_PASSIVE 0x0001
-#define CMD_SCAN_RADIO_TYPE_BG 0
+#define CMD_SCAN_RADIO_TYPE_BG 0
-#define CMD_SCAN_PROBE_DELAY_TIME 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 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
/* Event flags for CMD_802_11_SUBSCRIBE_EVENT */
-#define CMD_SUBSCRIBE_RSSI_LOW 0x0001
-#define CMD_SUBSCRIBE_SNR_LOW 0x0002
-#define CMD_SUBSCRIBE_FAILCOUNT 0x0004
-#define CMD_SUBSCRIBE_BCNMISS 0x0008
-#define CMD_SUBSCRIBE_RSSI_HIGH 0x0010
-#define CMD_SUBSCRIBE_SNR_HIGH 0x0020
+#define CMD_SUBSCRIBE_RSSI_LOW 0x0001
+#define CMD_SUBSCRIBE_SNR_LOW 0x0002
+#define CMD_SUBSCRIBE_FAILCOUNT 0x0004
+#define CMD_SUBSCRIBE_BCNMISS 0x0008
+#define CMD_SUBSCRIBE_RSSI_HIGH 0x0010
+#define CMD_SUBSCRIBE_SNR_HIGH 0x0020
-#define RADIO_PREAMBLE_LONG 0x00
-#define RADIO_PREAMBLE_SHORT 0x02
-#define RADIO_PREAMBLE_AUTO 0x04
+#define RADIO_PREAMBLE_LONG 0x00
+#define RADIO_PREAMBLE_SHORT 0x02
+#define RADIO_PREAMBLE_AUTO 0x04
/* 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 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_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 CMD_ACT_SET_TX_AUTO 0x0000
+#define CMD_ACT_SET_TX_FIX_RATE 0x0001
+#define CMD_ACT_GET_TX_RATE 0x0002
/* Options for CMD_802_11_FW_WAKE_METHOD */
-#define CMD_WAKE_METHOD_UNCHANGED 0x0000
-#define CMD_WAKE_METHOD_COMMAND_INT 0x0001
-#define CMD_WAKE_METHOD_GPIO 0x0002
+#define CMD_WAKE_METHOD_UNCHANGED 0x0000
+#define CMD_WAKE_METHOD_COMMAND_INT 0x0001
+#define CMD_WAKE_METHOD_GPIO 0x0002
/* Object IDs for CMD_802_11_SNMP_MIB */
-#define SNMP_MIB_OID_BSS_TYPE 0x0000
-#define SNMP_MIB_OID_OP_RATE_SET 0x0001
-#define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */
-#define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */
-#define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */
-#define SNMP_MIB_OID_RTS_THRESHOLD 0x0005
-#define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006
-#define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007
-#define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008
-#define SNMP_MIB_OID_11D_ENABLE 0x0009
-#define SNMP_MIB_OID_11H_ENABLE 0x000A
+#define SNMP_MIB_OID_BSS_TYPE 0x0000
+#define SNMP_MIB_OID_OP_RATE_SET 0x0001
+#define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */
+#define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */
+#define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */
+#define SNMP_MIB_OID_RTS_THRESHOLD 0x0005
+#define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006
+#define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007
+#define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008
+#define SNMP_MIB_OID_11D_ENABLE 0x0009
+#define SNMP_MIB_OID_11H_ENABLE 0x000A
/* Define action or option for CMD_BT_ACCESS */
enum cmd_bt_access_opts {
@@ -266,7 +251,7 @@ enum cmd_mesh_config_types {
CMD_TYPE_MESH_GET_MESH_IE, /* GET_DEFAULTS is superset of GET_MESHIE */
};
-/** Card Event definition */
+/* Card Event definition */
#define MACREG_INT_CODE_TX_PPA_FREE 0
#define MACREG_INT_CODE_TX_DMA_DONE 1
#define MACREG_INT_CODE_LINK_LOST_W_SCAN 2
@@ -299,4 +284,695 @@ enum cmd_mesh_config_types {
#define MACREG_INT_CODE_MESH_AUTO_STARTED 35
#define MACREG_INT_CODE_FIRMWARE_READY 48
+
+/* 802.11-related definitions */
+
+/* TxPD descriptor */
+struct txpd {
+ /* union to cope up with later FW revisions */
+ union {
+ /* Current Tx packet status */
+ __le32 tx_status;
+ struct {
+ /* BSS type: client, AP, etc. */
+ u8 bss_type;
+ /* BSS number */
+ u8 bss_num;
+ /* Reserved */
+ __le16 reserved;
+ } bss;
+ } u;
+ /* Tx control */
+ __le32 tx_control;
+ __le32 tx_packet_location;
+ /* Tx packet length */
+ __le16 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 (units = 2ms) */
+ u8 pktdelay_2ms;
+ /* reserved */
+ u8 reserved1;
+} __packed;
+
+/* RxPD Descriptor */
+struct rxpd {
+ /* union to cope up with later FW revisions */
+ union {
+ /* Current Rx packet status */
+ __le16 status;
+ struct {
+ /* BSS type: client, AP, etc. */
+ u8 bss_type;
+ /* BSS number */
+ u8 bss_num;
+ } __packed bss;
+ } __packed u;
+
+ /* SNR */
+ u8 snr;
+
+ /* Tx control */
+ u8 rx_control;
+
+ /* Pkt length */
+ __le16 pkt_len;
+
+ /* Noise Floor */
+ u8 nf;
+
+ /* Rx Packet Rate */
+ u8 rx_rate;
+
+ /* Pkt addr */
+ __le32 pkt_ptr;
+
+ /* Next Rx RxPD addr */
+ __le32 next_rxpd_ptr;
+
+ /* Pkt Priority */
+ u8 priority;
+ u8 reserved[3];
+} __packed;
+
+struct cmd_header {
+ __le16 command;
+ __le16 size;
+ __le16 seqnum;
+ __le16 result;
+} __packed;
+
+/* Generic structure to hold all key types. */
+struct enc_key {
+ u16 len;
+ u16 flags; /* KEY_INFO_* from defs.h */
+ u16 type; /* KEY_TYPE_* from defs.h */
+ u8 key[32];
+};
+
+/* lbs_offset_value */
+struct lbs_offset_value {
+ u32 offset;
+ u32 value;
+} __packed;
+
+#define MAX_11D_TRIPLETS 83
+
+struct mrvl_ie_domain_param_set {
+ struct mrvl_ie_header header;
+
+ u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+ struct ieee80211_country_ie_triplet triplet[MAX_11D_TRIPLETS];
+} __packed;
+
+struct cmd_ds_802_11d_domain_info {
+ struct cmd_header hdr;
+
+ __le16 action;
+ struct mrvl_ie_domain_param_set domain;
+} __packed;
+
+/*
+ * 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 {
+ struct cmd_header hdr;
+
+ /* HW Interface version number */
+ __le16 hwifversion;
+ /* HW version number */
+ __le16 version;
+ /* Max number of TxPD FW can handle */
+ __le16 nr_txpd;
+ /* Max no of Multicast address */
+ __le16 nr_mcast_adr;
+ /* MAC address */
+ u8 permanentaddr[6];
+
+ /* region Code */
+ __le16 regioncode;
+
+ /* Number of antenna used */
+ __le16 nr_antenna;
+
+ /* FW release number, example 0x01030304 = 2.3.4p1 */
+ __le32 fwrelease;
+
+ /* Base Address of TxPD queue */
+ __le32 wcb_base;
+ /* Read Pointer of RxPd queue */
+ __le32 rxpd_rdptr;
+
+ /* Write Pointer of RxPd queue */
+ __le32 rxpd_wrptr;
+
+ /*FW/HW capability */
+ __le32 fwcapinfo;
+} __packed;
+
+struct cmd_ds_802_11_subscribe_event {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 events;
+
+ /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a
+ * number of TLVs. From the v5.1 manual, those TLVs would add up to
+ * 40 bytes. However, future firmware might add additional TLVs, so I
+ * bump this up a bit.
+ */
+ uint8_t tlv[128];
+} __packed;
+
+/*
+ * This scan handle Country Information IE(802.11d compliant)
+ * Define data structure for CMD_802_11_SCAN
+ */
+struct cmd_ds_802_11_scan {
+ struct cmd_header hdr;
+
+ uint8_t bsstype;
+ uint8_t bssid[ETH_ALEN];
+ uint8_t tlvbuffer[0];
+} __packed;
+
+struct cmd_ds_802_11_scan_rsp {
+ struct cmd_header hdr;
+
+ __le16 bssdescriptsize;
+ uint8_t nr_sets;
+ uint8_t bssdesc_and_tlvbuffer[0];
+} __packed;
+
+struct cmd_ds_802_11_get_log {
+ struct cmd_header hdr;
+
+ __le32 mcasttxframe;
+ __le32 failed;
+ __le32 retry;
+ __le32 multiretry;
+ __le32 framedup;
+ __le32 rtssuccess;
+ __le32 rtsfailure;
+ __le32 ackfailure;
+ __le32 rxfrag;
+ __le32 mcastrxframe;
+ __le32 fcserror;
+ __le32 txframe;
+ __le32 wepundecryptable;
+} __packed;
+
+struct cmd_ds_mac_control {
+ struct cmd_header hdr;
+ __le16 action;
+ u16 reserved;
+} __packed;
+
+struct cmd_ds_mac_multicast_adr {
+ struct cmd_header hdr;
+ __le16 action;
+ __le16 nr_of_adrs;
+ u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
+} __packed;
+
+struct cmd_ds_802_11_authenticate {
+ struct cmd_header hdr;
+
+ u8 bssid[ETH_ALEN];
+ u8 authtype;
+ u8 reserved[10];
+} __packed;
+
+struct cmd_ds_802_11_deauthenticate {
+ struct cmd_header hdr;
+
+ u8 macaddr[ETH_ALEN];
+ __le16 reasoncode;
+} __packed;
+
+struct cmd_ds_802_11_associate {
+ struct cmd_header hdr;
+
+ u8 bssid[6];
+ __le16 capability;
+ __le16 listeninterval;
+ __le16 bcnperiod;
+ u8 dtimperiod;
+ u8 iebuf[512]; /* Enough for required and most optional IEs */
+} __packed;
+
+struct cmd_ds_802_11_associate_response {
+ struct cmd_header hdr;
+
+ __le16 capability;
+ __le16 statuscode;
+ __le16 aid;
+ u8 iebuf[512];
+} __packed;
+
+struct cmd_ds_802_11_set_wep {
+ struct cmd_header hdr;
+
+ /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */
+ __le16 action;
+
+ /* key Index selected for Tx */
+ __le16 keyindex;
+
+ /* 40, 128bit or TXWEP */
+ uint8_t keytype[4];
+ uint8_t keymaterial[4][16];
+} __packed;
+
+struct cmd_ds_802_11_snmp_mib {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 oid;
+ __le16 bufsize;
+ u8 value[128];
+} __packed;
+
+struct cmd_ds_reg_access {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 offset;
+ union {
+ u8 bbp_rf; /* for BBP and RF registers */
+ __le32 mac; /* for MAC registers */
+ } value;
+} __packed;
+
+struct cmd_ds_802_11_radio_control {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 control;
+} __packed;
+
+struct cmd_ds_802_11_beacon_control {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 beacon_enable;
+ __le16 beacon_period;
+} __packed;
+
+struct cmd_ds_802_11_sleep_params {
+ struct cmd_header hdr;
+
+ /* ACT_GET/ACT_SET */
+ __le16 action;
+
+ /* Sleep clock error in ppm */
+ __le16 error;
+
+ /* Wakeup offset in usec */
+ __le16 offset;
+
+ /* Clock stabilization time in usec */
+ __le16 stabletime;
+
+ /* control periodic calibration */
+ uint8_t calcontrol;
+
+ /* control the use of external sleep clock */
+ uint8_t externalsleepclk;
+
+ /* reserved field, should be set to zero */
+ __le16 reserved;
+} __packed;
+
+struct cmd_ds_802_11_rf_channel {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 channel;
+ __le16 rftype; /* unused */
+ __le16 reserved; /* unused */
+ u8 channellist[32]; /* unused */
+} __packed;
+
+struct cmd_ds_802_11_rssi {
+ struct cmd_header hdr;
+
+ /*
+ * request: number of beacons (N) to average the SNR and NF over
+ * response: SNR of most recent beacon
+ */
+ __le16 n_or_snr;
+
+ /*
+ * The following fields are only set in the response.
+ * In the request these are reserved and should be set to 0.
+ */
+ __le16 nf; /* most recent beacon noise floor */
+ __le16 avg_snr; /* average SNR weighted by N from request */
+ __le16 avg_nf; /* average noise floor weighted by N from request */
+} __packed;
+
+struct cmd_ds_802_11_mac_address {
+ struct cmd_header hdr;
+
+ __le16 action;
+ u8 macadd[ETH_ALEN];
+} __packed;
+
+struct cmd_ds_802_11_rf_tx_power {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 curlevel;
+ s8 maxlevel;
+ s8 minlevel;
+} __packed;
+
+/* MONITOR_MODE only exists in OLPC v5 firmware */
+struct cmd_ds_802_11_monitor_mode {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 mode;
+} __packed;
+
+struct cmd_ds_set_boot2_ver {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 version;
+} __packed;
+
+struct cmd_ds_802_11_fw_wake_method {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 method;
+} __packed;
+
+struct cmd_ds_802_11_ps_mode {
+ struct cmd_header hdr;
+
+ __le16 action;
+
+ /*
+ * Interval for keepalive in PS mode:
+ * 0x0000 = don't change
+ * 0x001E = firmware default
+ * 0xFFFF = disable
+ */
+ __le16 nullpktinterval;
+
+ /*
+ * Number of DTIM intervals to wake up for:
+ * 0 = don't change
+ * 1 = firmware default
+ * 5 = max
+ */
+ __le16 multipledtim;
+
+ __le16 reserved;
+ __le16 locallisteninterval;
+
+ /*
+ * AdHoc awake period (FW v9+ only):
+ * 0 = don't change
+ * 1 = always awake (IEEE standard behavior)
+ * 2 - 31 = sleep for (n - 1) periods and awake for 1 period
+ * 32 - 254 = invalid
+ * 255 = sleep at each ATIM
+ */
+ __le16 adhoc_awake_period;
+} __packed;
+
+struct cmd_confirm_sleep {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 nullpktinterval;
+ __le16 multipledtim;
+ __le16 reserved;
+ __le16 locallisteninterval;
+} __packed;
+
+struct cmd_ds_802_11_data_rate {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 reserved;
+ u8 rates[MAX_RATES];
+} __packed;
+
+struct cmd_ds_802_11_rate_adapt_rateset {
+ struct cmd_header hdr;
+ __le16 action;
+ __le16 enablehwauto;
+ __le16 bitmap;
+} __packed;
+
+struct cmd_ds_802_11_ad_hoc_start {
+ struct cmd_header hdr;
+
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 bsstype;
+ __le16 beaconperiod;
+ u8 dtimperiod; /* Reserved on v9 and later */
+ struct ieee_ie_ibss_param_set ibss;
+ u8 reserved1[4];
+ struct ieee_ie_ds_param_set ds;
+ u8 reserved2[4];
+ __le16 probedelay; /* Reserved on v9 and later */
+ __le16 capability;
+ u8 rates[MAX_RATES];
+ u8 tlv_memory_size_pad[100];
+} __packed;
+
+struct cmd_ds_802_11_ad_hoc_result {
+ struct cmd_header hdr;
+
+ u8 pad[3];
+ u8 bssid[ETH_ALEN];
+} __packed;
+
+struct adhoc_bssdesc {
+ u8 bssid[ETH_ALEN];
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 type;
+ __le16 beaconperiod;
+ u8 dtimperiod;
+ __le64 timestamp;
+ __le64 localtime;
+ struct ieee_ie_ds_param_set ds;
+ u8 reserved1[4];
+ struct ieee_ie_ibss_param_set ibss;
+ u8 reserved2[4];
+ __le16 capability;
+ u8 rates[MAX_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
+ */
+} __packed;
+
+struct cmd_ds_802_11_ad_hoc_join {
+ struct cmd_header hdr;
+
+ struct adhoc_bssdesc bss;
+ __le16 failtimeout; /* Reserved on v9 and later */
+ __le16 probedelay; /* Reserved on v9 and later */
+} __packed;
+
+struct cmd_ds_802_11_ad_hoc_stop {
+ struct cmd_header hdr;
+} __packed;
+
+struct cmd_ds_802_11_enable_rsn {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 enable;
+} __packed;
+
+struct MrvlIEtype_keyParamSet {
+ /* type ID */
+ __le16 type;
+
+ /* length of Payload */
+ __le16 length;
+
+ /* type of key: WEP=0, TKIP=1, AES=2 */
+ __le16 keytypeid;
+
+ /* key control Info specific to a keytypeid */
+ __le16 keyinfo;
+
+ /* length of key */
+ __le16 keylen;
+
+ /* key material of size keylen */
+ u8 key[32];
+} __packed;
+
+#define MAX_WOL_RULES 16
+
+struct host_wol_rule {
+ uint8_t rule_no;
+ uint8_t rule_ops;
+ __le16 sig_offset;
+ __le16 sig_length;
+ __le16 reserve;
+ __be32 sig_mask;
+ __be32 signature;
+} __packed;
+
+struct wol_config {
+ uint8_t action;
+ uint8_t pattern;
+ uint8_t no_rules_in_cmd;
+ uint8_t result;
+ struct host_wol_rule rule[MAX_WOL_RULES];
+} __packed;
+
+struct cmd_ds_host_sleep {
+ struct cmd_header hdr;
+ __le32 criteria;
+ uint8_t gpio;
+ uint16_t gap;
+ struct wol_config wol_conf;
+} __packed;
+
+
+
+struct cmd_ds_802_11_key_material {
+ struct cmd_header hdr;
+
+ __le16 action;
+ struct MrvlIEtype_keyParamSet keyParamSet[2];
+} __packed;
+
+struct cmd_ds_802_11_eeprom_access {
+ struct cmd_header hdr;
+ __le16 action;
+ __le16 offset;
+ __le16 len;
+ /* firmware says it returns a maximum of 20 bytes */
+#define LBS_EEPROM_READ_LEN 20
+ u8 value[LBS_EEPROM_READ_LEN];
+} __packed;
+
+struct cmd_ds_802_11_tpc_cfg {
+ struct cmd_header hdr;
+
+ __le16 action;
+ uint8_t enable;
+ int8_t P0;
+ int8_t P1;
+ int8_t P2;
+ uint8_t usesnr;
+} __packed;
+
+
+struct cmd_ds_802_11_pa_cfg {
+ struct cmd_header hdr;
+
+ __le16 action;
+ uint8_t enable;
+ int8_t P0;
+ int8_t P1;
+ int8_t P2;
+} __packed;
+
+
+struct cmd_ds_802_11_led_ctrl {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 numled;
+ u8 data[256];
+} __packed;
+
+/* Automatic Frequency Control */
+struct cmd_ds_802_11_afc {
+ struct cmd_header hdr;
+
+ __le16 afc_auto;
+ union {
+ struct {
+ __le16 threshold;
+ __le16 period;
+ };
+ struct {
+ __le16 timing_offset; /* signed */
+ __le16 carrier_offset; /* signed */
+ };
+ };
+} __packed;
+
+struct cmd_tx_rate_query {
+ __le16 txrate;
+} __packed;
+
+struct cmd_ds_get_tsf {
+ __le64 tsfvalue;
+} __packed;
+
+struct cmd_ds_bt_access {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le32 id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+} __packed;
+
+struct cmd_ds_fwt_access {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le32 id;
+ u8 valid;
+ u8 da[ETH_ALEN];
+ u8 dir;
+ u8 ra[ETH_ALEN];
+ __le32 ssn;
+ __le32 dsn;
+ __le32 metric;
+ u8 rate;
+ u8 hopcount;
+ u8 ttl;
+ __le32 expiration;
+ u8 sleepmode;
+ __le32 snr;
+ __le32 references;
+ u8 prec[ETH_ALEN];
+} __packed;
+
+struct cmd_ds_mesh_config {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 channel;
+ __le16 type;
+ __le16 length;
+ u8 data[128]; /* last position reserved */
+} __packed;
+
+struct cmd_ds_mesh_access {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le32 data[32]; /* last position reserved */
+} __packed;
+
+/* Number of stats counters returned by the firmware */
+#define MESH_STATS_NUM 8
#endif
diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h
deleted file mode 100644
index a899aeb676b..00000000000
--- a/drivers/net/wireless/libertas/hostcmd.h
+++ /dev/null
@@ -1,775 +0,0 @@
-/*
- * This file contains the function prototypes, data structure
- * and defines for all the host/station commands
- */
-#ifndef _LBS_HOSTCMD_H
-#define _LBS_HOSTCMD_H
-
-#include <linux/wireless.h>
-#include "11d.h"
-#include "types.h"
-
-/* 802.11-related definitions */
-
-/* TxPD descriptor */
-struct txpd {
- /* Current Tx packet status */
- __le32 tx_status;
- /* Tx control */
- __le32 tx_control;
- __le32 tx_packet_location;
- /* Tx packet length */
- __le16 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;
-} __attribute__ ((packed));
-
-/* RxPD Descriptor */
-struct rxpd {
- /* Current Rx packet status */
- __le16 status;
-
- /* SNR */
- u8 snr;
-
- /* Tx control */
- u8 rx_control;
-
- /* Pkt length */
- __le16 pkt_len;
-
- /* Noise Floor */
- u8 nf;
-
- /* Rx Packet Rate */
- u8 rx_rate;
-
- /* Pkt addr */
- __le32 pkt_ptr;
-
- /* Next Rx RxPD addr */
- __le32 next_rxpd_ptr;
-
- /* Pkt Priority */
- u8 priority;
- u8 reserved[3];
-} __attribute__ ((packed));
-
-struct cmd_header {
- __le16 command;
- __le16 size;
- __le16 seqnum;
- __le16 result;
-} __attribute__ ((packed));
-
-struct cmd_ctrl_node {
- struct list_head list;
- int result;
- /* command response */
- int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *);
- unsigned long callback_arg;
- /* command data */
- struct cmd_header *cmdbuf;
- /* wait queue */
- u16 cmdwaitqwoken;
- wait_queue_head_t cmdwait_q;
-};
-
-/* Generic structure to hold all key types. */
-struct enc_key {
- u16 len;
- u16 flags; /* KEY_INFO_* from defs.h */
- u16 type; /* KEY_TYPE_* from defs.h */
- u8 key[32];
-};
-
-/* lbs_offset_value */
-struct lbs_offset_value {
- u32 offset;
- u32 value;
-} __attribute__ ((packed));
-
-/* Define general data structure */
-/* cmd_DS_GEN */
-struct cmd_ds_gen {
- __le16 command;
- __le16 size;
- __le16 seqnum;
- __le16 result;
- void *cmdresp[0];
-} __attribute__ ((packed));
-
-#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 {
- struct cmd_header hdr;
-
- /* HW Interface version number */
- __le16 hwifversion;
- /* HW version number */
- __le16 version;
- /* Max number of TxPD FW can handle */
- __le16 nr_txpd;
- /* Max no of Multicast address */
- __le16 nr_mcast_adr;
- /* MAC address */
- u8 permanentaddr[6];
-
- /* region Code */
- __le16 regioncode;
-
- /* Number of antenna used */
- __le16 nr_antenna;
-
- /* FW release number, example 0x01030304 = 2.3.4p1 */
- __le32 fwrelease;
-
- /* Base Address of TxPD queue */
- __le32 wcb_base;
- /* Read Pointer of RxPd queue */
- __le32 rxpd_rdptr;
-
- /* Write Pointer of RxPd queue */
- __le32 rxpd_wrptr;
-
- /*FW/HW capability */
- __le32 fwcapinfo;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_subscribe_event {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 events;
-
- /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a
- * number of TLVs. From the v5.1 manual, those TLVs would add up to
- * 40 bytes. However, future firmware might add additional TLVs, so I
- * bump this up a bit.
- */
- uint8_t tlv[128];
-} __attribute__ ((packed));
-
-/*
- * This scan handle Country Information IE(802.11d compliant)
- * Define data structure for CMD_802_11_SCAN
- */
-struct cmd_ds_802_11_scan {
- struct cmd_header hdr;
-
- uint8_t bsstype;
- uint8_t bssid[ETH_ALEN];
- uint8_t tlvbuffer[0];
-#if 0
- mrvlietypes_ssidparamset_t ssidParamSet;
- mrvlietypes_chanlistparamset_t ChanListParamSet;
- mrvlietypes_ratesparamset_t OpRateSet;
-#endif
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_scan_rsp {
- struct cmd_header hdr;
-
- __le16 bssdescriptsize;
- uint8_t nr_sets;
- uint8_t bssdesc_and_tlvbuffer[0];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_get_log {
- struct cmd_header hdr;
-
- __le32 mcasttxframe;
- __le32 failed;
- __le32 retry;
- __le32 multiretry;
- __le32 framedup;
- __le32 rtssuccess;
- __le32 rtsfailure;
- __le32 ackfailure;
- __le32 rxfrag;
- __le32 mcastrxframe;
- __le32 fcserror;
- __le32 txframe;
- __le32 wepundecryptable;
-} __attribute__ ((packed));
-
-struct cmd_ds_mac_control {
- struct cmd_header hdr;
- __le16 action;
- u16 reserved;
-} __attribute__ ((packed));
-
-struct cmd_ds_mac_multicast_adr {
- struct cmd_header hdr;
- __le16 action;
- __le16 nr_of_adrs;
- u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
-} __attribute__ ((packed));
-
-struct cmd_ds_gspi_bus_config {
- struct cmd_header hdr;
- __le16 action;
- __le16 bus_delay_mode;
- __le16 host_time_delay_to_read_port;
- __le16 host_time_delay_to_read_register;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_authenticate {
- u8 macaddr[ETH_ALEN];
- u8 authtype;
- u8 reserved[10];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_deauthenticate {
- struct cmd_header hdr;
-
- u8 macaddr[ETH_ALEN];
- __le16 reasoncode;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_associate {
- u8 peerstaaddr[6];
- __le16 capability;
- __le16 listeninterval;
- __le16 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_associate_rsp {
- struct ieeetypes_assocrsp assocRsp;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_set_wep {
- struct cmd_header hdr;
-
- /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */
- __le16 action;
-
- /* key Index selected for Tx */
- __le16 keyindex;
-
- /* 40, 128bit or TXWEP */
- uint8_t keytype[4];
- uint8_t keymaterial[4][16];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_3_get_stat {
- __le32 xmitok;
- __le32 rcvok;
- __le32 xmiterror;
- __le32 rcverror;
- __le32 rcvnobuffer;
- __le32 rcvcrcerror;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_get_stat {
- __le32 txfragmentcnt;
- __le32 mcasttxframecnt;
- __le32 failedcnt;
- __le32 retrycnt;
- __le32 Multipleretrycnt;
- __le32 rtssuccesscnt;
- __le32 rtsfailurecnt;
- __le32 ackfailurecnt;
- __le32 frameduplicatecnt;
- __le32 rxfragmentcnt;
- __le32 mcastrxframecnt;
- __le32 fcserrorcnt;
- __le32 bcasttxframecnt;
- __le32 bcastrxframecnt;
- __le32 txbeacon;
- __le32 rxbeacon;
- __le32 wepundecryptable;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_snmp_mib {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 oid;
- __le16 bufsize;
- u8 value[128];
-} __attribute__ ((packed));
-
-struct cmd_ds_mac_reg_map {
- __le16 buffersize;
- u8 regmap[128];
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct cmd_ds_bbp_reg_map {
- __le16 buffersize;
- u8 regmap[128];
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct cmd_ds_rf_reg_map {
- __le16 buffersize;
- u8 regmap[64];
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct cmd_ds_mac_reg_access {
- __le16 action;
- __le16 offset;
- __le32 value;
-} __attribute__ ((packed));
-
-struct cmd_ds_bbp_reg_access {
- __le16 action;
- __le16 offset;
- u8 value;
- u8 reserved[3];
-} __attribute__ ((packed));
-
-struct cmd_ds_rf_reg_access {
- __le16 action;
- __le16 offset;
- u8 value;
- u8 reserved[3];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_radio_control {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 control;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_beacon_control {
- __le16 action;
- __le16 beacon_enable;
- __le16 beacon_period;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_sleep_params {
- struct cmd_header hdr;
-
- /* ACT_GET/ACT_SET */
- __le16 action;
-
- /* Sleep clock error in ppm */
- __le16 error;
-
- /* Wakeup offset in usec */
- __le16 offset;
-
- /* Clock stabilization time in usec */
- __le16 stabletime;
-
- /* control periodic calibration */
- uint8_t calcontrol;
-
- /* control the use of external sleep clock */
- uint8_t externalsleepclk;
-
- /* reserved field, should be set to zero */
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_inactivity_timeout {
- struct cmd_header hdr;
-
- /* ACT_GET/ACT_SET */
- __le16 action;
-
- /* Inactivity timeout in msec */
- __le16 timeout;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rf_channel {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 channel;
- __le16 rftype; /* unused */
- __le16 reserved; /* unused */
- u8 channellist[32]; /* unused */
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rssi {
- /* weighting factor */
- __le16 N;
-
- __le16 reserved_0;
- __le16 reserved_1;
- __le16 reserved_2;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rssi_rsp {
- __le16 SNR;
- __le16 noisefloor;
- __le16 avgSNR;
- __le16 avgnoisefloor;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_mac_address {
- struct cmd_header hdr;
-
- __le16 action;
- u8 macadd[ETH_ALEN];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rf_tx_power {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 curlevel;
- s8 maxlevel;
- s8 minlevel;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rf_antenna {
- __le16 action;
-
- /* Number of antennas or 0xffff(diversity) */
- __le16 antennamode;
-
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_monitor_mode {
- __le16 action;
- __le16 mode;
-} __attribute__ ((packed));
-
-struct cmd_ds_set_boot2_ver {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 version;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_fw_wake_method {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 method;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_sleep_period {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 period;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_ps_mode {
- __le16 action;
- __le16 nullpktinterval;
- __le16 multipledtim;
- __le16 reserved;
- __le16 locallisteninterval;
-} __attribute__ ((packed));
-
-struct cmd_confirm_sleep {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 nullpktinterval;
- __le16 multipledtim;
- __le16 reserved;
- __le16 locallisteninterval;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_data_rate {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 reserved;
- u8 rates[MAX_RATES];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rate_adapt_rateset {
- struct cmd_header hdr;
- __le16 action;
- __le16 enablehwauto;
- __le16 bitmap;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_ad_hoc_start {
- struct cmd_header hdr;
-
- u8 ssid[IW_ESSID_MAX_SIZE];
- u8 bsstype;
- __le16 beaconperiod;
- u8 dtimperiod; /* Reserved on v9 and later */
- union IEEEtypes_ssparamset ssparamset;
- union ieeetypes_phyparamset phyparamset;
- __le16 probedelay;
- __le16 capability;
- u8 rates[MAX_RATES];
- u8 tlv_memory_size_pad[100];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_ad_hoc_result {
- struct cmd_header hdr;
-
- u8 pad[3];
- u8 bssid[ETH_ALEN];
-} __attribute__ ((packed));
-
-struct adhoc_bssdesc {
- u8 bssid[ETH_ALEN];
- u8 ssid[IW_ESSID_MAX_SIZE];
- u8 type;
- __le16 beaconperiod;
- u8 dtimperiod;
- __le64 timestamp;
- __le64 localtime;
- union ieeetypes_phyparamset phyparamset;
- union IEEEtypes_ssparamset ssparamset;
- __le16 capability;
- u8 rates[MAX_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 cmd_header hdr;
-
- struct adhoc_bssdesc bss;
- __le16 failtimeout; /* Reserved on v9 and later */
- __le16 probedelay; /* Reserved on v9 and later */
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_ad_hoc_stop {
- struct cmd_header hdr;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_enable_rsn {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 enable;
-} __attribute__ ((packed));
-
-struct MrvlIEtype_keyParamSet {
- /* type ID */
- __le16 type;
-
- /* length of Payload */
- __le16 length;
-
- /* type of key: WEP=0, TKIP=1, AES=2 */
- __le16 keytypeid;
-
- /* key control Info specific to a keytypeid */
- __le16 keyinfo;
-
- /* length of key */
- __le16 keylen;
-
- /* key material of size keylen */
- u8 key[32];
-} __attribute__ ((packed));
-
-#define MAX_WOL_RULES 16
-
-struct host_wol_rule {
- uint8_t rule_no;
- uint8_t rule_ops;
- __le16 sig_offset;
- __le16 sig_length;
- __le16 reserve;
- __be32 sig_mask;
- __be32 signature;
-} __attribute__ ((packed));
-
-struct wol_config {
- uint8_t action;
- uint8_t pattern;
- uint8_t no_rules_in_cmd;
- uint8_t result;
- struct host_wol_rule rule[MAX_WOL_RULES];
-} __attribute__ ((packed));
-
-struct cmd_ds_host_sleep {
- struct cmd_header hdr;
- __le32 criteria;
- uint8_t gpio;
- uint16_t gap;
- struct wol_config wol_conf;
-} __attribute__ ((packed));
-
-
-
-struct cmd_ds_802_11_key_material {
- struct cmd_header hdr;
-
- __le16 action;
- struct MrvlIEtype_keyParamSet keyParamSet[2];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_eeprom_access {
- struct cmd_header hdr;
- __le16 action;
- __le16 offset;
- __le16 len;
- /* firmware says it returns a maximum of 20 bytes */
-#define LBS_EEPROM_READ_LEN 20
- u8 value[LBS_EEPROM_READ_LEN];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_tpc_cfg {
- struct cmd_header hdr;
-
- __le16 action;
- uint8_t enable;
- int8_t P0;
- int8_t P1;
- int8_t P2;
- uint8_t usesnr;
-} __attribute__ ((packed));
-
-
-struct cmd_ds_802_11_pa_cfg {
- struct cmd_header hdr;
-
- __le16 action;
- uint8_t enable;
- int8_t P0;
- int8_t P1;
- int8_t P2;
-} __attribute__ ((packed));
-
-
-struct cmd_ds_802_11_led_ctrl {
- __le16 action;
- __le16 numled;
- u8 data[256];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_afc {
- __le16 afc_auto;
- union {
- struct {
- __le16 threshold;
- __le16 period;
- };
- struct {
- __le16 timing_offset; /* signed */
- __le16 carrier_offset; /* signed */
- };
- };
-} __attribute__ ((packed));
-
-struct cmd_tx_rate_query {
- __le16 txrate;
-} __attribute__ ((packed));
-
-struct cmd_ds_get_tsf {
- __le64 tsfvalue;
-} __attribute__ ((packed));
-
-struct cmd_ds_bt_access {
- __le16 action;
- __le32 id;
- u8 addr1[ETH_ALEN];
- u8 addr2[ETH_ALEN];
-} __attribute__ ((packed));
-
-struct cmd_ds_fwt_access {
- __le16 action;
- __le32 id;
- u8 valid;
- u8 da[ETH_ALEN];
- u8 dir;
- u8 ra[ETH_ALEN];
- __le32 ssn;
- __le32 dsn;
- __le32 metric;
- u8 rate;
- u8 hopcount;
- u8 ttl;
- __le32 expiration;
- u8 sleepmode;
- __le32 snr;
- __le32 references;
- u8 prec[ETH_ALEN];
-} __attribute__ ((packed));
-
-
-struct cmd_ds_mesh_config {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 channel;
- __le16 type;
- __le16 length;
- u8 data[128]; /* last position reserved */
-} __attribute__ ((packed));
-
-
-struct cmd_ds_mesh_access {
- struct cmd_header hdr;
-
- __le16 action;
- __le32 data[32]; /* last position reserved */
-} __attribute__ ((packed));
-
-/* Number of stats counters returned by the firmware */
-#define MESH_STATS_NUM 8
-
-struct cmd_ds_command {
- /* command header */
- __le16 command;
- __le16 size;
- __le16 seqnum;
- __le16 result;
-
- /* command Body */
- union {
- struct cmd_ds_802_11_ps_mode psmode;
- struct cmd_ds_802_11_associate associate;
- 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_rf_antenna rant;
- struct cmd_ds_802_11_monitor_mode monitor;
- struct cmd_ds_802_11_rssi rssi;
- struct cmd_ds_802_11_rssi_rsp rssirsp;
- 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_11d_domain_info domaininfo;
- struct cmd_ds_802_11d_domain_info domaininforesp;
-
- struct cmd_ds_802_11_tpc_cfg tpccfg;
- 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_get_tsf gettsf;
- struct cmd_ds_802_11_beacon_control bcn_ctrl;
- } params;
-} __attribute__ ((packed));
-
-#endif
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c
index cedeac6322f..f499efc6abc 100644
--- a/drivers/net/wireless/libertas/if_cs.c
+++ b/drivers/net/wireless/libertas/if_cs.c
@@ -21,14 +21,15 @@
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
@@ -59,9 +60,35 @@ struct if_cs_card {
struct pcmcia_device *p_dev;
struct lbs_private *priv;
void __iomem *iobase;
+ bool align_regs;
+ u32 model;
};
+enum {
+ MODEL_UNKNOWN = 0x00,
+ MODEL_8305 = 0x01,
+ MODEL_8381 = 0x02,
+ MODEL_8385 = 0x03
+};
+
+static const struct lbs_fw_table fw_table[] = {
+ { MODEL_8305, "libertas/cf8305.bin", NULL },
+ { MODEL_8305, "libertas_cs_helper.fw", NULL },
+ { MODEL_8381, "libertas/cf8381_helper.bin", "libertas/cf8381.bin" },
+ { MODEL_8381, "libertas_cs_helper.fw", "libertas_cs.fw" },
+ { MODEL_8385, "libertas/cf8385_helper.bin", "libertas/cf8385.bin" },
+ { MODEL_8385, "libertas_cs_helper.fw", "libertas_cs.fw" },
+ { 0, NULL, NULL }
+};
+MODULE_FIRMWARE("libertas/cf8305.bin");
+MODULE_FIRMWARE("libertas/cf8381_helper.bin");
+MODULE_FIRMWARE("libertas/cf8381.bin");
+MODULE_FIRMWARE("libertas/cf8385_helper.bin");
+MODULE_FIRMWARE("libertas/cf8385.bin");
+MODULE_FIRMWARE("libertas_cs_helper.fw");
+MODULE_FIRMWARE("libertas_cs.fw");
+
/********************************************************************/
/* Hardware access */
@@ -273,7 +300,35 @@ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 r
*/
#define IF_CS_PRODUCT_ID 0x0000001C
#define IF_CS_CF8385_B1_REV 0x12
+#define IF_CS_CF8381_B3_REV 0x04
+#define IF_CS_CF8305_B1_REV 0x03
+/*
+ * Used to detect other cards than CF8385 since their revisions of silicon
+ * doesn't match those from CF8385, eg. CF8381 B3 works with this driver.
+ */
+#define CF8305_MANFID 0x02db
+#define CF8305_CARDID 0x8103
+#define CF8381_MANFID 0x02db
+#define CF8381_CARDID 0x6064
+#define CF8385_MANFID 0x02df
+#define CF8385_CARDID 0x8103
+
+/*
+ * FIXME: just use the 'driver_info' field of 'struct pcmcia_device_id' when
+ * that gets fixed. Currently there's no way to access it from the probe hook.
+ */
+static inline u32 get_model(u16 manf_id, u16 card_id)
+{
+ /* NOTE: keep in sync with if_cs_ids */
+ if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID)
+ return MODEL_8305;
+ else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID)
+ return MODEL_8381;
+ else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID)
+ return MODEL_8385;
+ return MODEL_UNKNOWN;
+}
/********************************************************************/
/* I/O and interrupt handling */
@@ -309,7 +364,7 @@ static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb)
if (status & IF_CS_BIT_COMMAND)
break;
if (++loops > 100) {
- lbs_pr_err("card not ready for commands\n");
+ netdev_err(priv->dev, "card not ready for commands\n");
goto done;
}
mdelay(1);
@@ -379,14 +434,16 @@ static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len)
/* is hardware ready? */
status = if_cs_read16(priv->card, IF_CS_CARD_STATUS);
if ((status & IF_CS_BIT_RESP) == 0) {
- lbs_pr_err("no cmd response in card\n");
+ netdev_err(priv->dev, "no cmd response in card\n");
*len = 0;
goto out;
}
*len = if_cs_read16(priv->card, IF_CS_RESP_LEN);
if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) {
- lbs_pr_err("card cmd buffer has invalid # of bytes (%d)\n", *len);
+ netdev_err(priv->dev,
+ "card cmd buffer has invalid # of bytes (%d)\n",
+ *len);
goto out;
}
@@ -420,7 +477,9 @@ static struct sk_buff *if_cs_receive_data(struct lbs_private *priv)
len = if_cs_read16(priv->card, IF_CS_READ_LEN);
if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
- lbs_pr_err("card data buffer has invalid # of bytes (%d)\n", len);
+ netdev_err(priv->dev,
+ "card data buffer has invalid # of bytes (%d)\n",
+ len);
priv->dev->stats.rx_dropped++;
goto dat_err;
}
@@ -526,16 +585,23 @@ static irqreturn_t if_cs_interrupt(int irq, void *data)
*
* Return 0 on success
*/
-static int if_cs_prog_helper(struct if_cs_card *card)
+static int if_cs_prog_helper(struct if_cs_card *card, const struct firmware *fw)
{
int ret = 0;
int sent = 0;
u8 scratch;
- const struct firmware *fw;
lbs_deb_enter(LBS_DEB_CS);
- scratch = if_cs_read8(card, IF_CS_SCRATCH);
+ /*
+ * This is the only place where an unaligned register access happens on
+ * the CF8305 card, therefore for the sake of speed of the driver, we do
+ * the alignment correction here.
+ */
+ if (card->align_regs)
+ scratch = if_cs_read16(card, IF_CS_SCRATCH) >> 8;
+ else
+ scratch = if_cs_read8(card, IF_CS_SCRATCH);
/* "If the value is 0x5a, the firmware is already
* downloaded successfully"
@@ -549,14 +615,6 @@ static int if_cs_prog_helper(struct if_cs_card *card)
goto done;
}
- /* TODO: make firmware file configurable */
- ret = request_firmware(&fw, "libertas_cs_helper.fw",
- &handle_to_dev(card->p_dev));
- if (ret) {
- lbs_pr_err("can't load helper firmware\n");
- ret = -ENODEV;
- goto done;
- }
lbs_deb_cs("helper size %td\n", fw->size);
/* "Set the 5 bytes of the helper image to 0" */
@@ -570,8 +628,10 @@ static int if_cs_prog_helper(struct if_cs_card *card)
if (remain < count)
count = remain;
- /* "write the number of bytes to be sent to the I/O Command
- * write length register" */
+ /*
+ * "write the number of bytes to be sent to the I/O Command
+ * write length register"
+ */
if_cs_write16(card, IF_CS_CMD_LEN, count);
/* "write this to I/O Command port register as 16 bit writes */
@@ -580,22 +640,28 @@ static int if_cs_prog_helper(struct if_cs_card *card)
&fw->data[sent],
count >> 1);
- /* "Assert the download over interrupt command in the Host
- * status register" */
+ /*
+ * "Assert the download over interrupt command in the Host
+ * status register"
+ */
if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND);
- /* "Assert the download over interrupt command in the Card
- * interrupt case register" */
+ /*
+ * "Assert the download over interrupt command in the Card
+ * interrupt case register"
+ */
if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND);
- /* "The host polls the Card Status register ... for 50 ms before
- declaring a failure */
+ /*
+ * "The host polls the Card Status register ... for 50 ms before
+ * declaring a failure"
+ */
ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS,
IF_CS_BIT_COMMAND);
if (ret < 0) {
- lbs_pr_err("can't download helper at 0x%x, ret %d\n",
- sent, ret);
- goto err_release;
+ pr_err("can't download helper at 0x%x, ret %d\n",
+ sent, ret);
+ goto done;
}
if (count == 0)
@@ -604,17 +670,14 @@ static int if_cs_prog_helper(struct if_cs_card *card)
sent += count;
}
-err_release:
- release_firmware(fw);
done:
lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
return ret;
}
-static int if_cs_prog_real(struct if_cs_card *card)
+static int if_cs_prog_real(struct if_cs_card *card, const struct firmware *fw)
{
- const struct firmware *fw;
int ret = 0;
int retry = 0;
int len = 0;
@@ -622,36 +685,28 @@ static int if_cs_prog_real(struct if_cs_card *card)
lbs_deb_enter(LBS_DEB_CS);
- /* TODO: make firmware file configurable */
- ret = request_firmware(&fw, "libertas_cs.fw",
- &handle_to_dev(card->p_dev));
- if (ret) {
- lbs_pr_err("can't load firmware\n");
- ret = -ENODEV;
- goto done;
- }
lbs_deb_cs("fw size %td\n", fw->size);
ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW,
IF_CS_SQ_HELPER_OK);
if (ret < 0) {
- lbs_pr_err("helper firmware doesn't answer\n");
- goto err_release;
+ pr_err("helper firmware doesn't answer\n");
+ goto done;
}
for (sent = 0; sent < fw->size; sent += len) {
len = if_cs_read16(card, IF_CS_SQ_READ_LOW);
if (len & 1) {
retry++;
- lbs_pr_info("odd, need to retry this firmware block\n");
+ pr_info("odd, need to retry this firmware block\n");
} else {
retry = 0;
}
if (retry > 20) {
- lbs_pr_err("could not download firmware\n");
+ pr_err("could not download firmware\n");
ret = -ENODEV;
- goto err_release;
+ goto done;
}
if (retry) {
sent -= len;
@@ -669,23 +724,60 @@ static int if_cs_prog_real(struct if_cs_card *card)
ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS,
IF_CS_BIT_COMMAND);
if (ret < 0) {
- lbs_pr_err("can't download firmware at 0x%x\n", sent);
- goto err_release;
+ pr_err("can't download firmware at 0x%x\n", sent);
+ goto done;
}
}
ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a);
if (ret < 0)
- lbs_pr_err("firmware download failed\n");
-
-err_release:
- release_firmware(fw);
+ pr_err("firmware download failed\n");
done:
lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
return ret;
}
+static void if_cs_prog_firmware(struct lbs_private *priv, int ret,
+ const struct firmware *helper,
+ const struct firmware *mainfw)
+{
+ struct if_cs_card *card = priv->card;
+
+ if (ret) {
+ pr_err("failed to find firmware (%d)\n", ret);
+ return;
+ }
+
+ /* Load the firmware */
+ ret = if_cs_prog_helper(card, helper);
+ if (ret == 0 && (card->model != MODEL_8305))
+ ret = if_cs_prog_real(card, mainfw);
+ if (ret)
+ return;
+
+ /* Now actually get the IRQ */
+ ret = request_irq(card->p_dev->irq, if_cs_interrupt,
+ IRQF_SHARED, DRV_NAME, card);
+ if (ret) {
+ pr_err("error in request_irq\n");
+ return;
+ }
+
+ /*
+ * Clear any interrupt cause that happened while sending
+ * firmware/initializing card
+ */
+ if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK);
+ if_cs_enable_ints(card);
+
+ /* And finally bring the card up */
+ priv->fw_ready = 1;
+ if (lbs_start_card(priv) != 0) {
+ pr_err("could not activate card\n");
+ free_irq(card->p_dev->irq, card);
+ }
+}
/********************************************************************/
@@ -713,7 +805,8 @@ static int if_cs_host_to_card(struct lbs_private *priv,
ret = if_cs_send_cmd(priv, buf, nb);
break;
default:
- lbs_pr_err("%s: unsupported type %d\n", __func__, type);
+ netdev_err(priv->dev, "%s: unsupported type %d\n",
+ __func__, type);
}
lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
@@ -721,22 +814,13 @@ static int if_cs_host_to_card(struct lbs_private *priv,
}
-/********************************************************************/
-/* Card Services */
-/********************************************************************/
-
-/*
- * After a card is removed, if_cs_release() will unregister the
- * device, and release the PCMCIA configuration. If the device is
- * still open, this will be postponed until it is closed.
- */
static void if_cs_release(struct pcmcia_device *p_dev)
{
struct if_cs_card *card = p_dev->priv;
lbs_deb_enter(LBS_DEB_CS);
- free_irq(p_dev->irq.AssignedIRQ, card);
+ free_irq(p_dev->irq, card);
pcmcia_disable_device(p_dev);
if (card->iobase)
ioport_unmap(card->iobase);
@@ -745,79 +829,40 @@ static void if_cs_release(struct pcmcia_device *p_dev)
}
-/*
- * This creates an "instance" of the driver, allocating local data
- * structures for one device. The device is registered with Card
- * Services.
- *
- * The dev_link structure is initialized, but we don't actually
- * configure the card at this point -- we wait until we receive a card
- * insertion event.
- */
+static int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data)
+{
+ p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+
+ if (p_dev->resource[1]->end) {
+ pr_err("wrong CIS (check number of IO windows)\n");
+ return -ENODEV;
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ return pcmcia_request_io(p_dev);
+}
+
static int if_cs_probe(struct pcmcia_device *p_dev)
{
int ret = -ENOMEM;
+ unsigned int prod_id;
struct lbs_private *priv;
struct if_cs_card *card;
- /* CIS parsing */
- tuple_t tuple;
- cisparse_t parse;
- cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
- cistpl_io_t *io = &cfg->io;
- u_char buf[64];
lbs_deb_enter(LBS_DEB_CS);
card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL);
- if (!card) {
- lbs_pr_err("error in kzalloc\n");
+ if (!card)
goto out;
- }
+
card->p_dev = p_dev;
p_dev->priv = card;
- p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- p_dev->irq.Handler = NULL;
- p_dev->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+ p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
- p_dev->conf.Attributes = 0;
- p_dev->conf.IntType = INT_MEMORY_AND_IO;
-
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- if ((ret = pcmcia_get_first_tuple(p_dev, &tuple)) != 0 ||
- (ret = pcmcia_get_tuple_data(p_dev, &tuple)) != 0 ||
- (ret = pcmcia_parse_tuple(&tuple, &parse)) != 0)
- {
- lbs_pr_err("error in pcmcia_get_first_tuple etc\n");
- goto out1;
- }
-
- p_dev->conf.ConfigIndex = cfg->index;
-
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1) {
- p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
- }
-
- /* IO window settings */
- if (cfg->io.nwin != 1) {
- lbs_pr_err("wrong CIS (check number of IO windows)\n");
- ret = -ENODEV;
- goto out1;
- }
- p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- p_dev->io.BasePort1 = io->win[0].base;
- p_dev->io.NumPorts1 = io->win[0].len;
-
- /* This reserves IO space but doesn't actually enable it */
- ret = pcmcia_request_io(p_dev, &p_dev->io);
- if (ret) {
- lbs_pr_err("error in pcmcia_request_io\n");
+ if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) {
+ pr_err("error in pcmcia_loop_config\n");
goto out1;
}
@@ -826,51 +871,63 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
* a handler to the interrupt, unless the 'Handler' member of
* the irq structure is initialized.
*/
- if (p_dev->conf.Attributes & CONF_ENABLE_IRQ) {
- ret = pcmcia_request_irq(p_dev, &p_dev->irq);
- if (ret) {
- lbs_pr_err("error in pcmcia_request_irq\n");
- goto out1;
- }
- }
+ if (!p_dev->irq)
+ goto out1;
/* Initialize io access */
- card->iobase = ioport_map(p_dev->io.BasePort1, p_dev->io.NumPorts1);
+ card->iobase = ioport_map(p_dev->resource[0]->start,
+ resource_size(p_dev->resource[0]));
if (!card->iobase) {
- lbs_pr_err("error in ioport_map\n");
+ pr_err("error in ioport_map\n");
ret = -EIO;
goto out1;
}
- /*
- * This actually configures the PCMCIA socket -- setting up
- * the I/O windows and the interrupt mapping, and putting the
- * card and host interface into "Memory and IO" mode.
- */
- ret = pcmcia_request_configuration(p_dev, &p_dev->conf);
+ ret = pcmcia_enable_device(p_dev);
if (ret) {
- lbs_pr_err("error in pcmcia_request_configuration\n");
+ pr_err("error in pcmcia_enable_device\n");
goto out2;
}
/* Finally, report what we've done */
- lbs_deb_cs("irq %d, io 0x%04x-0x%04x\n",
- p_dev->irq.AssignedIRQ, p_dev->io.BasePort1,
- p_dev->io.BasePort1 + p_dev->io.NumPorts1 - 1);
+ lbs_deb_cs("irq %d, io %pR", p_dev->irq, p_dev->resource[0]);
+
+ /*
+ * Most of the libertas cards can do unaligned register access, but some
+ * weird ones cannot. That's especially true for the CF8305 card.
+ */
+ card->align_regs = false;
+
+ card->model = get_model(p_dev->manf_id, p_dev->card_id);
+ if (card->model == MODEL_UNKNOWN) {
+ pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n",
+ p_dev->manf_id, p_dev->card_id);
+ ret = -ENODEV;
+ goto out2;
+ }
/* Check if we have a current silicon */
- if (if_cs_read8(card, IF_CS_PRODUCT_ID) < IF_CS_CF8385_B1_REV) {
- lbs_pr_err("old chips like 8385 rev B1 aren't supported\n");
+ prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID);
+ if (card->model == MODEL_8305) {
+ card->align_regs = true;
+ if (prod_id < IF_CS_CF8305_B1_REV) {
+ pr_err("8305 rev B0 and older are not supported\n");
+ ret = -ENODEV;
+ goto out2;
+ }
+ }
+
+ if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) {
+ pr_err("8381 rev B2 and older are not supported\n");
ret = -ENODEV;
goto out2;
}
- /* Load the firmware early, before calling into libertas.ko */
- ret = if_cs_prog_helper(card);
- if (ret == 0)
- ret = if_cs_prog_real(card);
- if (ret)
+ if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) {
+ pr_err("8385 rev B0 and older are not supported\n");
+ ret = -ENODEV;
goto out2;
+ }
/* Make this card known to the libertas driver */
priv = lbs_add_card(card, &p_dev->dev);
@@ -879,35 +936,22 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
goto out2;
}
- /* Finish setting up fields in lbs_private */
+ /* Set up fields in lbs_private */
card->priv = priv;
priv->card = card;
priv->hw_host_to_card = if_cs_host_to_card;
- priv->fw_ready = 1;
+ priv->enter_deep_sleep = NULL;
+ priv->exit_deep_sleep = NULL;
+ priv->reset_deep_sleep_wakeup = NULL;
- /* Now actually get the IRQ */
- ret = request_irq(p_dev->irq.AssignedIRQ, if_cs_interrupt,
- IRQF_SHARED, DRV_NAME, card);
+ /* Get firmware */
+ ret = lbs_get_firmware_async(priv, &p_dev->dev, card->model, fw_table,
+ if_cs_prog_firmware);
if (ret) {
- lbs_pr_err("error in request_irq\n");
- goto out3;
- }
-
- /* Clear any interrupt cause that happend while sending
- * firmware/initializing card */
- if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK);
- if_cs_enable_ints(card);
-
- /* And finally bring the card up */
- if (lbs_start_card(priv) != 0) {
- lbs_pr_err("could not activate card\n");
+ pr_err("failed to find firmware (%d)\n", ret);
goto out3;
}
- /* The firmware for the CF card supports powersave */
- priv->ps_supported = 1;
-
- ret = 0;
goto out;
out3:
@@ -922,12 +966,6 @@ out:
}
-/*
- * This deletes a driver "instance". The device is de-registered with
- * Card Services. If it has been released, all local data structures
- * are freed. Otherwise, the structures will be freed when the device
- * is released.
- */
static void if_cs_detach(struct pcmcia_device *p_dev)
{
struct if_cs_card *card = p_dev->priv;
@@ -949,42 +987,20 @@ static void if_cs_detach(struct pcmcia_device *p_dev)
/* Module initialization */
/********************************************************************/
-static struct pcmcia_device_id if_cs_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x02df, 0x8103),
+static const struct pcmcia_device_id if_cs_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID),
+ PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID),
+ PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID),
+ /* NOTE: keep in sync with get_model() */
PCMCIA_DEVICE_NULL,
};
MODULE_DEVICE_TABLE(pcmcia, if_cs_ids);
-
static struct pcmcia_driver lbs_driver = {
.owner = THIS_MODULE,
- .drv = {
- .name = DRV_NAME,
- },
+ .name = DRV_NAME,
.probe = if_cs_probe,
.remove = if_cs_detach,
.id_table = if_cs_ids,
};
-
-
-static int __init if_cs_init(void)
-{
- int ret;
-
- lbs_deb_enter(LBS_DEB_CS);
- ret = pcmcia_register_driver(&lbs_driver);
- lbs_deb_leave(LBS_DEB_CS);
- return ret;
-}
-
-
-static void __exit if_cs_exit(void)
-{
- lbs_deb_enter(LBS_DEB_CS);
- pcmcia_unregister_driver(&lbs_driver);
- lbs_deb_leave(LBS_DEB_CS);
-}
-
-
-module_init(if_cs_init);
-module_exit(if_cs_exit);
+module_pcmcia_driver(lbs_driver);
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 76f4c653d64..33ceda296c9 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -26,54 +26,83 @@
* if_sdio_card_to_host() to pad the data.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/host.h>
+#include <linux/pm_runtime.h>
#include "host.h"
#include "decl.h"
#include "defs.h"
#include "dev.h"
+#include "cmd.h"
#include "if_sdio.h"
-static char *lbs_helper_name = NULL;
-module_param_named(helper_name, lbs_helper_name, charp, 0644);
+static void if_sdio_interrupt(struct sdio_func *func);
-static char *lbs_fw_name = NULL;
-module_param_named(fw_name, lbs_fw_name, charp, 0644);
+/* The if_sdio_remove() callback function is called when
+ * user removes this module from kernel space or ejects
+ * the card from the slot. The driver handles these 2 cases
+ * differently for SD8688 combo chip.
+ * If the user is removing the module, the FUNC_SHUTDOWN
+ * command for SD8688 is sent to the firmware.
+ * If the card is removed, there is no need to send this command.
+ *
+ * The variable 'user_rmmod' is used to distinguish these two
+ * scenarios. This flag is initialized as FALSE in case the card
+ * is removed, and will be set to TRUE for module removal when
+ * module_exit function is called.
+ */
+static u8 user_rmmod;
static const struct sdio_device_id if_sdio_ids[] = {
- { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
- { /* end: all zeroes */ },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
+ SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
+ SDIO_DEVICE_ID_MARVELL_8688WLAN) },
+ { /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(sdio, if_sdio_ids);
-struct if_sdio_model {
- int model;
- const char *helper;
- const char *firmware;
-};
-
-static struct if_sdio_model if_sdio_models[] = {
- {
- /* 8385 */
- .model = 0x04,
- .helper = "sd8385_helper.bin",
- .firmware = "sd8385.bin",
- },
- {
- /* 8686 */
- .model = 0x0B,
- .helper = "sd8686_helper.bin",
- .firmware = "sd8686.bin",
- },
+#define MODEL_8385 0x04
+#define MODEL_8686 0x0b
+#define MODEL_8688 0x10
+
+static const struct lbs_fw_table fw_table[] = {
+ { MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" },
+ { MODEL_8385, "sd8385_helper.bin", "sd8385.bin" },
+ { MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" },
+ { MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" },
+ { MODEL_8686, "sd8686_helper.bin", "sd8686.bin" },
+ { MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" },
+ { MODEL_8688, "sd8688_helper.bin", "sd8688.bin" },
+ { 0, NULL, NULL }
};
+MODULE_FIRMWARE("libertas/sd8385_helper.bin");
+MODULE_FIRMWARE("libertas/sd8385.bin");
+MODULE_FIRMWARE("sd8385_helper.bin");
+MODULE_FIRMWARE("sd8385.bin");
+MODULE_FIRMWARE("libertas/sd8686_v9_helper.bin");
+MODULE_FIRMWARE("libertas/sd8686_v9.bin");
+MODULE_FIRMWARE("libertas/sd8686_v8_helper.bin");
+MODULE_FIRMWARE("libertas/sd8686_v8.bin");
+MODULE_FIRMWARE("sd8686_helper.bin");
+MODULE_FIRMWARE("sd8686.bin");
+MODULE_FIRMWARE("libertas/sd8688_helper.bin");
+MODULE_FIRMWARE("libertas/sd8688.bin");
+MODULE_FIRMWARE("sd8688_helper.bin");
+MODULE_FIRMWARE("sd8688.bin");
struct if_sdio_packet {
struct if_sdio_packet *next;
@@ -87,36 +116,43 @@ struct if_sdio_card {
int model;
unsigned long ioport;
+ unsigned int scratch_reg;
+ bool started;
+ wait_queue_head_t pwron_waitq;
- const char *helper;
- const char *firmware;
-
- u8 buffer[65536];
+ u8 buffer[65536] __attribute__((aligned(4)));
spinlock_t lock;
struct if_sdio_packet *packets;
struct workqueue_struct *workqueue;
struct work_struct packet_worker;
+
+ u8 rx_unit;
};
+static void if_sdio_finish_power_on(struct if_sdio_card *card);
+static int if_sdio_power_off(struct if_sdio_card *card);
+
/********************************************************************/
/* I/O */
/********************************************************************/
+/*
+ * For SD8385/SD8686, this function reads firmware status after
+ * the image is downloaded, or reads RX packet length when
+ * interrupt (with IF_SDIO_H_INT_UPLD bit set) is received.
+ * For SD8688, this function reads firmware status only.
+ */
static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
{
- int ret, reg;
+ int ret;
u16 scratch;
- if (card->model == 0x04)
- reg = IF_SDIO_SCRATCH_OLD;
- else
- reg = IF_SDIO_SCRATCH;
-
- scratch = sdio_readb(card->func, reg, &ret);
+ scratch = sdio_readb(card->func, card->scratch_reg, &ret);
if (!ret)
- scratch |= sdio_readb(card->func, reg + 1, &ret) << 8;
+ scratch |= sdio_readb(card->func, card->scratch_reg + 1,
+ &ret) << 8;
if (err)
*err = ret;
@@ -127,6 +163,46 @@ static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
return scratch;
}
+static u8 if_sdio_read_rx_unit(struct if_sdio_card *card)
+{
+ int ret;
+ u8 rx_unit;
+
+ rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret);
+
+ if (ret)
+ rx_unit = 0;
+
+ return rx_unit;
+}
+
+static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err)
+{
+ int ret;
+ u16 rx_len;
+
+ switch (card->model) {
+ case MODEL_8385:
+ case MODEL_8686:
+ rx_len = if_sdio_read_scratch(card, &ret);
+ break;
+ case MODEL_8688:
+ default: /* for newer chipsets */
+ rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret);
+ if (!ret)
+ rx_len <<= card->rx_unit;
+ else
+ rx_len = 0xffff; /* invalid length */
+
+ break;
+ }
+
+ if (err)
+ *err = ret;
+
+ return rx_len;
+}
+
static int if_sdio_handle_cmd(struct if_sdio_card *card,
u8 *buffer, unsigned size)
{
@@ -207,7 +283,7 @@ static int if_sdio_handle_event(struct if_sdio_card *card,
lbs_deb_enter(LBS_DEB_SDIO);
- if (card->model == 0x04) {
+ if (card->model == MODEL_8385) {
event = sdio_readb(card->func, IF_SDIO_EVENT, &ret);
if (ret)
goto out;
@@ -236,16 +312,34 @@ out:
return ret;
}
+static int if_sdio_wait_status(struct if_sdio_card *card, const u8 condition)
+{
+ u8 status;
+ unsigned long timeout;
+ int ret = 0;
+
+ timeout = jiffies + HZ;
+ while (1) {
+ status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
+ if (ret)
+ return ret;
+ if ((status & condition) == condition)
+ break;
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ mdelay(1);
+ }
+ return ret;
+}
+
static int if_sdio_card_to_host(struct if_sdio_card *card)
{
int ret;
- u8 status;
u16 size, type, chunk;
- unsigned long timeout;
lbs_deb_enter(LBS_DEB_SDIO);
- size = if_sdio_read_scratch(card, &ret);
+ size = if_sdio_read_rx_len(card, &ret);
if (ret)
goto out;
@@ -256,19 +350,9 @@ static int if_sdio_card_to_host(struct if_sdio_card *card)
goto out;
}
- timeout = jiffies + HZ;
- while (1) {
- status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
- if (ret)
- goto out;
- if (status & IF_SDIO_IO_RDY)
- break;
- if (time_after(jiffies, timeout)) {
- ret = -ETIMEDOUT;
- goto out;
- }
- mdelay(1);
- }
+ ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY);
+ if (ret)
+ goto out;
/*
* The transfer must be in one transaction or the firmware
@@ -324,7 +408,7 @@ static int if_sdio_card_to_host(struct if_sdio_card *card)
out:
if (ret)
- lbs_pr_err("problem fetching packet from firmware\n");
+ pr_err("problem fetching packet from firmware\n");
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
@@ -335,8 +419,6 @@ static void if_sdio_host_to_card_worker(struct work_struct *work)
{
struct if_sdio_card *card;
struct if_sdio_packet *packet;
- unsigned long timeout;
- u8 status;
int ret;
unsigned long flags;
@@ -356,25 +438,15 @@ static void if_sdio_host_to_card_worker(struct work_struct *work)
sdio_claim_host(card->func);
- timeout = jiffies + HZ;
- while (1) {
- status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
- if (ret)
- goto release;
- if (status & IF_SDIO_IO_RDY)
- break;
- if (time_after(jiffies, timeout)) {
- ret = -ETIMEDOUT;
- goto release;
- }
- mdelay(1);
+ ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY);
+ if (ret == 0) {
+ ret = sdio_writesb(card->func, card->ioport,
+ packet->buffer, packet->nb);
}
- ret = sdio_writesb(card->func, card->ioport,
- packet->buffer, packet->nb);
if (ret)
- goto release;
-release:
+ pr_err("error %d sending packet to firmware\n", ret);
+
sdio_release_host(card->func);
kfree(packet);
@@ -387,11 +459,12 @@ release:
/* Firmware */
/********************************************************************/
-static int if_sdio_prog_helper(struct if_sdio_card *card)
+#define FW_DL_READY_STATUS (IF_SDIO_IO_RDY | IF_SDIO_DL_RDY)
+
+static int if_sdio_prog_helper(struct if_sdio_card *card,
+ const struct firmware *fw)
{
int ret;
- u8 status;
- const struct firmware *fw;
unsigned long timeout;
u8 *chunk_buffer;
u32 chunk_size;
@@ -400,16 +473,10 @@ static int if_sdio_prog_helper(struct if_sdio_card *card)
lbs_deb_enter(LBS_DEB_SDIO);
- ret = request_firmware(&fw, card->helper, &card->func->dev);
- if (ret) {
- lbs_pr_err("can't load helper firmware\n");
- goto out;
- }
-
chunk_buffer = kzalloc(64, GFP_KERNEL);
if (!chunk_buffer) {
ret = -ENOMEM;
- goto release_fw;
+ goto out;
}
sdio_claim_host(card->func);
@@ -422,22 +489,16 @@ static int if_sdio_prog_helper(struct if_sdio_card *card)
size = fw->size;
while (size) {
- timeout = jiffies + HZ;
- while (1) {
- status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
- if (ret)
- goto release;
- if ((status & IF_SDIO_IO_RDY) &&
- (status & IF_SDIO_DL_RDY))
- break;
- if (time_after(jiffies, timeout)) {
- ret = -ETIMEDOUT;
- goto release;
- }
- mdelay(1);
- }
+ ret = if_sdio_wait_status(card, FW_DL_READY_STATUS);
+ if (ret)
+ goto release;
+
+ /* On some platforms (like Davinci) the chip needs more time
+ * between helper blocks.
+ */
+ mdelay(2);
- chunk_size = min(size, (size_t)60);
+ chunk_size = min_t(size_t, size, 60);
*((__le32*)chunk_buffer) = cpu_to_le32(chunk_size);
memcpy(chunk_buffer + 4, firmware, chunk_size);
@@ -488,26 +549,21 @@ static int if_sdio_prog_helper(struct if_sdio_card *card)
ret = 0;
release:
- sdio_set_block_size(card->func, 0);
sdio_release_host(card->func);
kfree(chunk_buffer);
-release_fw:
- release_firmware(fw);
out:
if (ret)
- lbs_pr_err("failed to load helper firmware\n");
+ pr_err("failed to load helper firmware\n");
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
-
return ret;
}
-static int if_sdio_prog_real(struct if_sdio_card *card)
+static int if_sdio_prog_real(struct if_sdio_card *card,
+ const struct firmware *fw)
{
int ret;
- u8 status;
- const struct firmware *fw;
unsigned long timeout;
u8 *chunk_buffer;
u32 chunk_size;
@@ -516,16 +572,10 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
lbs_deb_enter(LBS_DEB_SDIO);
- ret = request_firmware(&fw, card->firmware, &card->func->dev);
- if (ret) {
- lbs_pr_err("can't load firmware\n");
- goto out;
- }
-
chunk_buffer = kzalloc(512, GFP_KERNEL);
if (!chunk_buffer) {
ret = -ENOMEM;
- goto release_fw;
+ goto out;
}
sdio_claim_host(card->func);
@@ -540,11 +590,28 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
while (size) {
timeout = jiffies + HZ;
while (1) {
- status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
+ ret = if_sdio_wait_status(card, FW_DL_READY_STATUS);
+ if (ret)
+ goto release;
+
+ req_size = sdio_readb(card->func, IF_SDIO_RD_BASE,
+ &ret);
+ if (ret)
+ goto release;
+
+ req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1,
+ &ret) << 8;
if (ret)
goto release;
- if ((status & IF_SDIO_IO_RDY) &&
- (status & IF_SDIO_DL_RDY))
+
+ /*
+ * For SD8688 wait until the length is not 0, 1 or 2
+ * before downloading the first FW block,
+ * since BOOT code writes the register to indicate the
+ * helper/FW download winner,
+ * the value could be 1 or 2 (Func1 or Func2).
+ */
+ if ((size != fw->size) || (req_size > 2))
break;
if (time_after(jiffies, timeout)) {
ret = -ETIMEDOUT;
@@ -553,13 +620,6 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
mdelay(1);
}
- req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret);
- if (ret)
- goto release;
-
- req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8;
- if (ret)
- goto release;
/*
lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size);
*/
@@ -579,7 +639,7 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
req_size = size;
while (req_size) {
- chunk_size = min(req_size, (size_t)512);
+ chunk_size = min_t(size_t, req_size, 512);
memcpy(chunk_buffer, firmware, chunk_size);
/*
@@ -624,21 +684,42 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
ret = 0;
release:
- sdio_set_block_size(card->func, 0);
sdio_release_host(card->func);
kfree(chunk_buffer);
-release_fw:
- release_firmware(fw);
out:
if (ret)
- lbs_pr_err("failed to load firmware\n");
+ pr_err("failed to load firmware\n");
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
-
return ret;
}
+static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret,
+ const struct firmware *helper,
+ const struct firmware *mainfw)
+{
+ struct if_sdio_card *card = priv->card;
+
+ if (ret) {
+ pr_err("failed to find firmware (%d)\n", ret);
+ return;
+ }
+
+ ret = if_sdio_prog_helper(card, helper);
+ if (ret)
+ return;
+
+ lbs_deb_sdio("Helper firmware loaded\n");
+
+ ret = if_sdio_prog_real(card, mainfw);
+ if (ret)
+ return;
+
+ lbs_deb_sdio("Firmware loaded\n");
+ if_sdio_finish_power_on(card);
+}
+
static int if_sdio_prog_firmware(struct if_sdio_card *card)
{
int ret;
@@ -646,35 +727,214 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
lbs_deb_enter(LBS_DEB_SDIO);
+ /*
+ * Disable interrupts
+ */
+ sdio_claim_host(card->func);
+ sdio_writeb(card->func, 0x00, IF_SDIO_H_INT_MASK, &ret);
+ sdio_release_host(card->func);
+
sdio_claim_host(card->func);
scratch = if_sdio_read_scratch(card, &ret);
sdio_release_host(card->func);
+ lbs_deb_sdio("firmware status = %#x\n", scratch);
+ lbs_deb_sdio("scratch ret = %d\n", ret);
+
if (ret)
goto out;
+
+ /*
+ * The manual clearly describes that FEDC is the right code to use
+ * to detect firmware presence, but for SD8686 it is not that simple.
+ * Scratch is also used to store the RX packet length, so we lose
+ * the FEDC value early on. So we use a non-zero check in order
+ * to validate firmware presence.
+ * Additionally, the SD8686 in the Gumstix always has the high scratch
+ * bit set, even when the firmware is not loaded. So we have to
+ * exclude that from the test.
+ */
if (scratch == IF_SDIO_FIRMWARE_OK) {
lbs_deb_sdio("firmware already loaded\n");
- goto success;
+ if_sdio_finish_power_on(card);
+ return 0;
+ } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) {
+ lbs_deb_sdio("firmware may be running\n");
+ if_sdio_finish_power_on(card);
+ return 0;
}
- ret = if_sdio_prog_helper(card);
+ ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model,
+ fw_table, if_sdio_do_prog_firmware);
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+}
+
+/********************************************************************/
+/* Power management */
+/********************************************************************/
+
+/* Finish power on sequence (after firmware is loaded) */
+static void if_sdio_finish_power_on(struct if_sdio_card *card)
+{
+ struct sdio_func *func = card->func;
+ struct lbs_private *priv = card->priv;
+ int ret;
+
+ sdio_claim_host(func);
+ sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
+
+ /*
+ * Get rx_unit if the chip is SD8688 or newer.
+ * SD8385 & SD8686 do not have rx_unit.
+ */
+ if ((card->model != MODEL_8385)
+ && (card->model != MODEL_8686))
+ card->rx_unit = if_sdio_read_rx_unit(card);
+ else
+ card->rx_unit = 0;
+
+ /*
+ * Set up the interrupt handler late.
+ *
+ * If we set it up earlier, the (buggy) hardware generates a spurious
+ * interrupt, even before the interrupt has been enabled, with
+ * CCCR_INTx = 0.
+ *
+ * We register the interrupt handler late so that we can handle any
+ * spurious interrupts, and also to avoid generation of that known
+ * spurious interrupt in the first place.
+ */
+ ret = sdio_claim_irq(func, if_sdio_interrupt);
if (ret)
- goto out;
+ goto release;
- ret = if_sdio_prog_real(card);
+ /*
+ * Enable interrupts now that everything is set up
+ */
+ sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
if (ret)
- goto out;
+ goto release_irq;
-success:
- ret = 0;
+ sdio_release_host(func);
-out:
- lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ /* Set fw_ready before queuing any commands so that
+ * lbs_thread won't block from sending them to firmware.
+ */
+ priv->fw_ready = 1;
+
+ /*
+ * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
+ */
+ if (card->model == MODEL_8688) {
+ struct cmd_header cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ lbs_deb_sdio("send function INIT command\n");
+ if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
+ lbs_cmd_copyback, (unsigned long) &cmd))
+ netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
+ }
+
+ wake_up(&card->pwron_waitq);
+
+ if (!card->started) {
+ ret = lbs_start_card(priv);
+ if_sdio_power_off(card);
+ if (ret == 0) {
+ card->started = true;
+ /* Tell PM core that we don't need the card to be
+ * powered now */
+ pm_runtime_put(&func->dev);
+ }
+ }
+ return;
+
+release_irq:
+ sdio_release_irq(func);
+release:
+ sdio_release_host(func);
+}
+
+static int if_sdio_power_on(struct if_sdio_card *card)
+{
+ struct sdio_func *func = card->func;
+ struct mmc_host *host = func->card->host;
+ int ret;
+
+ sdio_claim_host(func);
+
+ ret = sdio_enable_func(func);
+ if (ret)
+ goto release;
+
+ /* For 1-bit transfers to the 8686 model, we need to enable the
+ * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
+ * bit to allow access to non-vendor registers. */
+ if ((card->model == MODEL_8686) &&
+ (host->caps & MMC_CAP_SDIO_IRQ) &&
+ (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
+ u8 reg;
+
+ func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+ reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
+ if (ret)
+ goto disable;
+
+ reg |= SDIO_BUS_ECSI;
+ sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
+ if (ret)
+ goto disable;
+ }
+
+ card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
+ if (ret)
+ goto disable;
+
+ card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
+ if (ret)
+ goto disable;
+
+ card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
+ if (ret)
+ goto disable;
+
+ sdio_release_host(func);
+ ret = if_sdio_prog_firmware(card);
+ if (ret) {
+ sdio_claim_host(func);
+ goto disable;
+ }
+
+ return 0;
+
+disable:
+ sdio_disable_func(func);
+release:
+ sdio_release_host(func);
return ret;
}
+static int if_sdio_power_off(struct if_sdio_card *card)
+{
+ struct sdio_func *func = card->func;
+ struct lbs_private *priv = card->priv;
+
+ priv->fw_ready = 0;
+
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+ return 0;
+}
+
+
/*******************************************************************/
/* Libertas callbacks */
/*******************************************************************/
@@ -758,6 +1018,121 @@ out:
return ret;
}
+static int if_sdio_enter_deep_sleep(struct lbs_private *priv)
+{
+ int ret = -1;
+ struct cmd_header cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ lbs_deb_sdio("send DEEP_SLEEP command\n");
+ ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd),
+ lbs_cmd_copyback, (unsigned long) &cmd);
+ if (ret)
+ netdev_err(priv->dev, "DEEP_SLEEP cmd failed\n");
+
+ mdelay(200);
+ return ret;
+}
+
+static int if_sdio_exit_deep_sleep(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int ret = -1;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+ sdio_claim_host(card->func);
+
+ sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
+ if (ret)
+ netdev_err(priv->dev, "sdio_writeb failed!\n");
+
+ sdio_release_host(card->func);
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+}
+
+static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int ret = -1;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+ sdio_claim_host(card->func);
+
+ sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret);
+ if (ret)
+ netdev_err(priv->dev, "sdio_writeb failed!\n");
+
+ sdio_release_host(card->func);
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+
+}
+
+static struct mmc_host *reset_host;
+
+static void if_sdio_reset_card_worker(struct work_struct *work)
+{
+ /*
+ * The actual reset operation must be run outside of lbs_thread. This
+ * is because mmc_remove_host() will cause the device to be instantly
+ * destroyed, and the libertas driver then needs to end lbs_thread,
+ * leading to a deadlock.
+ *
+ * We run it in a workqueue totally independent from the if_sdio_card
+ * instance for that reason.
+ */
+
+ pr_info("Resetting card...");
+ mmc_remove_host(reset_host);
+ mmc_add_host(reset_host);
+}
+static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker);
+
+static void if_sdio_reset_card(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+
+ if (work_pending(&card_reset_work))
+ return;
+
+ reset_host = card->func->card->host;
+ schedule_work(&card_reset_work);
+}
+
+static int if_sdio_power_save(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int ret;
+
+ flush_workqueue(card->workqueue);
+
+ ret = if_sdio_power_off(card);
+
+ /* Let runtime PM know the card is powered off */
+ pm_runtime_put_sync(&card->func->dev);
+
+ return ret;
+}
+
+static int if_sdio_power_restore(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int r;
+
+ /* Make sure the card will not be powered off by runtime PM */
+ pm_runtime_get_sync(&card->func->dev);
+
+ r = if_sdio_power_on(card);
+ if (r)
+ return r;
+
+ wait_event(card->pwron_waitq, priv->fw_ready);
+ return 0;
+}
+
+
/*******************************************************************/
/* SDIO callbacks */
/*******************************************************************/
@@ -773,7 +1148,7 @@ static void if_sdio_interrupt(struct sdio_func *func)
card = sdio_get_drvdata(func);
cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret);
- if (ret)
+ if (ret || !cause)
goto out;
lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause);
@@ -786,6 +1161,7 @@ static void if_sdio_interrupt(struct sdio_func *func)
* Ignore the define name, this really means the card has
* successfully received the command.
*/
+ card->priv->is_activity_detected = 1;
if (cause & IF_SDIO_H_INT_DNLD)
lbs_host_to_card_done(card->priv);
@@ -820,14 +1196,14 @@ static int if_sdio_probe(struct sdio_func *func,
if (sscanf(func->card->info[i],
"ID: %x", &model) == 1)
break;
- if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
- model = 4;
- break;
- }
+ if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
+ model = MODEL_8385;
+ break;
+ }
}
if (i == func->card->num_info) {
- lbs_pr_err("unable to identify card model\n");
+ pr_err("unable to identify card model\n");
return -ENODEV;
}
@@ -837,59 +1213,36 @@ static int if_sdio_probe(struct sdio_func *func,
card->func = func;
card->model = model;
+
+ switch (card->model) {
+ case MODEL_8385:
+ card->scratch_reg = IF_SDIO_SCRATCH_OLD;
+ break;
+ case MODEL_8686:
+ card->scratch_reg = IF_SDIO_SCRATCH;
+ break;
+ case MODEL_8688:
+ default: /* for newer chipsets */
+ card->scratch_reg = IF_SDIO_FW_STATUS;
+ break;
+ }
+
spin_lock_init(&card->lock);
card->workqueue = create_workqueue("libertas_sdio");
INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
+ init_waitqueue_head(&card->pwron_waitq);
- for (i = 0;i < ARRAY_SIZE(if_sdio_models);i++) {
- if (card->model == if_sdio_models[i].model)
+ /* Check if we support this card */
+ for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
+ if (card->model == fw_table[i].model)
break;
}
-
- if (i == ARRAY_SIZE(if_sdio_models)) {
- lbs_pr_err("unkown card model 0x%x\n", card->model);
+ if (i == ARRAY_SIZE(fw_table)) {
+ pr_err("unknown card model 0x%x\n", card->model);
ret = -ENODEV;
goto free;
}
- card->helper = if_sdio_models[i].helper;
- card->firmware = if_sdio_models[i].firmware;
-
- if (lbs_helper_name) {
- lbs_deb_sdio("overriding helper firmware: %s\n",
- lbs_helper_name);
- card->helper = lbs_helper_name;
- }
-
- if (lbs_fw_name) {
- lbs_deb_sdio("overriding firmware: %s\n", lbs_fw_name);
- card->firmware = lbs_fw_name;
- }
-
- sdio_claim_host(func);
-
- ret = sdio_enable_func(func);
- if (ret)
- goto release;
-
- ret = sdio_claim_irq(func, if_sdio_interrupt);
- if (ret)
- goto disable;
-
- card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
- if (ret)
- goto release_int;
-
- card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
- if (ret)
- goto release_int;
-
- card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
- if (ret)
- goto release_int;
-
- sdio_release_host(func);
-
sdio_set_drvdata(func, card);
lbs_deb_sdio("class = 0x%X, vendor = 0x%X, "
@@ -897,39 +1250,28 @@ static int if_sdio_probe(struct sdio_func *func,
func->class, func->vendor, func->device,
model, (unsigned)card->ioport);
- ret = if_sdio_prog_firmware(card);
- if (ret)
- goto reclaim;
priv = lbs_add_card(card, &func->dev);
if (!priv) {
ret = -ENOMEM;
- goto reclaim;
+ goto free;
}
card->priv = priv;
priv->card = card;
priv->hw_host_to_card = if_sdio_host_to_card;
-
- priv->fw_ready = 1;
-
- /*
- * Enable interrupts now that everything is set up
- */
- sdio_claim_host(func);
- sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
- sdio_release_host(func);
- if (ret)
- goto reclaim;
-
- ret = lbs_start_card(priv);
+ priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
+ priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
+ priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
+ priv->reset_card = if_sdio_reset_card;
+ priv->power_save = if_sdio_power_save;
+ priv->power_restore = if_sdio_power_restore;
+
+ ret = if_sdio_power_on(card);
if (ret)
goto err_activate_card;
- if (priv->fwcapinfo & FW_CAPINFO_PS)
- priv->ps_supported = 1;
-
out:
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
@@ -938,14 +1280,6 @@ out:
err_activate_card:
flush_workqueue(card->workqueue);
lbs_remove_card(priv);
-reclaim:
- sdio_claim_host(func);
-release_int:
- sdio_release_irq(func);
-disable:
- sdio_disable_func(func);
-release:
- sdio_release_host(func);
free:
destroy_workqueue(card->workqueue);
while (card->packets) {
@@ -968,7 +1302,25 @@ static void if_sdio_remove(struct sdio_func *func)
card = sdio_get_drvdata(func);
- card->priv->surpriseremoved = 1;
+ /* Undo decrement done above in if_sdio_probe */
+ pm_runtime_get_noresume(&func->dev);
+
+ if (user_rmmod && (card->model == MODEL_8688)) {
+ /*
+ * FUNC_SHUTDOWN is required for SD8688 WLAN/BT
+ * multiple functions
+ */
+ struct cmd_header cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ lbs_deb_sdio("send function SHUTDOWN command\n");
+ if (__lbs_cmd(card->priv, CMD_FUNC_SHUTDOWN,
+ &cmd, sizeof(cmd), lbs_cmd_copyback,
+ (unsigned long) &cmd))
+ pr_alert("CMD_FUNC_SHUTDOWN cmd failed\n");
+ }
+
lbs_deb_sdio("call remove card\n");
lbs_stop_card(card->priv);
@@ -977,11 +1329,6 @@ static void if_sdio_remove(struct sdio_func *func)
flush_workqueue(card->workqueue);
destroy_workqueue(card->workqueue);
- sdio_claim_host(func);
- sdio_release_irq(func);
- sdio_disable_func(func);
- sdio_release_host(func);
-
while (card->packets) {
packet = card->packets;
card->packets = card->packets->next;
@@ -989,15 +1336,76 @@ static void if_sdio_remove(struct sdio_func *func)
}
kfree(card);
-
lbs_deb_leave(LBS_DEB_SDIO);
}
+static int if_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ int ret;
+ struct if_sdio_card *card = sdio_get_drvdata(func);
+
+ mmc_pm_flag_t flags = sdio_get_host_pm_caps(func);
+
+ /* If we're powered off anyway, just let the mmc layer remove the
+ * card. */
+ if (!lbs_iface_active(card->priv))
+ return -ENOSYS;
+
+ dev_info(dev, "%s: suspend: PM flags = 0x%x\n",
+ sdio_func_id(func), flags);
+
+ /* If we aren't being asked to wake on anything, we should bail out
+ * and let the SD stack power down the card.
+ */
+ if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) {
+ dev_info(dev, "Suspend without wake params -- powering down card\n");
+ return -ENOSYS;
+ }
+
+ if (!(flags & MMC_PM_KEEP_POWER)) {
+ dev_err(dev, "%s: cannot remain alive while host is suspended\n",
+ sdio_func_id(func));
+ return -ENOSYS;
+ }
+
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+ if (ret)
+ return ret;
+
+ ret = lbs_suspend(card->priv);
+ if (ret)
+ return ret;
+
+ return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+}
+
+static int if_sdio_resume(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct if_sdio_card *card = sdio_get_drvdata(func);
+ int ret;
+
+ dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func));
+
+ ret = lbs_resume(card->priv);
+
+ return ret;
+}
+
+static const struct dev_pm_ops if_sdio_pm_ops = {
+ .suspend = if_sdio_suspend,
+ .resume = if_sdio_resume,
+};
+
static struct sdio_driver if_sdio_driver = {
.name = "libertas_sdio",
.id_table = if_sdio_ids,
.probe = if_sdio_probe,
.remove = if_sdio_remove,
+ .drv = {
+ .pm = &if_sdio_pm_ops,
+ },
};
/*******************************************************************/
@@ -1015,6 +1423,9 @@ static int __init if_sdio_init_module(void)
ret = sdio_register_driver(&if_sdio_driver);
+ /* Clear the flag in case user removes the card. */
+ user_rmmod = 0;
+
lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
return ret;
@@ -1024,6 +1435,11 @@ static void __exit if_sdio_exit_module(void)
{
lbs_deb_enter(LBS_DEB_SDIO);
+ /* Set the flag as user is removing this module. */
+ user_rmmod = 1;
+
+ cancel_work_sync(&card_reset_work);
+
sdio_unregister_driver(&if_sdio_driver);
lbs_deb_leave(LBS_DEB_SDIO);
diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h
index 533bdfbf5d2..62fda3592f6 100644
--- a/drivers/net/wireless/libertas/if_sdio.h
+++ b/drivers/net/wireless/libertas/if_sdio.h
@@ -38,8 +38,15 @@
#define IF_SDIO_SCRATCH 0x34
#define IF_SDIO_SCRATCH_OLD 0x80fe
+#define IF_SDIO_FW_STATUS 0x40
#define IF_SDIO_FIRMWARE_OK 0xfedc
+#define IF_SDIO_RX_LEN 0x42
+#define IF_SDIO_RX_UNIT 0x43
+
#define IF_SDIO_EVENT 0x80fc
+#define IF_SDIO_BLOCK_SIZE 256
+#define CONFIGURATION_REG 0x03
+#define HOST_POWER_UP (0x1U << 1)
#endif
diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
index 07311e71af9..f11728a866f 100644
--- a/drivers/net/wireless/libertas/if_spi.c
+++ b/drivers/net/wireless/libertas/if_spi.c
@@ -17,13 +17,16 @@
* (at your option) any later version.
*/
-#include <linux/moduleparam.h>
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/hardirq.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/firmware.h>
-#include <linux/gpio.h>
#include <linux/jiffies.h>
-#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/netdevice.h>
+#include <linux/slab.h>
#include <linux/spi/libertas_spi.h>
#include <linux/spi/spi.h>
@@ -44,20 +47,10 @@ struct if_spi_card {
struct lbs_private *priv;
struct libertas_spi_platform_data *pdata;
- char helper_fw_name[FIRMWARE_NAME_MAX];
- char main_fw_name[FIRMWARE_NAME_MAX];
-
/* The card ID and card revision, as reported by the hardware. */
u16 card_id;
u8 card_rev;
- /* Pin number for our GPIO chip-select. */
- /* TODO: Once the generic SPI layer has some additional features, we
- * should take this out and use the normal chip select here.
- * We need support for chip select delays, and not dropping chipselect
- * after each word. */
- int gpio_cs;
-
/* The last time that we initiated an SPU operation */
unsigned long prev_xfer_time;
@@ -66,12 +59,9 @@ struct if_spi_card {
unsigned long spu_reg_delay;
/* Handles all SPI communication (except for FW load) */
- struct task_struct *spi_thread;
- int run_thread;
-
- /* Used to wake up the spi_thread */
- struct semaphore spi_ready;
- struct semaphore spi_thread_terminated;
+ struct workqueue_struct *workqueue;
+ struct work_struct packet_work;
+ struct work_struct resume_work;
u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE];
@@ -83,6 +73,9 @@ struct if_spi_card {
/* Protects cmd_packet_list and data_packet_list */
spinlock_t buffer_lock;
+
+ /* True is card suspended */
+ u8 suspended;
};
static void free_if_spi_card(struct if_spi_card *card)
@@ -90,7 +83,6 @@ static void free_if_spi_card(struct if_spi_card *card)
struct list_head *cursor, *next;
struct if_spi_packet *packet;
- BUG_ON(card->run_thread);
list_for_each_safe(cursor, next, &card->cmd_packet_list) {
packet = container_of(cursor, struct if_spi_packet, list);
list_del(&packet->list);
@@ -101,14 +93,31 @@ static void free_if_spi_card(struct if_spi_card *card)
list_del(&packet->list);
kfree(packet);
}
- spi_set_drvdata(card->spi, NULL);
kfree(card);
}
-static struct chip_ident chip_id_to_device_name[] = {
- { .chip_id = 0x04, .name = 8385 },
- { .chip_id = 0x0b, .name = 8686 },
+#define MODEL_8385 0x04
+#define MODEL_8686 0x0b
+#define MODEL_8688 0x10
+
+static const struct lbs_fw_table fw_table[] = {
+ { MODEL_8385, "libertas/gspi8385_helper.bin", "libertas/gspi8385.bin" },
+ { MODEL_8385, "libertas/gspi8385_hlp.bin", "libertas/gspi8385.bin" },
+ { MODEL_8686, "libertas/gspi8686_v9_helper.bin", "libertas/gspi8686_v9.bin" },
+ { MODEL_8686, "libertas/gspi8686_hlp.bin", "libertas/gspi8686.bin" },
+ { MODEL_8688, "libertas/gspi8688_helper.bin", "libertas/gspi8688.bin" },
+ { 0, NULL, NULL }
};
+MODULE_FIRMWARE("libertas/gspi8385_helper.bin");
+MODULE_FIRMWARE("libertas/gspi8385_hlp.bin");
+MODULE_FIRMWARE("libertas/gspi8385.bin");
+MODULE_FIRMWARE("libertas/gspi8686_v9_helper.bin");
+MODULE_FIRMWARE("libertas/gspi8686_v9.bin");
+MODULE_FIRMWARE("libertas/gspi8686_hlp.bin");
+MODULE_FIRMWARE("libertas/gspi8686.bin");
+MODULE_FIRMWARE("libertas/gspi8688_helper.bin");
+MODULE_FIRMWARE("libertas/gspi8688.bin");
+
/*
* SPI Interface Unit Routines
@@ -119,9 +128,6 @@ static struct chip_ident chip_id_to_device_name[] = {
* First we have to put a SPU register name on the bus. Then we can
* either read from or write to that register.
*
- * For 16-bit transactions, byte order on the bus is big-endian.
- * We don't have to worry about that here, though.
- * The translation takes place in the SPI routines.
*/
static void spu_transaction_init(struct if_spi_card *card)
@@ -133,21 +139,28 @@ static void spu_transaction_init(struct if_spi_card *card)
* If not, we have to busy-wait to be on the safe side. */
ndelay(400);
}
- gpio_set_value(card->gpio_cs, 0); /* assert CS */
}
static void spu_transaction_finish(struct if_spi_card *card)
{
- gpio_set_value(card->gpio_cs, 1); /* drop CS */
card->prev_xfer_time = jiffies;
}
-/* Write out a byte buffer to an SPI register,
- * using a series of 16-bit transfers. */
+/*
+ * Write out a byte buffer to an SPI register,
+ * using a series of 16-bit transfers.
+ */
static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len)
{
int err = 0;
- u16 reg_out = reg | IF_SPI_WRITE_OPERATION_MASK;
+ __le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK);
+ struct spi_message m;
+ struct spi_transfer reg_trans;
+ struct spi_transfer data_trans;
+
+ spi_message_init(&m);
+ memset(&reg_trans, 0, sizeof(reg_trans));
+ memset(&data_trans, 0, sizeof(data_trans));
/* You must give an even number of bytes to the SPU, even if it
* doesn't care about the last one. */
@@ -156,29 +169,26 @@ static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len)
spu_transaction_init(card);
/* write SPU register index */
- err = spi_write(card->spi, (u8 *)&reg_out, sizeof(u16));
- if (err)
- goto out;
+ reg_trans.tx_buf = &reg_out;
+ reg_trans.len = sizeof(reg_out);
- err = spi_write(card->spi, buf, len);
+ data_trans.tx_buf = buf;
+ data_trans.len = len;
-out:
+ spi_message_add_tail(&reg_trans, &m);
+ spi_message_add_tail(&data_trans, &m);
+
+ err = spi_sync(card->spi, &m);
spu_transaction_finish(card);
return err;
}
static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val)
{
- return spu_write(card, reg, (u8 *)&val, sizeof(u16));
-}
+ __le16 buff;
-static inline int spu_write_u32(struct if_spi_card *card, u16 reg, u32 val)
-{
- /* The lower 16 bits are written first. */
- u16 out[2];
- out[0] = val & 0xffff;
- out[1] = (val & 0xffff0000) >> 16;
- return spu_write(card, reg, (u8 *)&out, sizeof(u32));
+ buff = cpu_to_le16(val);
+ return spu_write(card, reg, (u8 *)&buff, sizeof(u16));
}
static inline int spu_reg_is_port_reg(u16 reg)
@@ -195,40 +205,50 @@ static inline int spu_reg_is_port_reg(u16 reg)
static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len)
{
- unsigned int i, delay;
+ unsigned int delay;
int err = 0;
- u16 zero = 0;
- u16 reg_out = reg | IF_SPI_READ_OPERATION_MASK;
-
- /* You must take an even number of bytes from the SPU, even if you
- * don't care about the last one. */
+ __le16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK);
+ struct spi_message m;
+ struct spi_transfer reg_trans;
+ struct spi_transfer dummy_trans;
+ struct spi_transfer data_trans;
+
+ /*
+ * You must take an even number of bytes from the SPU, even if you
+ * don't care about the last one.
+ */
BUG_ON(len & 0x1);
spu_transaction_init(card);
+ spi_message_init(&m);
+ memset(&reg_trans, 0, sizeof(reg_trans));
+ memset(&dummy_trans, 0, sizeof(dummy_trans));
+ memset(&data_trans, 0, sizeof(data_trans));
+
/* write SPU register index */
- err = spi_write(card->spi, (u8 *)&reg_out, sizeof(u16));
- if (err)
- goto out;
+ reg_trans.tx_buf = &reg_out;
+ reg_trans.len = sizeof(reg_out);
+ spi_message_add_tail(&reg_trans, &m);
delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay :
card->spu_reg_delay;
if (card->use_dummy_writes) {
/* Clock in dummy cycles while the SPU fills the FIFO */
- for (i = 0; i < delay / 16; ++i) {
- err = spi_write(card->spi, (u8 *)&zero, sizeof(u16));
- if (err)
- return err;
- }
+ dummy_trans.len = delay / 8;
+ spi_message_add_tail(&dummy_trans, &m);
} else {
/* Busy-wait while the SPU fills the FIFO */
- ndelay(100 + (delay * 10));
+ reg_trans.delay_usecs =
+ DIV_ROUND_UP((100 + (delay * 10)), 1000);
}
/* read in data */
- err = spi_read(card->spi, buf, len);
+ data_trans.rx_buf = buf;
+ data_trans.len = len;
+ spi_message_add_tail(&data_trans, &m);
-out:
+ err = spi_sync(card->spi, &m);
spu_transaction_finish(card);
return err;
}
@@ -236,28 +256,39 @@ out:
/* Read 16 bits from an SPI register */
static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val)
{
- return spu_read(card, reg, (u8 *)val, sizeof(u16));
+ __le16 buf;
+ int ret;
+
+ ret = spu_read(card, reg, (u8 *)&buf, sizeof(buf));
+ if (ret == 0)
+ *val = le16_to_cpup(&buf);
+ return ret;
}
-/* Read 32 bits from an SPI register.
- * The low 16 bits are read first. */
+/*
+ * Read 32 bits from an SPI register.
+ * The low 16 bits are read first.
+ */
static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val)
{
- u16 buf[2];
+ __le32 buf;
int err;
- err = spu_read(card, reg, (u8 *)buf, sizeof(u32));
+
+ err = spu_read(card, reg, (u8 *)&buf, sizeof(buf));
if (!err)
- *val = buf[0] | (buf[1] << 16);
+ *val = le32_to_cpup(&buf);
return err;
}
-/* Keep reading 16 bits from an SPI register until you get the correct result.
+/*
+ * Keep reading 16 bits from an SPI register until you get the correct result.
*
* If mask = 0, the correct result is any non-zero number.
* If mask != 0, the correct result is any number where
* number & target_mask == target
*
- * Returns -ETIMEDOUT if a second passes without the correct result. */
+ * Returns -ETIMEDOUT if a second passes without the correct result.
+ */
static int spu_wait_for_u16(struct if_spi_card *card, u16 reg,
u16 target_mask, u16 target)
{
@@ -277,16 +308,17 @@ static int spu_wait_for_u16(struct if_spi_card *card, u16 reg,
}
udelay(100);
if (time_after(jiffies, timeout)) {
- lbs_pr_err("%s: timeout with val=%02x, "
- "target_mask=%02x, target=%02x\n",
+ pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n",
__func__, val, target_mask, target);
return -ETIMEDOUT;
}
}
}
-/* Read 16 bits from an SPI register until you receive a specific value.
- * Returns -ETIMEDOUT if a 4 tries pass without success. */
+/*
+ * Read 16 bits from an SPI register until you receive a specific value.
+ * Returns -ETIMEDOUT if a 4 tries pass without success.
+ */
static int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target)
{
int err, try;
@@ -308,8 +340,10 @@ static int spu_set_interrupt_mode(struct if_spi_card *card,
{
int err = 0;
- /* We can suppress a host interrupt by clearing the appropriate
- * bit in the "host interrupt status mask" register */
+ /*
+ * We can suppress a host interrupt by clearing the appropriate
+ * bit in the "host interrupt status mask" register
+ */
if (suppress_host_int) {
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
if (err)
@@ -325,10 +359,12 @@ static int spu_set_interrupt_mode(struct if_spi_card *card,
return err;
}
- /* If auto-interrupts are on, the completion of certain transactions
+ /*
+ * If auto-interrupts are on, the completion of certain transactions
* will trigger an interrupt automatically. If auto-interrupts
* are off, we need to set the "Card Interrupt Cause" register to
- * trigger a card interrupt. */
+ * trigger a card interrupt.
+ */
if (auto_int) {
err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG,
IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO |
@@ -370,8 +406,8 @@ static int spu_set_bus_mode(struct if_spi_card *card, u16 mode)
err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval);
if (err)
return err;
- if (rval != mode) {
- lbs_pr_err("Can't read bus mode register.\n");
+ if ((rval & 0xF) != mode) {
+ pr_err("Can't read bus mode register\n");
return -EIO;
}
return 0;
@@ -382,8 +418,10 @@ static int spu_init(struct if_spi_card *card, int use_dummy_writes)
int err = 0;
u32 delay;
- /* We have to start up in timed delay mode so that we can safely
- * read the Delay Read Register. */
+ /*
+ * We have to start up in timed delay mode so that we can safely
+ * read the Delay Read Register.
+ */
card->use_dummy_writes = 0;
err = spu_set_bus_mode(card,
IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
@@ -420,46 +458,44 @@ static int spu_init(struct if_spi_card *card, int use_dummy_writes)
* Firmware Loading
*/
-static int if_spi_prog_helper_firmware(struct if_spi_card *card)
+static int if_spi_prog_helper_firmware(struct if_spi_card *card,
+ const struct firmware *firmware)
{
int err = 0;
- const struct firmware *firmware = NULL;
int bytes_remaining;
const u8 *fw;
u8 temp[HELPER_FW_LOAD_CHUNK_SZ];
- struct spi_device *spi = card->spi;
lbs_deb_enter(LBS_DEB_SPI);
err = spu_set_interrupt_mode(card, 1, 0);
if (err)
goto out;
- /* Get helper firmware image */
- err = request_firmware(&firmware, card->helper_fw_name, &spi->dev);
- if (err) {
- lbs_pr_err("request_firmware failed with err = %d\n", err);
- goto out;
- }
+
bytes_remaining = firmware->size;
fw = firmware->data;
/* Load helper firmware image */
while (bytes_remaining > 0) {
- /* Scratch pad 1 should contain the number of bytes we
- * want to download to the firmware */
+ /*
+ * Scratch pad 1 should contain the number of bytes we
+ * want to download to the firmware
+ */
err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,
HELPER_FW_LOAD_CHUNK_SZ);
if (err)
- goto release_firmware;
+ goto out;
err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
IF_SPI_HIST_CMD_DOWNLOAD_RDY,
IF_SPI_HIST_CMD_DOWNLOAD_RDY);
if (err)
- goto release_firmware;
+ goto out;
- /* Feed the data into the command read/write port reg
- * in chunks of 64 bytes */
+ /*
+ * Feed the data into the command read/write port reg
+ * in chunks of 64 bytes
+ */
memset(temp, 0, sizeof(temp));
memcpy(temp, fw,
min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ));
@@ -467,59 +503,59 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card)
err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
temp, HELPER_FW_LOAD_CHUNK_SZ);
if (err)
- goto release_firmware;
+ goto out;
/* Interrupt the boot code */
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
if (err)
- goto release_firmware;
+ goto out;
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
IF_SPI_CIC_CMD_DOWNLOAD_OVER);
if (err)
- goto release_firmware;
+ goto out;
bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ;
fw += HELPER_FW_LOAD_CHUNK_SZ;
}
- /* Once the helper / single stage firmware download is complete,
+ /*
+ * Once the helper / single stage firmware download is complete,
* write 0 to scratch pad 1 and interrupt the
- * bootloader. This completes the helper download. */
+ * bootloader. This completes the helper download.
+ */
err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK);
if (err)
- goto release_firmware;
+ goto out;
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
if (err)
- goto release_firmware;
+ goto out;
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
IF_SPI_CIC_CMD_DOWNLOAD_OVER);
- goto release_firmware;
-
- lbs_deb_spi("waiting for helper to boot...\n");
-
-release_firmware:
- release_firmware(firmware);
out:
if (err)
- lbs_pr_err("failed to load helper firmware (err=%d)\n", err);
+ pr_err("failed to load helper firmware (err=%d)\n", err);
lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err);
return err;
}
-/* Returns the length of the next packet the firmware expects us to send
- * Sets crc_err if the previous transfer had a CRC error. */
+/*
+ * Returns the length of the next packet the firmware expects us to send.
+ * Sets crc_err if the previous transfer had a CRC error.
+ */
static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,
int *crc_err)
{
u16 len;
int err = 0;
- /* wait until the host interrupt status register indicates
- * that we are ready to download */
+ /*
+ * wait until the host interrupt status register indicates
+ * that we are ready to download
+ */
err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
IF_SPI_HIST_CMD_DOWNLOAD_RDY,
IF_SPI_HIST_CMD_DOWNLOAD_RDY);
if (err) {
- lbs_pr_err("timed out waiting for host_int_status\n");
+ pr_err("timed out waiting for host_int_status\n");
return err;
}
@@ -529,9 +565,8 @@ static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,
return err;
if (len > IF_SPI_CMD_BUF_SIZE) {
- lbs_pr_err("firmware load device requested a larger "
- "tranfer than we are prepared to "
- "handle. (len = %d)\n", len);
+ pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n",
+ len);
return -EIO;
}
if (len & 0x1) {
@@ -544,13 +579,13 @@ static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,
return len;
}
-static int if_spi_prog_main_firmware(struct if_spi_card *card)
+static int if_spi_prog_main_firmware(struct if_spi_card *card,
+ const struct firmware *firmware)
{
+ struct lbs_private *priv = card->priv;
int len, prev_len;
int bytes, crc_err = 0, err = 0;
- const struct firmware *firmware = NULL;
const u8 *fw;
- struct spi_device *spi = card->spi;
u16 num_crc_errs;
lbs_deb_enter(LBS_DEB_SPI);
@@ -559,19 +594,12 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card)
if (err)
goto out;
- /* Get firmware image */
- err = request_firmware(&firmware, card->main_fw_name, &spi->dev);
- if (err) {
- lbs_pr_err("%s: can't get firmware '%s' from kernel. "
- "err = %d\n", __func__, card->main_fw_name, err);
- goto out;
- }
-
err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);
if (err) {
- lbs_pr_err("%s: timed out waiting for initial "
- "scratch reg = 0\n", __func__);
- goto release_firmware;
+ netdev_err(priv->dev,
+ "%s: timed out waiting for initial scratch reg = 0\n",
+ __func__);
+ goto out;
}
num_crc_errs = 0;
@@ -581,22 +609,23 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card)
while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) {
if (len < 0) {
err = len;
- goto release_firmware;
+ goto out;
}
if (bytes < 0) {
- /* If there are no more bytes left, we would normally
- * expect to have terminated with len = 0 */
- lbs_pr_err("Firmware load wants more bytes "
- "than we have to offer.\n");
+ /*
+ * If there are no more bytes left, we would normally
+ * expect to have terminated with len = 0
+ */
+ netdev_err(priv->dev,
+ "Firmware load wants more bytes than we have to offer.\n");
break;
}
if (crc_err) {
/* Previous transfer failed. */
if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) {
- lbs_pr_err("Too many CRC errors encountered "
- "in firmware load.\n");
+ pr_err("Too many CRC errors encountered in firmware load.\n");
err = -EIO;
- goto release_firmware;
+ goto out;
}
} else {
/* Previous transfer succeeded. Advance counters. */
@@ -611,36 +640,32 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card)
err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
if (err)
- goto release_firmware;
+ goto out;
err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
card->cmd_buffer, len);
if (err)
- goto release_firmware;
+ goto out;
err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG ,
IF_SPI_CIC_CMD_DOWNLOAD_OVER);
if (err)
- goto release_firmware;
+ goto out;
prev_len = len;
}
if (bytes > prev_len) {
- lbs_pr_err("firmware load wants fewer bytes than "
- "we have to offer.\n");
+ pr_err("firmware load wants fewer bytes than we have to offer\n");
}
/* Confirm firmware download */
err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG,
SUCCESSFUL_FW_DOWNLOAD_MAGIC);
if (err) {
- lbs_pr_err("failed to confirm the firmware download\n");
- goto release_firmware;
+ pr_err("failed to confirm the firmware download\n");
+ goto out;
}
-release_firmware:
- release_firmware(firmware);
-
out:
if (err)
- lbs_pr_err("failed to load firmware (err=%d)\n", err);
+ pr_err("failed to load firmware (err=%d)\n", err);
lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err);
return err;
}
@@ -648,7 +673,7 @@ out:
/*
* SPI Transfer Thread
*
- * The SPI thread handles all SPI transfers, so there is no need for a lock.
+ * The SPI worker handles all SPI transfers, so there is no need for a lock.
*/
/* Move a command from the card to the host */
@@ -660,14 +685,18 @@ static int if_spi_c2h_cmd(struct if_spi_card *card)
u16 len;
u8 i;
- /* We need a buffer big enough to handle whatever people send to
- * hw_host_to_card */
+ /*
+ * We need a buffer big enough to handle whatever people send to
+ * hw_host_to_card
+ */
BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE);
BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE);
- /* It's just annoying if the buffer size isn't a multiple of 4, because
- * then we might have len < IF_SPI_CMD_BUF_SIZE but
- * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE */
+ /*
+ * It's just annoying if the buffer size isn't a multiple of 4, because
+ * then we might have len < IF_SPI_CMD_BUF_SIZE but
+ * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE
+ */
BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0);
lbs_deb_enter(LBS_DEB_SPI);
@@ -677,13 +706,13 @@ static int if_spi_c2h_cmd(struct if_spi_card *card)
if (err)
goto out;
if (!len) {
- lbs_pr_err("%s: error: card has no data for host\n",
+ netdev_err(priv->dev, "%s: error: card has no data for host\n",
__func__);
err = -EINVAL;
goto out;
} else if (len > IF_SPI_CMD_BUF_SIZE) {
- lbs_pr_err("%s: error: response packet too large: "
- "%d bytes, but maximum is %d\n",
+ netdev_err(priv->dev,
+ "%s: error: response packet too large: %d bytes, but maximum is %d\n",
__func__, len, IF_SPI_CMD_BUF_SIZE);
err = -EINVAL;
goto out;
@@ -705,7 +734,7 @@ static int if_spi_c2h_cmd(struct if_spi_card *card)
out:
if (err)
- lbs_pr_err("%s: err=%d\n", __func__, err);
+ netdev_err(priv->dev, "%s: err=%d\n", __func__, err);
lbs_deb_leave(LBS_DEB_SPI);
return err;
}
@@ -713,6 +742,7 @@ out:
/* Move data from the card to the host */
static int if_spi_c2h_data(struct if_spi_card *card)
{
+ struct lbs_private *priv = card->priv;
struct sk_buff *skb;
char *data;
u16 len;
@@ -725,13 +755,13 @@ static int if_spi_c2h_data(struct if_spi_card *card)
if (err)
goto out;
if (!len) {
- lbs_pr_err("%s: error: card has no data for host\n",
+ netdev_err(priv->dev, "%s: error: card has no data for host\n",
__func__);
err = -EINVAL;
goto out;
} else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
- lbs_pr_err("%s: error: card has %d bytes of data, but "
- "our maximum skb size is %u\n",
+ netdev_err(priv->dev,
+ "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n",
__func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
err = -EINVAL;
goto out;
@@ -763,7 +793,7 @@ free_skb:
dev_kfree_skb(skb);
out:
if (err)
- lbs_pr_err("%s: err=%d\n", __func__, err);
+ netdev_err(priv->dev, "%s: err=%d\n", __func__, err);
lbs_deb_leave(LBS_DEB_SPI);
return err;
}
@@ -772,6 +802,7 @@ out:
static void if_spi_h2c(struct if_spi_card *card,
struct if_spi_packet *packet, int type)
{
+ struct lbs_private *priv = card->priv;
int err = 0;
u16 int_type, port_reg;
@@ -785,7 +816,8 @@ static void if_spi_h2c(struct if_spi_card *card,
port_reg = IF_SPI_CMD_RDWRPORT_REG;
break;
default:
- lbs_pr_err("can't transfer buffer of type %d\n", type);
+ netdev_err(priv->dev, "can't transfer buffer of type %d\n",
+ type);
err = -EINVAL;
goto out;
}
@@ -799,14 +831,13 @@ out:
kfree(packet);
if (err)
- lbs_pr_err("%s: error %d\n", __func__, err);
+ netdev_err(priv->dev, "%s: error %d\n", __func__, err);
}
/* Inform the host about a card event */
static void if_spi_e2h(struct if_spi_card *card)
{
int err = 0;
- unsigned long flags;
u32 cause;
struct lbs_private *priv = card->priv;
@@ -814,116 +845,114 @@ static void if_spi_e2h(struct if_spi_card *card)
if (err)
goto out;
- spin_lock_irqsave(&priv->driver_lock, flags);
- lbs_queue_event(priv, cause & 0xff);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
+ /* re-enable the card event interrupt */
+ spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG,
+ ~IF_SPI_HICU_CARD_EVENT);
+
+ /* generate a card interrupt */
+ spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT);
+ lbs_queue_event(priv, cause & 0xff);
out:
if (err)
- lbs_pr_err("%s: error %d\n", __func__, err);
+ netdev_err(priv->dev, "%s: error %d\n", __func__, err);
}
-static int lbs_spi_thread(void *data)
+static void if_spi_host_to_card_worker(struct work_struct *work)
{
int err;
- struct if_spi_card *card = data;
+ struct if_spi_card *card;
u16 hiStatus;
unsigned long flags;
struct if_spi_packet *packet;
+ struct lbs_private *priv;
- while (1) {
- /* Wait to be woken up by one of two things. First, our ISR
- * could tell us that something happened on the WLAN.
- * Secondly, libertas could call hw_host_to_card with more
- * data, which we might be able to send.
- */
- do {
- err = down_interruptible(&card->spi_ready);
- if (!card->run_thread) {
- up(&card->spi_thread_terminated);
- do_exit(0);
- }
- } while (err == EINTR);
+ card = container_of(work, struct if_spi_card, packet_work);
+ priv = card->priv;
- /* Read the host interrupt status register to see what we
- * can do. */
- err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG,
- &hiStatus);
- if (err) {
- lbs_pr_err("I/O error\n");
+ lbs_deb_enter(LBS_DEB_SPI);
+
+ /*
+ * Read the host interrupt status register to see what we
+ * can do.
+ */
+ err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG,
+ &hiStatus);
+ if (err) {
+ netdev_err(priv->dev, "I/O error\n");
+ goto err;
+ }
+
+ if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) {
+ err = if_spi_c2h_cmd(card);
+ if (err)
goto err;
- }
+ }
+ if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) {
+ err = if_spi_c2h_data(card);
+ if (err)
+ goto err;
+ }
- if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY)
- err = if_spi_c2h_cmd(card);
- if (err)
- goto err;
- if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY)
- err = if_spi_c2h_data(card);
- if (err)
- goto err;
- if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY) {
- /* This means two things. First of all,
- * if there was a previous command sent, the card has
- * successfully received it.
- * Secondly, it is now ready to download another
- * command.
- */
- lbs_host_to_card_done(card->priv);
-
- /* Do we have any command packets from the host to
- * send? */
- packet = NULL;
- spin_lock_irqsave(&card->buffer_lock, flags);
- if (!list_empty(&card->cmd_packet_list)) {
- packet = (struct if_spi_packet *)(card->
- cmd_packet_list.next);
- list_del(&packet->list);
- }
- spin_unlock_irqrestore(&card->buffer_lock, flags);
+ /*
+ * workaround: in PS mode, the card does not set the Command
+ * Download Ready bit, but it sets TX Download Ready.
+ */
+ if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY ||
+ (card->priv->psstate != PS_STATE_FULL_POWER &&
+ (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) {
+ /*
+ * This means two things. First of all,
+ * if there was a previous command sent, the card has
+ * successfully received it.
+ * Secondly, it is now ready to download another
+ * command.
+ */
+ lbs_host_to_card_done(card->priv);
- if (packet)
- if_spi_h2c(card, packet, MVMS_CMD);
+ /* Do we have any command packets from the host to send? */
+ packet = NULL;
+ spin_lock_irqsave(&card->buffer_lock, flags);
+ if (!list_empty(&card->cmd_packet_list)) {
+ packet = (struct if_spi_packet *)(card->
+ cmd_packet_list.next);
+ list_del(&packet->list);
}
- if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) {
- /* Do we have any data packets from the host to
- * send? */
- packet = NULL;
- spin_lock_irqsave(&card->buffer_lock, flags);
- if (!list_empty(&card->data_packet_list)) {
- packet = (struct if_spi_packet *)(card->
- data_packet_list.next);
- list_del(&packet->list);
- }
- spin_unlock_irqrestore(&card->buffer_lock, flags);
+ spin_unlock_irqrestore(&card->buffer_lock, flags);
- if (packet)
- if_spi_h2c(card, packet, MVMS_DAT);
+ if (packet)
+ if_spi_h2c(card, packet, MVMS_CMD);
+ }
+ if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) {
+ /* Do we have any data packets from the host to send? */
+ packet = NULL;
+ spin_lock_irqsave(&card->buffer_lock, flags);
+ if (!list_empty(&card->data_packet_list)) {
+ packet = (struct if_spi_packet *)(card->
+ data_packet_list.next);
+ list_del(&packet->list);
}
- if (hiStatus & IF_SPI_HIST_CARD_EVENT)
- if_spi_e2h(card);
+ spin_unlock_irqrestore(&card->buffer_lock, flags);
-err:
- if (err)
- lbs_pr_err("%s: got error %d\n", __func__, err);
+ if (packet)
+ if_spi_h2c(card, packet, MVMS_DAT);
}
-}
+ if (hiStatus & IF_SPI_HIST_CARD_EVENT)
+ if_spi_e2h(card);
-/* Block until lbs_spi_thread thread has terminated */
-static void if_spi_terminate_spi_thread(struct if_spi_card *card)
-{
- /* It would be nice to use kthread_stop here, but that function
- * can't wake threads waiting for a semaphore. */
- card->run_thread = 0;
- up(&card->spi_ready);
- down(&card->spi_thread_terminated);
+err:
+ if (err)
+ netdev_err(priv->dev, "%s: got error %d\n", __func__, err);
+
+ lbs_deb_leave(LBS_DEB_SPI);
}
/*
* Host to Card
*
* Called from Libertas to transfer some data to the WLAN device
- * We can't sleep here. */
+ * We can't sleep here.
+ */
static int if_spi_host_to_card(struct lbs_private *priv,
u8 type, u8 *buf, u16 nb)
{
@@ -936,7 +965,8 @@ static int if_spi_host_to_card(struct lbs_private *priv,
lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb);
if (nb == 0) {
- lbs_pr_err("%s: invalid size requested: %d\n", __func__, nb);
+ netdev_err(priv->dev, "%s: invalid size requested: %d\n",
+ __func__, nb);
err = -EINVAL;
goto out;
}
@@ -964,13 +994,15 @@ static int if_spi_host_to_card(struct lbs_private *priv,
spin_unlock_irqrestore(&card->buffer_lock, flags);
break;
default:
- lbs_pr_err("can't transfer buffer of type %d", type);
+ kfree(packet);
+ netdev_err(priv->dev, "can't transfer buffer of type %d\n",
+ type);
err = -EINVAL;
break;
}
- /* Wake up the spi thread */
- up(&card->spi_ready);
+ /* Queue spi xfer work */
+ queue_work(card->workqueue, &card->packet_work);
out:
lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err);
return err;
@@ -980,13 +1012,14 @@ out:
* Host Interrupts
*
* Service incoming interrupts from the WLAN device. We can't sleep here, so
- * don't try to talk on the SPI bus, just wake up the SPI thread.
+ * don't try to talk on the SPI bus, just queue the SPI xfer work.
*/
static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)
{
struct if_spi_card *card = dev_id;
- up(&card->spi_ready);
+ queue_work(card->workqueue, &card->packet_work);
+
return IRQ_HANDLED;
}
@@ -994,32 +1027,104 @@ static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)
* SPI callbacks
*/
-static int if_spi_calculate_fw_names(u16 card_id,
- char *helper_fw, char *main_fw)
+static int if_spi_init_card(struct if_spi_card *card)
{
- int i;
- for (i = 0; i < ARRAY_SIZE(chip_id_to_device_name); ++i) {
- if (card_id == chip_id_to_device_name[i].chip_id)
- break;
+ struct lbs_private *priv = card->priv;
+ int err, i;
+ u32 scratch;
+ const struct firmware *helper = NULL;
+ const struct firmware *mainfw = NULL;
+
+ lbs_deb_enter(LBS_DEB_SPI);
+
+ err = spu_init(card, card->pdata->use_dummy_writes);
+ if (err)
+ goto out;
+ err = spu_get_chip_revision(card, &card->card_id, &card->card_rev);
+ if (err)
+ goto out;
+
+ err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch);
+ if (err)
+ goto out;
+ if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC)
+ lbs_deb_spi("Firmware is already loaded for "
+ "Marvell WLAN 802.11 adapter\n");
+ else {
+ /* Check if we support this card */
+ for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
+ if (card->card_id == fw_table[i].model)
+ break;
+ }
+ if (i == ARRAY_SIZE(fw_table)) {
+ netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n",
+ card->card_id);
+ err = -ENODEV;
+ goto out;
+ }
+
+ err = lbs_get_firmware(&card->spi->dev, card->card_id,
+ &fw_table[0], &helper, &mainfw);
+ if (err) {
+ netdev_err(priv->dev, "failed to find firmware (%d)\n",
+ err);
+ goto out;
+ }
+
+ lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter "
+ "(chip_id = 0x%04x, chip_rev = 0x%02x) "
+ "attached to SPI bus_num %d, chip_select %d. "
+ "spi->max_speed_hz=%d\n",
+ card->card_id, card->card_rev,
+ card->spi->master->bus_num,
+ card->spi->chip_select,
+ card->spi->max_speed_hz);
+ err = if_spi_prog_helper_firmware(card, helper);
+ if (err)
+ goto out;
+ err = if_spi_prog_main_firmware(card, mainfw);
+ if (err)
+ goto out;
+ lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n");
}
- if (i == ARRAY_SIZE(chip_id_to_device_name)) {
- lbs_pr_err("Unsupported chip_id: 0x%02x\n", card_id);
- return -EAFNOSUPPORT;
+
+ err = spu_set_interrupt_mode(card, 0, 1);
+ if (err)
+ goto out;
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
+ return err;
+}
+
+static void if_spi_resume_worker(struct work_struct *work)
+{
+ struct if_spi_card *card;
+
+ card = container_of(work, struct if_spi_card, resume_work);
+
+ if (card->suspended) {
+ if (card->pdata->setup)
+ card->pdata->setup(card->spi);
+
+ /* Init card ... */
+ if_spi_init_card(card);
+
+ enable_irq(card->spi->irq);
+
+ /* And resume it ... */
+ lbs_resume(card->priv);
+
+ card->suspended = 0;
}
- snprintf(helper_fw, FIRMWARE_NAME_MAX, "libertas/gspi%d_hlp.bin",
- chip_id_to_device_name[i].name);
- snprintf(main_fw, FIRMWARE_NAME_MAX, "libertas/gspi%d.bin",
- chip_id_to_device_name[i].name);
- return 0;
}
-static int __devinit if_spi_probe(struct spi_device *spi)
+static int if_spi_probe(struct spi_device *spi)
{
struct if_spi_card *card;
struct lbs_private *priv = NULL;
- struct libertas_spi_platform_data *pdata = spi->dev.platform_data;
+ struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev);
int err = 0;
- u32 scratch;
lbs_deb_enter(LBS_DEB_SPI);
@@ -1038,101 +1143,59 @@ static int __devinit if_spi_probe(struct spi_device *spi)
card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL);
if (!card) {
err = -ENOMEM;
- goto out;
+ goto teardown;
}
spi_set_drvdata(spi, card);
card->pdata = pdata;
card->spi = spi;
- card->gpio_cs = pdata->gpio_cs;
card->prev_xfer_time = jiffies;
- sema_init(&card->spi_ready, 0);
- sema_init(&card->spi_thread_terminated, 0);
INIT_LIST_HEAD(&card->cmd_packet_list);
INIT_LIST_HEAD(&card->data_packet_list);
spin_lock_init(&card->buffer_lock);
- /* set up GPIO CS line. TODO: use regular CS line */
- err = gpio_request(card->gpio_cs, "if_spi_gpio_chip_select");
- if (err)
- goto free_card;
- err = gpio_direction_output(card->gpio_cs, 1);
- if (err)
- goto free_gpio;
-
/* Initialize the SPI Interface Unit */
- err = spu_init(card, pdata->use_dummy_writes);
- if (err)
- goto free_gpio;
- err = spu_get_chip_revision(card, &card->card_id, &card->card_rev);
- if (err)
- goto free_gpio;
/* Firmware load */
- err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch);
- if (err)
- goto free_gpio;
- if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC)
- lbs_deb_spi("Firmware is already loaded for "
- "Marvell WLAN 802.11 adapter\n");
- else {
- err = if_spi_calculate_fw_names(card->card_id,
- card->helper_fw_name, card->main_fw_name);
- if (err)
- goto free_gpio;
-
- lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter "
- "(chip_id = 0x%04x, chip_rev = 0x%02x) "
- "attached to SPI bus_num %d, chip_select %d. "
- "spi->max_speed_hz=%d\n",
- card->card_id, card->card_rev,
- spi->master->bus_num, spi->chip_select,
- spi->max_speed_hz);
- err = if_spi_prog_helper_firmware(card);
- if (err)
- goto free_gpio;
- err = if_spi_prog_main_firmware(card);
- if (err)
- goto free_gpio;
- lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n");
- }
-
- err = spu_set_interrupt_mode(card, 0, 1);
+ err = if_spi_init_card(card);
if (err)
- goto free_gpio;
+ goto free_card;
- /* Register our card with libertas.
- * This will call alloc_etherdev */
+ /*
+ * Register our card with libertas.
+ * This will call alloc_etherdev.
+ */
priv = lbs_add_card(card, &spi->dev);
if (!priv) {
err = -ENOMEM;
- goto free_gpio;
+ goto free_card;
}
card->priv = priv;
+ priv->setup_fw_on_resume = 1;
priv->card = card;
priv->hw_host_to_card = if_spi_host_to_card;
+ priv->enter_deep_sleep = NULL;
+ priv->exit_deep_sleep = NULL;
+ priv->reset_deep_sleep_wakeup = NULL;
priv->fw_ready = 1;
- priv->ps_supported = 1;
/* Initialize interrupt handling stuff. */
- card->run_thread = 1;
- card->spi_thread = kthread_run(lbs_spi_thread, card, "lbs_spi_thread");
- if (IS_ERR(card->spi_thread)) {
- card->run_thread = 0;
- err = PTR_ERR(card->spi_thread);
- lbs_pr_err("error creating SPI thread: err=%d\n", err);
- goto remove_card;
- }
+ card->workqueue = create_workqueue("libertas_spi");
+ INIT_WORK(&card->packet_work, if_spi_host_to_card_worker);
+ INIT_WORK(&card->resume_work, if_spi_resume_worker);
+
err = request_irq(spi->irq, if_spi_host_interrupt,
IRQF_TRIGGER_FALLING, "libertas_spi", card);
if (err) {
- lbs_pr_err("can't get host irq line-- request_irq failed\n");
- goto terminate_thread;
+ pr_err("can't get host irq line-- request_irq failed\n");
+ goto terminate_workqueue;
}
- /* Start the card.
+ /*
+ * Start the card.
* This will call register_netdev, and we'll start
- * getting interrupts... */
+ * getting interrupts...
+ */
err = lbs_start_card(priv);
if (err)
goto release_irq;
@@ -1144,33 +1207,36 @@ static int __devinit if_spi_probe(struct spi_device *spi)
release_irq:
free_irq(spi->irq, card);
-terminate_thread:
- if_spi_terminate_spi_thread(card);
-remove_card:
+terminate_workqueue:
+ flush_workqueue(card->workqueue);
+ destroy_workqueue(card->workqueue);
lbs_remove_card(priv); /* will call free_netdev */
-free_gpio:
- gpio_free(card->gpio_cs);
free_card:
free_if_spi_card(card);
+teardown:
+ if (pdata->teardown)
+ pdata->teardown(spi);
out:
lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
return err;
}
-static int __devexit libertas_spi_remove(struct spi_device *spi)
+static int libertas_spi_remove(struct spi_device *spi)
{
struct if_spi_card *card = spi_get_drvdata(spi);
struct lbs_private *priv = card->priv;
lbs_deb_spi("libertas_spi_remove\n");
lbs_deb_enter(LBS_DEB_SPI);
- priv->surpriseremoved = 1;
+
+ cancel_work_sync(&card->resume_work);
lbs_stop_card(priv);
- free_irq(spi->irq, card);
- if_spi_terminate_spi_thread(card);
lbs_remove_card(priv); /* will call free_netdev */
- gpio_free(card->gpio_cs);
+
+ free_irq(spi->irq, card);
+ flush_workqueue(card->workqueue);
+ destroy_workqueue(card->workqueue);
if (card->pdata->teardown)
card->pdata->teardown(spi);
free_if_spi_card(card);
@@ -1178,13 +1244,47 @@ static int __devexit libertas_spi_remove(struct spi_device *spi)
return 0;
}
+static int if_spi_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct if_spi_card *card = spi_get_drvdata(spi);
+
+ if (!card->suspended) {
+ lbs_suspend(card->priv);
+ flush_workqueue(card->workqueue);
+ disable_irq(spi->irq);
+
+ if (card->pdata->teardown)
+ card->pdata->teardown(spi);
+ card->suspended = 1;
+ }
+
+ return 0;
+}
+
+static int if_spi_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct if_spi_card *card = spi_get_drvdata(spi);
+
+ /* Schedule delayed work */
+ schedule_work(&card->resume_work);
+
+ return 0;
+}
+
+static const struct dev_pm_ops if_spi_pm_ops = {
+ .suspend = if_spi_suspend,
+ .resume = if_spi_resume,
+};
+
static struct spi_driver libertas_spi_driver = {
.probe = if_spi_probe,
- .remove = __devexit_p(libertas_spi_remove),
+ .remove = libertas_spi_remove,
.driver = {
.name = "libertas_spi",
- .bus = &spi_bus_type,
.owner = THIS_MODULE,
+ .pm = &if_spi_pm_ops,
},
};
@@ -1216,3 +1316,4 @@ MODULE_DESCRIPTION("Libertas SPI WLAN Driver");
MODULE_AUTHOR("Andrey Yurovsky <andrey@cozybit.com>, "
"Colin McCabe <colin@cozybit.com>");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:libertas_spi");
diff --git a/drivers/net/wireless/libertas/if_spi.h b/drivers/net/wireless/libertas/if_spi.h
index 2103869cc5b..e450e31fd11 100644
--- a/drivers/net/wireless/libertas/if_spi.h
+++ b/drivers/net/wireless/libertas/if_spi.h
@@ -22,10 +22,8 @@
#define IF_SPI_CMD_BUF_SIZE 2400
/***************** Firmware *****************/
-struct chip_ident {
- u16 chip_id;
- u16 name;
-};
+
+#define IF_SPI_FW_NAME_MAX 30
#define MAX_MAIN_FW_LOAD_CRC_ERR 10
@@ -68,7 +66,7 @@ struct chip_ident {
#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */
#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */
-#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interupt status reg */
+#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interrupt status reg */
#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */
#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */
@@ -88,34 +86,34 @@ struct chip_ident {
#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff)
/***************** IF_SPI_HOST_INT_CTRL_REG *****************/
-/** Host Interrupt Control bit : Wake up */
+/* Host Interrupt Control bit : Wake up */
#define IF_SPI_HICT_WAKE_UP (1<<0)
-/** Host Interrupt Control bit : WLAN ready */
+/* Host Interrupt Control bit : WLAN ready */
#define IF_SPI_HICT_WLAN_READY (1<<1)
/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */
/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */
/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */
-/** Host Interrupt Control bit : Tx auto download */
+/* Host Interrupt Control bit : Tx auto download */
#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5)
-/** Host Interrupt Control bit : Rx auto upload */
+/* Host Interrupt Control bit : Rx auto upload */
#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6)
-/** Host Interrupt Control bit : Command auto download */
+/* Host Interrupt Control bit : Command auto download */
#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7)
-/** Host Interrupt Control bit : Command auto upload */
+/* Host Interrupt Control bit : Command auto upload */
#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8)
/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/
-/** Card Interrupt Case bit : Tx download over */
+/* Card Interrupt Case bit : Tx download over */
#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0)
-/** Card Interrupt Case bit : Rx upload over */
+/* Card Interrupt Case bit : Rx upload over */
#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1)
-/** Card Interrupt Case bit : Command download over */
+/* Card Interrupt Case bit : Command download over */
#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2)
-/** Card Interrupt Case bit : Host event */
+/* Card Interrupt Case bit : Host event */
#define IF_SPI_CIC_HOST_EVENT (1<<3)
-/** Card Interrupt Case bit : Command upload over */
+/* Card Interrupt Case bit : Command upload over */
#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4)
-/** Card Interrupt Case bit : Power down */
+/* Card Interrupt Case bit : Power down */
#define IF_SPI_CIC_POWER_DOWN (1<<5)
/***************** IF_SPI_CARD_INT_STATUS_REG *****************/
@@ -140,51 +138,51 @@ struct chip_ident {
#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10)
/***************** IF_SPI_HOST_INT_STATUS_REG *****************/
-/** Host Interrupt Status bit : Tx download ready */
+/* Host Interrupt Status bit : Tx download ready */
#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0)
-/** Host Interrupt Status bit : Rx upload ready */
+/* Host Interrupt Status bit : Rx upload ready */
#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1)
-/** Host Interrupt Status bit : Command download ready */
+/* Host Interrupt Status bit : Command download ready */
#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2)
-/** Host Interrupt Status bit : Card event */
+/* Host Interrupt Status bit : Card event */
#define IF_SPI_HIST_CARD_EVENT (1<<3)
-/** Host Interrupt Status bit : Command upload ready */
+/* Host Interrupt Status bit : Command upload ready */
#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4)
-/** Host Interrupt Status bit : I/O write FIFO overflow */
+/* Host Interrupt Status bit : I/O write FIFO overflow */
#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5)
-/** Host Interrupt Status bit : I/O read FIFO underflow */
+/* Host Interrupt Status bit : I/O read FIFO underflow */
#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6)
-/** Host Interrupt Status bit : Data write FIFO overflow */
+/* Host Interrupt Status bit : Data write FIFO overflow */
#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7)
-/** Host Interrupt Status bit : Data read FIFO underflow */
+/* Host Interrupt Status bit : Data read FIFO underflow */
#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8)
-/** Host Interrupt Status bit : Command write FIFO overflow */
+/* Host Interrupt Status bit : Command write FIFO overflow */
#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9)
-/** Host Interrupt Status bit : Command read FIFO underflow */
+/* Host Interrupt Status bit : Command read FIFO underflow */
#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10)
/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/
-/** Host Interrupt Status Mask bit : Tx download ready */
+/* Host Interrupt Status Mask bit : Tx download ready */
#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0)
-/** Host Interrupt Status Mask bit : Rx upload ready */
+/* Host Interrupt Status Mask bit : Rx upload ready */
#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1)
-/** Host Interrupt Status Mask bit : Command download ready */
+/* Host Interrupt Status Mask bit : Command download ready */
#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2)
-/** Host Interrupt Status Mask bit : Card event */
+/* Host Interrupt Status Mask bit : Card event */
#define IF_SPI_HISM_CARDEVENT (1<<3)
-/** Host Interrupt Status Mask bit : Command upload ready */
+/* Host Interrupt Status Mask bit : Command upload ready */
#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4)
-/** Host Interrupt Status Mask bit : I/O write FIFO overflow */
+/* Host Interrupt Status Mask bit : I/O write FIFO overflow */
#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5)
-/** Host Interrupt Status Mask bit : I/O read FIFO underflow */
+/* Host Interrupt Status Mask bit : I/O read FIFO underflow */
#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6)
-/** Host Interrupt Status Mask bit : Data write FIFO overflow */
+/* Host Interrupt Status Mask bit : Data write FIFO overflow */
#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7)
-/** Host Interrupt Status Mask bit : Data write FIFO underflow */
+/* Host Interrupt Status Mask bit : Data write FIFO underflow */
#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8)
-/** Host Interrupt Status Mask bit : Command write FIFO overflow */
+/* Host Interrupt Status Mask bit : Command write FIFO overflow */
#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9)
-/** Host Interrupt Status Mask bit : Command write FIFO underflow */
+/* Host Interrupt Status Mask bit : Command write FIFO underflow */
#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10)
/***************** IF_SPI_SPU_BUS_MODE_REG *****************/
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index ea3dc038be7..dff08a2896a 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -1,11 +1,16 @@
-/**
- * This file contains functions used in USB interface module.
- */
+/*
+ * This file contains functions used in USB interface module.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/delay.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
+#include <linux/slab.h>
#include <linux/usb.h>
+#include <linux/olpc-ec.h>
#ifdef CONFIG_OLPC
#include <asm/olpc.h>
@@ -25,13 +30,32 @@
#define MESSAGE_HEADER_LEN 4
-static char *lbs_fw_name = "usb8388.bin";
-module_param_named(fw_name, lbs_fw_name, charp, 0644);
+MODULE_FIRMWARE("libertas/usb8388_v9.bin");
+MODULE_FIRMWARE("libertas/usb8388_v5.bin");
+MODULE_FIRMWARE("libertas/usb8388.bin");
+MODULE_FIRMWARE("libertas/usb8682.bin");
+MODULE_FIRMWARE("usb8388.bin");
+
+enum {
+ MODEL_UNKNOWN = 0x0,
+ MODEL_8388 = 0x1,
+ MODEL_8682 = 0x2
+};
+
+/* table of firmware file names */
+static const struct lbs_fw_table fw_table[] = {
+ { MODEL_8388, "libertas/usb8388_olpc.bin", NULL },
+ { MODEL_8388, "libertas/usb8388_v9.bin", NULL },
+ { MODEL_8388, "libertas/usb8388_v5.bin", NULL },
+ { MODEL_8388, "libertas/usb8388.bin", NULL },
+ { MODEL_8388, "usb8388.bin", NULL },
+ { MODEL_8682, "libertas/usb8682.bin", NULL }
+};
static struct usb_device_id if_usb_table[] = {
/* Enter the device signature inside */
- { USB_DEVICE(0x1286, 0x2001) },
- { USB_DEVICE(0x05a3, 0x8388) },
+ { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 },
+ { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 },
{} /* Terminating entry */
};
@@ -39,10 +63,9 @@ MODULE_DEVICE_TABLE(usb, if_usb_table);
static void if_usb_receive(struct urb *urb);
static void if_usb_receive_fwload(struct urb *urb);
-static int __if_usb_prog_firmware(struct if_usb_card *cardp,
- const char *fwname, int cmd);
-static int if_usb_prog_firmware(struct if_usb_card *cardp,
- const char *fwname, int cmd);
+static void if_usb_prog_firmware(struct lbs_private *priv, int ret,
+ const struct firmware *fw,
+ const struct firmware *unused);
static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type,
uint8_t *payload, uint16_t nb);
static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
@@ -51,66 +74,11 @@ static void if_usb_free(struct if_usb_card *cardp);
static int if_usb_submit_rx_urb(struct if_usb_card *cardp);
static int if_usb_reset_device(struct if_usb_card *cardp);
-/* sysfs hooks */
-
-/**
- * Set function to write firmware to device's persistent memory
- */
-static ssize_t if_usb_firmware_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct if_usb_card *cardp = priv->card;
- char fwname[FIRMWARE_NAME_MAX];
- int ret;
-
- sscanf(buf, "%29s", fwname); /* FIRMWARE_NAME_MAX - 1 = 29 */
- ret = if_usb_prog_firmware(cardp, fwname, BOOT_CMD_UPDATE_FW);
- if (ret == 0)
- return count;
-
- return ret;
-}
-
-/**
- * lbs_flash_fw attribute to be exported per ethX interface through sysfs
- * (/sys/class/net/ethX/lbs_flash_fw). Use this like so to write firmware to
- * the device's persistent memory:
- * echo usb8388-5.126.0.p5.bin > /sys/class/net/ethX/lbs_flash_fw
- */
-static DEVICE_ATTR(lbs_flash_fw, 0200, NULL, if_usb_firmware_set);
-
/**
- * Set function to write firmware to device's persistent memory
- */
-static ssize_t if_usb_boot2_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct if_usb_card *cardp = priv->card;
- char fwname[FIRMWARE_NAME_MAX];
- int ret;
-
- sscanf(buf, "%29s", fwname); /* FIRMWARE_NAME_MAX - 1 = 29 */
- ret = if_usb_prog_firmware(cardp, fwname, BOOT_CMD_UPDATE_BOOT2);
- if (ret == 0)
- return count;
-
- return ret;
-}
-
-/**
- * lbs_flash_boot2 attribute to be exported per ethX interface through sysfs
- * (/sys/class/net/ethX/lbs_flash_boot2). Use this like so to write firmware
- * to the device's persistent memory:
- * echo usb8388-5.126.0.p5.bin > /sys/class/net/ethX/lbs_flash_boot2
- */
-static DEVICE_ATTR(lbs_flash_boot2, 0200, NULL, if_usb_boot2_set);
-
-/**
- * @brief call back function to handle the status of the URB
- * @param urb pointer to urb structure
- * @return N/A
+ * if_usb_write_bulk_callback - callback function to handle the status
+ * of the URB
+ * @urb: pointer to &urb structure
+ * returns: N/A
*/
static void if_usb_write_bulk_callback(struct urb *urb)
{
@@ -132,16 +100,14 @@ static void if_usb_write_bulk_callback(struct urb *urb)
lbs_host_to_card_done(priv);
} else {
/* print the failure status number for debug */
- lbs_pr_info("URB in failure status: %d\n", urb->status);
+ pr_info("URB in failure status: %d\n", urb->status);
}
-
- return;
}
/**
- * @brief free tx/rx urb, skb and rx buffer
- * @param cardp pointer if_usb_card
- * @return N/A
+ * if_usb_free - free tx/rx urb, skb and rx buffer
+ * @cardp: pointer to &if_usb_card
+ * returns: N/A
*/
static void if_usb_free(struct if_usb_card *cardp)
{
@@ -184,15 +150,17 @@ static void if_usb_setup_firmware(struct lbs_private *priv)
wake_method.hdr.size = cpu_to_le16(sizeof(wake_method));
wake_method.action = cpu_to_le16(CMD_ACT_GET);
if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) {
- lbs_pr_info("Firmware does not seem to support PS mode\n");
+ netdev_info(priv->dev, "Firmware does not seem to support PS mode\n");
+ priv->fwcapinfo &= ~FW_CAPINFO_PS;
} else {
if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) {
lbs_deb_usb("Firmware seems to support PS with wake-via-command\n");
- priv->ps_supported = 1;
} else {
/* The versions which boot up this way don't seem to
work even if we set it to the command interrupt */
- lbs_pr_info("Firmware doesn't wake via command interrupt; disabling PS mode\n");
+ priv->fwcapinfo &= ~FW_CAPINFO_PS;
+ netdev_info(priv->dev,
+ "Firmware doesn't wake via command interrupt; disabling PS mode\n");
}
}
}
@@ -204,7 +172,7 @@ static void if_usb_fw_timeo(unsigned long priv)
if (cardp->fwdnldover) {
lbs_deb_usb("Download complete, no event. Assuming success\n");
} else {
- lbs_pr_err("Download timed out\n");
+ pr_err("Download timed out\n");
cardp->surprise_removed = 1;
}
wake_up(&cardp->fw_wq);
@@ -219,10 +187,10 @@ static void if_usb_reset_olpc_card(struct lbs_private *priv)
#endif
/**
- * @brief sets the configuration values
- * @param ifnum interface number
- * @param id pointer to usb_device_id
- * @return 0 on success, error code on failure
+ * if_usb_probe - sets the configuration values
+ * @intf: &usb_interface pointer
+ * @id: pointer to usb_device_id
+ * returns: 0 on success, error code on failure
*/
static int if_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -232,20 +200,20 @@ static int if_usb_probe(struct usb_interface *intf,
struct usb_endpoint_descriptor *endpoint;
struct lbs_private *priv;
struct if_usb_card *cardp;
+ int r = -ENOMEM;
int i;
udev = interface_to_usbdev(intf);
cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL);
- if (!cardp) {
- lbs_pr_err("Out of memory allocating private data.\n");
+ if (!cardp)
goto error;
- }
setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp);
init_waitqueue_head(&cardp->fw_wq);
cardp->udev = udev;
+ cardp->model = (uint32_t) id->driver_info;
iface_desc = intf->cur_altsetting;
lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
@@ -290,19 +258,15 @@ static int if_usb_probe(struct usb_interface *intf,
goto dealloc;
}
- /* Upload firmware */
- if (__if_usb_prog_firmware(cardp, lbs_fw_name, BOOT_CMD_FW_BY_USB)) {
- lbs_deb_usbd(&udev->dev, "FW upload failed\n");
- goto err_prog_firmware;
- }
-
- if (!(priv = lbs_add_card(cardp, &udev->dev)))
- goto err_prog_firmware;
+ if (!(priv = lbs_add_card(cardp, &intf->dev)))
+ goto err_add_card;
cardp->priv = priv;
- cardp->priv->fw_ready = 1;
priv->hw_host_to_card = if_usb_host_to_card;
+ priv->enter_deep_sleep = NULL;
+ priv->exit_deep_sleep = NULL;
+ priv->reset_deep_sleep_wakeup = NULL;
#ifdef CONFIG_OLPC
if (machine_is_olpc())
priv->reset_card = if_usb_reset_olpc_card;
@@ -310,54 +274,42 @@ static int if_usb_probe(struct usb_interface *intf,
cardp->boot2_version = udev->descriptor.bcdDevice;
- if_usb_submit_rx_urb(cardp);
-
- if (lbs_start_card(priv))
- goto err_start_card;
-
- if_usb_setup_firmware(priv);
-
usb_get_dev(udev);
usb_set_intfdata(intf, cardp);
- if (device_create_file(&priv->dev->dev, &dev_attr_lbs_flash_fw))
- lbs_pr_err("cannot register lbs_flash_fw attribute\n");
-
- if (device_create_file(&priv->dev->dev, &dev_attr_lbs_flash_boot2))
- lbs_pr_err("cannot register lbs_flash_boot2 attribute\n");
+ r = lbs_get_firmware_async(priv, &udev->dev, cardp->model,
+ fw_table, if_usb_prog_firmware);
+ if (r)
+ goto err_get_fw;
return 0;
-err_start_card:
+err_get_fw:
lbs_remove_card(priv);
-err_prog_firmware:
+err_add_card:
if_usb_reset_device(cardp);
dealloc:
if_usb_free(cardp);
error:
- return -ENOMEM;
+ return r;
}
/**
- * @brief free resource and cleanup
- * @param intf USB interface structure
- * @return N/A
+ * if_usb_disconnect - free resource and cleanup
+ * @intf: USB interface structure
+ * returns: N/A
*/
static void if_usb_disconnect(struct usb_interface *intf)
{
struct if_usb_card *cardp = usb_get_intfdata(intf);
- struct lbs_private *priv = (struct lbs_private *) cardp->priv;
+ struct lbs_private *priv = cardp->priv;
lbs_deb_enter(LBS_DEB_MAIN);
- device_remove_file(&priv->dev->dev, &dev_attr_lbs_flash_boot2);
- device_remove_file(&priv->dev->dev, &dev_attr_lbs_flash_fw);
-
cardp->surprise_removed = 1;
if (priv) {
- priv->surpriseremoved = 1;
lbs_stop_card(priv);
lbs_remove_card(priv);
}
@@ -372,9 +324,9 @@ static void if_usb_disconnect(struct usb_interface *intf)
}
/**
- * @brief This function download FW
- * @param priv pointer to struct lbs_private
- * @return 0
+ * if_usb_send_fw_pkt - download FW
+ * @cardp: pointer to &struct if_usb_card
+ * returns: 0
*/
static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
{
@@ -432,7 +384,7 @@ static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
static int if_usb_reset_device(struct if_usb_card *cardp)
{
- struct cmd_ds_command *cmd = cardp->ep_out_buf + 4;
+ struct cmd_header *cmd = cardp->ep_out_buf + 4;
int ret;
lbs_deb_enter(LBS_DEB_USB);
@@ -440,7 +392,7 @@ static int if_usb_reset_device(struct if_usb_card *cardp)
*(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
cmd->command = cpu_to_le16(CMD_802_11_RESET);
- cmd->size = cpu_to_le16(sizeof(struct cmd_header));
+ cmd->size = cpu_to_le16(sizeof(cmd));
cmd->result = cpu_to_le16(0);
cmd->seqnum = cpu_to_le16(0x5a5a);
usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_header));
@@ -460,19 +412,20 @@ static int if_usb_reset_device(struct if_usb_card *cardp)
}
/**
- * @brief This function transfer the data to the device.
- * @param priv pointer to struct lbs_private
- * @param payload pointer to payload data
- * @param nb data length
- * @return 0 or -1
+ * usb_tx_block - transfer the data to the device
+ * @cardp: pointer to &struct if_usb_card
+ * @payload: pointer to payload data
+ * @nb: data length
+ * returns: 0 for success or negative error code
*/
static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb)
{
- int ret = -1;
+ int ret;
/* check if device is removed */
if (cardp->surprise_removed) {
lbs_deb_usbd(&cardp->udev->dev, "Device removed\n");
+ ret = -ENODEV;
goto tx_ret;
}
@@ -485,7 +438,6 @@ static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb
if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) {
lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret);
- ret = -1;
} else {
lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n");
ret = 0;
@@ -502,7 +454,7 @@ static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
int ret = -1;
if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) {
- lbs_pr_err("No free skb\n");
+ pr_err("No free skb\n");
goto rx_ret;
}
@@ -511,7 +463,7 @@ static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
/* 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->ep_in),
- (void *) (skb->tail + (size_t) IPFIELD_ALIGN_OFFSET),
+ skb->data + IPFIELD_ALIGN_OFFSET,
MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn,
cardp);
@@ -561,7 +513,7 @@ static void if_usb_receive_fwload(struct urb *urb)
if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) &&
tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) {
- lbs_pr_info("Firmware ready event received\n");
+ pr_info("Firmware ready event received\n");
wake_up(&cardp->fw_wq);
} else {
lbs_deb_usb("Waiting for confirmation; got %x %x\n",
@@ -588,20 +540,20 @@ static void if_usb_receive_fwload(struct urb *urb)
bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) ||
bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) {
if (!cardp->bootcmdresp)
- lbs_pr_info("Firmware already seems alive; resetting\n");
+ pr_info("Firmware already seems alive; resetting\n");
cardp->bootcmdresp = -1;
} else {
- lbs_pr_info("boot cmd response wrong magic number (0x%x)\n",
+ pr_info("boot cmd response wrong magic number (0x%x)\n",
le32_to_cpu(bootcmdresp.magic));
}
} else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) &&
(bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) &&
(bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) {
- lbs_pr_info("boot cmd response cmd_tag error (%d)\n",
- bootcmdresp.cmd);
+ pr_info("boot cmd response cmd_tag error (%d)\n",
+ bootcmdresp.cmd);
} else if (bootcmdresp.result != BOOT_CMD_RESP_OK) {
- lbs_pr_info("boot cmd response result error (%d)\n",
- bootcmdresp.result);
+ pr_info("boot cmd response result error (%d)\n",
+ bootcmdresp.result);
} else {
cardp->bootcmdresp = 1;
lbs_deb_usbd(&cardp->udev->dev,
@@ -612,16 +564,14 @@ static void if_usb_receive_fwload(struct urb *urb)
return;
}
- syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC);
+ syncfwheader = kmemdup(skb->data + IPFIELD_ALIGN_OFFSET,
+ sizeof(struct fwsyncheader), GFP_ATOMIC);
if (!syncfwheader) {
lbs_deb_usbd(&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_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n");
lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n",
@@ -648,8 +598,6 @@ static void if_usb_receive_fwload(struct urb *urb)
if_usb_submit_rx_urb_fwload(cardp);
kfree(syncfwheader);
-
- return;
}
#define MRVDRV_MIN_PKT_LEN 30
@@ -686,8 +634,7 @@ static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff,
return;
}
- if (!in_interrupt())
- BUG();
+ BUG_ON(!in_interrupt());
spin_lock(&priv->driver_lock);
@@ -706,11 +653,11 @@ static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff,
}
/**
- * @brief This function reads of the packet into the upload buff,
- * wake up the main thread and initialise the Rx callack.
+ * if_usb_receive - read the packet into the upload buffer,
+ * wake up the main thread and initialise the Rx callack
*
- * @param urb pointer to struct urb
- * @return N/A
+ * @urb: pointer to &struct urb
+ * returns: N/A
*/
static void if_usb_receive(struct urb *urb)
{
@@ -781,12 +728,12 @@ rx_exit:
}
/**
- * @brief This function downloads data to FW
- * @param priv pointer to struct lbs_private structure
- * @param type type of data
- * @param buf pointer to data buffer
- * @param len number of bytes
- * @return 0 or -1
+ * if_usb_host_to_card - downloads data to FW
+ * @priv: pointer to &struct lbs_private structure
+ * @type: type of data
+ * @payload: pointer to data buffer
+ * @nb: number of bytes
+ * returns: 0 for success or negative error code
*/
static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type,
uint8_t *payload, uint16_t nb)
@@ -810,10 +757,11 @@ static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type,
}
/**
- * @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
+ * if_usb_issue_boot_command - issues Boot command to the Boot2 code
+ * @cardp: pointer to &if_usb_card
+ * @ivalue: 1:Boot from FW by USB-Download
+ * 2:Boot from FW in EEPROM
+ * returns: 0 for success or negative error code
*/
static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue)
{
@@ -832,11 +780,11 @@ static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue)
/**
- * @brief This function checks the validity of Boot2/FW image.
+ * check_fwfile_format - check the validity of Boot2/FW image
*
- * @param data pointer to image
- * len image length
- * @return 0 or -1
+ * @data: pointer to image
+ * @totlen: image length
+ * returns: 0 (good) or 1 (failure)
*/
static int check_fwfile_format(const uint8_t *data, uint32_t totlen)
{
@@ -871,79 +819,32 @@ static int check_fwfile_format(const uint8_t *data, uint32_t totlen)
} while (!exit);
if (ret)
- lbs_pr_err("firmware file format check FAIL\n");
+ pr_err("firmware file format check FAIL\n");
else
lbs_deb_fw("firmware file format check PASS\n");
return ret;
}
-
-/**
-* @brief This function programs the firmware subject to cmd
-*
-* @param cardp the if_usb_card descriptor
-* fwname firmware or boot2 image file name
-* cmd either BOOT_CMD_FW_BY_USB, BOOT_CMD_UPDATE_FW,
-* or BOOT_CMD_UPDATE_BOOT2.
-* @return 0 or error code
-*/
-static int if_usb_prog_firmware(struct if_usb_card *cardp,
- const char *fwname, int cmd)
-{
- struct lbs_private *priv = cardp->priv;
- unsigned long flags, caps;
- int ret;
-
- caps = priv->fwcapinfo;
- if (((cmd == BOOT_CMD_UPDATE_FW) && !(caps & FW_CAPINFO_FIRMWARE_UPGRADE)) ||
- ((cmd == BOOT_CMD_UPDATE_BOOT2) && !(caps & FW_CAPINFO_BOOT2_UPGRADE)))
- return -EOPNOTSUPP;
-
- /* Ensure main thread is idle. */
- spin_lock_irqsave(&priv->driver_lock, flags);
- while (priv->cur_cmd != NULL || priv->dnld_sent != DNLD_RES_RECEIVED) {
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- if (wait_event_interruptible(priv->waitq,
- (priv->cur_cmd == NULL &&
- priv->dnld_sent == DNLD_RES_RECEIVED))) {
- return -ERESTARTSYS;
- }
- spin_lock_irqsave(&priv->driver_lock, flags);
- }
- priv->dnld_sent = DNLD_BOOTCMD_SENT;
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
- ret = __if_usb_prog_firmware(cardp, fwname, cmd);
-
- spin_lock_irqsave(&priv->driver_lock, flags);
- priv->dnld_sent = DNLD_RES_RECEIVED;
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
- wake_up_interruptible(&priv->waitq);
-
- return ret;
-}
-
-static int __if_usb_prog_firmware(struct if_usb_card *cardp,
- const char *fwname, int cmd)
+static void if_usb_prog_firmware(struct lbs_private *priv, int ret,
+ const struct firmware *fw,
+ const struct firmware *unused)
{
+ struct if_usb_card *cardp = priv->card;
int i = 0;
static int reset_count = 10;
- int ret = 0;
lbs_deb_enter(LBS_DEB_USB);
- ret = request_firmware(&cardp->fw, fwname, &cardp->udev->dev);
- if (ret < 0) {
- lbs_pr_err("request_firmware() failed with %#x\n", ret);
- lbs_pr_err("firmware %s not found\n", fwname);
+ if (ret) {
+ pr_err("failed to find firmware (%d)\n", ret);
goto done;
}
+ cardp->fw = fw;
if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) {
ret = -EINVAL;
- goto release_fw;
+ goto done;
}
/* Cancel any pending usb business */
@@ -960,14 +861,14 @@ restart:
if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
ret = -EIO;
- goto release_fw;
+ goto done;
}
cardp->bootcmdresp = 0;
do {
int j = 0;
i++;
- if_usb_issue_boot_command(cardp, cmd);
+ if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB);
/* wait for command response */
do {
j++;
@@ -982,14 +883,14 @@ restart:
usb_kill_urb(cardp->tx_urb);
if (if_usb_submit_rx_urb(cardp) < 0)
ret = -EIO;
- goto release_fw;
+ goto done;
} else if (cardp->bootcmdresp <= 0) {
if (--reset_count >= 0) {
if_usb_reset_device(cardp);
goto restart;
}
ret = -EIO;
- goto release_fw;
+ goto done;
}
i = 0;
@@ -1012,24 +913,35 @@ restart:
usb_kill_urb(cardp->rx_urb);
if (!cardp->fwdnldover) {
- lbs_pr_info("failed to load fw, resetting device!\n");
+ pr_info("failed to load fw, resetting device!\n");
if (--reset_count >= 0) {
if_usb_reset_device(cardp);
goto restart;
}
- lbs_pr_info("FW download failure, time = %d ms\n", i * 100);
+ pr_info("FW download failure, time = %d ms\n", i * 100);
ret = -EIO;
- goto release_fw;
+ goto done;
}
- release_fw:
- release_firmware(cardp->fw);
- cardp->fw = NULL;
+ cardp->priv->fw_ready = 1;
+ if_usb_submit_rx_urb(cardp);
+
+ if (lbs_start_card(priv))
+ goto done;
+
+ if_usb_setup_firmware(priv);
+
+ /*
+ * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware.
+ */
+ priv->wol_criteria = EHS_REMOVE_WAKEUP;
+ if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL))
+ priv->ehs_remove_supported = false;
done:
- lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret);
- return ret;
+ cardp->fw = NULL;
+ lbs_deb_leave(LBS_DEB_USB);
}
@@ -1042,8 +954,19 @@ static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
lbs_deb_enter(LBS_DEB_USB);
- if (priv->psstate != PS_STATE_FULL_POWER)
- return -1;
+ if (priv->psstate != PS_STATE_FULL_POWER) {
+ ret = -1;
+ goto out;
+ }
+
+#ifdef CONFIG_OLPC
+ if (machine_is_olpc()) {
+ if (priv->wol_criteria == EHS_REMOVE_WAKEUP)
+ olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN);
+ else
+ olpc_ec_wakeup_set(EC_SCI_SRC_WLAN);
+ }
+#endif
ret = lbs_suspend(priv);
if (ret)
@@ -1085,31 +1008,10 @@ static struct usb_driver if_usb_driver = {
.suspend = if_usb_suspend,
.resume = if_usb_resume,
.reset_resume = if_usb_resume,
+ .disable_hub_initiated_lpm = 1,
};
-static int __init if_usb_init_module(void)
-{
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_MAIN);
-
- ret = usb_register(&if_usb_driver);
-
- lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
- return ret;
-}
-
-static void __exit if_usb_exit_module(void)
-{
- lbs_deb_enter(LBS_DEB_MAIN);
-
- usb_deregister(&if_usb_driver);
-
- lbs_deb_leave(LBS_DEB_MAIN);
-}
-
-module_init(if_usb_init_module);
-module_exit(if_usb_exit_module);
+module_usb_driver(if_usb_driver);
MODULE_DESCRIPTION("8388 USB WLAN Driver");
MODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc.");
diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h
index 5ba0aee0eb2..6e42eac331d 100644
--- a/drivers/net/wireless/libertas/if_usb.h
+++ b/drivers/net/wireless/libertas/if_usb.h
@@ -6,9 +6,9 @@
struct lbs_private;
-/**
- * This file contains definition for USB interface.
- */
+/*
+ * This file contains definition for USB interface.
+ */
#define CMD_TYPE_REQUEST 0xF00DFACE
#define CMD_TYPE_DATA 0xBEADC0DE
#define CMD_TYPE_INDICATION 0xBEEFFACE
@@ -40,9 +40,10 @@ struct bootcmdresp
uint8_t pad[2];
};
-/** USB card description structure*/
+/* USB card description structure*/
struct if_usb_card {
struct usb_device *udev;
+ uint32_t model; /* MODEL_* */
struct urb *rx_urb, *tx_urb;
struct lbs_private *priv;
@@ -76,7 +77,7 @@ struct if_usb_card {
__le16 boot2_version;
};
-/** fwheader */
+/* fwheader */
struct fwheader {
__le32 dnldcmd;
__le32 baseaddr;
@@ -85,14 +86,14 @@ struct fwheader {
};
#define FW_MAX_DATA_BLK_SIZE 600
-/** FWData */
+/* FWData */
struct fwdata {
struct fwheader hdr;
__le32 seqnum;
uint8_t data[0];
};
-/** fwsyncheader */
+/* fwsyncheader */
struct fwsyncheader {
__le32 cmd;
__le32 seqnum;
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 8ae935ac32f..0c02f0483d1 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -1,28 +1,29 @@
-/**
- * This file contains the major functions in WLAN
- * driver. It includes init, exit, open, close and main
- * thread etc..
- */
+/*
+ * This file contains the major functions in WLAN
+ * driver. It includes init, exit, open, close and main
+ * thread etc..
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
+#include <linux/hardirq.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/kthread.h>
#include <linux/kfifo.h>
-#include <linux/stddef.h>
-#include <linux/ieee80211.h>
-#include <net/iw_handler.h>
+#include <linux/slab.h>
+#include <net/cfg80211.h>
#include "host.h"
#include "decl.h"
#include "dev.h"
-#include "wext.h"
+#include "cfg.h"
#include "debugfs.h"
-#include "scan.h"
-#include "assoc.h"
#include "cmd.h"
+#include "mesh.h"
#define DRIVER_RELEASE_VERSION "323.p0"
const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION
@@ -37,139 +38,25 @@ unsigned int lbs_debug;
EXPORT_SYMBOL_GPL(lbs_debug);
module_param_named(libertas_debug, lbs_debug, int, 0644);
+unsigned int lbs_disablemesh;
+EXPORT_SYMBOL_GPL(lbs_disablemesh);
+module_param_named(libertas_disablemesh, lbs_disablemesh, int, 0644);
-/* This global structure is used to send the confirm_sleep command as
- * fast as possible down to the firmware. */
-struct cmd_confirm_sleep confirm_sleep;
-
-
-#define LBS_TX_PWR_DEFAULT 20 /*100mW */
-#define LBS_TX_PWR_US_DEFAULT 20 /*100mW */
-#define LBS_TX_PWR_JP_DEFAULT 16 /*50mW */
-#define LBS_TX_PWR_FR_DEFAULT 20 /*100mW */
-#define LBS_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, LBS_TX_PWR_US_DEFAULT},
- {2, 2417, LBS_TX_PWR_US_DEFAULT},
- {3, 2422, LBS_TX_PWR_US_DEFAULT},
- {4, 2427, LBS_TX_PWR_US_DEFAULT},
- {5, 2432, LBS_TX_PWR_US_DEFAULT},
- {6, 2437, LBS_TX_PWR_US_DEFAULT},
- {7, 2442, LBS_TX_PWR_US_DEFAULT},
- {8, 2447, LBS_TX_PWR_US_DEFAULT},
- {9, 2452, LBS_TX_PWR_US_DEFAULT},
- {10, 2457, LBS_TX_PWR_US_DEFAULT},
- {11, 2462, LBS_TX_PWR_US_DEFAULT}
-};
-
-/* band: 'B/G', region: Europe ETSI */
-static struct chan_freq_power channel_freq_power_EU_BG[] = {
- {1, 2412, LBS_TX_PWR_EMEA_DEFAULT},
- {2, 2417, LBS_TX_PWR_EMEA_DEFAULT},
- {3, 2422, LBS_TX_PWR_EMEA_DEFAULT},
- {4, 2427, LBS_TX_PWR_EMEA_DEFAULT},
- {5, 2432, LBS_TX_PWR_EMEA_DEFAULT},
- {6, 2437, LBS_TX_PWR_EMEA_DEFAULT},
- {7, 2442, LBS_TX_PWR_EMEA_DEFAULT},
- {8, 2447, LBS_TX_PWR_EMEA_DEFAULT},
- {9, 2452, LBS_TX_PWR_EMEA_DEFAULT},
- {10, 2457, LBS_TX_PWR_EMEA_DEFAULT},
- {11, 2462, LBS_TX_PWR_EMEA_DEFAULT},
- {12, 2467, LBS_TX_PWR_EMEA_DEFAULT},
- {13, 2472, LBS_TX_PWR_EMEA_DEFAULT}
-};
-
-/* band: 'B/G', region: Spain */
-static struct chan_freq_power channel_freq_power_SPN_BG[] = {
- {10, 2457, LBS_TX_PWR_DEFAULT},
- {11, 2462, LBS_TX_PWR_DEFAULT}
-};
-
-/* band: 'B/G', region: France */
-static struct chan_freq_power channel_freq_power_FR_BG[] = {
- {10, 2457, LBS_TX_PWR_FR_DEFAULT},
- {11, 2462, LBS_TX_PWR_FR_DEFAULT},
- {12, 2467, LBS_TX_PWR_FR_DEFAULT},
- {13, 2472, LBS_TX_PWR_FR_DEFAULT}
-};
-
-/* band: 'B/G', region: Japan */
-static struct chan_freq_power channel_freq_power_JPN_BG[] = {
- {1, 2412, LBS_TX_PWR_JP_DEFAULT},
- {2, 2417, LBS_TX_PWR_JP_DEFAULT},
- {3, 2422, LBS_TX_PWR_JP_DEFAULT},
- {4, 2427, LBS_TX_PWR_JP_DEFAULT},
- {5, 2432, LBS_TX_PWR_JP_DEFAULT},
- {6, 2437, LBS_TX_PWR_JP_DEFAULT},
- {7, 2442, LBS_TX_PWR_JP_DEFAULT},
- {8, 2447, LBS_TX_PWR_JP_DEFAULT},
- {9, 2452, LBS_TX_PWR_JP_DEFAULT},
- {10, 2457, LBS_TX_PWR_JP_DEFAULT},
- {11, 2462, LBS_TX_PWR_JP_DEFAULT},
- {12, 2467, LBS_TX_PWR_JP_DEFAULT},
- {13, 2472, LBS_TX_PWR_JP_DEFAULT},
- {14, 2484, LBS_TX_PWR_JP_DEFAULT}
-};
-/**
- * the structure for channel, frequency and power
+/*
+ * This global structure is used to send the confirm_sleep command as
+ * fast as possible down to the firmware.
*/
-struct region_cfp_table {
- u8 region;
- struct chan_freq_power *cfp_BG;
- int cfp_no_BG;
-};
+struct cmd_confirm_sleep confirm_sleep;
-/**
- * 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,
- ARRAY_SIZE(channel_freq_power_US_BG),
- }
- ,
- {0x20, /*CANADA IC */
- channel_freq_power_US_BG,
- ARRAY_SIZE(channel_freq_power_US_BG),
- }
- ,
- {0x30, /*EU*/ channel_freq_power_EU_BG,
- ARRAY_SIZE(channel_freq_power_EU_BG),
- }
- ,
- {0x31, /*SPAIN*/ channel_freq_power_SPN_BG,
- ARRAY_SIZE(channel_freq_power_SPN_BG),
- }
- ,
- {0x32, /*FRANCE*/ channel_freq_power_FR_BG,
- ARRAY_SIZE(channel_freq_power_FR_BG),
- }
- ,
- {0x40, /*JAPAN*/ channel_freq_power_JPN_BG,
- ARRAY_SIZE(channel_freq_power_JPN_BG),
- }
- ,
-/*Add new region here */
-};
-/**
+/*
* the table to keep region code
*/
u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] =
{ 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 };
-/**
- * 802.11b/g supported bitrates (in 500Kb/s units)
- */
-u8 lbs_bg_rates[MAX_RATES] =
- { 0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
-0x00, 0x00 };
-
-/**
+/*
* FW rate table. FW refers to rates by their index in this table, not by the
* rate value itself. Values of 0x00 are
* reserved positions.
@@ -180,10 +67,10 @@ static u8 fw_data_rates[MAX_RATES] =
};
/**
- * @brief use index to get the data rate
+ * lbs_fw_index_to_data_rate - use index to get the data rate
*
- * @param idx The index of data rate
- * @return data rate or 0
+ * @idx: The index of data rate
+ * returns: data rate or 0
*/
u32 lbs_fw_index_to_data_rate(u8 idx)
{
@@ -193,10 +80,10 @@ u32 lbs_fw_index_to_data_rate(u8 idx)
}
/**
- * @brief use rate to get the index
+ * lbs_data_rate_to_fw_index - use rate to get the index
*
- * @param rate data rate
- * @return index or 0
+ * @rate: data rate
+ * returns: index or 0
*/
u8 lbs_data_rate_to_fw_index(u32 rate)
{
@@ -212,243 +99,81 @@ u8 lbs_data_rate_to_fw_index(u32 rate)
return 0;
}
-/**
- * Attributes exported through sysfs
- */
-
-/**
- * @brief Get function for sysfs attribute anycast_mask
- */
-static ssize_t lbs_anycast_get(struct device *dev,
- struct device_attribute *attr, char * buf)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_access mesh_access;
- int ret;
-
- memset(&mesh_access, 0, sizeof(mesh_access));
-
- ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);
- if (ret)
- return ret;
-
- return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
-}
-
-/**
- * @brief Set function for sysfs attribute anycast_mask
- */
-static ssize_t lbs_anycast_set(struct device *dev,
- struct device_attribute *attr, const char * buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_access mesh_access;
- uint32_t datum;
- int ret;
-
- memset(&mesh_access, 0, sizeof(mesh_access));
- sscanf(buf, "%x", &datum);
- mesh_access.data[0] = cpu_to_le32(datum);
-
- ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute prb_rsp_limit
- */
-static ssize_t lbs_prb_rsp_limit_get(struct device *dev,
- struct device_attribute *attr, char *buf)
+int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type)
{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_access mesh_access;
- int ret;
- u32 retry_limit;
-
- memset(&mesh_access, 0, sizeof(mesh_access));
- mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET);
-
- ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
- &mesh_access);
- if (ret)
- return ret;
+ int ret = 0;
- retry_limit = le32_to_cpu(mesh_access.data[1]);
- return snprintf(buf, 10, "%d\n", retry_limit);
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ ret = lbs_set_monitor_mode(priv, 1);
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
+ ret = lbs_set_monitor_mode(priv, 0);
+ if (!ret)
+ ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
+ ret = lbs_set_monitor_mode(priv, 0);
+ if (!ret)
+ ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+ return ret;
}
-/**
- * @brief Set function for sysfs attribute prb_rsp_limit
- */
-static ssize_t lbs_prb_rsp_limit_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+int lbs_start_iface(struct lbs_private *priv)
{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_access mesh_access;
+ struct cmd_ds_802_11_mac_address cmd;
int ret;
- unsigned long retry_limit;
-
- memset(&mesh_access, 0, sizeof(mesh_access));
- mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);
-
- if (!strict_strtoul(buf, 10, &retry_limit))
- return -ENOTSUPP;
- if (retry_limit > 15)
- return -ENOTSUPP;
-
- mesh_access.data[1] = cpu_to_le32(retry_limit);
-
- ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
- &mesh_access);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-static int lbs_add_rtap(struct lbs_private *priv);
-static void lbs_remove_rtap(struct lbs_private *priv);
-static int lbs_add_mesh(struct lbs_private *priv);
-static void lbs_remove_mesh(struct lbs_private *priv);
-
-
-/**
- * Get function for sysfs attribute rtap
- */
-static ssize_t lbs_rtap_get(struct device *dev,
- struct device_attribute *attr, char * buf)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- return snprintf(buf, 5, "0x%X\n", priv->monitormode);
-}
+ if (priv->power_restore) {
+ ret = priv->power_restore(priv);
+ if (ret)
+ return ret;
+ }
-/**
- * Set function for sysfs attribute rtap
- */
-static ssize_t lbs_rtap_set(struct device *dev,
- struct device_attribute *attr, const char * buf, size_t count)
-{
- int monitor_mode;
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
-
- sscanf(buf, "%x", &monitor_mode);
- if (monitor_mode) {
- if (priv->monitormode == monitor_mode)
- return strlen(buf);
- if (!priv->monitormode) {
- if (priv->infra_open || priv->mesh_open)
- return -EBUSY;
- if (priv->mode == IW_MODE_INFRA)
- lbs_cmd_80211_deauthenticate(priv,
- priv->curbssparams.bssid,
- WLAN_REASON_DEAUTH_LEAVING);
- else if (priv->mode == IW_MODE_ADHOC)
- lbs_adhoc_stop(priv);
- lbs_add_rtap(priv);
- }
- priv->monitormode = monitor_mode;
- } else {
- if (!priv->monitormode)
- return strlen(buf);
- priv->monitormode = 0;
- lbs_remove_rtap(priv);
-
- if (priv->currenttxskb) {
- dev_kfree_skb_any(priv->currenttxskb);
- priv->currenttxskb = NULL;
- }
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+ memcpy(cmd.macadd, priv->current_addr, ETH_ALEN);
- /* Wake queues, command thread, etc. */
- lbs_host_to_card_done(priv);
+ ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd);
+ if (ret) {
+ lbs_deb_net("set MAC address failed\n");
+ goto err;
}
- lbs_prepare_and_send_command(priv,
- CMD_802_11_MONITOR_MODE, CMD_ACT_SET,
- CMD_OPTION_WAITFORRSP, 0, &priv->monitormode);
- return strlen(buf);
-}
-
-/**
- * lbs_rtap attribute to be exported per ethX interface
- * through sysfs (/sys/class/net/ethX/lbs_rtap)
- */
-static DEVICE_ATTR(lbs_rtap, 0644, lbs_rtap_get, lbs_rtap_set );
+ ret = lbs_set_iface_type(priv, priv->wdev->iftype);
+ if (ret) {
+ lbs_deb_net("set iface type failed\n");
+ goto err;
+ }
-/**
- * Get function for sysfs attribute mesh
- */
-static ssize_t lbs_mesh_get(struct device *dev,
- struct device_attribute *attr, char * buf)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev);
-}
+ ret = lbs_set_11d_domain_info(priv);
+ if (ret) {
+ lbs_deb_net("set 11d domain info failed\n");
+ goto err;
+ }
-/**
- * Set function for sysfs attribute mesh
- */
-static ssize_t lbs_mesh_set(struct device *dev,
- struct device_attribute *attr, const char * buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- int enable;
- int ret, action = CMD_ACT_MESH_CONFIG_STOP;
-
- sscanf(buf, "%x", &enable);
- enable = !!enable;
- if (enable == !!priv->mesh_dev)
- return count;
- if (enable)
- action = CMD_ACT_MESH_CONFIG_START;
- ret = lbs_mesh_config(priv, action, priv->curbssparams.channel);
- if (ret)
- return ret;
+ lbs_update_channel(priv);
- if (enable)
- lbs_add_mesh(priv);
- else
- lbs_remove_mesh(priv);
+ priv->iface_running = true;
+ return 0;
- return count;
+err:
+ if (priv->power_save)
+ priv->power_save(priv);
+ return ret;
}
/**
- * lbs_mesh attribute to be exported per ethX interface
- * through sysfs (/sys/class/net/ethX/lbs_mesh)
- */
-static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set);
-
-/**
- * anycast_mask attribute to be exported per mshX interface
- * through sysfs (/sys/class/net/mshX/anycast_mask)
- */
-static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set);
-
-/**
- * prb_rsp_limit attribute to be exported per mshX interface
- * through sysfs (/sys/class/net/mshX/prb_rsp_limit)
- */
-static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get,
- lbs_prb_rsp_limit_set);
-
-static struct attribute *lbs_mesh_sysfs_entries[] = {
- &dev_attr_anycast_mask.attr,
- &dev_attr_prb_rsp_limit.attr,
- NULL,
-};
-
-static struct attribute_group lbs_mesh_attr_group = {
- .attrs = lbs_mesh_sysfs_entries,
-};
-
-/**
- * @brief This function opens the ethX or mshX interface
+ * lbs_dev_open - open the ethX interface
*
- * @param dev A pointer to net_device structure
- * @return 0 or -EBUSY if monitor mode active
+ * @dev: A pointer to &net_device structure
+ * returns: 0 or -EBUSY if monitor mode active
*/
static int lbs_dev_open(struct net_device *dev)
{
@@ -456,68 +181,70 @@ static int lbs_dev_open(struct net_device *dev)
int ret = 0;
lbs_deb_enter(LBS_DEB_NET);
-
- spin_lock_irq(&priv->driver_lock);
-
- if (priv->monitormode) {
- ret = -EBUSY;
- goto out;
+ if (!priv->iface_running) {
+ ret = lbs_start_iface(priv);
+ if (ret)
+ goto out;
}
- if (dev == priv->mesh_dev) {
- priv->mesh_open = 1;
- priv->mesh_connect_status = LBS_CONNECTED;
- netif_carrier_on(dev);
- } else {
- priv->infra_open = 1;
+ spin_lock_irq(&priv->driver_lock);
- if (priv->connect_status == LBS_CONNECTED)
- netif_carrier_on(dev);
- else
- netif_carrier_off(dev);
- }
+ netif_carrier_off(dev);
if (!priv->tx_pending_len)
netif_wake_queue(dev);
- out:
spin_unlock_irq(&priv->driver_lock);
+
+out:
lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
return ret;
}
-/**
- * @brief This function closes the mshX interface
- *
- * @param dev A pointer to net_device structure
- * @return 0
- */
-static int lbs_mesh_stop(struct net_device *dev)
+static bool lbs_command_queue_empty(struct lbs_private *priv)
{
- struct lbs_private *priv = dev->ml_priv;
+ unsigned long flags;
+ bool ret;
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ return ret;
+}
- lbs_deb_enter(LBS_DEB_MESH);
- spin_lock_irq(&priv->driver_lock);
+int lbs_stop_iface(struct lbs_private *priv)
+{
+ unsigned long flags;
+ int ret = 0;
- priv->mesh_open = 0;
- priv->mesh_connect_status = LBS_DISCONNECTED;
+ lbs_deb_enter(LBS_DEB_MAIN);
- netif_stop_queue(dev);
- netif_carrier_off(dev);
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ priv->iface_running = false;
+ kfree_skb(priv->currenttxskb);
+ priv->currenttxskb = NULL;
+ priv->tx_pending_len = 0;
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
- spin_unlock_irq(&priv->driver_lock);
+ cancel_work_sync(&priv->mcast_work);
+ del_timer_sync(&priv->tx_lockup_timer);
- schedule_work(&priv->mcast_work);
+ /* Disable command processing, and wait for all commands to complete */
+ lbs_deb_main("waiting for commands to complete\n");
+ wait_event(priv->waitq, lbs_command_queue_empty(priv));
+ lbs_deb_main("all commands completed\n");
- lbs_deb_leave(LBS_DEB_MESH);
- return 0;
+ if (priv->power_save)
+ ret = priv->power_save(priv);
+
+ lbs_deb_leave(LBS_DEB_MAIN);
+ return ret;
}
/**
- * @brief This function closes the ethX interface
+ * lbs_eth_stop - close the ethX interface
*
- * @param dev A pointer to net_device structure
- * @return 0
+ * @dev: A pointer to &net_device structure
+ * returns: 0
*/
static int lbs_eth_stop(struct net_device *dev)
{
@@ -525,42 +252,25 @@ static int lbs_eth_stop(struct net_device *dev)
lbs_deb_enter(LBS_DEB_NET);
+ if (priv->connect_status == LBS_CONNECTED)
+ lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING);
+
spin_lock_irq(&priv->driver_lock);
- priv->infra_open = 0;
netif_stop_queue(dev);
spin_unlock_irq(&priv->driver_lock);
- schedule_work(&priv->mcast_work);
-
- lbs_deb_leave(LBS_DEB_NET);
- return 0;
-}
-
-static void lbs_tx_timeout(struct net_device *dev)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_TX);
-
- lbs_pr_err("tx watch dog timeout\n");
-
- dev->trans_start = jiffies;
-
- if (priv->currenttxskb)
- lbs_send_tx_feedback(priv, 0);
+ lbs_update_mcast(priv);
+ cancel_delayed_work_sync(&priv->scan_work);
+ if (priv->scan_req)
+ lbs_scan_done(priv);
- /* XX: Shouldn't we also call into the hw-specific driver
- to kick it somehow? */
- lbs_host_to_card_done(priv);
+ netif_carrier_off(priv->dev);
- /* More often than not, this actually happens because the
- firmware has crapped itself -- rather than just a very
- busy medium. So send a harmless command, and if/when
- _that_ times out, we'll kick it in the head. */
- lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
- 0, 0, NULL);
+ if (!lbs_iface_active(priv))
+ lbs_stop_iface(priv);
- lbs_deb_leave(LBS_DEB_TX);
+ lbs_deb_leave(LBS_DEB_NET);
+ return 0;
}
void lbs_host_to_card_done(struct lbs_private *priv)
@@ -570,46 +280,44 @@ void lbs_host_to_card_done(struct lbs_private *priv)
lbs_deb_enter(LBS_DEB_THREAD);
spin_lock_irqsave(&priv->driver_lock, flags);
+ del_timer(&priv->tx_lockup_timer);
priv->dnld_sent = DNLD_RES_RECEIVED;
/* Wake main thread if commands are pending */
- if (!priv->cur_cmd || priv->tx_pending_len > 0)
- wake_up_interruptible(&priv->waitq);
+ if (!priv->cur_cmd || priv->tx_pending_len > 0) {
+ if (!priv->wakeup_dev_required)
+ wake_up(&priv->waitq);
+ }
spin_unlock_irqrestore(&priv->driver_lock, flags);
lbs_deb_leave(LBS_DEB_THREAD);
}
EXPORT_SYMBOL_GPL(lbs_host_to_card_done);
-static int lbs_set_mac_address(struct net_device *dev, void *addr)
+int lbs_set_mac_address(struct net_device *dev, void *addr)
{
int ret = 0;
struct lbs_private *priv = dev->ml_priv;
struct sockaddr *phwaddr = addr;
- struct cmd_ds_802_11_mac_address cmd;
lbs_deb_enter(LBS_DEB_NET);
+ /*
+ * Can only set MAC address when all interfaces are down, to be written
+ * to the hardware when one of them is brought up.
+ */
+ if (lbs_iface_active(priv))
+ return -EBUSY;
+
/* In case it was called from the mesh device */
dev = priv->dev;
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
- memcpy(cmd.macadd, phwaddr->sa_data, ETH_ALEN);
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd);
- if (ret) {
- lbs_deb_net("set MAC address failed\n");
- goto done;
- }
-
memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN);
memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN);
if (priv->mesh_dev)
memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN);
-done:
lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
return ret;
}
@@ -632,45 +340,49 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd,
struct net_device *dev, int nr_addrs)
{
int i = nr_addrs;
- struct dev_mc_list *mc_list;
+ struct netdev_hw_addr *ha;
+ int cnt;
if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST))
return nr_addrs;
netif_addr_lock_bh(dev);
- for (mc_list = dev->mc_list; mc_list; mc_list = mc_list->next) {
- if (mac_in_list(cmd->maclist, nr_addrs, mc_list->dmi_addr)) {
+ cnt = netdev_mc_count(dev);
+ netdev_for_each_mc_addr(ha, dev) {
+ if (mac_in_list(cmd->maclist, nr_addrs, ha->addr)) {
lbs_deb_net("mcast address %s:%pM skipped\n", dev->name,
- mc_list->dmi_addr);
+ ha->addr);
+ cnt--;
continue;
}
if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE)
break;
- memcpy(&cmd->maclist[6*i], mc_list->dmi_addr, ETH_ALEN);
+ memcpy(&cmd->maclist[6*i], ha->addr, ETH_ALEN);
lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name,
- mc_list->dmi_addr);
+ ha->addr);
i++;
+ cnt--;
}
netif_addr_unlock_bh(dev);
- if (mc_list)
+ if (cnt)
return -EOVERFLOW;
return i;
}
-static void lbs_set_mcast_worker(struct work_struct *work)
+void lbs_update_mcast(struct lbs_private *priv)
{
- struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work);
struct cmd_ds_mac_multicast_adr mcast_cmd;
- int dev_flags;
+ int dev_flags = 0;
int nr_addrs;
int old_mac_control = priv->mac_control;
lbs_deb_enter(LBS_DEB_NET);
- dev_flags = priv->dev->flags;
- if (priv->mesh_dev)
+ if (netif_running(priv->dev))
+ dev_flags |= priv->dev->flags;
+ if (priv->mesh_dev && netif_running(priv->mesh_dev))
dev_flags |= priv->mesh_dev->flags;
if (dev_flags & IFF_PROMISC) {
@@ -716,7 +428,13 @@ static void lbs_set_mcast_worker(struct work_struct *work)
lbs_deb_leave(LBS_DEB_NET);
}
-static void lbs_set_multicast_list(struct net_device *dev)
+static void lbs_set_mcast_worker(struct work_struct *work)
+{
+ struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work);
+ lbs_update_mcast(priv);
+}
+
+void lbs_set_multicast_list(struct net_device *dev)
{
struct lbs_private *priv = dev->ml_priv;
@@ -724,12 +442,12 @@ static void lbs_set_multicast_list(struct net_device *dev)
}
/**
- * @brief This function handles the major jobs in the LBS driver.
+ * lbs_thread - handles the major jobs in the LBS driver.
* It handles all events generated by firmware, RX data received
* from firmware and TX data sent from kernel.
*
- * @param data A pointer to lbs_thread structure
- * @return 0
+ * @data: A pointer to &lbs_thread structure
+ * returns: 0
*/
static int lbs_thread(void *data)
{
@@ -770,9 +488,10 @@ static int lbs_thread(void *data)
shouldsleep = 0; /* We have a command response */
else if (priv->cur_cmd)
shouldsleep = 1; /* Can't send a command; one already running */
- else if (!list_empty(&priv->cmdpendingq))
+ else if (!list_empty(&priv->cmdpendingq) &&
+ !(priv->wakeup_dev_required))
shouldsleep = 0; /* We have a command to send */
- else if (__kfifo_len(priv->event_fifo))
+ else if (kfifo_len(&priv->event_fifo))
shouldsleep = 0; /* We have an event to process */
else
shouldsleep = 1; /* No command */
@@ -822,46 +541,44 @@ static int lbs_thread(void *data)
}
spin_unlock_irq(&priv->driver_lock);
- /* command timeout stuff */
- if (priv->cmd_timed_out && priv->cur_cmd) {
- struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
-
- if (++priv->nr_retries > 3) {
- lbs_pr_info("Excessive timeouts submitting "
- "command 0x%04x\n",
- le16_to_cpu(cmdnode->cmdbuf->command));
- lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
- priv->nr_retries = 0;
- if (priv->reset_card)
- priv->reset_card(priv);
- } else {
- priv->cur_cmd = NULL;
- priv->dnld_sent = DNLD_RES_RECEIVED;
- lbs_pr_info("requeueing command 0x%04x due "
- "to timeout (#%d)\n",
- le16_to_cpu(cmdnode->cmdbuf->command),
- priv->nr_retries);
-
- /* Stick it back at the _top_ of the pending queue
- for immediate resubmission */
- list_add(&cmdnode->list, &priv->cmdpendingq);
- }
- }
- priv->cmd_timed_out = 0;
-
/* Process hardware events, e.g. card removed, link lost */
spin_lock_irq(&priv->driver_lock);
- while (__kfifo_len(priv->event_fifo)) {
+ while (kfifo_len(&priv->event_fifo)) {
u32 event;
- __kfifo_get(priv->event_fifo, (unsigned char *) &event,
- sizeof(event));
+ if (kfifo_out(&priv->event_fifo,
+ (unsigned char *) &event, sizeof(event)) !=
+ sizeof(event))
+ break;
spin_unlock_irq(&priv->driver_lock);
lbs_process_event(priv, event);
spin_lock_irq(&priv->driver_lock);
}
spin_unlock_irq(&priv->driver_lock);
+ if (priv->wakeup_dev_required) {
+ lbs_deb_thread("Waking up device...\n");
+ /* Wake up device */
+ if (priv->exit_deep_sleep(priv))
+ lbs_deb_thread("Wakeup device failed\n");
+ continue;
+ }
+
+ /* command timeout stuff */
+ if (priv->cmd_timed_out && priv->cur_cmd) {
+ struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
+
+ netdev_info(dev, "Timeout submitting command 0x%04x\n",
+ le16_to_cpu(cmdnode->cmdbuf->command));
+ lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
+
+ /* Reset card, but only when it isn't in the process
+ * of being shutdown anyway. */
+ if (!dev->dismantle && priv->reset_card)
+ priv->reset_card(priv);
+ }
+ priv->cmd_timed_out = 0;
+
if (!priv->fw_ready)
continue;
@@ -882,8 +599,8 @@ static int lbs_thread(void *data)
* after firmware fixes it
*/
priv->psstate = PS_STATE_AWAKE;
- lbs_pr_alert("ignore PS_SleepConfirm in "
- "non-connected state\n");
+ netdev_alert(dev,
+ "ignore PS_SleepConfirm in non-connected state\n");
}
}
@@ -894,16 +611,13 @@ static int lbs_thread(void *data)
(priv->psstate == PS_STATE_PRE_SLEEP))
continue;
+ if (priv->is_deep_sleep)
+ continue;
+
/* Execute the next command */
if (!priv->dnld_sent && !priv->cur_cmd)
lbs_execute_next_command(priv);
- /* Wake-up command waiters which can't sleep in
- * lbs_prepare_and_send_command
- */
- if (!list_empty(&priv->cmdpendingq))
- wake_up_all(&priv->cmd_pending);
-
spin_lock_irq(&priv->driver_lock);
if (!priv->dnld_sent && priv->tx_pending_len > 0) {
int ret = priv->hw_host_to_card(priv, MVMS_DAT,
@@ -912,6 +626,9 @@ static int lbs_thread(void *data)
if (ret) {
lbs_deb_tx("host_to_card failed %d\n", ret);
priv->dnld_sent = DNLD_RES_RECEIVED;
+ } else {
+ mod_timer(&priv->tx_lockup_timer,
+ jiffies + (HZ * 5));
}
priv->tx_pending_len = 0;
if (!priv->currenttxskb) {
@@ -920,7 +637,7 @@ static int lbs_thread(void *data)
if (priv->connect_status == LBS_CONNECTED)
netif_wake_queue(priv->dev);
if (priv->mesh_dev &&
- priv->mesh_connect_status == LBS_CONNECTED)
+ netif_running(priv->mesh_dev))
netif_wake_queue(priv->mesh_dev);
}
}
@@ -928,192 +645,268 @@ static int lbs_thread(void *data)
}
del_timer(&priv->command_timer);
- wake_up_all(&priv->cmd_pending);
+ del_timer(&priv->tx_lockup_timer);
+ del_timer(&priv->auto_deepsleep_timer);
lbs_deb_leave(LBS_DEB_THREAD);
return 0;
}
-static int lbs_suspend_callback(struct lbs_private *priv, unsigned long dummy,
- struct cmd_header *cmd)
+/**
+ * lbs_setup_firmware - gets the HW spec from the firmware and sets
+ * some basic parameters
+ *
+ * @priv: A pointer to &struct lbs_private structure
+ * returns: 0 or -1
+ */
+static int lbs_setup_firmware(struct lbs_private *priv)
{
+ int ret = -1;
+ s16 curlevel = 0, minlevel = 0, maxlevel = 0;
+
lbs_deb_enter(LBS_DEB_FW);
- netif_device_detach(priv->dev);
- if (priv->mesh_dev)
- netif_device_detach(priv->mesh_dev);
+ /* Read MAC address from firmware */
+ memset(priv->current_addr, 0xff, ETH_ALEN);
+ ret = lbs_update_hw_spec(priv);
+ if (ret)
+ goto done;
- priv->fw_ready = 0;
- lbs_deb_leave(LBS_DEB_FW);
- return 0;
+ /* Read power levels if available */
+ ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel);
+ if (ret == 0) {
+ priv->txpower_cur = curlevel;
+ priv->txpower_min = minlevel;
+ priv->txpower_max = maxlevel;
+ }
+
+ /* Send cmd to FW to enable 11D function */
+ ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1);
+ if (ret)
+ goto done;
+
+ ret = lbs_set_mac_control_sync(priv);
+done:
+ lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret);
+ return ret;
}
int lbs_suspend(struct lbs_private *priv)
{
- struct cmd_header cmd;
int ret;
lbs_deb_enter(LBS_DEB_FW);
- if (priv->wol_criteria == 0xffffffff) {
- lbs_pr_info("Suspend attempt without configuring wake params!\n");
- return -EINVAL;
+ if (priv->is_deep_sleep) {
+ ret = lbs_set_deep_sleep(priv, 0);
+ if (ret) {
+ netdev_err(priv->dev,
+ "deep sleep cancellation failed: %d\n", ret);
+ return ret;
+ }
+ priv->deep_sleep_required = 1;
}
- memset(&cmd, 0, sizeof(cmd));
+ ret = lbs_set_host_sleep(priv, 1);
- ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_ACTIVATE, &cmd,
- sizeof(cmd), lbs_suspend_callback, 0);
- if (ret)
- lbs_pr_info("HOST_SLEEP_ACTIVATE failed: %d\n", ret);
+ netif_device_detach(priv->dev);
+ if (priv->mesh_dev)
+ netif_device_detach(priv->mesh_dev);
lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret);
return ret;
}
EXPORT_SYMBOL_GPL(lbs_suspend);
-void lbs_resume(struct lbs_private *priv)
+int lbs_resume(struct lbs_private *priv)
{
- lbs_deb_enter(LBS_DEB_FW);
+ int ret;
- priv->fw_ready = 1;
+ lbs_deb_enter(LBS_DEB_FW);
- /* Firmware doesn't seem to give us RX packets any more
- until we send it some command. Might as well update */
- lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
- 0, 0, NULL);
+ ret = lbs_set_host_sleep(priv, 0);
netif_device_attach(priv->dev);
if (priv->mesh_dev)
netif_device_attach(priv->mesh_dev);
- lbs_deb_leave(LBS_DEB_FW);
+ if (priv->deep_sleep_required) {
+ priv->deep_sleep_required = 0;
+ ret = lbs_set_deep_sleep(priv, 1);
+ if (ret)
+ netdev_err(priv->dev,
+ "deep sleep activation failed: %d\n", ret);
+ }
+
+ if (priv->setup_fw_on_resume)
+ ret = lbs_setup_firmware(priv);
+
+ lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret);
+ return ret;
}
EXPORT_SYMBOL_GPL(lbs_resume);
/**
- * @brief This function gets the HW spec from the firmware and sets
- * some basic parameters.
+ * lbs_cmd_timeout_handler - handles the timeout of command sending.
+ * It will re-send the same command again.
*
- * @param priv A pointer to struct lbs_private structure
- * @return 0 or -1
+ * @data: &struct lbs_private pointer
*/
-static int lbs_setup_firmware(struct lbs_private *priv)
+static void lbs_cmd_timeout_handler(unsigned long data)
{
- int ret = -1;
- s16 curlevel = 0, minlevel = 0, maxlevel = 0;
+ struct lbs_private *priv = (struct lbs_private *)data;
+ unsigned long flags;
- lbs_deb_enter(LBS_DEB_FW);
+ lbs_deb_enter(LBS_DEB_CMD);
+ spin_lock_irqsave(&priv->driver_lock, flags);
- /* Read MAC address from firmware */
- memset(priv->current_addr, 0xff, ETH_ALEN);
- ret = lbs_update_hw_spec(priv);
- if (ret)
- goto done;
+ if (!priv->cur_cmd)
+ goto out;
- /* Read power levels if available */
- ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel);
- if (ret == 0) {
- priv->txpower_cur = curlevel;
- priv->txpower_min = minlevel;
- priv->txpower_max = maxlevel;
- }
+ netdev_info(priv->dev, "command 0x%04x timed out\n",
+ le16_to_cpu(priv->cur_cmd->cmdbuf->command));
- lbs_set_mac_control(priv);
-done:
- lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret);
- return ret;
+ priv->cmd_timed_out = 1;
+
+ /*
+ * If the device didn't even acknowledge the command, reset the state
+ * so that we don't block all future commands due to this one timeout.
+ */
+ if (priv->dnld_sent == DNLD_CMD_SENT)
+ priv->dnld_sent = DNLD_RES_RECEIVED;
+
+ wake_up(&priv->waitq);
+out:
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ lbs_deb_leave(LBS_DEB_CMD);
}
/**
- * This function handles the timeout of command sending.
- * It will re-send the same command again.
+ * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames
+ * to the hardware. This is known to frequently happen with SD8686 when
+ * waking up after a Wake-on-WLAN-triggered resume.
+ *
+ * @data: &struct lbs_private pointer
*/
-static void command_timer_fn(unsigned long data)
+static void lbs_tx_lockup_handler(unsigned long data)
{
struct lbs_private *priv = (struct lbs_private *)data;
unsigned long flags;
- lbs_deb_enter(LBS_DEB_CMD);
+ lbs_deb_enter(LBS_DEB_TX);
spin_lock_irqsave(&priv->driver_lock, flags);
- if (!priv->cur_cmd)
- goto out;
-
- lbs_pr_info("command 0x%04x timed out\n",
- le16_to_cpu(priv->cur_cmd->cmdbuf->command));
+ netdev_info(priv->dev, "TX lockup detected\n");
+ if (priv->reset_card)
+ priv->reset_card(priv);
- priv->cmd_timed_out = 1;
+ priv->dnld_sent = DNLD_RES_RECEIVED;
wake_up_interruptible(&priv->waitq);
-out:
+
spin_unlock_irqrestore(&priv->driver_lock, flags);
+ lbs_deb_leave(LBS_DEB_TX);
+}
+
+/**
+ * auto_deepsleep_timer_fn - put the device back to deep sleep mode when
+ * timer expires and no activity (command, event, data etc.) is detected.
+ * @data: &struct lbs_private pointer
+ * returns: N/A
+ */
+static void auto_deepsleep_timer_fn(unsigned long data)
+{
+ struct lbs_private *priv = (struct lbs_private *)data;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ if (priv->is_activity_detected) {
+ priv->is_activity_detected = 0;
+ } else {
+ if (priv->is_auto_deep_sleep_enabled &&
+ (!priv->wakeup_dev_required) &&
+ (priv->connect_status != LBS_CONNECTED)) {
+ struct cmd_header cmd;
+
+ lbs_deb_main("Entering auto deep sleep mode...\n");
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.size = cpu_to_le16(sizeof(cmd));
+ lbs_cmd_async(priv, CMD_802_11_DEEP_SLEEP, &cmd,
+ sizeof(cmd));
+ }
+ }
+ mod_timer(&priv->auto_deepsleep_timer , jiffies +
+ (priv->auto_deep_sleep_timeout * HZ)/1000);
lbs_deb_leave(LBS_DEB_CMD);
}
-static void lbs_sync_channel_worker(struct work_struct *work)
+int lbs_enter_auto_deep_sleep(struct lbs_private *priv)
{
- struct lbs_private *priv = container_of(work, struct lbs_private,
- sync_channel);
+ lbs_deb_enter(LBS_DEB_SDIO);
- lbs_deb_enter(LBS_DEB_MAIN);
- if (lbs_update_channel(priv))
- lbs_pr_info("Channel synchronization failed.");
- lbs_deb_leave(LBS_DEB_MAIN);
+ priv->is_auto_deep_sleep_enabled = 1;
+ if (priv->is_deep_sleep)
+ priv->wakeup_dev_required = 1;
+ mod_timer(&priv->auto_deepsleep_timer ,
+ jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000);
+
+ lbs_deb_leave(LBS_DEB_SDIO);
+ return 0;
}
+int lbs_exit_auto_deep_sleep(struct lbs_private *priv)
+{
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ priv->is_auto_deep_sleep_enabled = 0;
+ priv->auto_deep_sleep_timeout = 0;
+ del_timer(&priv->auto_deepsleep_timer);
+
+ lbs_deb_leave(LBS_DEB_SDIO);
+ return 0;
+}
static int lbs_init_adapter(struct lbs_private *priv)
{
- size_t bufsize;
- int i, ret = 0;
+ int ret;
lbs_deb_enter(LBS_DEB_MAIN);
- /* Allocate buffer to store the BSSID list */
- bufsize = MAX_NETWORK_COUNT * sizeof(struct bss_descriptor);
- priv->networks = kzalloc(bufsize, GFP_KERNEL);
- if (!priv->networks) {
- lbs_pr_err("Out of memory allocating beacons\n");
- ret = -1;
- goto out;
- }
-
- /* Initialize scan result lists */
- INIT_LIST_HEAD(&priv->network_free_list);
- INIT_LIST_HEAD(&priv->network_list);
- for (i = 0; i < MAX_NETWORK_COUNT; i++) {
- list_add_tail(&priv->networks[i].list,
- &priv->network_free_list);
- }
-
memset(priv->current_addr, 0xff, ETH_ALEN);
priv->connect_status = LBS_DISCONNECTED;
- priv->mesh_connect_status = LBS_DISCONNECTED;
- priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
- priv->mode = IW_MODE_INFRA;
- priv->curbssparams.channel = DEFAULT_AD_HOC_CHANNEL;
+ priv->channel = DEFAULT_AD_HOC_CHANNEL;
priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;
priv->radio_on = 1;
- priv->enablehwauto = 1;
- priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
priv->psmode = LBS802_11POWERMODECAM;
priv->psstate = PS_STATE_FULL_POWER;
-
+ priv->is_deep_sleep = 0;
+ priv->is_auto_deep_sleep_enabled = 0;
+ priv->deep_sleep_required = 0;
+ priv->wakeup_dev_required = 0;
+ init_waitqueue_head(&priv->ds_awake_q);
+ init_waitqueue_head(&priv->scan_q);
+ priv->authtype_auto = 1;
+ priv->is_host_sleep_configured = 0;
+ priv->is_host_sleep_activated = 0;
+ init_waitqueue_head(&priv->host_sleep_q);
+ init_waitqueue_head(&priv->fw_waitq);
mutex_init(&priv->lock);
- setup_timer(&priv->command_timer, command_timer_fn,
+ setup_timer(&priv->command_timer, lbs_cmd_timeout_handler,
(unsigned long)priv);
+ setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler,
+ (unsigned long)priv);
+ setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
+ (unsigned long)priv);
INIT_LIST_HEAD(&priv->cmdfreeq);
INIT_LIST_HEAD(&priv->cmdpendingq);
spin_lock_init(&priv->driver_lock);
- init_waitqueue_head(&priv->cmd_pending);
/* Allocate the command buffers */
if (lbs_allocate_cmd_buffer(priv)) {
- lbs_pr_err("Out of memory allocating command buffers\n");
+ pr_err("Out of memory allocating command buffers\n");
ret = -ENOMEM;
goto out;
}
@@ -1121,10 +914,9 @@ static int lbs_init_adapter(struct lbs_private *priv)
priv->resp_len[0] = priv->resp_len[1] = 0;
/* Create the event FIFO */
- priv->event_fifo = kfifo_alloc(sizeof(u32) * 16, GFP_KERNEL, NULL);
- if (IS_ERR(priv->event_fifo)) {
- lbs_pr_err("Out of memory allocating event FIFO buffer\n");
- ret = -ENOMEM;
+ ret = kfifo_alloc(&priv->event_fifo, sizeof(u32) * 16, GFP_KERNEL);
+ if (ret) {
+ pr_err("Out of memory allocating event FIFO buffer\n");
goto out;
}
@@ -1139,11 +931,10 @@ static void lbs_free_adapter(struct lbs_private *priv)
lbs_deb_enter(LBS_DEB_MAIN);
lbs_free_cmd_buffer(priv);
- if (priv->event_fifo)
- kfifo_free(priv->event_fifo);
+ kfifo_free(&priv->event_fifo);
del_timer(&priv->command_timer);
- kfree(priv->networks);
- priv->networks = NULL;
+ del_timer(&priv->tx_lockup_timer);
+ del_timer(&priv->auto_deepsleep_timer);
lbs_deb_leave(LBS_DEB_MAIN);
}
@@ -1153,83 +944,91 @@ static const struct net_device_ops lbs_netdev_ops = {
.ndo_stop = lbs_eth_stop,
.ndo_start_xmit = lbs_hard_start_xmit,
.ndo_set_mac_address = lbs_set_mac_address,
- .ndo_tx_timeout = lbs_tx_timeout,
- .ndo_set_multicast_list = lbs_set_multicast_list,
+ .ndo_set_rx_mode = lbs_set_multicast_list,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
};
/**
- * @brief This function adds the card. it will probe the
+ * lbs_add_card - adds the card. It will probe the
* card, allocate the lbs_priv and initialize the device.
*
- * @param card A pointer to card
- * @return A pointer to struct lbs_private structure
+ * @card: A pointer to card
+ * @dmdev: A pointer to &struct device
+ * returns: A pointer to &struct lbs_private structure
*/
struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
{
- struct net_device *dev = NULL;
+ struct net_device *dev;
+ struct wireless_dev *wdev;
struct lbs_private *priv = NULL;
lbs_deb_enter(LBS_DEB_MAIN);
/* Allocate an Ethernet device and register it */
- dev = alloc_etherdev(sizeof(struct lbs_private));
- if (!dev) {
- lbs_pr_err("init ethX device failed\n");
+ wdev = lbs_cfg_alloc(dmdev);
+ if (IS_ERR(wdev)) {
+ pr_err("cfg80211 init failed\n");
goto done;
}
- priv = netdev_priv(dev);
- dev->ml_priv = priv;
+
+ wdev->iftype = NL80211_IFTYPE_STATION;
+ priv = wdev_priv(wdev);
+ priv->wdev = wdev;
if (lbs_init_adapter(priv)) {
- lbs_pr_err("failed to initialize adapter structure.\n");
- goto err_init_adapter;
+ pr_err("failed to initialize adapter structure\n");
+ goto err_wdev;
+ }
+
+ dev = alloc_netdev(0, "wlan%d", ether_setup);
+ if (!dev) {
+ dev_err(dmdev, "no memory for network device instance\n");
+ goto err_adapter;
}
+ dev->ieee80211_ptr = wdev;
+ dev->ml_priv = priv;
+ SET_NETDEV_DEV(dev, dmdev);
+ wdev->netdev = dev;
priv->dev = dev;
- priv->card = card;
- priv->mesh_open = 0;
- priv->infra_open = 0;
- /* Setup the OS Interface to our functions */
dev->netdev_ops = &lbs_netdev_ops;
dev->watchdog_timeo = 5 * HZ;
dev->ethtool_ops = &lbs_ethtool_ops;
-#ifdef WIRELESS_EXT
- dev->wireless_handlers = &lbs_handler_def;
-#endif
dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
- SET_NETDEV_DEV(dev, dmdev);
+ priv->card = card;
- priv->rtap_net_dev = NULL;
+ strcpy(dev->name, "wlan%d");
lbs_deb_thread("Starting main thread...\n");
init_waitqueue_head(&priv->waitq);
priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main");
if (IS_ERR(priv->main_thread)) {
lbs_deb_thread("Error creating main thread.\n");
- goto err_init_adapter;
+ goto err_ndev;
}
priv->work_thread = create_singlethread_workqueue("lbs_worker");
- INIT_DELAYED_WORK(&priv->assoc_work, lbs_association_worker);
- INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker);
INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker);
- INIT_WORK(&priv->sync_channel, lbs_sync_channel_worker);
- sprintf(priv->mesh_ssid, "mesh");
- priv->mesh_ssid_len = 4;
-
- priv->wol_criteria = 0xffffffff;
+ priv->wol_criteria = EHS_REMOVE_WAKEUP;
priv->wol_gpio = 0xff;
+ priv->wol_gap = 20;
+ priv->ehs_remove_supported = true;
goto done;
-err_init_adapter:
- lbs_free_adapter(priv);
+ err_ndev:
free_netdev(dev);
+
+ err_adapter:
+ lbs_free_adapter(priv);
+
+ err_wdev:
+ lbs_cfg_free(priv);
+
priv = NULL;
done:
@@ -1242,18 +1041,15 @@ EXPORT_SYMBOL_GPL(lbs_add_card);
void lbs_remove_card(struct lbs_private *priv)
{
struct net_device *dev = priv->dev;
- union iwreq_data wrqu;
lbs_deb_enter(LBS_DEB_MAIN);
lbs_remove_mesh(priv);
- lbs_remove_rtap(priv);
- dev = priv->dev;
+ if (priv->wiphy_registered)
+ lbs_scan_deinit(priv);
- cancel_delayed_work_sync(&priv->scan_work);
- cancel_delayed_work_sync(&priv->assoc_work);
- cancel_work_sync(&priv->mcast_work);
+ lbs_wait_for_firmware_load(priv);
/* worker thread destruction blocks on the in-flight command which
* should have been cleared already in lbs_stop_card().
@@ -1264,20 +1060,24 @@ void lbs_remove_card(struct lbs_private *priv)
if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
priv->psmode = LBS802_11POWERMODECAM;
- lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
+ lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, true);
}
- memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN);
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+ if (priv->is_deep_sleep) {
+ priv->is_deep_sleep = 0;
+ wake_up_interruptible(&priv->ds_awake_q);
+ }
+
+ priv->is_host_sleep_configured = 0;
+ priv->is_host_sleep_activated = 0;
+ wake_up_interruptible(&priv->host_sleep_q);
/* Stop the thread servicing the interrupts */
priv->surpriseremoved = 1;
kthread_stop(priv->main_thread);
lbs_free_adapter(priv);
-
- priv->dev = NULL;
+ lbs_cfg_free(priv);
free_netdev(dev);
lbs_deb_leave(LBS_DEB_MAIN);
@@ -1285,6 +1085,17 @@ void lbs_remove_card(struct lbs_private *priv)
EXPORT_SYMBOL_GPL(lbs_remove_card);
+int lbs_rtap_supported(struct lbs_private *priv)
+{
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5)
+ return 1;
+
+ /* newer firmware use a capability mask */
+ return ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
+ (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK));
+}
+
+
int lbs_start_card(struct lbs_private *priv)
{
struct net_device *dev = priv->dev;
@@ -1297,57 +1108,22 @@ int lbs_start_card(struct lbs_private *priv)
if (ret)
goto done;
- /* init 802.11d */
- lbs_init_11d(priv);
+ if (!lbs_disablemesh)
+ lbs_init_mesh(priv);
+ else
+ pr_info("%s: mesh disabled\n", dev->name);
- if (register_netdev(dev)) {
- lbs_pr_err("cannot register ethX device\n");
+ if (lbs_cfg_register(priv)) {
+ pr_err("cannot register device\n");
goto done;
}
- lbs_update_channel(priv);
-
- /* 5.0.16p0 is known to NOT support any mesh */
- if (priv->fwrelease > 0x05001000) {
- /* Enable mesh, if supported, and work out which TLV it uses.
- 0x100 + 291 is an unofficial value used in 5.110.20.pXX
- 0x100 + 37 is the official value used in 5.110.21.pXX
- but we check them in that order because 20.pXX doesn't
- give an error -- it just silently fails. */
-
- /* 5.110.20.pXX firmware will fail the command if the channel
- doesn't match the existing channel. But only if the TLV
- is correct. If the channel is wrong, _BOTH_ versions will
- give an error to 0x100+291, and allow 0x100+37 to succeed.
- It's just that 5.110.20.pXX will not have done anything
- useful */
-
- priv->mesh_tlv = 0x100 + 291;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->curbssparams.channel)) {
- priv->mesh_tlv = 0x100 + 37;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->curbssparams.channel))
- priv->mesh_tlv = 0;
- }
- if (priv->mesh_tlv) {
- lbs_add_mesh(priv);
-
- if (device_create_file(&dev->dev, &dev_attr_lbs_mesh))
- lbs_pr_err("cannot register lbs_mesh attribute\n");
-
- /* While rtap isn't related to mesh, only mesh-enabled
- * firmware implements the rtap functionality via
- * CMD_802_11_MONITOR_MODE.
- */
- if (device_create_file(&dev->dev, &dev_attr_lbs_rtap))
- lbs_pr_err("cannot register lbs_rtap attribute\n");
- }
- }
+ if (lbs_mesh_activated(priv))
+ lbs_start_mesh(priv);
lbs_debugfs_init_one(priv, dev);
- lbs_pr_info("%s: Marvell WLAN 802.11 adapter\n", dev->name);
+ netdev_info(dev, "Marvell WLAN 802.11 adapter\n");
ret = 0;
@@ -1361,8 +1137,6 @@ EXPORT_SYMBOL_GPL(lbs_start_card);
void lbs_stop_card(struct lbs_private *priv)
{
struct net_device *dev;
- struct cmd_ctrl_node *cmdnode;
- unsigned long flags;
lbs_deb_enter(LBS_DEB_MAIN);
@@ -1370,37 +1144,16 @@ void lbs_stop_card(struct lbs_private *priv)
goto out;
dev = priv->dev;
+ /* If the netdev isn't registered, it means that lbs_start_card() was
+ * never called so we have nothing to do here. */
+ if (dev->reg_state != NETREG_REGISTERED)
+ goto out;
+
netif_stop_queue(dev);
netif_carrier_off(dev);
lbs_debugfs_remove_one(priv);
- if (priv->mesh_tlv) {
- device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
- device_remove_file(&dev->dev, &dev_attr_lbs_rtap);
- }
-
- /* Delete the timeout of the currently processing command */
- del_timer_sync(&priv->command_timer);
-
- /* Flush pending command nodes */
- spin_lock_irqsave(&priv->driver_lock, flags);
- lbs_deb_main("clearing pending commands\n");
- list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
- cmdnode->result = -ENOENT;
- cmdnode->cmdwaitqwoken = 1;
- wake_up_interruptible(&cmdnode->cmdwait_q);
- }
-
- /* Flush the command the card is currently processing */
- if (priv->cur_cmd) {
- lbs_deb_main("clearing current command\n");
- priv->cur_cmd->result = -ENOENT;
- priv->cur_cmd->cmdwaitqwoken = 1;
- wake_up_interruptible(&priv->cur_cmd->cmdwait_q);
- }
- lbs_deb_main("done clearing commands\n");
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
+ lbs_deinit_mesh(priv);
unregister_netdev(dev);
out:
@@ -1409,157 +1162,6 @@ out:
EXPORT_SYMBOL_GPL(lbs_stop_card);
-static const struct net_device_ops mesh_netdev_ops = {
- .ndo_open = lbs_dev_open,
- .ndo_stop = lbs_mesh_stop,
- .ndo_start_xmit = lbs_hard_start_xmit,
- .ndo_set_mac_address = lbs_set_mac_address,
- .ndo_set_multicast_list = lbs_set_multicast_list,
-};
-
-/**
- * @brief This function adds mshX interface
- *
- * @param priv A pointer to the struct lbs_private structure
- * @return 0 if successful, -X otherwise
- */
-static int lbs_add_mesh(struct lbs_private *priv)
-{
- struct net_device *mesh_dev = NULL;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_MESH);
-
- /* Allocate a virtual mesh device */
- if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) {
- lbs_deb_mesh("init mshX device failed\n");
- ret = -ENOMEM;
- goto done;
- }
- mesh_dev->ml_priv = priv;
- priv->mesh_dev = mesh_dev;
-
- mesh_dev->netdev_ops = &mesh_netdev_ops;
- mesh_dev->ethtool_ops = &lbs_ethtool_ops;
- memcpy(mesh_dev->dev_addr, priv->dev->dev_addr,
- sizeof(priv->dev->dev_addr));
-
- SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
-
-#ifdef WIRELESS_EXT
- mesh_dev->wireless_handlers = (struct iw_handler_def *)&mesh_handler_def;
-#endif
- mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
- /* Register virtual mesh interface */
- ret = register_netdev(mesh_dev);
- if (ret) {
- lbs_pr_err("cannot register mshX virtual interface\n");
- goto err_free;
- }
-
- ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
- if (ret)
- goto err_unregister;
-
- lbs_persist_config_init(mesh_dev);
-
- /* Everything successful */
- ret = 0;
- goto done;
-
-err_unregister:
- unregister_netdev(mesh_dev);
-
-err_free:
- free_netdev(mesh_dev);
-
-done:
- lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
- return ret;
-}
-
-static void lbs_remove_mesh(struct lbs_private *priv)
-{
- struct net_device *mesh_dev;
-
-
- mesh_dev = priv->mesh_dev;
- if (!mesh_dev)
- return;
-
- lbs_deb_enter(LBS_DEB_MESH);
- netif_stop_queue(mesh_dev);
- netif_carrier_off(mesh_dev);
- sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
- lbs_persist_config_remove(mesh_dev);
- unregister_netdev(mesh_dev);
- priv->mesh_dev = NULL;
- free_netdev(mesh_dev);
- lbs_deb_leave(LBS_DEB_MESH);
-}
-
-/**
- * @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 *lbs_get_region_cfp_table(u8 region, int *cfp_no)
-{
- int i, end;
-
- lbs_deb_enter(LBS_DEB_MAIN);
-
- end = ARRAY_SIZE(region_cfp_table);
-
- for (i = 0; i < end ; i++) {
- lbs_deb_main("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;
- lbs_deb_leave(LBS_DEB_MAIN);
- return region_cfp_table[i].cfp_BG;
- }
- }
-
- lbs_deb_leave_args(LBS_DEB_MAIN, "ret NULL");
- return NULL;
-}
-
-int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band)
-{
- int ret = 0;
- int i = 0;
-
- struct chan_freq_power *cfp;
- int cfp_no;
-
- lbs_deb_enter(LBS_DEB_MAIN);
-
- memset(priv->region_channel, 0, sizeof(priv->region_channel));
-
- cfp = lbs_get_region_cfp_table(region, &cfp_no);
- if (cfp != NULL) {
- priv->region_channel[i].nrcfp = cfp_no;
- priv->region_channel[i].CFP = cfp;
- } else {
- lbs_deb_main("wrong region code %#x in band B/G\n",
- region);
- ret = -1;
- goto out;
- }
- priv->region_channel[i].valid = 1;
- priv->region_channel[i].region = region;
- priv->region_channel[i].band = band;
- i++;
-out:
- lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
- return ret;
-}
-
void lbs_queue_event(struct lbs_private *priv, u32 event)
{
unsigned long flags;
@@ -1570,9 +1172,9 @@ void lbs_queue_event(struct lbs_private *priv, u32 event)
if (priv->psstate == PS_STATE_SLEEP)
priv->psstate = PS_STATE_AWAKE;
- __kfifo_put(priv->event_fifo, (unsigned char *) &event, sizeof(u32));
+ kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32));
- wake_up_interruptible(&priv->waitq);
+ wake_up(&priv->waitq);
spin_unlock_irqrestore(&priv->driver_lock, flags);
lbs_deb_leave(LBS_DEB_THREAD);
@@ -1590,7 +1192,7 @@ void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx)
BUG_ON(resp_idx > 1);
priv->resp_idx = resp_idx;
- wake_up_interruptible(&priv->waitq);
+ wake_up(&priv->waitq);
lbs_deb_leave(LBS_DEB_THREAD);
}
@@ -1602,7 +1204,7 @@ static int __init lbs_init_module(void)
memset(&confirm_sleep, 0, sizeof(confirm_sleep));
confirm_sleep.hdr.command = cpu_to_le16(CMD_802_11_PS_MODE);
confirm_sleep.hdr.size = cpu_to_le16(sizeof(confirm_sleep));
- confirm_sleep.action = cpu_to_le16(CMD_SUBCMD_SLEEP_CONFIRMED);
+ confirm_sleep.action = cpu_to_le16(PS_MODE_ACTION_SLEEP_CONFIRMED);
lbs_debugfs_init();
lbs_deb_leave(LBS_DEB_MAIN);
return 0;
@@ -1615,86 +1217,6 @@ static void __exit lbs_exit_module(void)
lbs_deb_leave(LBS_DEB_MAIN);
}
-/*
- * rtap interface support fuctions
- */
-
-static int lbs_rtap_open(struct net_device *dev)
-{
- /* Yes, _stop_ the queue. Because we don't support injection */
- lbs_deb_enter(LBS_DEB_MAIN);
- netif_carrier_off(dev);
- netif_stop_queue(dev);
- lbs_deb_leave(LBS_DEB_LEAVE);
- return 0;
-}
-
-static int lbs_rtap_stop(struct net_device *dev)
-{
- lbs_deb_enter(LBS_DEB_MAIN);
- lbs_deb_leave(LBS_DEB_MAIN);
- return 0;
-}
-
-static int lbs_rtap_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- netif_stop_queue(dev);
- return NETDEV_TX_BUSY;
-}
-
-static void lbs_remove_rtap(struct lbs_private *priv)
-{
- lbs_deb_enter(LBS_DEB_MAIN);
- if (priv->rtap_net_dev == NULL)
- goto out;
- unregister_netdev(priv->rtap_net_dev);
- free_netdev(priv->rtap_net_dev);
- priv->rtap_net_dev = NULL;
-out:
- lbs_deb_leave(LBS_DEB_MAIN);
-}
-
-static const struct net_device_ops rtap_netdev_ops = {
- .ndo_open = lbs_rtap_open,
- .ndo_stop = lbs_rtap_stop,
- .ndo_start_xmit = lbs_rtap_hard_start_xmit,
-};
-
-static int lbs_add_rtap(struct lbs_private *priv)
-{
- int ret = 0;
- struct net_device *rtap_dev;
-
- lbs_deb_enter(LBS_DEB_MAIN);
- if (priv->rtap_net_dev) {
- ret = -EPERM;
- goto out;
- }
-
- rtap_dev = alloc_netdev(0, "rtap%d", ether_setup);
- if (rtap_dev == NULL) {
- ret = -ENOMEM;
- goto out;
- }
-
- memcpy(rtap_dev->dev_addr, priv->current_addr, ETH_ALEN);
- rtap_dev->type = ARPHRD_IEEE80211_RADIOTAP;
- rtap_dev->netdev_ops = &rtap_netdev_ops;
- rtap_dev->ml_priv = priv;
- SET_NETDEV_DEV(rtap_dev, priv->dev->dev.parent);
-
- ret = register_netdev(rtap_dev);
- if (ret) {
- free_netdev(rtap_dev);
- goto out;
- }
- priv->rtap_net_dev = rtap_dev;
-
-out:
- lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
- return ret;
-}
-
module_init(lbs_init_module);
module_exit(lbs_exit_module);
diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c
new file mode 100644
index 00000000000..6fef746345b
--- /dev/null
+++ b/drivers/net/wireless/libertas/mesh.c
@@ -0,0 +1,1188 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/hardirq.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/kthread.h>
+#include <linux/kfifo.h>
+#include <net/cfg80211.h>
+
+#include "mesh.h"
+#include "decl.h"
+#include "cmd.h"
+
+
+static int lbs_add_mesh(struct lbs_private *priv);
+
+/***************************************************************************
+ * Mesh command handling
+ */
+
+static int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
+ struct cmd_ds_mesh_access *cmd)
+{
+ int ret;
+
+ lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
+
+ cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
+ cmd->hdr.size = cpu_to_le16(sizeof(*cmd));
+ cmd->hdr.result = 0;
+
+ cmd->action = cpu_to_le16(cmd_action);
+
+ ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return ret;
+}
+
+static int __lbs_mesh_config_send(struct lbs_private *priv,
+ struct cmd_ds_mesh_config *cmd,
+ uint16_t action, uint16_t type)
+{
+ int ret;
+ u16 command = CMD_MESH_CONFIG_OLD;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ /*
+ * Command id is 0xac for v10 FW along with mesh interface
+ * id in bits 14-13-12.
+ */
+ if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
+ command = CMD_MESH_CONFIG |
+ (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);
+
+ cmd->hdr.command = cpu_to_le16(command);
+ cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
+ cmd->hdr.result = 0;
+
+ cmd->type = cpu_to_le16(type);
+ cmd->action = cpu_to_le16(action);
+
+ ret = lbs_cmd_with_response(priv, command, cmd);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return ret;
+}
+
+static int lbs_mesh_config_send(struct lbs_private *priv,
+ struct cmd_ds_mesh_config *cmd,
+ uint16_t action, uint16_t type)
+{
+ int ret;
+
+ if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
+ return -EOPNOTSUPP;
+
+ ret = __lbs_mesh_config_send(priv, cmd, action, type);
+ return ret;
+}
+
+/* This function is the CMD_MESH_CONFIG legacy function. It only handles the
+ * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG
+ * are all handled by preparing a struct cmd_ds_mesh_config and passing it to
+ * lbs_mesh_config_send.
+ */
+static int lbs_mesh_config(struct lbs_private *priv, uint16_t action,
+ uint16_t chan)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_meshie *ie;
+ DECLARE_SSID_BUF(ssid);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.channel = cpu_to_le16(chan);
+ ie = (struct mrvl_meshie *)cmd.data;
+
+ switch (action) {
+ case CMD_ACT_MESH_CONFIG_START:
+ ie->id = WLAN_EID_VENDOR_SPECIFIC;
+ ie->val.oui[0] = 0x00;
+ ie->val.oui[1] = 0x50;
+ ie->val.oui[2] = 0x43;
+ ie->val.type = MARVELL_MESH_IE_TYPE;
+ ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
+ ie->val.version = MARVELL_MESH_IE_VERSION;
+ ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
+ ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
+ ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
+ ie->val.mesh_id_len = priv->mesh_ssid_len;
+ memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len);
+ ie->len = sizeof(struct mrvl_meshie_val) -
+ IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len;
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
+ break;
+ case CMD_ACT_MESH_CONFIG_STOP:
+ break;
+ default:
+ return -1;
+ }
+ lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
+ action, priv->mesh_tlv, chan,
+ print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len));
+
+ return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
+}
+
+int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)
+{
+ priv->mesh_channel = channel;
+ return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel);
+}
+
+static uint16_t lbs_mesh_get_channel(struct lbs_private *priv)
+{
+ return priv->mesh_channel ?: 1;
+}
+
+/***************************************************************************
+ * Mesh sysfs support
+ */
+
+/*
+ * Attributes exported through sysfs
+ */
+
+/**
+ * lbs_anycast_get - Get function for sysfs attribute anycast_mask
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer where data will be returned
+ */
+static ssize_t lbs_anycast_get(struct device *dev,
+ struct device_attribute *attr, char * buf)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_access mesh_access;
+ int ret;
+
+ memset(&mesh_access, 0, sizeof(mesh_access));
+
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
+}
+
+/**
+ * lbs_anycast_set - Set function for sysfs attribute anycast_mask
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer that contains new attribute value
+ * @count: size of buffer
+ */
+static ssize_t lbs_anycast_set(struct device *dev,
+ struct device_attribute *attr, const char * buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_access mesh_access;
+ uint32_t datum;
+ int ret;
+
+ memset(&mesh_access, 0, sizeof(mesh_access));
+ sscanf(buf, "%x", &datum);
+ mesh_access.data[0] = cpu_to_le32(datum);
+
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * lbs_prb_rsp_limit_get - Get function for sysfs attribute prb_rsp_limit
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer where data will be returned
+ */
+static ssize_t lbs_prb_rsp_limit_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_access mesh_access;
+ int ret;
+ u32 retry_limit;
+
+ memset(&mesh_access, 0, sizeof(mesh_access));
+ mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET);
+
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
+ &mesh_access);
+ if (ret)
+ return ret;
+
+ retry_limit = le32_to_cpu(mesh_access.data[1]);
+ return snprintf(buf, 10, "%d\n", retry_limit);
+}
+
+/**
+ * lbs_prb_rsp_limit_set - Set function for sysfs attribute prb_rsp_limit
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer that contains new attribute value
+ * @count: size of buffer
+ */
+static ssize_t lbs_prb_rsp_limit_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_access mesh_access;
+ int ret;
+ unsigned long retry_limit;
+
+ memset(&mesh_access, 0, sizeof(mesh_access));
+ mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);
+
+ if (!kstrtoul(buf, 10, &retry_limit))
+ return -ENOTSUPP;
+ if (retry_limit > 15)
+ return -ENOTSUPP;
+
+ mesh_access.data[1] = cpu_to_le32(retry_limit);
+
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
+ &mesh_access);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * lbs_mesh_get - Get function for sysfs attribute mesh
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer where data will be returned
+ */
+static ssize_t lbs_mesh_get(struct device *dev,
+ struct device_attribute *attr, char * buf)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev);
+}
+
+/**
+ * lbs_mesh_set - Set function for sysfs attribute mesh
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer that contains new attribute value
+ * @count: size of buffer
+ */
+static ssize_t lbs_mesh_set(struct device *dev,
+ struct device_attribute *attr, const char * buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ int enable;
+
+ sscanf(buf, "%x", &enable);
+ enable = !!enable;
+ if (enable == !!priv->mesh_dev)
+ return count;
+
+ if (enable)
+ lbs_add_mesh(priv);
+ else
+ lbs_remove_mesh(priv);
+
+ return count;
+}
+
+/*
+ * lbs_mesh attribute to be exported per ethX interface
+ * through sysfs (/sys/class/net/ethX/lbs_mesh)
+ */
+static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set);
+
+/*
+ * anycast_mask attribute to be exported per mshX interface
+ * through sysfs (/sys/class/net/mshX/anycast_mask)
+ */
+static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set);
+
+/*
+ * prb_rsp_limit attribute to be exported per mshX interface
+ * through sysfs (/sys/class/net/mshX/prb_rsp_limit)
+ */
+static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get,
+ lbs_prb_rsp_limit_set);
+
+static struct attribute *lbs_mesh_sysfs_entries[] = {
+ &dev_attr_anycast_mask.attr,
+ &dev_attr_prb_rsp_limit.attr,
+ NULL,
+};
+
+static const struct attribute_group lbs_mesh_attr_group = {
+ .attrs = lbs_mesh_sysfs_entries,
+};
+
+
+/***************************************************************************
+ * Persistent configuration support
+ */
+
+static int mesh_get_default_parameters(struct device *dev,
+ struct mrvl_mesh_defaults *defs)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_config cmd;
+ int ret;
+
+ memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET,
+ CMD_TYPE_MESH_GET_DEFAULTS);
+
+ if (ret)
+ return -EOPNOTSUPP;
+
+ memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));
+
+ return 0;
+}
+
+/**
+ * bootflag_get - Get function for sysfs attribute bootflag
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer where data will be returned
+ */
+static ssize_t bootflag_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag));
+}
+
+/**
+ * bootflag_set - Set function for sysfs attribute bootflag
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer that contains new attribute value
+ * @count: size of buffer
+ */
+static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_config cmd;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if ((ret != 1) || (datum > 1))
+ return -EINVAL;
+
+ *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum);
+ cmd.length = cpu_to_le16(sizeof(uint32_t));
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_BOOTFLAG);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * boottime_get - Get function for sysfs attribute boottime
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer where data will be returned
+ */
+static ssize_t boottime_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 12, "%d\n", defs.boottime);
+}
+
+/**
+ * boottime_set - Set function for sysfs attribute boottime
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer that contains new attribute value
+ * @count: size of buffer
+ */
+static ssize_t boottime_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_config cmd;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if ((ret != 1) || (datum > 255))
+ return -EINVAL;
+
+ /* A too small boot time will result in the device booting into
+ * standalone (no-host) mode before the host can take control of it,
+ * so the change will be hard to revert. This may be a desired
+ * feature (e.g to configure a very fast boot time for devices that
+ * will not be attached to a host), but dangerous. So I'm enforcing a
+ * lower limit of 20 seconds: remove and recompile the driver if this
+ * does not work for you.
+ */
+ datum = (datum < 20) ? 20 : datum;
+ cmd.data[0] = datum;
+ cmd.length = cpu_to_le16(sizeof(uint8_t));
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_BOOTTIME);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * channel_get - Get function for sysfs attribute channel
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer where data will be returned
+ */
+static ssize_t channel_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel));
+}
+
+/**
+ * channel_set - Set function for sysfs attribute channel
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer that contains new attribute value
+ * @count: size of buffer
+ */
+static ssize_t channel_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_config cmd;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if (ret != 1 || datum < 1 || datum > 11)
+ return -EINVAL;
+
+ *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum);
+ cmd.length = cpu_to_le16(sizeof(uint16_t));
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_DEF_CHANNEL);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * mesh_id_get - Get function for sysfs attribute mesh_id
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer where data will be returned
+ */
+static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) {
+ dev_err(dev, "inconsistent mesh ID length\n");
+ defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN;
+ }
+
+ memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len);
+ buf[defs.meshie.val.mesh_id_len] = '\n';
+ buf[defs.meshie.val.mesh_id_len + 1] = '\0';
+
+ return defs.meshie.val.mesh_id_len + 1;
+}
+
+/**
+ * mesh_id_set - Set function for sysfs attribute mesh_id
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer that contains new attribute value
+ * @count: size of buffer
+ */
+static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_mesh_defaults defs;
+ struct mrvl_meshie *ie;
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ int len;
+ int ret;
+
+ if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1)
+ return -EINVAL;
+
+ memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
+ ie = (struct mrvl_meshie *) &cmd.data[0];
+
+ /* fetch all other Information Element parameters */
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
+
+ /* transfer IE elements */
+ memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
+
+ len = count - 1;
+ memcpy(ie->val.mesh_id, buf, len);
+ /* SSID len */
+ ie->val.mesh_id_len = len;
+ /* IE len */
+ ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len;
+
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_MESH_IE);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * protocol_id_get - Get function for sysfs attribute protocol_id
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer where data will be returned
+ */
+static ssize_t protocol_id_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id);
+}
+
+/**
+ * protocol_id_set - Set function for sysfs attribute protocol_id
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer that contains new attribute value
+ * @count: size of buffer
+ */
+static ssize_t protocol_id_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_mesh_defaults defs;
+ struct mrvl_meshie *ie;
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if ((ret != 1) || (datum > 255))
+ return -EINVAL;
+
+ /* fetch all other Information Element parameters */
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
+
+ /* transfer IE elements */
+ ie = (struct mrvl_meshie *) &cmd.data[0];
+ memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
+ /* update protocol id */
+ ie->val.active_protocol_id = datum;
+
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_MESH_IE);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * metric_id_get - Get function for sysfs attribute metric_id
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer where data will be returned
+ */
+static ssize_t metric_id_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id);
+}
+
+/**
+ * metric_id_set - Set function for sysfs attribute metric_id
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer that contains new attribute value
+ * @count: size of buffer
+ */
+static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_mesh_defaults defs;
+ struct mrvl_meshie *ie;
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if ((ret != 1) || (datum > 255))
+ return -EINVAL;
+
+ /* fetch all other Information Element parameters */
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
+
+ /* transfer IE elements */
+ ie = (struct mrvl_meshie *) &cmd.data[0];
+ memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
+ /* update metric id */
+ ie->val.active_metric_id = datum;
+
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_MESH_IE);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * capability_get - Get function for sysfs attribute capability
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer where data will be returned
+ */
+static ssize_t capability_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability);
+}
+
+/**
+ * capability_set - Set function for sysfs attribute capability
+ * @dev: the &struct device
+ * @attr: device attributes
+ * @buf: buffer that contains new attribute value
+ * @count: size of buffer
+ */
+static ssize_t capability_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_mesh_defaults defs;
+ struct mrvl_meshie *ie;
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if ((ret != 1) || (datum > 255))
+ return -EINVAL;
+
+ /* fetch all other Information Element parameters */
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
+
+ /* transfer IE elements */
+ ie = (struct mrvl_meshie *) &cmd.data[0];
+ memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
+ /* update value */
+ ie->val.mesh_capability = datum;
+
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_MESH_IE);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+
+static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set);
+static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set);
+static DEVICE_ATTR(channel, 0644, channel_get, channel_set);
+static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set);
+static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set);
+static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set);
+static DEVICE_ATTR(capability, 0644, capability_get, capability_set);
+
+static struct attribute *boot_opts_attrs[] = {
+ &dev_attr_bootflag.attr,
+ &dev_attr_boottime.attr,
+ &dev_attr_channel.attr,
+ NULL
+};
+
+static const struct attribute_group boot_opts_group = {
+ .name = "boot_options",
+ .attrs = boot_opts_attrs,
+};
+
+static struct attribute *mesh_ie_attrs[] = {
+ &dev_attr_mesh_id.attr,
+ &dev_attr_protocol_id.attr,
+ &dev_attr_metric_id.attr,
+ &dev_attr_capability.attr,
+ NULL
+};
+
+static const struct attribute_group mesh_ie_group = {
+ .name = "mesh_ie",
+ .attrs = mesh_ie_attrs,
+};
+
+static void lbs_persist_config_init(struct net_device *dev)
+{
+ int ret;
+ ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group);
+ ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group);
+}
+
+static void lbs_persist_config_remove(struct net_device *dev)
+{
+ sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group);
+ sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group);
+}
+
+
+/***************************************************************************
+ * Initializing and starting, stopping mesh
+ */
+
+/*
+ * Check mesh FW version and appropriately send the mesh start
+ * command
+ */
+int lbs_init_mesh(struct lbs_private *priv)
+{
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+
+ /* Determine mesh_fw_ver from fwrelease and fwcapinfo */
+ /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */
+ /* 5.110.22 have mesh command with 0xa3 command id */
+ /* 10.0.0.p0 FW brings in mesh config command with different id */
+ /* Check FW version MSB and initialize mesh_fw_ver */
+ if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) {
+ /* Enable mesh, if supported, and work out which TLV it uses.
+ 0x100 + 291 is an unofficial value used in 5.110.20.pXX
+ 0x100 + 37 is the official value used in 5.110.21.pXX
+ but we check them in that order because 20.pXX doesn't
+ give an error -- it just silently fails. */
+
+ /* 5.110.20.pXX firmware will fail the command if the channel
+ doesn't match the existing channel. But only if the TLV
+ is correct. If the channel is wrong, _BOTH_ versions will
+ give an error to 0x100+291, and allow 0x100+37 to succeed.
+ It's just that 5.110.20.pXX will not have done anything
+ useful */
+
+ priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) {
+ priv->mesh_tlv = TLV_TYPE_MESH_ID;
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
+ priv->mesh_tlv = 0;
+ }
+ } else
+ if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
+ (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) {
+ /* 10.0.0.pXX new firmwares should succeed with TLV
+ * 0x100+37; Do not invoke command with old TLV.
+ */
+ priv->mesh_tlv = TLV_TYPE_MESH_ID;
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
+ priv->mesh_tlv = 0;
+ }
+
+ /* Stop meshing until interface is brought up */
+ lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1);
+
+ if (priv->mesh_tlv) {
+ sprintf(priv->mesh_ssid, "mesh");
+ priv->mesh_ssid_len = 4;
+ ret = 1;
+ }
+
+ lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
+ return ret;
+}
+
+void lbs_start_mesh(struct lbs_private *priv)
+{
+ lbs_add_mesh(priv);
+
+ if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh))
+ netdev_err(priv->dev, "cannot register lbs_mesh attribute\n");
+}
+
+int lbs_deinit_mesh(struct lbs_private *priv)
+{
+ struct net_device *dev = priv->dev;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+
+ if (priv->mesh_tlv) {
+ device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
+ ret = 1;
+ }
+
+ lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
+ return ret;
+}
+
+
+/**
+ * lbs_mesh_stop - close the mshX interface
+ *
+ * @dev: A pointer to &net_device structure
+ * returns: 0
+ */
+static int lbs_mesh_stop(struct net_device *dev)
+{
+ struct lbs_private *priv = dev->ml_priv;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+ lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
+ lbs_mesh_get_channel(priv));
+
+ spin_lock_irq(&priv->driver_lock);
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ spin_unlock_irq(&priv->driver_lock);
+
+ lbs_update_mcast(priv);
+ if (!lbs_iface_active(priv))
+ lbs_stop_iface(priv);
+
+ lbs_deb_leave(LBS_DEB_MESH);
+ return 0;
+}
+
+/**
+ * lbs_mesh_dev_open - open the mshX interface
+ *
+ * @dev: A pointer to &net_device structure
+ * returns: 0 or -EBUSY if monitor mode active
+ */
+static int lbs_mesh_dev_open(struct net_device *dev)
+{
+ struct lbs_private *priv = dev->ml_priv;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_NET);
+ if (!priv->iface_running) {
+ ret = lbs_start_iface(priv);
+ if (ret)
+ goto out;
+ }
+
+ spin_lock_irq(&priv->driver_lock);
+
+ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+ ret = -EBUSY;
+ spin_unlock_irq(&priv->driver_lock);
+ goto out;
+ }
+
+ netif_carrier_on(dev);
+
+ if (!priv->tx_pending_len)
+ netif_wake_queue(dev);
+
+ spin_unlock_irq(&priv->driver_lock);
+
+ ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
+ lbs_mesh_get_channel(priv));
+
+out:
+ lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
+ return ret;
+}
+
+static const struct net_device_ops mesh_netdev_ops = {
+ .ndo_open = lbs_mesh_dev_open,
+ .ndo_stop = lbs_mesh_stop,
+ .ndo_start_xmit = lbs_hard_start_xmit,
+ .ndo_set_mac_address = lbs_set_mac_address,
+ .ndo_set_rx_mode = lbs_set_multicast_list,
+};
+
+/**
+ * lbs_add_mesh - add mshX interface
+ *
+ * @priv: A pointer to the &struct lbs_private structure
+ * returns: 0 if successful, -X otherwise
+ */
+static int lbs_add_mesh(struct lbs_private *priv)
+{
+ struct net_device *mesh_dev = NULL;
+ struct wireless_dev *mesh_wdev;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+
+ /* Allocate a virtual mesh device */
+ mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ if (!mesh_wdev) {
+ lbs_deb_mesh("init mshX wireless device failed\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ mesh_dev = alloc_netdev(0, "msh%d", ether_setup);
+ if (!mesh_dev) {
+ lbs_deb_mesh("init mshX device failed\n");
+ ret = -ENOMEM;
+ goto err_free_wdev;
+ }
+
+ mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT;
+ mesh_wdev->wiphy = priv->wdev->wiphy;
+ mesh_wdev->netdev = mesh_dev;
+
+ mesh_dev->ml_priv = priv;
+ mesh_dev->ieee80211_ptr = mesh_wdev;
+ priv->mesh_dev = mesh_dev;
+
+ mesh_dev->netdev_ops = &mesh_netdev_ops;
+ mesh_dev->ethtool_ops = &lbs_ethtool_ops;
+ eth_hw_addr_inherit(mesh_dev, priv->dev);
+
+ SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
+
+ mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+ /* Register virtual mesh interface */
+ ret = register_netdev(mesh_dev);
+ if (ret) {
+ pr_err("cannot register mshX virtual interface\n");
+ goto err_free_netdev;
+ }
+
+ ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
+ if (ret)
+ goto err_unregister;
+
+ lbs_persist_config_init(mesh_dev);
+
+ /* Everything successful */
+ ret = 0;
+ goto done;
+
+err_unregister:
+ unregister_netdev(mesh_dev);
+
+err_free_netdev:
+ free_netdev(mesh_dev);
+
+err_free_wdev:
+ kfree(mesh_wdev);
+
+done:
+ lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
+ return ret;
+}
+
+void lbs_remove_mesh(struct lbs_private *priv)
+{
+ struct net_device *mesh_dev;
+
+ mesh_dev = priv->mesh_dev;
+ if (!mesh_dev)
+ return;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+ netif_stop_queue(mesh_dev);
+ netif_carrier_off(mesh_dev);
+ sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
+ lbs_persist_config_remove(mesh_dev);
+ unregister_netdev(mesh_dev);
+ priv->mesh_dev = NULL;
+ kfree(mesh_dev->ieee80211_ptr);
+ free_netdev(mesh_dev);
+ lbs_deb_leave(LBS_DEB_MESH);
+}
+
+
+/***************************************************************************
+ * Sending and receiving
+ */
+struct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
+ struct net_device *dev, struct rxpd *rxpd)
+{
+ if (priv->mesh_dev) {
+ if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) {
+ if (rxpd->rx_control & RxPD_MESH_FRAME)
+ dev = priv->mesh_dev;
+ } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) {
+ if (rxpd->u.bss.bss_num == MESH_IFACE_ID)
+ dev = priv->mesh_dev;
+ }
+ }
+ return dev;
+}
+
+
+void lbs_mesh_set_txpd(struct lbs_private *priv,
+ struct net_device *dev, struct txpd *txpd)
+{
+ if (dev == priv->mesh_dev) {
+ if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID)
+ txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
+ else if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
+ txpd->u.bss.bss_num = MESH_IFACE_ID;
+ }
+}
+
+
+/***************************************************************************
+ * Ethtool related
+ */
+
+static const char * const 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",
+ "tx_failed_cnt"
+};
+
+void lbs_mesh_ethtool_get_stats(struct net_device *dev,
+ struct ethtool_stats *stats, uint64_t *data)
+{
+ struct lbs_private *priv = dev->ml_priv;
+ struct cmd_ds_mesh_access mesh_access;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_ETHTOOL);
+
+ /* Get Mesh Statistics */
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access);
+
+ if (ret) {
+ memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t)));
+ return;
+ }
+
+ priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
+ priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
+ priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
+ priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
+ priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
+ priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
+ priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
+ priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);
+
+ 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;
+ data[7] = priv->mstats.tx_failed_cnt;
+
+ lbs_deb_enter(LBS_DEB_ETHTOOL);
+}
+
+int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset)
+{
+ struct lbs_private *priv = dev->ml_priv;
+
+ if (sset == ETH_SS_STATS && dev == priv->mesh_dev)
+ return MESH_STATS_NUM;
+
+ return -EOPNOTSUPP;
+}
+
+void lbs_mesh_ethtool_get_strings(struct net_device *dev,
+ uint32_t stringset, uint8_t *s)
+{
+ int i;
+
+ lbs_deb_enter(LBS_DEB_ETHTOOL);
+
+ 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;
+ }
+ lbs_deb_enter(LBS_DEB_ETHTOOL);
+}
diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/libertas/mesh.h
new file mode 100644
index 00000000000..6603f341c87
--- /dev/null
+++ b/drivers/net/wireless/libertas/mesh.h
@@ -0,0 +1,77 @@
+/*
+ * Contains all definitions needed for the Libertas' MESH implementation.
+ */
+#ifndef _LBS_MESH_H_
+#define _LBS_MESH_H_
+
+
+#include <net/iw_handler.h>
+#include <net/lib80211.h>
+
+#include "host.h"
+#include "dev.h"
+
+#ifdef CONFIG_LIBERTAS_MESH
+
+struct net_device;
+
+int lbs_init_mesh(struct lbs_private *priv);
+void lbs_start_mesh(struct lbs_private *priv);
+int lbs_deinit_mesh(struct lbs_private *priv);
+
+void lbs_remove_mesh(struct lbs_private *priv);
+
+static inline bool lbs_mesh_activated(struct lbs_private *priv)
+{
+ /* Mesh SSID is only programmed after successful init */
+ return priv->mesh_ssid_len != 0;
+}
+
+int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel);
+
+/* Sending / Receiving */
+
+struct rxpd;
+struct txpd;
+
+struct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
+ struct net_device *dev, struct rxpd *rxpd);
+void lbs_mesh_set_txpd(struct lbs_private *priv,
+ struct net_device *dev, struct txpd *txpd);
+
+
+/* Command handling */
+
+struct cmd_ds_command;
+struct cmd_ds_mesh_access;
+struct cmd_ds_mesh_config;
+
+
+/* Ethtool statistics */
+
+struct ethtool_stats;
+
+void lbs_mesh_ethtool_get_stats(struct net_device *dev,
+ struct ethtool_stats *stats, uint64_t *data);
+int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset);
+void lbs_mesh_ethtool_get_strings(struct net_device *dev,
+ uint32_t stringset, uint8_t *s);
+
+
+#else
+
+#define lbs_init_mesh(priv)
+#define lbs_deinit_mesh(priv)
+#define lbs_start_mesh(priv)
+#define lbs_add_mesh(priv)
+#define lbs_remove_mesh(priv)
+#define lbs_mesh_set_dev(priv, dev, rxpd) (dev)
+#define lbs_mesh_set_txpd(priv, dev, txpd)
+#define lbs_mesh_set_channel(priv, channel) (0)
+#define lbs_mesh_activated(priv) (false)
+
+#endif
+
+
+
+#endif
diff --git a/drivers/net/wireless/libertas/persistcfg.c b/drivers/net/wireless/libertas/persistcfg.c
deleted file mode 100644
index 18fe29faf99..00000000000
--- a/drivers/net/wireless/libertas/persistcfg.c
+++ /dev/null
@@ -1,453 +0,0 @@
-#include <linux/moduleparam.h>
-#include <linux/delay.h>
-#include <linux/etherdevice.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/kthread.h>
-#include <linux/kfifo.h>
-
-#include "host.h"
-#include "decl.h"
-#include "dev.h"
-#include "wext.h"
-#include "debugfs.h"
-#include "scan.h"
-#include "assoc.h"
-#include "cmd.h"
-
-static int mesh_get_default_parameters(struct device *dev,
- struct mrvl_mesh_defaults *defs)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_config cmd;
- int ret;
-
- memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET,
- CMD_TYPE_MESH_GET_DEFAULTS);
-
- if (ret)
- return -EOPNOTSUPP;
-
- memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));
-
- return 0;
-}
-
-/**
- * @brief Get function for sysfs attribute bootflag
- */
-static ssize_t bootflag_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag));
-}
-
-/**
- * @brief Set function for sysfs attribute bootflag
- */
-static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_config cmd;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if ((ret != 1) || (datum > 1))
- return -EINVAL;
-
- *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum);
- cmd.length = cpu_to_le16(sizeof(uint32_t));
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_BOOTFLAG);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute boottime
- */
-static ssize_t boottime_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 12, "%d\n", defs.boottime);
-}
-
-/**
- * @brief Set function for sysfs attribute boottime
- */
-static ssize_t boottime_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_config cmd;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if ((ret != 1) || (datum > 255))
- return -EINVAL;
-
- /* A too small boot time will result in the device booting into
- * standalone (no-host) mode before the host can take control of it,
- * so the change will be hard to revert. This may be a desired
- * feature (e.g to configure a very fast boot time for devices that
- * will not be attached to a host), but dangerous. So I'm enforcing a
- * lower limit of 20 seconds: remove and recompile the driver if this
- * does not work for you.
- */
- datum = (datum < 20) ? 20 : datum;
- cmd.data[0] = datum;
- cmd.length = cpu_to_le16(sizeof(uint8_t));
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_BOOTTIME);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute channel
- */
-static ssize_t channel_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel));
-}
-
-/**
- * @brief Set function for sysfs attribute channel
- */
-static ssize_t channel_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_config cmd;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if (ret != 1 || datum < 1 || datum > 11)
- return -EINVAL;
-
- *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum);
- cmd.length = cpu_to_le16(sizeof(uint16_t));
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_DEF_CHANNEL);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute mesh_id
- */
-static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int maxlen;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- if (defs.meshie.val.mesh_id_len > IW_ESSID_MAX_SIZE) {
- lbs_pr_err("inconsistent mesh ID length");
- defs.meshie.val.mesh_id_len = IW_ESSID_MAX_SIZE;
- }
-
- /* SSID not null terminated: reserve room for \0 + \n */
- maxlen = defs.meshie.val.mesh_id_len + 2;
- maxlen = (PAGE_SIZE > maxlen) ? maxlen : PAGE_SIZE;
-
- defs.meshie.val.mesh_id[defs.meshie.val.mesh_id_len] = '\0';
-
- return snprintf(buf, maxlen, "%s\n", defs.meshie.val.mesh_id);
-}
-
-/**
- * @brief Set function for sysfs attribute mesh_id
- */
-static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_mesh_defaults defs;
- struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- int len;
- int ret;
-
- if (count < 2 || count > IW_ESSID_MAX_SIZE + 1)
- return -EINVAL;
-
- memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
- ie = (struct mrvl_meshie *) &cmd.data[0];
-
- /* fetch all other Information Element parameters */
- ret = mesh_get_default_parameters(dev, &defs);
-
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
-
- /* transfer IE elements */
- memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
-
- len = count - 1;
- memcpy(ie->val.mesh_id, buf, len);
- /* SSID len */
- ie->val.mesh_id_len = len;
- /* IE len */
- ie->len = sizeof(struct mrvl_meshie_val) - IW_ESSID_MAX_SIZE + len;
-
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_MESH_IE);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute protocol_id
- */
-static ssize_t protocol_id_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id);
-}
-
-/**
- * @brief Set function for sysfs attribute protocol_id
- */
-static ssize_t protocol_id_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_mesh_defaults defs;
- struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if ((ret != 1) || (datum > 255))
- return -EINVAL;
-
- /* fetch all other Information Element parameters */
- ret = mesh_get_default_parameters(dev, &defs);
-
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
-
- /* transfer IE elements */
- ie = (struct mrvl_meshie *) &cmd.data[0];
- memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
- /* update protocol id */
- ie->val.active_protocol_id = datum;
-
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_MESH_IE);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute metric_id
- */
-static ssize_t metric_id_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id);
-}
-
-/**
- * @brief Set function for sysfs attribute metric_id
- */
-static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_mesh_defaults defs;
- struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if ((ret != 1) || (datum > 255))
- return -EINVAL;
-
- /* fetch all other Information Element parameters */
- ret = mesh_get_default_parameters(dev, &defs);
-
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
-
- /* transfer IE elements */
- ie = (struct mrvl_meshie *) &cmd.data[0];
- memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
- /* update metric id */
- ie->val.active_metric_id = datum;
-
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_MESH_IE);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute capability
- */
-static ssize_t capability_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability);
-}
-
-/**
- * @brief Set function for sysfs attribute capability
- */
-static ssize_t capability_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_mesh_defaults defs;
- struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if ((ret != 1) || (datum > 255))
- return -EINVAL;
-
- /* fetch all other Information Element parameters */
- ret = mesh_get_default_parameters(dev, &defs);
-
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
-
- /* transfer IE elements */
- ie = (struct mrvl_meshie *) &cmd.data[0];
- memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
- /* update value */
- ie->val.mesh_capability = datum;
-
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_MESH_IE);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-
-static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set);
-static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set);
-static DEVICE_ATTR(channel, 0644, channel_get, channel_set);
-static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set);
-static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set);
-static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set);
-static DEVICE_ATTR(capability, 0644, capability_get, capability_set);
-
-static struct attribute *boot_opts_attrs[] = {
- &dev_attr_bootflag.attr,
- &dev_attr_boottime.attr,
- &dev_attr_channel.attr,
- NULL
-};
-
-static struct attribute_group boot_opts_group = {
- .name = "boot_options",
- .attrs = boot_opts_attrs,
-};
-
-static struct attribute *mesh_ie_attrs[] = {
- &dev_attr_mesh_id.attr,
- &dev_attr_protocol_id.attr,
- &dev_attr_metric_id.attr,
- &dev_attr_capability.attr,
- NULL
-};
-
-static struct attribute_group mesh_ie_group = {
- .name = "mesh_ie",
- .attrs = mesh_ie_attrs,
-};
-
-void lbs_persist_config_init(struct net_device *dev)
-{
- int ret;
- ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group);
- ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group);
-}
-
-void lbs_persist_config_remove(struct net_device *dev)
-{
- sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group);
- sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group);
-}
diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h
index d16b26416e8..b3c8ea6d610 100644
--- a/drivers/net/wireless/libertas/radiotap.h
+++ b/drivers/net/wireless/libertas/radiotap.h
@@ -6,7 +6,7 @@ struct tx_radiotap_hdr {
u8 txpower;
u8 rts_retries;
u8 data_retries;
-} __attribute__ ((packed));
+} __packed;
#define TX_RADIOTAP_PRESENT ( \
(1 << IEEE80211_RADIOTAP_RATE) | \
@@ -34,7 +34,7 @@ struct rx_radiotap_hdr {
u8 flags;
u8 rate;
u8 antsignal;
-} __attribute__ ((packed));
+} __packed;
#define RX_RADIOTAP_PRESENT ( \
(1 << IEEE80211_RADIOTAP_FLAGS) | \
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c
index 8e669775cb5..e446fed7b34 100644
--- a/drivers/net/wireless/libertas/rx.c
+++ b/drivers/net/wireless/libertas/rx.c
@@ -1,20 +1,28 @@
-/**
- * This file contains the handling of RX in wlan driver.
- */
+/*
+ * This file contains the handling of RX in wlan driver.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/etherdevice.h>
+#include <linux/hardirq.h>
+#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/export.h>
+#include <net/cfg80211.h>
-#include "hostcmd.h"
+#include "defs.h"
+#include "host.h"
#include "radiotap.h"
#include "decl.h"
#include "dev.h"
-#include "wext.h"
+#include "mesh.h"
struct eth803hdr {
u8 dest_addr[6];
u8 src_addr[6];
u16 h803_len;
-} __attribute__ ((packed));
+} __packed;
struct rfc1042hdr {
u8 llc_dsap;
@@ -22,122 +30,28 @@ struct rfc1042hdr {
u8 llc_ctrl;
u8 snap_oui[3];
u16 snap_type;
-} __attribute__ ((packed));
+} __packed;
struct rxpackethdr {
- struct rxpd rx_pd;
struct eth803hdr eth803_hdr;
struct rfc1042hdr rfc1042_hdr;
-} __attribute__ ((packed));
+} __packed;
struct rx80211packethdr {
struct rxpd rx_pd;
void *eth80211_hdr;
-} __attribute__ ((packed));
+} __packed;
static int process_rxed_802_11_packet(struct lbs_private *priv,
struct sk_buff *skb);
/**
- * @brief This function computes the avgSNR .
- *
- * @param priv A pointer to struct lbs_private structure
- * @return avgSNR
- */
-static u8 lbs_getavgsnr(struct lbs_private *priv)
-{
- u8 i;
- u16 temp = 0;
- if (priv->numSNRNF == 0)
- return 0;
- for (i = 0; i < priv->numSNRNF; i++)
- temp += priv->rawSNR[i];
- return (u8) (temp / priv->numSNRNF);
-
-}
-
-/**
- * @brief This function computes the AvgNF
- *
- * @param priv A pointer to struct lbs_private structure
- * @return AvgNF
- */
-static u8 lbs_getavgnf(struct lbs_private *priv)
-{
- u8 i;
- u16 temp = 0;
- if (priv->numSNRNF == 0)
- return 0;
- for (i = 0; i < priv->numSNRNF; i++)
- temp += priv->rawNF[i];
- return (u8) (temp / priv->numSNRNF);
-
-}
-
-/**
- * @brief This function save the raw SNR/NF to our internel buffer
- *
- * @param priv A pointer to struct lbs_private structure
- * @param prxpd A pointer to rxpd structure of received packet
- * @return n/a
- */
-static void lbs_save_rawSNRNF(struct lbs_private *priv, struct rxpd *p_rx_pd)
-{
- if (priv->numSNRNF < DEFAULT_DATA_AVG_FACTOR)
- priv->numSNRNF++;
- priv->rawSNR[priv->nextSNRNF] = p_rx_pd->snr;
- priv->rawNF[priv->nextSNRNF] = p_rx_pd->nf;
- priv->nextSNRNF++;
- if (priv->nextSNRNF >= DEFAULT_DATA_AVG_FACTOR)
- priv->nextSNRNF = 0;
- return;
-}
-
-/**
- * @brief This function computes the RSSI in received packet.
- *
- * @param priv A pointer to struct lbs_private structure
- * @param prxpd A pointer to rxpd structure of received packet
- * @return n/a
- */
-static void lbs_compute_rssi(struct lbs_private *priv, struct rxpd *p_rx_pd)
-{
-
- lbs_deb_enter(LBS_DEB_RX);
-
- lbs_deb_rx("rxpd: SNR %d, NF %d\n", p_rx_pd->snr, p_rx_pd->nf);
- lbs_deb_rx("before computing SNR: SNR-avg = %d, NF-avg = %d\n",
- priv->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
- priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
-
- priv->SNR[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->snr;
- priv->NF[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->nf;
- lbs_save_rawSNRNF(priv, p_rx_pd);
-
- priv->SNR[TYPE_RXPD][TYPE_AVG] = lbs_getavgsnr(priv) * AVG_SCALE;
- priv->NF[TYPE_RXPD][TYPE_AVG] = lbs_getavgnf(priv) * AVG_SCALE;
- lbs_deb_rx("after computing SNR: SNR-avg = %d, NF-avg = %d\n",
- priv->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
- priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
-
- priv->RSSI[TYPE_RXPD][TYPE_NOAVG] =
- CAL_RSSI(priv->SNR[TYPE_RXPD][TYPE_NOAVG],
- priv->NF[TYPE_RXPD][TYPE_NOAVG]);
-
- priv->RSSI[TYPE_RXPD][TYPE_AVG] =
- CAL_RSSI(priv->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
- priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
-
- lbs_deb_leave(LBS_DEB_RX);
-}
-
-/**
- * @brief This function processes received packet and forwards it
- * to kernel/upper layer
+ * lbs_process_rxed_packet - processes received packet and forwards it
+ * to kernel/upper layer
*
- * @param priv A pointer to struct lbs_private
- * @param skb A pointer to skb which includes the received packet
- * @return 0 or -1
+ * @priv: A pointer to &struct lbs_private
+ * @skb: A pointer to skb which includes the received packet
+ * returns: 0 or -1
*/
int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
{
@@ -147,7 +61,9 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
struct rxpd *p_rx_pd;
int hdrchop;
struct ethhdr *p_ethhdr;
- const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+ static const u8 rfc1042_eth_hdr[] = {
+ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00
+ };
lbs_deb_enter(LBS_DEB_RX);
@@ -155,13 +71,16 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
skb->ip_summed = CHECKSUM_NONE;
- if (priv->monitormode)
- return process_rxed_802_11_packet(priv, skb);
+ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+ ret = process_rxed_802_11_packet(priv, skb);
+ goto done;
+ }
- p_rx_pkt = (struct rxpackethdr *) skb->data;
- p_rx_pd = &p_rx_pkt->rx_pd;
- if (priv->mesh_dev && (p_rx_pd->rx_control & RxPD_MESH_FRAME))
- dev = priv->mesh_dev;
+ p_rx_pd = (struct rxpd *) skb->data;
+ p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd +
+ le32_to_cpu(p_rx_pd->pkt_ptr));
+
+ dev = lbs_mesh_set_dev(priv, dev, p_rx_pd);
lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data,
min_t(unsigned int, skb->len, 100));
@@ -169,25 +88,14 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
lbs_deb_rx("rx err: frame received with bad length\n");
dev->stats.rx_length_errors++;
- ret = 0;
- dev_kfree_skb(skb);
- goto done;
- }
-
- /*
- * Check rxpd status and update 802.3 stat,
- */
- if (!(p_rx_pd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) {
- lbs_deb_rx("rx err: frame received with bad status\n");
- lbs_pr_alert("rxpd not ok\n");
- dev->stats.rx_errors++;
- ret = 0;
+ ret = -EINVAL;
dev_kfree_skb(skb);
goto done;
}
- lbs_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n",
- skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
+ lbs_deb_rx("rx data: skb->len - pkt_ptr = %d-%zd = %zd\n",
+ skb->len, (size_t)le32_to_cpu(p_rx_pd->pkt_ptr),
+ skb->len - (size_t)le32_to_cpu(p_rx_pd->pkt_ptr));
lbs_deb_hex(LBS_DEB_RX, "RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr,
sizeof(p_rx_pkt->eth803_hdr.dest_addr));
@@ -207,7 +115,7 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
* before the snap_type.
*/
p_ethhdr = (struct ethhdr *)
- ((u8 *) & p_rx_pkt->eth803_hdr
+ ((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)
@@ -221,14 +129,14 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
/* 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;
+ hdrchop = (u8 *)p_ethhdr - (u8 *)p_rx_pd;
} else {
lbs_deb_hex(LBS_DEB_RX, "RX Data: LLC/SNAP",
- (u8 *) & p_rx_pkt->rfc1042_hdr,
+ (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;
+ hdrchop = (u8 *)&p_rx_pkt->eth803_hdr - (u8 *)p_rx_pd;
}
/* Chop off the leading header bytes so the skb points to the start of
@@ -236,13 +144,7 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
*/
skb_pull(skb, hdrchop);
- /* Take the data rate from the rxpd structure
- * only if the rate is auto
- */
- if (priv->enablehwauto)
- priv->cur_rate = lbs_fw_index_to_data_rate(p_rx_pd->rx_rate);
-
- lbs_compute_rssi(priv, p_rx_pd);
+ priv->cur_rate = lbs_fw_index_to_data_rate(p_rx_pd->rx_rate);
lbs_deb_rx("rx data: size of actual packet %d\n", skb->len);
dev->stats.rx_bytes += skb->len;
@@ -262,11 +164,11 @@ done:
EXPORT_SYMBOL_GPL(lbs_process_rxed_packet);
/**
- * @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)
+ * convert_mv_rate_to_radiotap - converts Tx/Rx rates from 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)
+ * @rate: Input rate
+ * returns: Output Rate (0 if invalid)
*/
static u8 convert_mv_rate_to_radiotap(u8 rate)
{
@@ -297,17 +199,17 @@ static u8 convert_mv_rate_to_radiotap(u8 rate)
case 12: /* 54 Mbps */
return 108;
}
- lbs_pr_alert("Invalid Marvell WLAN rate %i\n", rate);
+ 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
+ * process_rxed_802_11_packet - processes a received 802.11 packet and forwards
+ * it to kernel/upper layer
*
- * @param priv A pointer to struct lbs_private
- * @param skb A pointer to skb which includes the received packet
- * @return 0 or -1
+ * @priv: A pointer to &struct lbs_private
+ * @skb: A pointer to skb which includes the received packet
+ * returns: 0 or -1
*/
static int process_rxed_802_11_packet(struct lbs_private *priv,
struct sk_buff *skb)
@@ -324,7 +226,7 @@ static int process_rxed_802_11_packet(struct lbs_private *priv,
p_rx_pkt = (struct rx80211packethdr *) skb->data;
prxpd = &p_rx_pkt->rx_pd;
- // lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, min(skb->len, 100));
+ /* lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); */
if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
lbs_deb_rx("rx err: frame received with bad length\n");
@@ -334,27 +236,16 @@ static int process_rxed_802_11_packet(struct lbs_private *priv,
goto done;
}
- /*
- * Check rxpd status and update 802.3 stat,
- */
- if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) {
- //lbs_deb_rx("rx err: frame received with bad status\n");
- dev->stats.rx_errors++;
- }
-
lbs_deb_rx("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 */
/* radiotap header */
- radiotap_hdr.hdr.it_version = 0;
- /* XXX must check this value for pad */
- radiotap_hdr.hdr.it_pad = 0;
+ memset(&radiotap_hdr, 0, sizeof(radiotap_hdr));
+ /* XXX must check radiotap_hdr.hdr.it_pad for pad */
radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr));
radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT);
- if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
- radiotap_hdr.flags |= IEEE80211_RADIOTAP_F_BADFCS;
radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate);
/* XXX must check no carryout */
radiotap_hdr.antsignal = prxpd->snr + prxpd->nf;
@@ -365,7 +256,7 @@ static int process_rxed_802_11_packet(struct lbs_private *priv,
/* 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__);
+ netdev_alert(dev, "%s: couldn't pskb_expand_head\n", __func__);
ret = -ENOMEM;
kfree_skb(skb);
goto done;
@@ -374,20 +265,18 @@ static int process_rxed_802_11_packet(struct lbs_private *priv,
pradiotap_hdr = (void *)skb_push(skb, sizeof(struct rx_radiotap_hdr));
memcpy(pradiotap_hdr, &radiotap_hdr, sizeof(struct rx_radiotap_hdr));
- /* Take the data rate from the rxpd structure
- * only if the rate is auto
- */
- if (priv->enablehwauto)
- priv->cur_rate = lbs_fw_index_to_data_rate(prxpd->rx_rate);
-
- lbs_compute_rssi(priv, prxpd);
+ priv->cur_rate = lbs_fw_index_to_data_rate(prxpd->rx_rate);
lbs_deb_rx("rx data: size of actual packet %d\n", skb->len);
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
- skb->protocol = eth_type_trans(skb, priv->rtap_net_dev);
- netif_rx(skb);
+ skb->protocol = eth_type_trans(skb, priv->dev);
+
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ netif_rx_ni(skb);
ret = 0;
diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
deleted file mode 100644
index 8124db36aaf..00000000000
--- a/drivers/net/wireless/libertas/scan.c
+++ /dev/null
@@ -1,1209 +0,0 @@
-/**
- * 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/types.h>
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-#include <asm/unaligned.h>
-#include <net/lib80211.h>
-
-#include "host.h"
-#include "decl.h"
-#include "dev.h"
-#include "scan.h"
-#include "cmd.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 cmd_ds_802_11_scan with all TLVs at max
-#define MAX_SCAN_CFG_ALLOC (sizeof(struct cmd_ds_802_11_scan) \
- + 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
-
-#define DEFAULT_MAX_SCAN_AGE (15 * HZ)
-
-static int lbs_ret_80211_scan(struct lbs_private *priv, unsigned long dummy,
- struct cmd_header *resp);
-
-/*********************************************************************/
-/* */
-/* Misc helper functions */
-/* */
-/*********************************************************************/
-
-/**
- * @brief Unsets the MSB on basic rates
- *
- * Scan through an array and unset the MSB for basic data rates.
- *
- * @param rates buffer of data rates
- * @param len size of buffer
- */
-static void lbs_unset_basic_rate_flags(u8 *rates, size_t len)
-{
- int i;
-
- for (i = 0; i < len; i++)
- rates[i] &= 0x7f;
-}
-
-
-static inline void clear_bss_descriptor(struct bss_descriptor *bss)
-{
- /* Don't blow away ->list, just BSS data */
- memset(bss, 0, offsetof(struct bss_descriptor, list));
-}
-
-/**
- * @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 lbs_ssid_cmp(uint8_t *ssid1, uint8_t ssid1_len, uint8_t *ssid2,
- uint8_t ssid2_len)
-{
- if (ssid1_len != ssid2_len)
- return -1;
-
- return memcmp(ssid1, ssid2, ssid1_len);
-}
-
-static inline int is_same_network(struct bss_descriptor *src,
- struct bss_descriptor *dst)
-{
- /* A network is only a duplicate if the channel, BSSID, and ESSID
- * all match. We treat all <hidden> with the same BSSID and channel
- * as one network */
- return ((src->ssid_len == dst->ssid_len) &&
- (src->channel == dst->channel) &&
- !compare_ether_addr(src->bssid, dst->bssid) &&
- !memcmp(src->ssid, dst->ssid, src->ssid_len));
-}
-
-
-
-
-/*********************************************************************/
-/* */
-/* Main scanning support */
-/* */
-/*********************************************************************/
-
-/**
- * @brief Create a channel list for the driver to scan based on region info
- *
- * Only used from lbs_scan_setup_scan_config()
- *
- * 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 struct lbs_private structure
- * @param scanchanlist Output parameter: resulting channel list to scan
- *
- * @return void
- */
-static int lbs_scan_create_channel_list(struct lbs_private *priv,
- struct chanscanparamset *scanchanlist)
-{
- struct region_channel *scanregion;
- struct chan_freq_power *cfp;
- int rgnidx;
- int chanidx;
- int nextchan;
- uint8_t 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 = CMD_SCAN_TYPE_ACTIVE;
-
- for (rgnidx = 0; rgnidx < ARRAY_SIZE(priv->region_channel); rgnidx++) {
- if (priv->enable11d && (priv->connect_status != LBS_CONNECTED)
- && (priv->mesh_connect_status != LBS_CONNECTED)) {
- /* Scan all the supported chan for the first scan */
- if (!priv->universal_channel[rgnidx].valid)
- continue;
- scanregion = &priv->universal_channel[rgnidx];
-
- /* clear the parsed_region_chan for the first scan */
- memset(&priv->parsed_region_chan, 0x00,
- sizeof(priv->parsed_region_chan));
- } else {
- if (!priv->region_channel[rgnidx].valid)
- continue;
- scanregion = &priv->region_channel[rgnidx];
- }
-
- for (nextchan = 0; nextchan < scanregion->nrcfp; nextchan++, chanidx++) {
- struct chanscanparamset *chan = &scanchanlist[chanidx];
-
- cfp = scanregion->CFP + nextchan;
-
- if (priv->enable11d)
- scantype = lbs_get_scan_type_11d(cfp->channel,
- &priv->parsed_region_chan);
-
- if (scanregion->band == BAND_B || scanregion->band == BAND_G)
- chan->radiotype = CMD_SCAN_RADIO_TYPE_BG;
-
- if (scantype == CMD_SCAN_TYPE_PASSIVE) {
- chan->maxscantime = cpu_to_le16(MRVDRV_PASSIVE_SCAN_CHAN_TIME);
- chan->chanscanmode.passivescan = 1;
- } else {
- chan->maxscantime = cpu_to_le16(MRVDRV_ACTIVE_SCAN_CHAN_TIME);
- chan->chanscanmode.passivescan = 0;
- }
-
- chan->channumber = cfp->channel;
- }
- }
- return chanidx;
-}
-
-/*
- * Add SSID TLV of the form:
- *
- * TLV-ID SSID 00 00
- * length 06 00
- * ssid 4d 4e 54 45 53 54
- */
-static int lbs_scan_add_ssid_tlv(struct lbs_private *priv, u8 *tlv)
-{
- struct mrvlietypes_ssidparamset *ssid_tlv = (void *)tlv;
-
- ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID);
- ssid_tlv->header.len = cpu_to_le16(priv->scan_ssid_len);
- memcpy(ssid_tlv->ssid, priv->scan_ssid, priv->scan_ssid_len);
- return sizeof(ssid_tlv->header) + priv->scan_ssid_len;
-}
-
-/*
- * Add CHANLIST TLV of the form
- *
- * TLV-ID CHANLIST 01 01
- * length 5b 00
- * channel 1 00 01 00 00 00 64 00
- * radio type 00
- * channel 01
- * scan type 00
- * min scan time 00 00
- * max scan time 64 00
- * channel 2 00 02 00 00 00 64 00
- * channel 3 00 03 00 00 00 64 00
- * channel 4 00 04 00 00 00 64 00
- * channel 5 00 05 00 00 00 64 00
- * channel 6 00 06 00 00 00 64 00
- * channel 7 00 07 00 00 00 64 00
- * channel 8 00 08 00 00 00 64 00
- * channel 9 00 09 00 00 00 64 00
- * channel 10 00 0a 00 00 00 64 00
- * channel 11 00 0b 00 00 00 64 00
- * channel 12 00 0c 00 00 00 64 00
- * channel 13 00 0d 00 00 00 64 00
- *
- */
-static int lbs_scan_add_chanlist_tlv(uint8_t *tlv,
- struct chanscanparamset *chan_list,
- int chan_count)
-{
- size_t size = sizeof(struct chanscanparamset) *chan_count;
- struct mrvlietypes_chanlistparamset *chan_tlv = (void *)tlv;
-
- chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
- memcpy(chan_tlv->chanscanparam, chan_list, size);
- chan_tlv->header.len = cpu_to_le16(size);
- return sizeof(chan_tlv->header) + size;
-}
-
-/*
- * Add RATES TLV of the form
- *
- * TLV-ID RATES 01 00
- * length 0e 00
- * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c
- *
- * The rates are in lbs_bg_rates[], but for the 802.11b
- * rates the high bit isn't set.
- */
-static int lbs_scan_add_rates_tlv(uint8_t *tlv)
-{
- int i;
- struct mrvlietypes_ratesparamset *rate_tlv = (void *)tlv;
-
- rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
- tlv += sizeof(rate_tlv->header);
- for (i = 0; i < MAX_RATES; i++) {
- *tlv = lbs_bg_rates[i];
- if (*tlv == 0)
- break;
- /* This code makes sure that the 802.11b rates (1 MBit/s, 2
- MBit/s, 5.5 MBit/s and 11 MBit/s get's the high bit set.
- Note that the values are MBit/s * 2, to mark them as
- basic rates so that the firmware likes it better */
- if (*tlv == 0x02 || *tlv == 0x04 ||
- *tlv == 0x0b || *tlv == 0x16)
- *tlv |= 0x80;
- tlv++;
- }
- rate_tlv->header.len = cpu_to_le16(i);
- return sizeof(rate_tlv->header) + i;
-}
-
-/*
- * Generate the CMD_802_11_SCAN command with the proper tlv
- * for a bunch of channels.
- */
-static int lbs_do_scan(struct lbs_private *priv, uint8_t bsstype,
- struct chanscanparamset *chan_list, int chan_count)
-{
- int ret = -ENOMEM;
- struct cmd_ds_802_11_scan *scan_cmd;
- uint8_t *tlv; /* pointer into our current, growing TLV storage area */
-
- lbs_deb_enter_args(LBS_DEB_SCAN, "bsstype %d, chanlist[].chan %d, chan_count %d",
- bsstype, chan_list ? chan_list[0].channumber : -1,
- chan_count);
-
- /* create the fixed part for scan command */
- scan_cmd = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL);
- if (scan_cmd == NULL)
- goto out;
-
- tlv = scan_cmd->tlvbuffer;
- /* TODO: do we need to scan for a specific BSSID?
- memcpy(scan_cmd->bssid, priv->scan_bssid, ETH_ALEN); */
- scan_cmd->bsstype = bsstype;
-
- /* add TLVs */
- if (priv->scan_ssid_len)
- tlv += lbs_scan_add_ssid_tlv(priv, tlv);
- if (chan_list && chan_count)
- tlv += lbs_scan_add_chanlist_tlv(tlv, chan_list, chan_count);
- tlv += lbs_scan_add_rates_tlv(tlv);
-
- /* This is the final data we are about to send */
- scan_cmd->hdr.size = cpu_to_le16(tlv - (uint8_t *)scan_cmd);
- lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd,
- sizeof(*scan_cmd));
- lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer,
- tlv - scan_cmd->tlvbuffer);
-
- ret = __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr,
- le16_to_cpu(scan_cmd->hdr.size),
- lbs_ret_80211_scan, 0);
-
-out:
- kfree(scan_cmd);
- lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
- 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 struct lbs_private structure
- * @param full_scan Do a full-scan (blocking)
- *
- * @return 0 or < 0 if error
- */
-int lbs_scan_networks(struct lbs_private *priv, int full_scan)
-{
- int ret = -ENOMEM;
- struct chanscanparamset *chan_list;
- struct chanscanparamset *curr_chans;
- int chan_count;
- uint8_t bsstype = CMD_BSS_TYPE_ANY;
- int numchannels = MRVDRV_CHANNELS_PER_SCAN_CMD;
- union iwreq_data wrqu;
-#ifdef CONFIG_LIBERTAS_DEBUG
- struct bss_descriptor *iter;
- int i = 0;
- DECLARE_SSID_BUF(ssid);
-#endif
-
- lbs_deb_enter_args(LBS_DEB_SCAN, "full_scan %d", full_scan);
-
- /* Cancel any partial outstanding partial scans if this scan
- * is a full scan.
- */
- if (full_scan && delayed_work_pending(&priv->scan_work))
- cancel_delayed_work(&priv->scan_work);
-
- /* User-specified bsstype or channel list
- TODO: this can be implemented if some user-space application
- need the feature. Formerly, it was accessible from debugfs,
- but then nowhere used.
- if (user_cfg) {
- if (user_cfg->bsstype)
- bsstype = user_cfg->bsstype;
- } */
-
- lbs_deb_scan("numchannels %d, bsstype %d\n", numchannels, bsstype);
-
- /* Create list of channels to scan */
- chan_list = kzalloc(sizeof(struct chanscanparamset) *
- LBS_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL);
- if (!chan_list) {
- lbs_pr_alert("SCAN: chan_list empty\n");
- goto out;
- }
-
- /* We want to scan all channels */
- chan_count = lbs_scan_create_channel_list(priv, chan_list);
-
- netif_stop_queue(priv->dev);
- netif_carrier_off(priv->dev);
- if (priv->mesh_dev) {
- netif_stop_queue(priv->mesh_dev);
- netif_carrier_off(priv->mesh_dev);
- }
-
- /* Prepare to continue an interrupted scan */
- lbs_deb_scan("chan_count %d, scan_channel %d\n",
- chan_count, priv->scan_channel);
- curr_chans = chan_list;
- /* advance channel list by already-scanned-channels */
- if (priv->scan_channel > 0) {
- curr_chans += priv->scan_channel;
- chan_count -= priv->scan_channel;
- }
-
- /* Send scan command(s)
- * numchannels contains the number of channels we should maximally scan
- * chan_count is the total number of channels to scan
- */
-
- while (chan_count) {
- int to_scan = min(numchannels, chan_count);
- lbs_deb_scan("scanning %d of %d channels\n",
- to_scan, chan_count);
- ret = lbs_do_scan(priv, bsstype, curr_chans,
- to_scan);
- if (ret) {
- lbs_pr_err("SCAN_CMD failed\n");
- goto out2;
- }
- curr_chans += to_scan;
- chan_count -= to_scan;
-
- /* somehow schedule the next part of the scan */
- if (chan_count && !full_scan &&
- !priv->surpriseremoved) {
- /* -1 marks just that we're currently scanning */
- if (priv->scan_channel < 0)
- priv->scan_channel = to_scan;
- else
- priv->scan_channel += to_scan;
- cancel_delayed_work(&priv->scan_work);
- queue_delayed_work(priv->work_thread, &priv->scan_work,
- msecs_to_jiffies(300));
- /* skip over GIWSCAN event */
- goto out;
- }
-
- }
- memset(&wrqu, 0, sizeof(union iwreq_data));
- wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL);
-
-#ifdef CONFIG_LIBERTAS_DEBUG
- /* Dump the scan table */
- mutex_lock(&priv->lock);
- lbs_deb_scan("scan table:\n");
- list_for_each_entry(iter, &priv->network_list, list)
- lbs_deb_scan("%02d: BSSID %pM, RSSI %d, SSID '%s'\n",
- i++, iter->bssid, iter->rssi,
- print_ssid(ssid, iter->ssid, iter->ssid_len));
- mutex_unlock(&priv->lock);
-#endif
-
-out2:
- priv->scan_channel = 0;
-
-out:
- if (priv->connect_status == LBS_CONNECTED) {
- netif_carrier_on(priv->dev);
- if (!priv->tx_pending_len)
- netif_wake_queue(priv->dev);
- }
- if (priv->mesh_dev && (priv->mesh_connect_status == LBS_CONNECTED)) {
- netif_carrier_on(priv->mesh_dev);
- if (!priv->tx_pending_len)
- netif_wake_queue(priv->mesh_dev);
- }
- kfree(chan_list);
-
- lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
- return ret;
-}
-
-void lbs_scan_worker(struct work_struct *work)
-{
- struct lbs_private *priv =
- container_of(work, struct lbs_private, scan_work.work);
-
- lbs_deb_enter(LBS_DEB_SCAN);
- lbs_scan_networks(priv, 0);
- lbs_deb_leave(LBS_DEB_SCAN);
-}
-
-
-/*********************************************************************/
-/* */
-/* Result interpretation */
-/* */
-/*********************************************************************/
-
-/**
- * @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 bss Output parameter: Pointer to the BSS Entry
- *
- * @return 0 or -1
- */
-static int lbs_process_bss(struct bss_descriptor *bss,
- uint8_t **pbeaconinfo, int *bytesleft)
-{
- struct ieeetypes_fhparamset *pFH;
- struct ieeetypes_dsparamset *pDS;
- struct ieeetypes_cfparamset *pCF;
- struct ieeetypes_ibssparamset *pibss;
- DECLARE_SSID_BUF(ssid);
- struct ieeetypes_countryinfoset *pcountryinfo;
- uint8_t *pos, *end, *p;
- uint8_t n_ex_rates = 0, got_basic_rates = 0, n_basic_rates = 0;
- uint16_t beaconsize = 0;
- int ret;
-
- lbs_deb_enter(LBS_DEB_SCAN);
-
- if (*bytesleft >= sizeof(beaconsize)) {
- /* Extract & convert beacon size from the command buffer */
- beaconsize = get_unaligned_le16(*pbeaconinfo);
- *bytesleft -= sizeof(beaconsize);
- *pbeaconinfo += sizeof(beaconsize);
- }
-
- if (beaconsize == 0 || beaconsize > *bytesleft) {
- *pbeaconinfo += *bytesleft;
- *bytesleft = 0;
- ret = -1;
- goto done;
- }
-
- /* Initialize the current working beacon pointer for this BSS iteration */
- pos = *pbeaconinfo;
- end = pos + beaconsize;
-
- /* Advance the return beacon pointer past the current beacon */
- *pbeaconinfo += beaconsize;
- *bytesleft -= beaconsize;
-
- memcpy(bss->bssid, pos, ETH_ALEN);
- lbs_deb_scan("process_bss: BSSID %pM\n", bss->bssid);
- pos += ETH_ALEN;
-
- if ((end - pos) < 12) {
- lbs_deb_scan("process_bss: Not enough bytes left\n");
- ret = -1;
- goto done;
- }
-
- /*
- * next 4 fields are RSSI, time stamp, beacon interval,
- * and capability information
- */
-
- /* RSSI is 1 byte long */
- bss->rssi = *pos;
- lbs_deb_scan("process_bss: RSSI %d\n", *pos);
- pos++;
-
- /* time stamp is 8 bytes long */
- pos += 8;
-
- /* beacon interval is 2 bytes long */
- bss->beaconperiod = get_unaligned_le16(pos);
- pos += 2;
-
- /* capability information is 2 bytes long */
- bss->capability = get_unaligned_le16(pos);
- lbs_deb_scan("process_bss: capabilities 0x%04x\n", bss->capability);
- pos += 2;
-
- if (bss->capability & WLAN_CAPABILITY_PRIVACY)
- lbs_deb_scan("process_bss: WEP enabled\n");
- if (bss->capability & WLAN_CAPABILITY_IBSS)
- bss->mode = IW_MODE_ADHOC;
- else
- bss->mode = IW_MODE_INFRA;
-
- /* rest of the current buffer are IE's */
- lbs_deb_scan("process_bss: IE len %zd\n", end - pos);
- lbs_deb_hex(LBS_DEB_SCAN, "process_bss: IE info", pos, end - pos);
-
- /* process variable IE */
- while (pos <= end - 2) {
- if (pos + pos[1] > end) {
- lbs_deb_scan("process_bss: error in processing IE, "
- "bytes left < IE length\n");
- break;
- }
-
- switch (pos[0]) {
- case WLAN_EID_SSID:
- bss->ssid_len = min_t(int, IEEE80211_MAX_SSID_LEN, pos[1]);
- memcpy(bss->ssid, pos + 2, bss->ssid_len);
- lbs_deb_scan("got SSID IE: '%s', len %u\n",
- print_ssid(ssid, bss->ssid, bss->ssid_len),
- bss->ssid_len);
- break;
-
- case WLAN_EID_SUPP_RATES:
- n_basic_rates = min_t(uint8_t, MAX_RATES, pos[1]);
- memcpy(bss->rates, pos + 2, n_basic_rates);
- got_basic_rates = 1;
- lbs_deb_scan("got RATES IE\n");
- break;
-
- case WLAN_EID_FH_PARAMS:
- pFH = (struct ieeetypes_fhparamset *) pos;
- memmove(&bss->phyparamset.fhparamset, pFH,
- sizeof(struct ieeetypes_fhparamset));
- lbs_deb_scan("got FH IE\n");
- break;
-
- case WLAN_EID_DS_PARAMS:
- pDS = (struct ieeetypes_dsparamset *) pos;
- bss->channel = pDS->currentchan;
- memcpy(&bss->phyparamset.dsparamset, pDS,
- sizeof(struct ieeetypes_dsparamset));
- lbs_deb_scan("got DS IE, channel %d\n", bss->channel);
- break;
-
- case WLAN_EID_CF_PARAMS:
- pCF = (struct ieeetypes_cfparamset *) pos;
- memcpy(&bss->ssparamset.cfparamset, pCF,
- sizeof(struct ieeetypes_cfparamset));
- lbs_deb_scan("got CF IE\n");
- break;
-
- case WLAN_EID_IBSS_PARAMS:
- pibss = (struct ieeetypes_ibssparamset *) pos;
- bss->atimwindow = le16_to_cpu(pibss->atimwindow);
- memmove(&bss->ssparamset.ibssparamset, pibss,
- sizeof(struct ieeetypes_ibssparamset));
- lbs_deb_scan("got IBSS IE\n");
- break;
-
- case WLAN_EID_COUNTRY:
- pcountryinfo = (struct ieeetypes_countryinfoset *) pos;
- lbs_deb_scan("got COUNTRY IE\n");
- if (pcountryinfo->len < sizeof(pcountryinfo->countrycode)
- || pcountryinfo->len > 254) {
- lbs_deb_scan("process_bss: 11D- Err CountryInfo len %d, min %zd, max 254\n",
- pcountryinfo->len, sizeof(pcountryinfo->countrycode));
- ret = -1;
- goto done;
- }
-
- memcpy(&bss->countryinfo, pcountryinfo, pcountryinfo->len + 2);
- lbs_deb_hex(LBS_DEB_SCAN, "process_bss: 11d countryinfo",
- (uint8_t *) pcountryinfo,
- (int) (pcountryinfo->len + 2));
- break;
-
- case WLAN_EID_EXT_SUPP_RATES:
- /* only process extended supported rate if data rate is
- * already found. Data rate IE should come before
- * extended supported rate IE
- */
- lbs_deb_scan("got RATESEX IE\n");
- if (!got_basic_rates) {
- lbs_deb_scan("... but ignoring it\n");
- break;
- }
-
- n_ex_rates = pos[1];
- if (n_basic_rates + n_ex_rates > MAX_RATES)
- n_ex_rates = MAX_RATES - n_basic_rates;
-
- p = bss->rates + n_basic_rates;
- memcpy(p, pos + 2, n_ex_rates);
- break;
-
- case WLAN_EID_GENERIC:
- if (pos[1] >= 4 &&
- pos[2] == 0x00 && pos[3] == 0x50 &&
- pos[4] == 0xf2 && pos[5] == 0x01) {
- bss->wpa_ie_len = min(pos[1] + 2, MAX_WPA_IE_LEN);
- memcpy(bss->wpa_ie, pos, bss->wpa_ie_len);
- lbs_deb_scan("got WPA IE\n");
- lbs_deb_hex(LBS_DEB_SCAN, "WPA IE", bss->wpa_ie,
- bss->wpa_ie_len);
- } else if (pos[1] >= MARVELL_MESH_IE_LENGTH &&
- pos[2] == 0x00 && pos[3] == 0x50 &&
- pos[4] == 0x43 && pos[5] == 0x04) {
- lbs_deb_scan("got mesh IE\n");
- bss->mesh = 1;
- } else {
- lbs_deb_scan("got generic IE: %02x:%02x:%02x:%02x, len %d\n",
- pos[2], pos[3],
- pos[4], pos[5],
- pos[1]);
- }
- break;
-
- case WLAN_EID_RSN:
- lbs_deb_scan("got RSN IE\n");
- bss->rsn_ie_len = min(pos[1] + 2, MAX_WPA_IE_LEN);
- memcpy(bss->rsn_ie, pos, bss->rsn_ie_len);
- lbs_deb_hex(LBS_DEB_SCAN, "process_bss: RSN_IE",
- bss->rsn_ie, bss->rsn_ie_len);
- break;
-
- default:
- lbs_deb_scan("got IE 0x%04x, len %d\n",
- pos[0], pos[1]);
- break;
- }
-
- pos += pos[1] + 2;
- }
-
- /* Timestamp */
- bss->last_scanned = jiffies;
- lbs_unset_basic_rate_flags(bss->rates, sizeof(bss->rates));
-
- ret = 0;
-
-done:
- lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
- return ret;
-}
-
-/**
- * @brief Send a scan command for all available channels filtered on a spec
- *
- * Used in association code and from debugfs
- *
- * @param priv A pointer to struct lbs_private structure
- * @param ssid A pointer to the SSID to scan for
- * @param ssid_len Length of the SSID
- *
- * @return 0-success, otherwise fail
- */
-int lbs_send_specific_ssid_scan(struct lbs_private *priv, uint8_t *ssid,
- uint8_t ssid_len)
-{
- DECLARE_SSID_BUF(ssid_buf);
- int ret = 0;
-
- lbs_deb_enter_args(LBS_DEB_SCAN, "SSID '%s'\n",
- print_ssid(ssid_buf, ssid, ssid_len));
-
- if (!ssid_len)
- goto out;
-
- memcpy(priv->scan_ssid, ssid, ssid_len);
- priv->scan_ssid_len = ssid_len;
-
- lbs_scan_networks(priv, 1);
- if (priv->surpriseremoved) {
- ret = -1;
- goto out;
- }
-
-out:
- lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
- return ret;
-}
-
-
-
-
-/*********************************************************************/
-/* */
-/* Support for Wireless Extensions */
-/* */
-/*********************************************************************/
-
-
-#define MAX_CUSTOM_LEN 64
-
-static inline char *lbs_translate_scan(struct lbs_private *priv,
- struct iw_request_info *info,
- char *start, char *stop,
- struct bss_descriptor *bss)
-{
- struct chan_freq_power *cfp;
- char *current_val; /* For rates */
- struct iw_event iwe; /* Temporary buffer */
- int j;
-#define PERFECT_RSSI ((uint8_t)50)
-#define WORST_RSSI ((uint8_t)0)
-#define RSSI_DIFF ((uint8_t)(PERFECT_RSSI - WORST_RSSI))
- uint8_t rssi;
-
- lbs_deb_enter(LBS_DEB_SCAN);
-
- cfp = lbs_find_cfp_by_band_and_channel(priv, 0, bss->channel);
- if (!cfp) {
- lbs_deb_scan("Invalid channel number %d\n", bss->channel);
- start = NULL;
- goto out;
- }
-
- /* First entry *MUST* be the BSSID */
- iwe.cmd = SIOCGIWAP;
- iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
- memcpy(iwe.u.ap_addr.sa_data, &bss->bssid, ETH_ALEN);
- start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
-
- /* SSID */
- iwe.cmd = SIOCGIWESSID;
- iwe.u.data.flags = 1;
- iwe.u.data.length = min((uint32_t) bss->ssid_len, (uint32_t) IW_ESSID_MAX_SIZE);
- start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid);
-
- /* Mode */
- iwe.cmd = SIOCGIWMODE;
- iwe.u.mode = bss->mode;
- start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN);
-
- /* Frequency */
- iwe.cmd = SIOCGIWFREQ;
- iwe.u.freq.m = (long)cfp->freq * 100000;
- iwe.u.freq.e = 1;
- start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
-
- /* Add quality statistics */
- iwe.cmd = IWEVQUAL;
- iwe.u.qual.updated = IW_QUAL_ALL_UPDATED;
- iwe.u.qual.level = SCAN_RSSI(bss->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;
-
- if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
- iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
- } else {
- iwe.u.qual.noise = CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]);
- }
-
- /* Locally created ad-hoc BSSs won't have beacons if this is the
- * only station in the adhoc network; so get signal strength
- * from receive statistics.
- */
- if ((priv->mode == IW_MODE_ADHOC) && priv->adhoccreate
- && !lbs_ssid_cmp(priv->curbssparams.ssid,
- priv->curbssparams.ssid_len,
- bss->ssid, bss->ssid_len)) {
- int snr, nf;
- snr = priv->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
- nf = priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
- iwe.u.qual.level = CAL_RSSI(snr, nf);
- }
- start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
-
- /* Add encryption capability */
- iwe.cmd = SIOCGIWENCODE;
- if (bss->capability & WLAN_CAPABILITY_PRIVACY) {
- iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
- } else {
- iwe.u.data.flags = IW_ENCODE_DISABLED;
- }
- iwe.u.data.length = 0;
- start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid);
-
- current_val = start + iwe_stream_lcp_len(info);
-
- iwe.cmd = SIOCGIWRATE;
- iwe.u.bitrate.fixed = 0;
- iwe.u.bitrate.disabled = 0;
- iwe.u.bitrate.value = 0;
-
- for (j = 0; bss->rates[j] && (j < sizeof(bss->rates)); j++) {
- /* Bit rate given in 500 kb/s units */
- iwe.u.bitrate.value = bss->rates[j] * 500000;
- current_val = iwe_stream_add_value(info, start, current_val,
- stop, &iwe, IW_EV_PARAM_LEN);
- }
- if ((bss->mode == IW_MODE_ADHOC) && priv->adhoccreate
- && !lbs_ssid_cmp(priv->curbssparams.ssid,
- priv->curbssparams.ssid_len,
- bss->ssid, bss->ssid_len)) {
- iwe.u.bitrate.value = 22 * 500000;
- current_val = iwe_stream_add_value(info, start, current_val,
- stop, &iwe, IW_EV_PARAM_LEN);
- }
- /* Check if we added any event */
- if ((current_val - start) > iwe_stream_lcp_len(info))
- start = current_val;
-
- memset(&iwe, 0, sizeof(iwe));
- if (bss->wpa_ie_len) {
- char buf[MAX_WPA_IE_LEN];
- memcpy(buf, bss->wpa_ie, bss->wpa_ie_len);
- iwe.cmd = IWEVGENIE;
- iwe.u.data.length = bss->wpa_ie_len;
- start = iwe_stream_add_point(info, start, stop, &iwe, buf);
- }
-
- memset(&iwe, 0, sizeof(iwe));
- if (bss->rsn_ie_len) {
- char buf[MAX_WPA_IE_LEN];
- memcpy(buf, bss->rsn_ie, bss->rsn_ie_len);
- iwe.cmd = IWEVGENIE;
- iwe.u.data.length = bss->rsn_ie_len;
- start = iwe_stream_add_point(info, start, stop, &iwe, buf);
- }
-
- if (bss->mesh) {
- char custom[MAX_CUSTOM_LEN];
- char *p = custom;
-
- iwe.cmd = IWEVCUSTOM;
- p += snprintf(p, MAX_CUSTOM_LEN, "mesh-type: olpc");
- iwe.u.data.length = p - custom;
- if (iwe.u.data.length)
- start = iwe_stream_add_point(info, start, stop,
- &iwe, custom);
- }
-
-out:
- lbs_deb_leave_args(LBS_DEB_SCAN, "start %p", start);
- return start;
-}
-
-
-/**
- * @brief Handle Scan Network ioctl
- *
- * @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 lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- DECLARE_SSID_BUF(ssid);
- struct lbs_private *priv = dev->ml_priv;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (!priv->radio_on) {
- ret = -EINVAL;
- goto out;
- }
-
- if (!netif_running(dev)) {
- ret = -ENETDOWN;
- goto out;
- }
-
- /* mac80211 does this:
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->type != IEEE80211_IF_TYPE_xxx) {
- ret = -EOPNOTSUPP;
- goto out;
- }
- */
-
- if (wrqu->data.length == sizeof(struct iw_scan_req) &&
- wrqu->data.flags & IW_SCAN_THIS_ESSID) {
- struct iw_scan_req *req = (struct iw_scan_req *)extra;
- priv->scan_ssid_len = req->essid_len;
- memcpy(priv->scan_ssid, req->essid, priv->scan_ssid_len);
- lbs_deb_wext("set_scan, essid '%s'\n",
- print_ssid(ssid, priv->scan_ssid, priv->scan_ssid_len));
- } else {
- priv->scan_ssid_len = 0;
- }
-
- if (!delayed_work_pending(&priv->scan_work))
- queue_delayed_work(priv->work_thread, &priv->scan_work,
- msecs_to_jiffies(50));
- /* set marker that currently a scan is taking place */
- priv->scan_channel = -1;
-
- if (priv->surpriseremoved)
- ret = -EIO;
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-
-/**
- * @brief Handle Retrieve scan table ioctl
- *
- * @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 lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
-#define SCAN_ITEM_SIZE 128
- struct lbs_private *priv = dev->ml_priv;
- int err = 0;
- char *ev = extra;
- char *stop = ev + dwrq->length;
- struct bss_descriptor *iter_bss;
- struct bss_descriptor *safe;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- /* iwlist should wait until the current scan is finished */
- if (priv->scan_channel)
- return -EAGAIN;
-
- /* Update RSSI if current BSS is a locally created ad-hoc BSS */
- if ((priv->mode == IW_MODE_ADHOC) && priv->adhoccreate)
- lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
- CMD_OPTION_WAITFORRSP, 0, NULL);
-
- mutex_lock(&priv->lock);
- list_for_each_entry_safe (iter_bss, safe, &priv->network_list, list) {
- char *next_ev;
- unsigned long stale_time;
-
- if (stop - ev < SCAN_ITEM_SIZE) {
- err = -E2BIG;
- break;
- }
-
- /* For mesh device, list only mesh networks */
- if (dev == priv->mesh_dev && !iter_bss->mesh)
- continue;
-
- /* Prune old an old scan result */
- stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE;
- if (time_after(jiffies, stale_time)) {
- list_move_tail(&iter_bss->list, &priv->network_free_list);
- clear_bss_descriptor(iter_bss);
- continue;
- }
-
- /* Translate to WE format this entry */
- next_ev = lbs_translate_scan(priv, info, ev, stop, iter_bss);
- if (next_ev == NULL)
- continue;
- ev = next_ev;
- }
- mutex_unlock(&priv->lock);
-
- dwrq->length = (ev - extra);
- dwrq->flags = 0;
-
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", err);
- return err;
-}
-
-
-
-
-/*********************************************************************/
-/* */
-/* Command execution */
-/* */
-/*********************************************************************/
-
-
-/**
- * @brief This function handles the command response of scan
- *
- * Called from handle_cmd_response() in cmdrespc.
- *
- * 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 struct lbs_private structure
- * @param resp A pointer to cmd_ds_command
- *
- * @return 0 or -1
- */
-static int lbs_ret_80211_scan(struct lbs_private *priv, unsigned long dummy,
- struct cmd_header *resp)
-{
- struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp;
- struct bss_descriptor *iter_bss;
- struct bss_descriptor *safe;
- uint8_t *bssinfo;
- uint16_t scanrespsize;
- int bytesleft;
- int idx;
- int tlvbufsize;
- int ret;
-
- lbs_deb_enter(LBS_DEB_SCAN);
-
- /* Prune old entries from scan table */
- list_for_each_entry_safe (iter_bss, safe, &priv->network_list, list) {
- unsigned long stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE;
- if (time_before(jiffies, stale_time))
- continue;
- list_move_tail (&iter_bss->list, &priv->network_free_list);
- clear_bss_descriptor(iter_bss);
- }
-
- if (scanresp->nr_sets > MAX_NETWORK_COUNT) {
- lbs_deb_scan("SCAN_RESP: too many scan results (%d, max %d)\n",
- scanresp->nr_sets, MAX_NETWORK_COUNT);
- ret = -1;
- goto done;
- }
-
- bytesleft = le16_to_cpu(scanresp->bssdescriptsize);
- lbs_deb_scan("SCAN_RESP: bssdescriptsize %d\n", bytesleft);
-
- scanrespsize = le16_to_cpu(resp->size);
- lbs_deb_scan("SCAN_RESP: scan results %d\n", scanresp->nr_sets);
-
- bssinfo = scanresp->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(scanresp->bssdescriptsize)
- + sizeof(scanresp->nr_sets)
- + S_DS_GEN);
-
- /*
- * Process each scan response returned (scanresp->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 < scanresp->nr_sets && bytesleft; idx++) {
- struct bss_descriptor new;
- struct bss_descriptor *found = NULL;
- struct bss_descriptor *oldest = NULL;
-
- /* Process the data fields and IEs returned for this BSS */
- memset(&new, 0, sizeof (struct bss_descriptor));
- if (lbs_process_bss(&new, &bssinfo, &bytesleft) != 0) {
- /* error parsing the scan response, skipped */
- lbs_deb_scan("SCAN_RESP: process_bss returned ERROR\n");
- continue;
- }
-
- /* Try to find this bss in the scan table */
- list_for_each_entry (iter_bss, &priv->network_list, list) {
- if (is_same_network(iter_bss, &new)) {
- found = iter_bss;
- break;
- }
-
- if ((oldest == NULL) ||
- (iter_bss->last_scanned < oldest->last_scanned))
- oldest = iter_bss;
- }
-
- if (found) {
- /* found, clear it */
- clear_bss_descriptor(found);
- } else if (!list_empty(&priv->network_free_list)) {
- /* Pull one from the free list */
- found = list_entry(priv->network_free_list.next,
- struct bss_descriptor, list);
- list_move_tail(&found->list, &priv->network_list);
- } else if (oldest) {
- /* If there are no more slots, expire the oldest */
- found = oldest;
- clear_bss_descriptor(found);
- list_move_tail(&found->list, &priv->network_list);
- } else {
- continue;
- }
-
- lbs_deb_scan("SCAN_RESP: BSSID %pM\n", new.bssid);
-
- /* Copy the locally created newbssentry to the scan table */
- memcpy(found, &new, offsetof(struct bss_descriptor, list));
- }
-
- ret = 0;
-
-done:
- lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
- return ret;
-}
diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h
deleted file mode 100644
index fab7d5d097f..00000000000
--- a/drivers/net/wireless/libertas/scan.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Interface for the wlan network scan routines
- *
- * Driver interface functions and type declarations for the scan module
- * implemented in scan.c.
- */
-#ifndef _LBS_SCAN_H
-#define _LBS_SCAN_H
-
-#include <net/iw_handler.h>
-
-#define MAX_NETWORK_COUNT 128
-
-/**
- * @brief Maximum number of channels that can be sent in a setuserscan ioctl
- */
-#define LBS_IOCTL_USER_SCAN_CHAN_MAX 50
-
-int lbs_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len);
-
-int lbs_send_specific_ssid_scan(struct lbs_private *priv, u8 *ssid,
- u8 ssid_len);
-
-int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra);
-int lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra);
-
-int lbs_scan_networks(struct lbs_private *priv, int full_scan);
-
-void lbs_scan_worker(struct work_struct *work);
-
-#endif
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c
index f10aa39a6b6..c025f9c1828 100644
--- a/drivers/net/wireless/libertas/tx.c
+++ b/drivers/net/wireless/libertas/tx.c
@@ -1,22 +1,26 @@
-/**
- * This file contains the handling of TX in wlan driver.
- */
+/*
+ * This file contains the handling of TX in wlan driver.
+ */
+#include <linux/hardirq.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/sched.h>
+#include <linux/export.h>
+#include <net/cfg80211.h>
-#include "hostcmd.h"
+#include "host.h"
#include "radiotap.h"
#include "decl.h"
#include "defs.h"
#include "dev.h"
-#include "wext.h"
+#include "mesh.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)
+ * convert_radiotap_rate_to_mv - 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)
+ * @rate: Input rate
+ * returns: Output Rate (0 if invalid)
*/
static u32 convert_radiotap_rate_to_mv(u8 rate)
{
@@ -50,26 +54,24 @@ static u32 convert_radiotap_rate_to_mv(u8 rate)
}
/**
- * @brief This function checks the conditions and sends packet to IF
- * layer if everything is ok.
+ * lbs_hard_start_xmit - checks the conditions and sends packet to IF
+ * layer if everything is ok
*
- * @param priv A pointer to struct lbs_private structure
- * @param skb A pointer to skb which includes TX packet
- * @return 0 or -1
+ * @skb: A pointer to skb which includes TX packet
+ * @dev: A pointer to the &struct net_device
+ * returns: 0 or -1
*/
-int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
unsigned long flags;
struct lbs_private *priv = dev->ml_priv;
struct txpd *txpd;
char *p802x_hdr;
uint16_t pkt_len;
- int ret;
+ netdev_tx_t ret = NETDEV_TX_OK;
lbs_deb_enter(LBS_DEB_TX);
- ret = NETDEV_TX_OK;
-
/* We need to protect against the queues being restarted before
we get round to stopping them */
spin_lock_irqsave(&priv->driver_lock, flags);
@@ -112,7 +114,7 @@ int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
p802x_hdr = skb->data;
pkt_len = skb->len;
- if (dev == priv->rtap_net_dev) {
+ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
struct tx_radiotap_hdr *rtap_hdr = (void *)skb->data;
/* set txpd fields from the radiotap header */
@@ -132,8 +134,7 @@ int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
txpd->tx_packet_length = cpu_to_le16(pkt_len);
txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
- if (dev == priv->mesh_dev)
- txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
+ lbs_mesh_set_txpd(priv, dev, txpd);
lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd));
@@ -149,9 +150,7 @@ int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
- dev->trans_start = jiffies;
-
- if (priv->monitormode) {
+ if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
/* Keep the skb to echo it back once Tx feedback is
received from FW */
skb_orphan(skb);
@@ -162,6 +161,7 @@ int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
free:
dev_kfree_skb_any(skb);
}
+
unlock:
spin_unlock_irqrestore(&priv->driver_lock, flags);
wake_up(&priv->waitq);
@@ -171,19 +171,20 @@ int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
/**
- * @brief This function sends to the host the last transmitted packet,
- * filling the radiotap headers with transmission information.
+ * lbs_send_tx_feedback - sends to the host the last transmitted packet,
+ * filling the radiotap headers with transmission information.
*
- * @param priv A pointer to struct lbs_private structure
- * @param status A 32 bit value containing transmission status.
+ * @priv: A pointer to &struct lbs_private structure
+ * @try_count: A 32-bit value containing transmission retry status.
*
- * @returns void
+ * returns: void
*/
void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count)
{
struct tx_radiotap_hdr *radiotap_hdr;
- if (!priv->monitormode || priv->currenttxskb == NULL)
+ if (priv->wdev->iftype != NL80211_IFTYPE_MONITOR ||
+ priv->currenttxskb == NULL)
return;
radiotap_hdr = (struct tx_radiotap_hdr *)priv->currenttxskb->data;
@@ -192,7 +193,7 @@ void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count)
(1 + priv->txretrycount - try_count) : 0;
priv->currenttxskb->protocol = eth_type_trans(priv->currenttxskb,
- priv->rtap_net_dev);
+ priv->dev);
netif_rx(priv->currenttxskb);
priv->currenttxskb = NULL;
@@ -200,7 +201,7 @@ void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count)
if (priv->connect_status == LBS_CONNECTED)
netif_wake_queue(priv->dev);
- if (priv->mesh_dev && (priv->mesh_connect_status == LBS_CONNECTED))
+ if (priv->mesh_dev && netif_running(priv->mesh_dev))
netif_wake_queue(priv->mesh_dev);
}
EXPORT_SYMBOL_GPL(lbs_send_tx_feedback);
diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h
index fb7a2d1a252..cf1d9b047ee 100644
--- a/drivers/net/wireless/libertas/types.h
+++ b/drivers/net/wireless/libertas/types.h
@@ -1,62 +1,60 @@
-/**
- * This header file contains definition for global types
- */
+/*
+ * This header file contains definition for global types
+ */
#ifndef _LBS_TYPES_H_
#define _LBS_TYPES_H_
#include <linux/if_ether.h>
+#include <linux/ieee80211.h>
#include <asm/byteorder.h>
-#include <linux/wireless.h>
-struct ieeetypes_cfparamset {
- u8 elementid;
+struct ieee_ie_header {
+ u8 id;
u8 len;
+} __packed;
+
+struct ieee_ie_cf_param_set {
+ struct ieee_ie_header header;
+
u8 cfpcnt;
u8 cfpperiod;
__le16 cfpmaxduration;
__le16 cfpdurationremaining;
-} __attribute__ ((packed));
+} __packed;
-struct ieeetypes_ibssparamset {
- u8 elementid;
- u8 len;
+struct ieee_ie_ibss_param_set {
+ struct ieee_ie_header header;
+
__le16 atimwindow;
-} __attribute__ ((packed));
+} __packed;
-union IEEEtypes_ssparamset {
- struct ieeetypes_cfparamset cfparamset;
- struct ieeetypes_ibssparamset ibssparamset;
-} __attribute__ ((packed));
+union ieee_ss_param_set {
+ struct ieee_ie_cf_param_set cf;
+ struct ieee_ie_ibss_param_set ibss;
+} __packed;
+
+struct ieee_ie_fh_param_set {
+ struct ieee_ie_header header;
-struct ieeetypes_fhparamset {
- u8 elementid;
- u8 len;
__le16 dwelltime;
u8 hopset;
u8 hoppattern;
u8 hopindex;
-} __attribute__ ((packed));
+} __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 {
- __le16 capability;
- __le16 statuscode;
- __le16 aid;
- u8 iebuffer[1];
-} __attribute__ ((packed));
-
-/** TLV type ID definition */
+struct ieee_ie_ds_param_set {
+ struct ieee_ie_header header;
+
+ u8 channel;
+} __packed;
+
+union ieee_phy_param_set {
+ struct ieee_ie_fh_param_set fh;
+ struct ieee_ie_ds_param_set ds;
+} __packed;
+
+/* TLV type ID definition */
#define PROPRIETARY_TLV_BASE_ID 0x0100
/* Terminating TLV type */
@@ -94,33 +92,36 @@ struct ieeetypes_assocrsp {
#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)
+#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31)
+#define TLV_TYPE_MESH_ID (PROPRIETARY_TLV_BASE_ID + 37)
+#define TLV_TYPE_OLD_MESH_ID (PROPRIETARY_TLV_BASE_ID + 291)
-/** TLV related data structures*/
-struct mrvlietypesheader {
+/* TLV related data structures */
+struct mrvl_ie_header {
__le16 type;
__le16 len;
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_data {
- struct mrvlietypesheader header;
+struct mrvl_ie_data {
+ struct mrvl_ie_header header;
u8 Data[1];
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_ratesparamset {
- struct mrvlietypesheader header;
+struct mrvl_ie_rates_param_set {
+ struct mrvl_ie_header header;
u8 rates[1];
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_ssidparamset {
- struct mrvlietypesheader header;
+struct mrvl_ie_ssid_param_set {
+ struct mrvl_ie_header header;
u8 ssid[1];
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_wildcardssidparamset {
- struct mrvlietypesheader header;
+struct mrvl_ie_wildcard_ssid_param_set {
+ struct mrvl_ie_header header;
u8 MaxSsidlength;
u8 ssid[1];
-} __attribute__ ((packed));
+} __packed;
struct chanscanmode {
#ifdef __BIG_ENDIAN_BITFIELD
@@ -132,7 +133,7 @@ struct chanscanmode {
u8 disablechanfilt:1;
u8 reserved_2_7:6;
#endif
-} __attribute__ ((packed));
+} __packed;
struct chanscanparamset {
u8 radiotype;
@@ -140,122 +141,105 @@ struct chanscanparamset {
struct chanscanmode chanscanmode;
__le16 minscantime;
__le16 maxscantime;
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_chanlistparamset {
- struct mrvlietypesheader header;
+struct mrvl_ie_chanlist_param_set {
+ struct mrvl_ie_header header;
struct chanscanparamset chanscanparam[1];
-} __attribute__ ((packed));
+} __packed;
-struct cfparamset {
+struct mrvl_ie_cf_param_set {
+ struct mrvl_ie_header header;
u8 cfpcnt;
u8 cfpperiod;
__le16 cfpmaxduration;
__le16 cfpdurationremaining;
-} __attribute__ ((packed));
-
-struct ibssparamset {
- __le16 atimwindow;
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_ssparamset {
- struct mrvlietypesheader header;
- union {
- struct cfparamset cfparamset[1];
- struct ibssparamset ibssparamset[1];
- } cf_ibss;
-} __attribute__ ((packed));
+struct mrvl_ie_ds_param_set {
+ struct mrvl_ie_header header;
+ u8 channel;
+} __packed;
-struct fhparamset {
- __le16 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;
+struct mrvl_ie_rsn_param_set {
+ struct mrvl_ie_header header;
u8 rsnie[1];
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_tsftimestamp {
- struct mrvlietypesheader header;
+struct mrvl_ie_tsf_timestamp {
+ struct mrvl_ie_header header;
__le64 tsftable[1];
-} __attribute__ ((packed));
+} __packed;
+
+/* v9 and later firmware only */
+struct mrvl_ie_auth_type {
+ struct mrvl_ie_header header;
+ __le16 auth;
+} __packed;
-/** Local Power capability */
-struct mrvlietypes_powercapability {
- struct mrvlietypesheader header;
+/* Local Power capability */
+struct mrvl_ie_power_capability {
+ struct mrvl_ie_header header;
s8 minpower;
s8 maxpower;
-} __attribute__ ((packed));
+} __packed;
/* used in CMD_802_11_SUBSCRIBE_EVENT for SNR, RSSI and Failure */
-struct mrvlietypes_thresholds {
- struct mrvlietypesheader header;
+struct mrvl_ie_thresholds {
+ struct mrvl_ie_header header;
u8 value;
u8 freq;
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_beaconsmissed {
- struct mrvlietypesheader header;
+struct mrvl_ie_beacons_missed {
+ struct mrvl_ie_header header;
u8 beaconmissed;
u8 reserved;
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_numprobes {
- struct mrvlietypesheader header;
+struct mrvl_ie_num_probes {
+ struct mrvl_ie_header header;
__le16 numprobes;
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_bcastprobe {
- struct mrvlietypesheader header;
+struct mrvl_ie_bcast_probe {
+ struct mrvl_ie_header header;
__le16 bcastprobe;
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_numssidprobe {
- struct mrvlietypesheader header;
+struct mrvl_ie_num_ssid_probe {
+ struct mrvl_ie_header header;
__le16 numssidprobe;
-} __attribute__ ((packed));
+} __packed;
struct led_pin {
u8 led;
u8 pin;
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_ledgpio {
- struct mrvlietypesheader header;
+struct mrvl_ie_ledgpio {
+ struct mrvl_ie_header header;
struct led_pin ledpin[1];
-} __attribute__ ((packed));
+} __packed;
struct led_bhv {
uint8_t firmwarestate;
uint8_t led;
uint8_t ledstate;
uint8_t ledarg;
-} __attribute__ ((packed));
+} __packed;
-struct mrvlietypes_ledbhv {
- struct mrvlietypesheader header;
+struct mrvl_ie_ledbhv {
+ struct mrvl_ie_header header;
struct led_bhv ledbhv[1];
-} __attribute__ ((packed));
+} __packed;
-/* Meant to be packed as the value member of a struct ieee80211_info_element.
+/*
+ * Meant to be packed as the value member of a struct ieee80211_info_element.
* Note that the len member of the ieee80211_info_element varies depending on
- * the mesh_id_len */
+ * the mesh_id_len
+ */
struct mrvl_meshie_val {
uint8_t oui[3];
uint8_t type;
@@ -265,13 +249,13 @@ struct mrvl_meshie_val {
uint8_t active_metric_id;
uint8_t mesh_capability;
uint8_t mesh_id_len;
- uint8_t mesh_id[IW_ESSID_MAX_SIZE];
-} __attribute__ ((packed));
+ uint8_t mesh_id[IEEE80211_MAX_SSID_LEN];
+} __packed;
struct mrvl_meshie {
u8 id, len;
struct mrvl_meshie_val val;
-} __attribute__ ((packed));
+} __packed;
struct mrvl_mesh_defaults {
__le32 bootflag;
@@ -279,6 +263,6 @@ struct mrvl_mesh_defaults {
uint8_t reserved;
__le16 channel;
struct mrvl_meshie meshie;
-} __attribute__ ((packed));
+} __packed;
#endif
diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
deleted file mode 100644
index 8bc1907458b..00000000000
--- a/drivers/net/wireless/libertas/wext.c
+++ /dev/null
@@ -1,2266 +0,0 @@
-/**
- * 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/lib80211.h>
-#include <net/iw_handler.h>
-
-#include "host.h"
-#include "radiotap.h"
-#include "decl.h"
-#include "defs.h"
-#include "dev.h"
-#include "wext.h"
-#include "scan.h"
-#include "assoc.h"
-#include "cmd.h"
-
-
-static inline void lbs_postpone_association_work(struct lbs_private *priv)
-{
- if (priv->surpriseremoved)
- return;
- cancel_delayed_work(&priv->assoc_work);
- queue_delayed_work(priv->work_thread, &priv->assoc_work, HZ / 2);
-}
-
-static inline void lbs_do_association_work(struct lbs_private *priv)
-{
- if (priv->surpriseremoved)
- return;
- cancel_delayed_work(&priv->assoc_work);
- queue_delayed_work(priv->work_thread, &priv->assoc_work, 0);
-}
-
-static inline void lbs_cancel_association_work(struct lbs_private *priv)
-{
- cancel_delayed_work(&priv->assoc_work);
- kfree(priv->pending_assoc_req);
- priv->pending_assoc_req = NULL;
-}
-
-
-/**
- * @brief Find the channel frequency power info with specific channel
- *
- * @param priv A pointer to struct lbs_private 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 *lbs_find_cfp_by_band_and_channel(
- struct lbs_private *priv,
- u8 band,
- u16 channel)
-{
- struct chan_freq_power *cfp = NULL;
- struct region_channel *rc;
- int i, j;
-
- for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
- rc = &priv->region_channel[j];
-
- if (priv->enable11d)
- rc = &priv->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_deb_wext("lbs_find_cfp_by_band_and_channel: can't find "
- "cfp by band %d / channel %d\n", band, channel);
-
- return cfp;
-}
-
-/**
- * @brief Find the channel frequency power info with specific frequency
- *
- * @param priv A pointer to struct lbs_private 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(
- struct lbs_private *priv,
- u8 band,
- u32 freq)
-{
- struct chan_freq_power *cfp = NULL;
- struct region_channel *rc;
- int i, j;
-
- for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
- rc = &priv->region_channel[j];
-
- if (priv->enable11d)
- rc = &priv->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_deb_wext("find_cfp_by_band_and_freql: can't find cfp by "
- "band %d / freq %d\n", band, freq);
-
- return cfp;
-}
-
-/**
- * @brief Copy active data rates based on adapter mode and status
- *
- * @param priv A pointer to struct lbs_private structure
- * @param rate The buf to return the active rates
- */
-static void copy_active_data_rates(struct lbs_private *priv, u8 *rates)
-{
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if ((priv->connect_status != LBS_CONNECTED) &&
- (priv->mesh_connect_status != LBS_CONNECTED))
- memcpy(rates, lbs_bg_rates, MAX_RATES);
- else
- memcpy(rates, priv->curbssparams.rates, MAX_RATES);
-
- lbs_deb_leave(LBS_DEB_WEXT);
-}
-
-static int lbs_get_name(struct net_device *dev, struct iw_request_info *info,
- char *cwrq, char *extra)
-{
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- /* We could add support for 802.11n here as needed. Jean II */
- snprintf(cwrq, IFNAMSIZ, "IEEE 802.11b/g");
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
- struct iw_freq *fwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- struct chan_freq_power *cfp;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
- priv->curbssparams.channel);
-
- if (!cfp) {
- if (priv->curbssparams.channel)
- lbs_deb_wext("invalid channel %d\n",
- priv->curbssparams.channel);
- return -EINVAL;
- }
-
- fwrq->m = (long)cfp->freq * 100000;
- fwrq->e = 1;
-
- lbs_deb_wext("freq %u\n", fwrq->m);
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info,
- struct sockaddr *awrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (priv->connect_status == LBS_CONNECTED) {
- memcpy(awrq->sa_data, priv->curbssparams.bssid, ETH_ALEN);
- } else {
- memset(awrq->sa_data, 0, ETH_ALEN);
- }
- awrq->sa_family = ARPHRD_ETHER;
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- /*
- * Check the size of the string
- */
-
- if (dwrq->length > 16) {
- return -E2BIG;
- }
-
- mutex_lock(&priv->lock);
- memset(priv->nodename, 0, sizeof(priv->nodename));
- memcpy(priv->nodename, extra, dwrq->length);
- mutex_unlock(&priv->lock);
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- dwrq->length = strlen(priv->nodename);
- memcpy(extra, priv->nodename, dwrq->length);
- extra[dwrq->length] = '\0';
-
- dwrq->flags = 1; /* active */
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- /* Use nickname to indicate that mesh is on */
-
- if (priv->mesh_connect_status == LBS_CONNECTED) {
- strncpy(extra, "Mesh", 12);
- extra[12] = '\0';
- dwrq->length = strlen(extra);
- }
-
- else {
- extra[0] = '\0';
- dwrq->length = 0;
- }
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- int ret = 0;
- struct lbs_private *priv = dev->ml_priv;
- u32 val = vwrq->value;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (vwrq->disabled)
- val = MRVDRV_RTS_MAX_VALUE;
-
- if (val > MRVDRV_RTS_MAX_VALUE) /* min rts value is 0 */
- return -EINVAL;
-
- ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, (u16) val);
-
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- int ret = 0;
- u16 val = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
- if (ret)
- goto out;
-
- vwrq->value = val;
- vwrq->disabled = val > MRVDRV_RTS_MAX_VALUE; /* min rts value is 0 */
- vwrq->fixed = 1;
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- int ret = 0;
- u32 val = vwrq->value;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (vwrq->disabled)
- val = MRVDRV_FRAG_MAX_VALUE;
-
- if (val < MRVDRV_FRAG_MIN_VALUE || val > MRVDRV_FRAG_MAX_VALUE)
- return -EINVAL;
-
- ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, (u16) val);
-
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- int ret = 0;
- u16 val = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
- if (ret)
- goto out;
-
- vwrq->value = val;
- vwrq->disabled = ((val < MRVDRV_FRAG_MIN_VALUE)
- || (val > MRVDRV_FRAG_MAX_VALUE));
- vwrq->fixed = 1;
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_get_mode(struct net_device *dev,
- struct iw_request_info *info, u32 * uwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- *uwrq = priv->mode;
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int mesh_wlan_get_mode(struct net_device *dev,
- struct iw_request_info *info, u32 * uwrq,
- char *extra)
-{
- lbs_deb_enter(LBS_DEB_WEXT);
-
- *uwrq = IW_MODE_REPEAT;
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_get_txpow(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- s16 curlevel = 0;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (!priv->radio_on) {
- lbs_deb_wext("tx power off\n");
- vwrq->value = 0;
- vwrq->disabled = 1;
- goto out;
- }
-
- ret = lbs_get_tx_power(priv, &curlevel, NULL, NULL);
- if (ret)
- goto out;
-
- lbs_deb_wext("tx power level %d dbm\n", curlevel);
- priv->txpower_cur = curlevel;
-
- vwrq->value = curlevel;
- vwrq->fixed = 1;
- vwrq->disabled = 0;
- vwrq->flags = IW_TXPOW_DBM;
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- int ret = 0;
- u16 slimit = 0, llimit = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if ((vwrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
- return -EOPNOTSUPP;
-
- /* 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;
-
- /* Add 1 to convert retry count to try count */
- if (vwrq->flags & IW_RETRY_SHORT)
- slimit = (u16) (vwrq->value + 1);
- else if (vwrq->flags & IW_RETRY_LONG)
- llimit = (u16) (vwrq->value + 1);
- else
- slimit = llimit = (u16) (vwrq->value + 1); /* set both */
-
- if (llimit) {
- ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT,
- llimit);
- if (ret)
- goto out;
- }
-
- if (slimit) {
- /* txretrycount follows the short retry limit */
- priv->txretrycount = slimit;
- ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT,
- slimit);
- if (ret)
- goto out;
- }
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- int ret = 0;
- u16 val = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- vwrq->disabled = 0;
-
- if (vwrq->flags & IW_RETRY_LONG) {
- ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT, &val);
- if (ret)
- goto out;
-
- /* Subtract 1 to convert try count to retry count */
- vwrq->value = val - 1;
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
- } else {
- ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT, &val);
- if (ret)
- goto out;
-
- /* txretry count follows the short retry limit */
- priv->txretrycount = val;
- /* Subtract 1 to convert try count to retry count */
- vwrq->value = val - 1;
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
- }
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-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 lbs_get_range(struct net_device *dev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- int i, j;
- struct lbs_private *priv = dev->ml_priv;
- struct iw_range *range = (struct iw_range *)extra;
- struct chan_freq_power *cfp;
- u8 rates[MAX_RATES + 1];
-
- u8 flag = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- 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));
- copy_active_data_rates(priv, rates);
- range->num_bitrates = strnlen(rates, IW_MAX_BITRATES);
- for (i = 0; i < range->num_bitrates; i++)
- range->bitrate[i] = rates[i] * 500000;
- range->num_bitrates = i;
- lbs_deb_wext("IW_MAX_BITRATES %d, num_bitrates %d\n", IW_MAX_BITRATES,
- range->num_bitrates);
-
- range->num_frequency = 0;
-
- range->scan_capa = IW_SCAN_CAPA_ESSID;
-
- if (priv->enable11d &&
- (priv->connect_status == LBS_CONNECTED ||
- priv->mesh_connect_status == LBS_CONNECTED)) {
- u8 chan_no;
- u8 band;
-
- struct parsed_region_chan_11d *parsed_region_chan =
- &priv->parsed_region_chan;
-
- if (parsed_region_chan == NULL) {
- lbs_deb_wext("11d: parsed_region_chan is NULL\n");
- goto out;
- }
- band = parsed_region_chan->band;
- lbs_deb_wext("band %d, nr_char %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_deb_wext("chan_no %d\n", chan_no);
- range->freq[range->num_frequency].i = (long)chan_no;
- range->freq[range->num_frequency].m =
- (long)lbs_chan_2_freq(chan_no) * 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 < ARRAY_SIZE(priv->region_channel)); j++) {
- cfp = priv->region_channel[j].CFP;
- for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
- && priv->region_channel[j].valid
- && cfp
- && (i < priv->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_deb_wext("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;
-
- /*
- * Right now we support only "iwconfig ethX power on|off"
- */
- range->pm_capa = IW_POWER_ON;
-
- /*
- * 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_capa = IW_TXPOW_DBM | IW_TXPOW_RANGE;
- range->txpower[0] = priv->txpower_min;
- range->txpower[1] = priv->txpower_max;
- range->num_txpower = 2;
-
- 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 (priv->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;
- }
-
-out:
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (!priv->ps_supported) {
- if (vwrq->disabled)
- return 0;
- else
- return -EINVAL;
- }
-
- /* PS is currently supported only in Infrastructure mode
- * Remove this check if it is to be supported in IBSS mode also
- */
-
- if (vwrq->disabled) {
- priv->psmode = LBS802_11POWERMODECAM;
- if (priv->psstate != PS_STATE_FULL_POWER) {
- lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
- }
-
- return 0;
- }
-
- if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
- lbs_deb_wext(
- "setting power timeout is not supported\n");
- return -EINVAL;
- } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
- lbs_deb_wext("setting power period not supported\n");
- return -EINVAL;
- }
-
- if (priv->psmode != LBS802_11POWERMODECAM) {
- return 0;
- }
-
- priv->psmode = LBS802_11POWERMODEMAX_PSP;
-
- if (priv->connect_status == LBS_CONNECTED) {
- lbs_ps_sleep(priv, CMD_OPTION_WAITFORRSP);
- }
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_get_power(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- vwrq->value = 0;
- vwrq->flags = 0;
- vwrq->disabled = priv->psmode == LBS802_11POWERMODECAM
- || priv->connect_status == LBS_DISCONNECTED;
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
-{
- enum {
- POOR = 30,
- FAIR = 60,
- GOOD = 80,
- VERY_GOOD = 90,
- EXCELLENT = 95,
- PERFECT = 100
- };
- struct lbs_private *priv = dev->ml_priv;
- u32 rssi_qual;
- u32 tx_qual;
- u32 quality = 0;
- int stats_valid = 0;
- u8 rssi;
- u32 tx_retries;
- struct cmd_ds_802_11_get_log log;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- priv->wstats.status = priv->mode;
-
- /* If we're not associated, all quality values are meaningless */
- if ((priv->connect_status != LBS_CONNECTED) &&
- (priv->mesh_connect_status != LBS_CONNECTED))
- goto out;
-
- /* Quality by RSSI */
- priv->wstats.qual.level =
- CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
- priv->NF[TYPE_BEACON][TYPE_NOAVG]);
-
- if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
- priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
- } else {
- priv->wstats.qual.noise =
- CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]);
- }
-
- lbs_deb_wext("signal level %#x\n", priv->wstats.qual.level);
- lbs_deb_wext("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 = dev->stats.tx_errors;
-
- memset(&log, 0, sizeof(log));
- log.hdr.size = cpu_to_le16(sizeof(log));
- lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);
-
- tx_retries = le32_to_cpu(log.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 = le32_to_cpu(log.wepundecryptable);
- priv->wstats.discard.retries = tx_retries;
- priv->wstats.discard.misc = le32_to_cpu(log.ackfailure);
-
- /* Calculate quality */
- priv->wstats.qual.qual = min_t(u8, quality, 100);
- priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
- stats_valid = 1;
-
- /* update stats asynchronously for future calls */
- lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 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;
- }
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return &priv->wstats;
-
-
-}
-
-static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
- struct iw_freq *fwrq, char *extra)
-{
- int ret = -EINVAL;
- struct lbs_private *priv = dev->ml_priv;
- struct chan_freq_power *cfp;
- struct assoc_request * assoc_req;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- mutex_lock(&priv->lock);
- assoc_req = lbs_get_association_request(priv);
- if (!assoc_req) {
- ret = -ENOMEM;
- goto out;
- }
-
- /* If setting by frequency, convert to a channel */
- if (fwrq->e == 1) {
- long f = fwrq->m / 100000;
-
- cfp = find_cfp_by_band_and_freq(priv, 0, f);
- if (!cfp) {
- lbs_deb_wext("invalid freq %ld\n", f);
- goto out;
- }
-
- fwrq->e = 0;
- fwrq->m = (int) cfp->channel;
- }
-
- /* Setting by channel number */
- if (fwrq->m > 1000 || fwrq->e > 0) {
- goto out;
- }
-
- cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
- if (!cfp) {
- goto out;
- }
-
- assoc_req->channel = fwrq->m;
- ret = 0;
-
-out:
- if (ret == 0) {
- set_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags);
- lbs_postpone_association_work(priv);
- } else {
- lbs_cancel_association_work(priv);
- }
- mutex_unlock(&priv->lock);
-
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_mesh_set_freq(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_freq *fwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- struct chan_freq_power *cfp;
- int ret = -EINVAL;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- /* If setting by frequency, convert to a channel */
- if (fwrq->e == 1) {
- long f = fwrq->m / 100000;
-
- cfp = find_cfp_by_band_and_freq(priv, 0, f);
- if (!cfp) {
- lbs_deb_wext("invalid freq %ld\n", f);
- goto out;
- }
-
- fwrq->e = 0;
- fwrq->m = (int) cfp->channel;
- }
-
- /* Setting by channel number */
- if (fwrq->m > 1000 || fwrq->e > 0) {
- goto out;
- }
-
- cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
- if (!cfp) {
- goto out;
- }
-
- if (fwrq->m != priv->curbssparams.channel) {
- lbs_deb_wext("mesh channel change forces eth disconnect\n");
- if (priv->mode == IW_MODE_INFRA)
- lbs_cmd_80211_deauthenticate(priv,
- priv->curbssparams.bssid,
- WLAN_REASON_DEAUTH_LEAVING);
- else if (priv->mode == IW_MODE_ADHOC)
- lbs_adhoc_stop(priv);
- }
- lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, fwrq->m);
- lbs_update_channel(priv);
- ret = 0;
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- u8 new_rate = 0;
- int ret = -EINVAL;
- u8 rates[MAX_RATES + 1];
-
- lbs_deb_enter(LBS_DEB_WEXT);
- lbs_deb_wext("vwrq->value %d\n", vwrq->value);
- lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);
-
- if (vwrq->fixed && vwrq->value == -1)
- goto out;
-
- /* Auto rate? */
- priv->enablehwauto = !vwrq->fixed;
-
- if (vwrq->value == -1)
- priv->cur_rate = 0;
- else {
- if (vwrq->value % 100000)
- goto out;
-
- new_rate = vwrq->value / 500000;
- priv->cur_rate = new_rate;
- /* the rest is only needed for lbs_set_data_rate() */
- memset(rates, 0, sizeof(rates));
- copy_active_data_rates(priv, rates);
- if (!memchr(rates, new_rate, sizeof(rates))) {
- lbs_pr_alert("fixed data rate 0x%X out of range\n",
- new_rate);
- goto out;
- }
- if (priv->fwrelease < 0x09000000) {
- ret = lbs_set_power_adapt_cfg(priv, 0,
- POW_ADAPT_DEFAULT_P0,
- POW_ADAPT_DEFAULT_P1,
- POW_ADAPT_DEFAULT_P2);
- if (ret)
- goto out;
- }
- ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
- TPC_DEFAULT_P2, 1);
- if (ret)
- goto out;
- }
-
- /* Try the newer command first (Firmware Spec 5.1 and above) */
- ret = lbs_cmd_802_11_rate_adapt_rateset(priv, CMD_ACT_SET);
-
- /* Fallback to older version */
- if (ret)
- ret = lbs_set_data_rate(priv, new_rate);
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (priv->connect_status == LBS_CONNECTED) {
- vwrq->value = priv->cur_rate * 500000;
-
- if (priv->enablehwauto)
- vwrq->fixed = 0;
- else
- vwrq->fixed = 1;
-
- } else {
- vwrq->fixed = 0;
- vwrq->value = 0;
- }
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_set_mode(struct net_device *dev,
- struct iw_request_info *info, u32 * uwrq, char *extra)
-{
- int ret = 0;
- struct lbs_private *priv = dev->ml_priv;
- struct assoc_request * assoc_req;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if ( (*uwrq != IW_MODE_ADHOC)
- && (*uwrq != IW_MODE_INFRA)
- && (*uwrq != IW_MODE_AUTO)) {
- lbs_deb_wext("Invalid mode: 0x%x\n", *uwrq);
- ret = -EINVAL;
- goto out;
- }
-
- mutex_lock(&priv->lock);
- assoc_req = lbs_get_association_request(priv);
- if (!assoc_req) {
- ret = -ENOMEM;
- lbs_cancel_association_work(priv);
- } else {
- assoc_req->mode = *uwrq;
- set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
- lbs_postpone_association_work(priv);
- lbs_deb_wext("Switching to mode: 0x%x\n", *uwrq);
- }
- mutex_unlock(&priv->lock);
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- 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 lbs_get_encode(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq, u8 * extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- lbs_deb_wext("flags 0x%x, index %d, length %d, wep_tx_keyidx %d\n",
- dwrq->flags, index, dwrq->length, priv->wep_tx_keyidx);
-
- dwrq->flags = 0;
-
- /* Authentication method */
- switch (priv->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;
- }
-
- memset(extra, 0, 16);
-
- mutex_lock(&priv->lock);
-
- /* Default to returning current transmit key */
- if (index < 0)
- index = priv->wep_tx_keyidx;
-
- if ((priv->wep_keys[index].len) && priv->secinfo.wep_enabled) {
- memcpy(extra, priv->wep_keys[index].key,
- priv->wep_keys[index].len);
- dwrq->length = priv->wep_keys[index].len;
-
- dwrq->flags |= (index + 1);
- /* Return WEP enabled */
- dwrq->flags &= ~IW_ENCODE_DISABLED;
- } else if ((priv->secinfo.WPAenabled)
- || (priv->secinfo.WPA2enabled)) {
- /* return WPA enabled */
- dwrq->flags &= ~IW_ENCODE_DISABLED;
- dwrq->flags |= IW_ENCODE_NOKEY;
- } else {
- dwrq->flags |= IW_ENCODE_DISABLED;
- }
-
- mutex_unlock(&priv->lock);
-
- lbs_deb_wext("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_deb_wext("return flags 0x%x\n", dwrq->flags);
-
- lbs_deb_leave(LBS_DEB_WEXT);
- 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 lbs_set_wep_key(struct assoc_request *assoc_req,
- const char *key_material,
- u16 key_length,
- u16 index,
- int set_tx_key)
-{
- int ret = 0;
- struct enc_key *pkey;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- /* Paranoid validation of key index */
- if (index > 3) {
- ret = -EINVAL;
- goto out;
- }
-
- /* validate max key length */
- if (key_length > KEY_LEN_WEP_104) {
- ret = -EINVAL;
- goto out;
- }
-
- pkey = &assoc_req->wep_keys[index];
-
- if (key_length > 0) {
- memset(pkey, 0, sizeof(struct enc_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_deb_wext("key not set, so cannot enable it\n");
- ret = -EINVAL;
- goto out;
- }
- assoc_req->wep_tx_keyidx = index;
- }
-
- assoc_req->secinfo.wep_enabled = 1;
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-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;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- /* 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);
-
- lbs_deb_leave(LBS_DEB_WEXT);
-}
-
-static void disable_wpa(struct assoc_request *assoc_req)
-{
- lbs_deb_enter(LBS_DEB_WEXT);
-
- memset(&assoc_req->wpa_mcast_key, 0, sizeof (struct enc_key));
- assoc_req->wpa_mcast_key.flags = KEY_INFO_WPA_MCAST;
- set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
-
- memset(&assoc_req->wpa_unicast_key, 0, sizeof (struct enc_key));
- assoc_req->wpa_unicast_key.flags = KEY_INFO_WPA_UNICAST;
- set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
-
- assoc_req->secinfo.WPAenabled = 0;
- assoc_req->secinfo.WPA2enabled = 0;
- set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
-
- lbs_deb_leave(LBS_DEB_WEXT);
-}
-
-/**
- * @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 lbs_set_encode(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- int ret = 0;
- struct lbs_private *priv = dev->ml_priv;
- struct assoc_request * assoc_req;
- u16 is_default = 0, index = 0, set_tx_key = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- mutex_lock(&priv->lock);
- assoc_req = lbs_get_association_request(priv);
- if (!assoc_req) {
- ret = -ENOMEM;
- goto out;
- }
-
- if (dwrq->flags & IW_ENCODE_DISABLED) {
- disable_wep (assoc_req);
- disable_wpa (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 = lbs_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);
- lbs_postpone_association_work(priv);
- } else {
- lbs_cancel_association_work(priv);
- }
- mutex_unlock(&priv->lock);
-
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- 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 lbs_get_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- int ret = -EINVAL;
- struct lbs_private *priv = dev->ml_priv;
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- int index, max_key_len;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- 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 = priv->wep_tx_keyidx;
- }
-
- if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
- ext->alg != IW_ENCODE_ALG_WEP) {
- if (index != 0 || priv->mode != IW_MODE_INFRA)
- goto out;
- }
-
- dwrq->flags = index + 1;
- memset(ext, 0, sizeof(*ext));
-
- if ( !priv->secinfo.wep_enabled
- && !priv->secinfo.WPAenabled
- && !priv->secinfo.WPA2enabled) {
- ext->alg = IW_ENCODE_ALG_NONE;
- ext->key_len = 0;
- dwrq->flags |= IW_ENCODE_DISABLED;
- } else {
- u8 *key = NULL;
-
- if ( priv->secinfo.wep_enabled
- && !priv->secinfo.WPAenabled
- && !priv->secinfo.WPA2enabled) {
- /* WEP */
- ext->alg = IW_ENCODE_ALG_WEP;
- ext->key_len = priv->wep_keys[index].len;
- key = &priv->wep_keys[index].key[0];
- } else if ( !priv->secinfo.wep_enabled
- && (priv->secinfo.WPAenabled ||
- priv->secinfo.WPA2enabled)) {
- /* WPA */
- struct enc_key * pkey = NULL;
-
- if ( priv->wpa_mcast_key.len
- && (priv->wpa_mcast_key.flags & KEY_INFO_WPA_ENABLED))
- pkey = &priv->wpa_mcast_key;
- else if ( priv->wpa_unicast_key.len
- && (priv->wpa_unicast_key.flags & KEY_INFO_WPA_ENABLED))
- pkey = &priv->wpa_unicast_key;
-
- if (pkey) {
- if (pkey->type == KEY_TYPE_ID_AES) {
- ext->alg = IW_ENCODE_ALG_CCMP;
- } else {
- ext->alg = IW_ENCODE_ALG_TKIP;
- }
- ext->key_len = pkey->len;
- key = &pkey->key[0];
- } else {
- 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:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- 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 lbs_set_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- int ret = 0;
- struct lbs_private *priv = dev->ml_priv;
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- int alg = ext->alg;
- struct assoc_request * assoc_req;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- mutex_lock(&priv->lock);
- assoc_req = lbs_get_association_request(priv);
- if (!assoc_req) {
- ret = -ENOMEM;
- goto out;
- }
-
- if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
- disable_wep (assoc_req);
- disable_wpa (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 = lbs_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 enc_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_deb_wext("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;
- set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
- } else {
- pkey = &assoc_req->wpa_unicast_key;
- set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
- }
-
- memset(pkey, 0, sizeof (struct enc_key));
- memcpy(pkey->key, ext->key, ext->key_len);
- pkey->len = ext->key_len;
- if (pkey->len)
- pkey->flags |= KEY_INFO_WPA_ENABLED;
-
- /* Do this after zeroing key structure */
- if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
- pkey->flags |= KEY_INFO_WPA_MCAST;
- } else {
- pkey->flags |= KEY_INFO_WPA_UNICAST;
- }
-
- 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);
- }
-
- /* Only disable wep if necessary: can't waste time here. */
- if (priv->mac_control & CMD_ACT_MAC_WEP_ENABLE)
- disable_wep(assoc_req);
- }
-
-out:
- if (ret == 0) {
- /* 802.1x and WPA rekeying must happen as quickly as possible,
- * especially during the 4-way handshake; thus if in
- * infrastructure mode, and either (a) 802.1x is enabled or
- * (b) WPA is being used, set the key right away.
- */
- if (assoc_req->mode == IW_MODE_INFRA &&
- ((assoc_req->secinfo.key_mgmt & IW_AUTH_KEY_MGMT_802_1X) ||
- (assoc_req->secinfo.key_mgmt & IW_AUTH_KEY_MGMT_PSK) ||
- assoc_req->secinfo.WPAenabled ||
- assoc_req->secinfo.WPA2enabled)) {
- lbs_do_association_work(priv);
- } else
- lbs_postpone_association_work(priv);
- } else {
- lbs_cancel_association_work(priv);
- }
- mutex_unlock(&priv->lock);
-
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-
-static int lbs_set_genie(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- int ret = 0;
- struct assoc_request * assoc_req;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- mutex_lock(&priv->lock);
- assoc_req = lbs_get_association_request(priv);
- 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(priv->wpa_ie));
- assoc_req->wpa_ie_len = 0;
- }
-
-out:
- if (ret == 0) {
- set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
- lbs_postpone_association_work(priv);
- } else {
- lbs_cancel_association_work(priv);
- }
- mutex_unlock(&priv->lock);
-
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_get_genie(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq,
- char *extra)
-{
- int ret = 0;
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (priv->wpa_ie_len == 0) {
- dwrq->length = 0;
- goto out;
- }
-
- if (dwrq->length < priv->wpa_ie_len) {
- ret = -E2BIG;
- goto out;
- }
-
- dwrq->length = priv->wpa_ie_len;
- memcpy(extra, &priv->wpa_ie[0], priv->wpa_ie_len);
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-
-static int lbs_set_auth(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *dwrq,
- char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- struct assoc_request * assoc_req;
- int ret = 0;
- int updated = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- mutex_lock(&priv->lock);
- assoc_req = lbs_get_association_request(priv);
- 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_DROP_UNENCRYPTED:
- /*
- * libertas does not use these parameters
- */
- break;
-
- case IW_AUTH_KEY_MGMT:
- assoc_req->secinfo.key_mgmt = dwrq->value;
- updated = 1;
- break;
-
- case IW_AUTH_WPA_VERSION:
- if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
- assoc_req->secinfo.WPAenabled = 0;
- assoc_req->secinfo.WPA2enabled = 0;
- disable_wpa (assoc_req);
- }
- 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_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;
- disable_wpa (assoc_req);
- }
- updated = 1;
- break;
-
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
-out:
- if (ret == 0) {
- if (updated)
- set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
- lbs_postpone_association_work(priv);
- } else if (ret != -EOPNOTSUPP) {
- lbs_cancel_association_work(priv);
- }
- mutex_unlock(&priv->lock);
-
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_get_auth(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *dwrq,
- char *extra)
-{
- int ret = 0;
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- switch (dwrq->flags & IW_AUTH_INDEX) {
- case IW_AUTH_KEY_MGMT:
- dwrq->value = priv->secinfo.key_mgmt;
- break;
-
- case IW_AUTH_WPA_VERSION:
- dwrq->value = 0;
- if (priv->secinfo.WPAenabled)
- dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
- if (priv->secinfo.WPA2enabled)
- dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
- if (!dwrq->value)
- dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
- break;
-
- case IW_AUTH_80211_AUTH_ALG:
- dwrq->value = priv->secinfo.auth_mode;
- break;
-
- case IW_AUTH_WPA_ENABLED:
- if (priv->secinfo.WPAenabled && priv->secinfo.WPA2enabled)
- dwrq->value = 1;
- break;
-
- default:
- ret = -EOPNOTSUPP;
- }
-
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-
-static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
- struct iw_param *vwrq, char *extra)
-{
- int ret = 0;
- struct lbs_private *priv = dev->ml_priv;
- s16 dbm = (s16) vwrq->value;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (vwrq->disabled) {
- lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
- goto out;
- }
-
- if (vwrq->fixed == 0) {
- /* User requests automatic tx power control, however there are
- * many auto tx settings. For now use firmware defaults until
- * we come up with a good way to expose these to the user. */
- if (priv->fwrelease < 0x09000000) {
- ret = lbs_set_power_adapt_cfg(priv, 1,
- POW_ADAPT_DEFAULT_P0,
- POW_ADAPT_DEFAULT_P1,
- POW_ADAPT_DEFAULT_P2);
- if (ret)
- goto out;
- }
- ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
- TPC_DEFAULT_P2, 1);
- if (ret)
- goto out;
- dbm = priv->txpower_max;
- } else {
- /* Userspace check in iwrange if it should use dBm or mW,
- * therefore this should never happen... Jean II */
- if ((vwrq->flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {
- ret = -EOPNOTSUPP;
- goto out;
- }
-
- /* Validate requested power level against firmware allowed
- * levels */
- if (priv->txpower_min && (dbm < priv->txpower_min)) {
- ret = -EINVAL;
- goto out;
- }
-
- if (priv->txpower_max && (dbm > priv->txpower_max)) {
- ret = -EINVAL;
- goto out;
- }
- if (priv->fwrelease < 0x09000000) {
- ret = lbs_set_power_adapt_cfg(priv, 0,
- POW_ADAPT_DEFAULT_P0,
- POW_ADAPT_DEFAULT_P1,
- POW_ADAPT_DEFAULT_P2);
- if (ret)
- goto out;
- }
- ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1,
- TPC_DEFAULT_P2, 1);
- if (ret)
- goto out;
- }
-
- /* If the radio was off, turn it on */
- if (!priv->radio_on) {
- ret = lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 1);
- if (ret)
- goto out;
- }
-
- lbs_deb_wext("txpower set %d dBm\n", dbm);
-
- ret = lbs_set_tx_power(priv, dbm);
-
-out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- /*
- * Note : if dwrq->flags != 0, we should get the relevant SSID from
- * the SSID list...
- */
-
- /*
- * Get the current SSID
- */
- if (priv->connect_status == LBS_CONNECTED) {
- memcpy(extra, priv->curbssparams.ssid,
- priv->curbssparams.ssid_len);
- extra[priv->curbssparams.ssid_len] = '\0';
- } else {
- memset(extra, 0, 32);
- extra[priv->curbssparams.ssid_len] = '\0';
- }
- /*
- * If none, we may want to get the one that was set
- */
-
- dwrq->length = priv->curbssparams.ssid_len;
-
- dwrq->flags = 1; /* active */
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- int ret = 0;
- u8 ssid[IW_ESSID_MAX_SIZE];
- u8 ssid_len = 0;
- struct assoc_request * assoc_req;
- int in_ssid_len = dwrq->length;
- DECLARE_SSID_BUF(ssid_buf);
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (!priv->radio_on) {
- ret = -EINVAL;
- goto out;
- }
-
- /* Check the size of the string */
- if (in_ssid_len > IW_ESSID_MAX_SIZE) {
- ret = -E2BIG;
- goto out;
- }
-
- memset(&ssid, 0, sizeof(ssid));
-
- if (!dwrq->flags || !in_ssid_len) {
- /* "any" SSID requested; leave SSID blank */
- } else {
- /* Specific SSID requested */
- memcpy(&ssid, extra, in_ssid_len);
- ssid_len = in_ssid_len;
- }
-
- if (!ssid_len) {
- lbs_deb_wext("requested any SSID\n");
- } else {
- lbs_deb_wext("requested SSID '%s'\n",
- print_ssid(ssid_buf, ssid, ssid_len));
- }
-
-out:
- mutex_lock(&priv->lock);
- if (ret == 0) {
- /* Get or create the current association request */
- assoc_req = lbs_get_association_request(priv);
- if (!assoc_req) {
- ret = -ENOMEM;
- } else {
- /* Copy the SSID to the association request */
- memcpy(&assoc_req->ssid, &ssid, IW_ESSID_MAX_SIZE);
- assoc_req->ssid_len = ssid_len;
- set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
- lbs_postpone_association_work(priv);
- }
- }
-
- /* Cancel the association request if there was an error */
- if (ret != 0) {
- lbs_cancel_association_work(priv);
- }
-
- mutex_unlock(&priv->lock);
-
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- return ret;
-}
-
-static int lbs_mesh_get_essid(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- memcpy(extra, priv->mesh_ssid, priv->mesh_ssid_len);
-
- dwrq->length = priv->mesh_ssid_len;
-
- dwrq->flags = 1; /* active */
-
- lbs_deb_leave(LBS_DEB_WEXT);
- return 0;
-}
-
-static int lbs_mesh_set_essid(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *dwrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (!priv->radio_on) {
- ret = -EINVAL;
- goto out;
- }
-
- /* Check the size of the string */
- if (dwrq->length > IW_ESSID_MAX_SIZE) {
- ret = -E2BIG;
- goto out;
- }
-
- if (!dwrq->flags || !dwrq->length) {
- ret = -EINVAL;
- goto out;
- } else {
- /* Specific SSID requested */
- memcpy(priv->mesh_ssid, extra, dwrq->length);
- priv->mesh_ssid_len = dwrq->length;
- }
-
- lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->curbssparams.channel);
- out:
- lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
- 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 lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
- struct sockaddr *awrq, char *extra)
-{
- struct lbs_private *priv = dev->ml_priv;
- struct assoc_request * assoc_req;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- if (!priv->radio_on)
- return -EINVAL;
-
- if (awrq->sa_family != ARPHRD_ETHER)
- return -EINVAL;
-
- lbs_deb_wext("ASSOC: WAP: sa_data %pM\n", awrq->sa_data);
-
- mutex_lock(&priv->lock);
-
- /* Get or create the current association request */
- assoc_req = lbs_get_association_request(priv);
- if (!assoc_req) {
- lbs_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);
- lbs_postpone_association_work(priv);
- }
-
- mutex_unlock(&priv->lock);
-
- return ret;
-}
-
-/*
- * iwconfig settable callbacks
- */
-static const iw_handler lbs_handler[] = {
- (iw_handler) NULL, /* SIOCSIWCOMMIT */
- (iw_handler) lbs_get_name, /* SIOCGIWNAME */
- (iw_handler) NULL, /* SIOCSIWNWID */
- (iw_handler) NULL, /* SIOCGIWNWID */
- (iw_handler) lbs_set_freq, /* SIOCSIWFREQ */
- (iw_handler) lbs_get_freq, /* SIOCGIWFREQ */
- (iw_handler) lbs_set_mode, /* SIOCSIWMODE */
- (iw_handler) lbs_get_mode, /* SIOCGIWMODE */
- (iw_handler) NULL, /* SIOCSIWSENS */
- (iw_handler) NULL, /* SIOCGIWSENS */
- (iw_handler) NULL, /* SIOCSIWRANGE */
- (iw_handler) lbs_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) lbs_set_wap, /* SIOCSIWAP */
- (iw_handler) lbs_get_wap, /* SIOCGIWAP */
- (iw_handler) NULL, /* SIOCSIWMLME */
- (iw_handler) NULL, /* SIOCGIWAPLIST - deprecated */
- (iw_handler) lbs_set_scan, /* SIOCSIWSCAN */
- (iw_handler) lbs_get_scan, /* SIOCGIWSCAN */
- (iw_handler) lbs_set_essid, /* SIOCSIWESSID */
- (iw_handler) lbs_get_essid, /* SIOCGIWESSID */
- (iw_handler) lbs_set_nick, /* SIOCSIWNICKN */
- (iw_handler) lbs_get_nick, /* SIOCGIWNICKN */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) lbs_set_rate, /* SIOCSIWRATE */
- (iw_handler) lbs_get_rate, /* SIOCGIWRATE */
- (iw_handler) lbs_set_rts, /* SIOCSIWRTS */
- (iw_handler) lbs_get_rts, /* SIOCGIWRTS */
- (iw_handler) lbs_set_frag, /* SIOCSIWFRAG */
- (iw_handler) lbs_get_frag, /* SIOCGIWFRAG */
- (iw_handler) lbs_set_txpow, /* SIOCSIWTXPOW */
- (iw_handler) lbs_get_txpow, /* SIOCGIWTXPOW */
- (iw_handler) lbs_set_retry, /* SIOCSIWRETRY */
- (iw_handler) lbs_get_retry, /* SIOCGIWRETRY */
- (iw_handler) lbs_set_encode, /* SIOCSIWENCODE */
- (iw_handler) lbs_get_encode, /* SIOCGIWENCODE */
- (iw_handler) lbs_set_power, /* SIOCSIWPOWER */
- (iw_handler) lbs_get_power, /* SIOCGIWPOWER */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) lbs_set_genie, /* SIOCSIWGENIE */
- (iw_handler) lbs_get_genie, /* SIOCGIWGENIE */
- (iw_handler) lbs_set_auth, /* SIOCSIWAUTH */
- (iw_handler) lbs_get_auth, /* SIOCGIWAUTH */
- (iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */
- (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
- (iw_handler) NULL, /* SIOCSIWPMKSA */
-};
-
-static const iw_handler mesh_wlan_handler[] = {
- (iw_handler) NULL, /* SIOCSIWCOMMIT */
- (iw_handler) lbs_get_name, /* SIOCGIWNAME */
- (iw_handler) NULL, /* SIOCSIWNWID */
- (iw_handler) NULL, /* SIOCGIWNWID */
- (iw_handler) lbs_mesh_set_freq, /* SIOCSIWFREQ */
- (iw_handler) lbs_get_freq, /* SIOCGIWFREQ */
- (iw_handler) NULL, /* SIOCSIWMODE */
- (iw_handler) mesh_wlan_get_mode, /* SIOCGIWMODE */
- (iw_handler) NULL, /* SIOCSIWSENS */
- (iw_handler) NULL, /* SIOCGIWSENS */
- (iw_handler) NULL, /* SIOCSIWRANGE */
- (iw_handler) lbs_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) NULL, /* SIOCSIWAP */
- (iw_handler) NULL, /* SIOCGIWAP */
- (iw_handler) NULL, /* SIOCSIWMLME */
- (iw_handler) NULL, /* SIOCGIWAPLIST - deprecated */
- (iw_handler) lbs_set_scan, /* SIOCSIWSCAN */
- (iw_handler) lbs_get_scan, /* SIOCGIWSCAN */
- (iw_handler) lbs_mesh_set_essid,/* SIOCSIWESSID */
- (iw_handler) lbs_mesh_get_essid,/* SIOCGIWESSID */
- (iw_handler) NULL, /* SIOCSIWNICKN */
- (iw_handler) mesh_get_nick, /* SIOCGIWNICKN */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) lbs_set_rate, /* SIOCSIWRATE */
- (iw_handler) lbs_get_rate, /* SIOCGIWRATE */
- (iw_handler) lbs_set_rts, /* SIOCSIWRTS */
- (iw_handler) lbs_get_rts, /* SIOCGIWRTS */
- (iw_handler) lbs_set_frag, /* SIOCSIWFRAG */
- (iw_handler) lbs_get_frag, /* SIOCGIWFRAG */
- (iw_handler) lbs_set_txpow, /* SIOCSIWTXPOW */
- (iw_handler) lbs_get_txpow, /* SIOCGIWTXPOW */
- (iw_handler) lbs_set_retry, /* SIOCSIWRETRY */
- (iw_handler) lbs_get_retry, /* SIOCGIWRETRY */
- (iw_handler) lbs_set_encode, /* SIOCSIWENCODE */
- (iw_handler) lbs_get_encode, /* SIOCGIWENCODE */
- (iw_handler) lbs_set_power, /* SIOCSIWPOWER */
- (iw_handler) lbs_get_power, /* SIOCGIWPOWER */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* -- hole -- */
- (iw_handler) lbs_set_genie, /* SIOCSIWGENIE */
- (iw_handler) lbs_get_genie, /* SIOCGIWGENIE */
- (iw_handler) lbs_set_auth, /* SIOCSIWAUTH */
- (iw_handler) lbs_get_auth, /* SIOCGIWAUTH */
- (iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */
- (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
- (iw_handler) NULL, /* SIOCSIWPMKSA */
-};
-struct iw_handler_def lbs_handler_def = {
- .num_standard = ARRAY_SIZE(lbs_handler),
- .standard = (iw_handler *) lbs_handler,
- .get_wireless_stats = lbs_get_wireless_stats,
-};
-
-struct iw_handler_def mesh_handler_def = {
- .num_standard = ARRAY_SIZE(mesh_wlan_handler),
- .standard = (iw_handler *) mesh_wlan_handler,
- .get_wireless_stats = lbs_get_wireless_stats,
-};
diff --git a/drivers/net/wireless/libertas/wext.h b/drivers/net/wireless/libertas/wext.h
deleted file mode 100644
index 4c08db49760..00000000000
--- a/drivers/net/wireless/libertas/wext.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * This file contains definition for IOCTL call.
- */
-#ifndef _LBS_WEXT_H_
-#define _LBS_WEXT_H_
-
-extern struct iw_handler_def lbs_handler_def;
-extern struct iw_handler_def mesh_handler_def;
-
-#endif