diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx')
41 files changed, 2221 insertions, 1398 deletions
diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile index 62e37ad01cc0..f47ec94c16dc 100644 --- a/drivers/net/wireless/wl12xx/Makefile +++ b/drivers/net/wireless/wl12xx/Makefile @@ -10,5 +10,7 @@ obj-$(CONFIG_WL1251_SDIO) += wl1251_sdio.o wl1271-objs = wl1271_main.o wl1271_spi.o wl1271_cmd.o \ wl1271_event.o wl1271_tx.o wl1271_rx.o \ wl1271_ps.o wl1271_acx.o wl1271_boot.o \ - wl1271_init.o wl1271_debugfs.o + wl1271_init.o wl1271_debugfs.o wl1271_io.o + +wl1271-$(CONFIG_NL80211_TESTMODE) += wl1271_testmode.o obj-$(CONFIG_WL1271) += wl1271.o diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h index 054533f7a124..37c61c19cae5 100644 --- a/drivers/net/wireless/wl12xx/wl1251.h +++ b/drivers/net/wireless/wl12xx/wl1251.h @@ -247,6 +247,7 @@ struct wl1251_debugfs { struct dentry *rxpipe_tx_xfr_host_int_trig_rx_data; struct dentry *tx_queue_len; + struct dentry *tx_queue_status; struct dentry *retry_count; struct dentry *excessive_retries; @@ -340,9 +341,6 @@ struct wl1251 { /* Are we currently scanning */ bool scanning; - /* Our association ID */ - u16 aid; - /* Default key (for WEP) */ u32 default_key; diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.c b/drivers/net/wireless/wl12xx/wl1251_acx.c index acfa086dbfc5..91891f928070 100644 --- a/drivers/net/wireless/wl12xx/wl1251_acx.c +++ b/drivers/net/wireless/wl12xx/wl1251_acx.c @@ -1,6 +1,7 @@ #include "wl1251_acx.h" #include <linux/module.h> +#include <linux/slab.h> #include <linux/crc7.h> #include "wl1251.h" @@ -976,3 +977,72 @@ out: kfree(acx); return ret; } + +int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max, + u8 aifs, u16 txop) +{ + struct wl1251_acx_ac_cfg *acx; + int ret = 0; + + wl1251_debug(DEBUG_ACX, "acx ac cfg %d cw_ming %d cw_max %d " + "aifs %d txop %d", ac, cw_min, cw_max, aifs, txop); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->ac = ac; + acx->cw_min = cw_min; + acx->cw_max = cw_max; + acx->aifsn = aifs; + acx->txop_limit = txop; + + ret = wl1251_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("acx ac cfg failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue, + enum wl1251_acx_channel_type type, + u8 tsid, enum wl1251_acx_ps_scheme ps_scheme, + enum wl1251_acx_ack_policy ack_policy) +{ + struct wl1251_acx_tid_cfg *acx; + int ret = 0; + + wl1251_debug(DEBUG_ACX, "acx tid cfg %d type %d tsid %d " + "ps_scheme %d ack_policy %d", queue, type, tsid, + ps_scheme, ack_policy); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->queue = queue; + acx->type = type; + acx->tsid = tsid; + acx->ps_scheme = ps_scheme; + acx->ack_policy = ack_policy; + + ret = wl1251_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("acx tid cfg failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.h b/drivers/net/wireless/wl12xx/wl1251_acx.h index 652371432cd8..26160c45784c 100644 --- a/drivers/net/wireless/wl12xx/wl1251_acx.h +++ b/drivers/net/wireless/wl12xx/wl1251_acx.h @@ -1166,6 +1166,87 @@ struct wl1251_acx_wr_tbtt_and_dtim { u8 padding; } __attribute__ ((packed)); +struct wl1251_acx_ac_cfg { + struct acx_header header; + + /* + * Access Category - The TX queue's access category + * (refer to AccessCategory_enum) + */ + u8 ac; + + /* + * The contention window minimum size (in slots) for + * the access class. + */ + u8 cw_min; + + /* + * The contention window maximum size (in slots) for + * the access class. + */ + u16 cw_max; + + /* The AIF value (in slots) for the access class. */ + u8 aifsn; + + u8 reserved; + + /* The TX Op Limit (in microseconds) for the access class. */ + u16 txop_limit; +} __attribute__ ((packed)); + + +enum wl1251_acx_channel_type { + CHANNEL_TYPE_DCF = 0, + CHANNEL_TYPE_EDCF = 1, + CHANNEL_TYPE_HCCA = 2, +}; + +enum wl1251_acx_ps_scheme { + /* regular ps: simple sending of packets */ + WL1251_ACX_PS_SCHEME_LEGACY = 0, + + /* sending a packet triggers a unscheduled apsd downstream */ + WL1251_ACX_PS_SCHEME_UPSD_TRIGGER = 1, + + /* a pspoll packet will be sent before every data packet */ + WL1251_ACX_PS_SCHEME_LEGACY_PSPOLL = 2, + + /* scheduled apsd mode */ + WL1251_ACX_PS_SCHEME_SAPSD = 3, +}; + +enum wl1251_acx_ack_policy { + WL1251_ACX_ACK_POLICY_LEGACY = 0, + WL1251_ACX_ACK_POLICY_NO_ACK = 1, + WL1251_ACX_ACK_POLICY_BLOCK = 2, +}; + +struct wl1251_acx_tid_cfg { + struct acx_header header; + + /* tx queue id number (0-7) */ + u8 queue; + + /* channel access type for the queue, enum wl1251_acx_channel_type */ + u8 type; + + /* EDCA: ac index (0-3), HCCA: traffic stream id (8-15) */ + u8 tsid; + + /* ps scheme of the specified queue, enum wl1251_acx_ps_scheme */ + u8 ps_scheme; + + /* the tx queue ack policy, enum wl1251_acx_ack_policy */ + u8 ack_policy; + + u8 padding[3]; + + /* not supported */ + u32 apsdconf[2]; +} __attribute__ ((packed)); + /************************************************************************* Host Interrupt Register (WiLink -> Host) @@ -1322,5 +1403,11 @@ int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime); int wl1251_acx_rate_policies(struct wl1251 *wl); int wl1251_acx_mem_cfg(struct wl1251 *wl); int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim); +int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max, + u8 aifs, u16 txop); +int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue, + enum wl1251_acx_channel_type type, + u8 tsid, enum wl1251_acx_ps_scheme ps_scheme, + enum wl1251_acx_ack_policy ack_policy); #endif /* __WL1251_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1251_boot.c b/drivers/net/wireless/wl12xx/wl1251_boot.c index 28a808674080..d5ac79aeaa73 100644 --- a/drivers/net/wireless/wl12xx/wl1251_boot.c +++ b/drivers/net/wireless/wl12xx/wl1251_boot.c @@ -22,6 +22,7 @@ */ #include <linux/gpio.h> +#include <linux/slab.h> #include "wl1251_reg.h" #include "wl1251_boot.h" diff --git a/drivers/net/wireless/wl12xx/wl1251_cmd.c b/drivers/net/wireless/wl12xx/wl1251_cmd.c index 770f260726bd..a37b30cef489 100644 --- a/drivers/net/wireless/wl12xx/wl1251_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1251_cmd.c @@ -1,6 +1,7 @@ #include "wl1251_cmd.h" #include <linux/module.h> +#include <linux/slab.h> #include <linux/crc7.h> #include "wl1251.h" @@ -410,3 +411,86 @@ out: kfree(cmd); return ret; } + +int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, + struct ieee80211_channel *channels[], + unsigned int n_channels, unsigned int n_probes) +{ + struct wl1251_cmd_scan *cmd; + int i, ret = 0; + + wl1251_debug(DEBUG_CMD, "cmd scan"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); + cmd->params.rx_filter_options = cpu_to_le32(CFG_RX_PRSP_EN | + CFG_RX_MGMT_EN | + CFG_RX_BCN_EN); + cmd->params.scan_options = 0; + cmd->params.num_channels = n_channels; + cmd->params.num_probe_requests = n_probes; + cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ + cmd->params.tid_trigger = 0; + + for (i = 0; i < n_channels; i++) { + cmd->channels[i].min_duration = + cpu_to_le32(WL1251_SCAN_MIN_DURATION); + cmd->channels[i].max_duration = + cpu_to_le32(WL1251_SCAN_MAX_DURATION); + memset(&cmd->channels[i].bssid_lsb, 0xff, 4); + memset(&cmd->channels[i].bssid_msb, 0xff, 2); + cmd->channels[i].early_termination = 0; + cmd->channels[i].tx_power_att = 0; + cmd->channels[i].channel = channels[i]->hw_value; + } + + cmd->params.ssid_len = ssid_len; + if (ssid) + memcpy(cmd->params.ssid, ssid, ssid_len); + + ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1251_error("cmd scan failed: %d", ret); + goto out; + } + + wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); + + if (cmd->header.status != CMD_STATUS_SUCCESS) { + wl1251_error("cmd scan status wasn't success: %d", + cmd->header.status); + ret = -EIO; + goto out; + } + +out: + kfree(cmd); + return ret; +} + +int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout) +{ + struct wl1251_cmd_trigger_scan_to *cmd; + int ret; + + wl1251_debug(DEBUG_CMD, "cmd trigger scan to"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->timeout = timeout; + + ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1251_error("cmd trigger scan to failed: %d", ret); + goto out; + } + +out: + kfree(cmd); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1251_cmd.h b/drivers/net/wireless/wl12xx/wl1251_cmd.h index dff798ad0ef5..4ad67cae94d2 100644 --- a/drivers/net/wireless/wl12xx/wl1251_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1251_cmd.h @@ -27,6 +27,8 @@ #include "wl1251.h" +#include <net/cfg80211.h> + struct acx_header; int wl1251_cmd_send(struct wl1251 *wl, u16 type, void *buf, size_t buf_len); @@ -43,6 +45,10 @@ int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, size_t len); int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, void *buf, size_t buf_len); +int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, + struct ieee80211_channel *channels[], + unsigned int n_channels, unsigned int n_probes); +int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout); /* unit ms */ #define WL1251_COMMAND_TIMEOUT 2000 @@ -163,8 +169,12 @@ struct cmd_read_write_memory { #define CMDMBOX_HEADER_LEN 4 #define CMDMBOX_INFO_ELEM_HEADER_LEN 4 +#define WL1251_SCAN_MIN_DURATION 30000 +#define WL1251_SCAN_MAX_DURATION 60000 + +#define WL1251_SCAN_NUM_PROBES 3 -struct basic_scan_parameters { +struct wl1251_scan_parameters { u32 rx_config_options; u32 rx_filter_options; @@ -189,11 +199,11 @@ struct basic_scan_parameters { u8 tid_trigger; u8 ssid_len; - u32 ssid[8]; + u8 ssid[32]; } __attribute__ ((packed)); -struct basic_scan_channel_parameters { +struct wl1251_scan_ch_parameters { u32 min_duration; /* in TU */ u32 max_duration; /* in TU */ u32 bssid_lsb; @@ -213,11 +223,11 @@ struct basic_scan_channel_parameters { /* SCAN parameters */ #define SCAN_MAX_NUM_OF_CHANNELS 16 -struct cmd_scan { +struct wl1251_cmd_scan { struct wl1251_cmd_header header; - struct basic_scan_parameters params; - struct basic_scan_channel_parameters channels[SCAN_MAX_NUM_OF_CHANNELS]; + struct wl1251_scan_parameters params; + struct wl1251_scan_ch_parameters channels[SCAN_MAX_NUM_OF_CHANNELS]; } __attribute__ ((packed)); enum { diff --git a/drivers/net/wireless/wl12xx/wl1251_debugfs.c b/drivers/net/wireless/wl12xx/wl1251_debugfs.c index a00723059f83..5e4465ac08fa 100644 --- a/drivers/net/wireless/wl12xx/wl1251_debugfs.c +++ b/drivers/net/wireless/wl12xx/wl1251_debugfs.c @@ -24,6 +24,7 @@ #include "wl1251_debugfs.h" #include <linux/skbuff.h> +#include <linux/slab.h> #include "wl1251.h" #include "wl1251_acx.h" @@ -237,6 +238,27 @@ static const struct file_operations tx_queue_len_ops = { .open = wl1251_open_file_generic, }; +static ssize_t tx_queue_status_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct wl1251 *wl = file->private_data; + char buf[3], status; + int len; + + if (wl->tx_queue_stopped) + status = 's'; + else + status = 'r'; + + len = scnprintf(buf, sizeof(buf), "%c\n", status); + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static const struct file_operations tx_queue_status_ops = { + .read = tx_queue_status_read, + .open = wl1251_open_file_generic, +}; + static void wl1251_debugfs_delete_files(struct wl1251 *wl) { DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow); @@ -331,6 +353,7 @@ static void wl1251_debugfs_delete_files(struct wl1251 *wl) DEBUGFS_FWSTATS_DEL(rxpipe, tx_xfr_host_int_trig_rx_data); DEBUGFS_DEL(tx_queue_len); + DEBUGFS_DEL(tx_queue_status); DEBUGFS_DEL(retry_count); DEBUGFS_DEL(excessive_retries); } @@ -431,6 +454,7 @@ static int wl1251_debugfs_add_files(struct wl1251 *wl) DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); DEBUGFS_ADD(tx_queue_len, wl->debugfs.rootdir); + DEBUGFS_ADD(tx_queue_status, wl->debugfs.rootdir); DEBUGFS_ADD(retry_count, wl->debugfs.rootdir); DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir); @@ -443,7 +467,8 @@ out: void wl1251_debugfs_reset(struct wl1251 *wl) { - memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats)); + if (wl->stats.fw_stats != NULL) + memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats)); wl->stats.retry_count = 0; wl->stats.excessive_retries = 0; } diff --git a/drivers/net/wireless/wl12xx/wl1251_init.c b/drivers/net/wireless/wl12xx/wl1251_init.c index 5cb573383eeb..b538bdd7b320 100644 --- a/drivers/net/wireless/wl12xx/wl1251_init.c +++ b/drivers/net/wireless/wl12xx/wl1251_init.c @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include "wl1251_init.h" #include "wl12xx_80211.h" @@ -294,6 +295,11 @@ static int wl1251_hw_init_tx_queue_config(struct wl1251 *wl) goto out; } + wl1251_acx_ac_cfg(wl, AC_BE, CWMIN_BE, CWMAX_BE, AIFS_DIFS, TXOP_BE); + wl1251_acx_ac_cfg(wl, AC_BK, CWMIN_BK, CWMAX_BK, AIFS_DIFS, TXOP_BK); + wl1251_acx_ac_cfg(wl, AC_VI, CWMIN_VI, CWMAX_VI, AIFS_DIFS, TXOP_VI); + wl1251_acx_ac_cfg(wl, AC_VO, CWMIN_VO, CWMAX_VO, AIFS_DIFS, TXOP_VO); + out: kfree(config); return ret; diff --git a/drivers/net/wireless/wl12xx/wl1251_init.h b/drivers/net/wireless/wl12xx/wl1251_init.h index b3b25ec885ea..269cefb3e7d4 100644 --- a/drivers/net/wireless/wl12xx/wl1251_init.h +++ b/drivers/net/wireless/wl12xx/wl1251_init.h @@ -26,6 +26,53 @@ #include "wl1251.h" +enum { + /* best effort/legacy */ + AC_BE = 0, + + /* background */ + AC_BK = 1, + + /* video */ + AC_VI = 2, + + /* voice */ + AC_VO = 3, + + /* broadcast dummy access category */ + AC_BCAST = 4, + + NUM_ACCESS_CATEGORIES = 4 +}; + +/* following are defult values for the IE fields*/ +#define CWMIN_BK 15 +#define CWMIN_BE 15 +#define CWMIN_VI 7 +#define CWMIN_VO 3 +#define CWMAX_BK 1023 +#define CWMAX_BE 63 +#define CWMAX_VI 15 +#define CWMAX_VO 7 + +/* slot number setting to start transmission at PIFS interval */ +#define AIFS_PIFS 1 + +/* + * slot number setting to start transmission at DIFS interval - normal DCF + * access + */ +#define AIFS_DIFS 2 + +#define AIFSN_BK 7 +#define AIFSN_BE 3 +#define AIFSN_VI AIFS_PIFS +#define AIFSN_VO AIFS_PIFS +#define TXOP_BK 0 +#define TXOP_BE 0 +#define TXOP_VI 3008 +#define TXOP_VO 1504 + int wl1251_hw_init_hwenc_config(struct wl1251 *wl); int wl1251_hw_init_templates_config(struct wl1251 *wl); int wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter); diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 2f50a256efa5..1c8226eee409 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -29,6 +29,7 @@ #include <linux/crc32.h> #include <linux/etherdevice.h> #include <linux/vmalloc.h> +#include <linux/slab.h> #include "wl1251.h" #include "wl12xx_80211.h" @@ -395,6 +396,7 @@ static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * the queue here, otherwise the queue will get too long. */ if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_MAX_LENGTH) { + wl1251_debug(DEBUG_TX, "op_tx: tx_queue full, stop queues"); ieee80211_stop_queues(wl->hw); /* @@ -510,13 +512,13 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) } static int wl1251_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct wl1251 *wl = hw->priv; int ret = 0; wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", - conf->type, conf->mac_addr); + vif->type, vif->addr); mutex_lock(&wl->mutex); if (wl->vif) { @@ -524,9 +526,9 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, goto out; } - wl->vif = conf->vif; + wl->vif = vif; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_STATION: wl->bss_type = BSS_TYPE_STA_BSS; break; @@ -538,8 +540,8 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, goto out; } - if (memcmp(wl->mac_addr, conf->mac_addr, ETH_ALEN)) { - memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN); + if (memcmp(wl->mac_addr, vif->addr, ETH_ALEN)) { + memcpy(wl->mac_addr, vif->addr, ETH_ALEN); SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); ret = wl1251_acx_station_id(wl); if (ret < 0) @@ -552,7 +554,7 @@ out: } static void wl1251_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct wl1251 *wl = hw->priv; @@ -562,43 +564,25 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&wl->mutex); } -static int wl1251_build_null_data(struct wl1251 *wl) +static int wl1251_build_qos_null_data(struct wl1251 *wl) { - struct wl12xx_null_data_template template; + struct ieee80211_qos_hdr template; - if (!is_zero_ether_addr(wl->bssid)) { - memcpy(template.header.da, wl->bssid, ETH_ALEN); - memcpy(template.header.bssid, wl->bssid, ETH_ALEN); - } else { - memset(template.header.da, 0xff, ETH_ALEN); - memset(template.header.bssid, 0xff, ETH_ALEN); - } - - memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); - template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC | - IEEE80211_FCTL_TODS); - - return wl1251_cmd_template_set(wl, CMD_NULL_DATA, &template, - sizeof(template)); - -} - -static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid) -{ - struct wl12xx_ps_poll_template template; + memset(&template, 0, sizeof(template)); - memcpy(template.bssid, wl->bssid, ETH_ALEN); - memcpy(template.ta, wl->mac_addr, ETH_ALEN); + memcpy(template.addr1, wl->bssid, ETH_ALEN); + memcpy(template.addr2, wl->mac_addr, ETH_ALEN); + memcpy(template.addr3, wl->bssid, ETH_ALEN); - /* aid in PS-Poll has its two MSBs each set to 1 */ - template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); + template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_TODS); - template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); + /* FIXME: not sure what priority to use here */ + template.qos_ctrl = cpu_to_le16(0); - return wl1251_cmd_template_set(wl, CMD_PS_POLL, &template, + return wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, &template, sizeof(template)); - } static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) @@ -634,26 +618,34 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) wl->psm_requested = true; + wl->dtim_period = conf->ps_dtim_period; + + ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, + wl->dtim_period); + /* - * We enter PSM only if we're already associated. - * If we're not, we'll enter it when joining an SSID, - * through the bss_info_changed() hook. + * mac80211 enables PSM only if we're already associated. */ ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + if (ret < 0) + goto out_sleep; } else if (!(conf->flags & IEEE80211_CONF_PS) && wl->psm_requested) { wl1251_debug(DEBUG_PSM, "psm disabled"); wl->psm_requested = false; - if (wl->psm) + if (wl->psm) { ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); + if (ret < 0) + goto out_sleep; + } } if (conf->power_level != wl->power_level) { ret = wl1251_acx_tx_power(wl, conf->power_level); if (ret < 0) - goto out; + goto out_sleep; wl->power_level = conf->power_level; } @@ -864,199 +856,61 @@ out: return ret; } -static int wl1251_build_basic_rates(char *rates) -{ - u8 index = 0; - - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; - - return index; -} - -static int wl1251_build_extended_rates(char *rates) +static int wl1251_op_hw_scan(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req) { - u8 index = 0; - - rates[index++] = IEEE80211_OFDM_RATE_6MB; - rates[index++] = IEEE80211_OFDM_RATE_9MB; - rates[index++] = IEEE80211_OFDM_RATE_12MB; - rates[index++] = IEEE80211_OFDM_RATE_18MB; - rates[index++] = IEEE80211_OFDM_RATE_24MB; - rates[index++] = IEEE80211_OFDM_RATE_36MB; - rates[index++] = IEEE80211_OFDM_RATE_48MB; - rates[index++] = IEEE80211_OFDM_RATE_54MB; - - return index; -} - + struct wl1251 *wl = hw->priv; + struct sk_buff *skb; + size_t ssid_len = 0; + u8 *ssid = NULL; + int ret; -static int wl1251_build_probe_req(struct wl1251 *wl, u8 *ssid, size_t ssid_len) -{ - struct wl12xx_probe_req_template template; - struct wl12xx_ie_rates *rates; - char *ptr; - u16 size; - - ptr = (char *)&template; - size = sizeof(struct ieee80211_header); - - memset(template.header.da, 0xff, ETH_ALEN); - memset(template.header.bssid, 0xff, ETH_ALEN); - memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); - template.header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - - /* IEs */ - /* SSID */ - template.ssid.header.id = WLAN_EID_SSID; - template.ssid.header.len = ssid_len; - if (ssid_len && ssid) - memcpy(template.ssid.ssid, ssid, ssid_len); - size += sizeof(struct wl12xx_ie_header) + ssid_len; - ptr += size; - - /* Basic Rates */ - rates = (struct wl12xx_ie_rates *)ptr; - rates->header.id = WLAN_EID_SUPP_RATES; - rates->header.len = wl1251_build_basic_rates(rates->rates); - size += sizeof(struct wl12xx_ie_header) + rates->header.len; - ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; - - /* Extended rates */ - rates = (struct wl12xx_ie_rates *)ptr; - rates->header.id = WLAN_EID_EXT_SUPP_RATES; - rates->header.len = wl1251_build_extended_rates(rates->rates); - size += sizeof(struct wl12xx_ie_header) + rates->header.len; - - wl1251_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); - - return wl1251_cmd_template_set(wl, CMD_PROBE_REQ, &template, - size); -} + wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan"); -static int wl1251_hw_scan(struct wl1251 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 num_channels, - u8 probe_requests) -{ - struct wl1251_cmd_trigger_scan_to *trigger = NULL; - struct cmd_scan *params = NULL; - int i, ret; - u16 scan_options = 0; - - if (wl->scanning) - return -EINVAL; - - params = kzalloc(sizeof(*params), GFP_KERNEL); - if (!params) - return -ENOMEM; - - params->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); - params->params.rx_filter_options = - cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN); - - /* High priority scan */ - if (!active_scan) - scan_options |= SCAN_PASSIVE; - if (high_prio) - scan_options |= SCAN_PRIORITY_HIGH; - params->params.scan_options = scan_options; - - params->params.num_channels = num_channels; - params->params.num_probe_requests = probe_requests; - params->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ - params->params.tid_trigger = 0; - - for (i = 0; i < num_channels; i++) { - params->channels[i].min_duration = cpu_to_le32(30000); - params->channels[i].max_duration = cpu_to_le32(60000); - memset(¶ms->channels[i].bssid_lsb, 0xff, 4); - memset(¶ms->channels[i].bssid_msb, 0xff, 2); - params->channels[i].early_termination = 0; - params->channels[i].tx_power_att = 0; - params->channels[i].channel = i + 1; - memset(params->channels[i].pad, 0, 3); + if (req->n_ssids) { + ssid = req->ssids[0].ssid; + ssid_len = req->ssids[0].ssid_len; } - for (i = num_channels; i < SCAN_MAX_NUM_OF_CHANNELS; i++) - memset(¶ms->channels[i], 0, - sizeof(struct basic_scan_channel_parameters)); - - if (len && ssid) { - params->params.ssid_len = len; - memcpy(params->params.ssid, ssid, len); - } else { - params->params.ssid_len = 0; - memset(params->params.ssid, 0, 32); - } + mutex_lock(&wl->mutex); - ret = wl1251_build_probe_req(wl, ssid, len); - if (ret < 0) { - wl1251_error("PROBE request template failed"); + if (wl->scanning) { + wl1251_debug(DEBUG_SCAN, "scan already in progress"); + ret = -EINVAL; goto out; } - trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); - if (!trigger) + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) goto out; - trigger->timeout = 0; - - ret = wl1251_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, - sizeof(*trigger)); - if (ret < 0) { - wl1251_error("trigger scan to failed for hw scan"); + skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, + req->ie, req->ie_len); + if (!skb) { + ret = -ENOMEM; goto out; } - wl1251_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); - - wl->scanning = true; + ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data, + skb->len); + dev_kfree_skb(skb); + if (ret < 0) + goto out_sleep; - ret = wl1251_cmd_send(wl, CMD_SCAN, params, sizeof(*params)); + ret = wl1251_cmd_trigger_scan_to(wl, 0); if (ret < 0) - wl1251_error("SCAN failed"); + goto out_sleep; - wl1251_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params)); + wl->scanning = true; - if (params->header.status != CMD_STATUS_SUCCESS) { - wl1251_error("TEST command answer error: %d", - params->header.status); + ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels, + req->n_channels, WL1251_SCAN_NUM_PROBES); + if (ret < 0) { wl->scanning = false; - ret = -EIO; - goto out; - } - -out: - kfree(params); - return ret; - -} - -static int wl1251_op_hw_scan(struct ieee80211_hw *hw, - struct cfg80211_scan_request *req) -{ - struct wl1251 *wl = hw->priv; - int ret; - u8 *ssid = NULL; - size_t ssid_len = 0; - - wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan"); - - if (req->n_ssids) { - ssid = req->ssids[0].ssid; - ssid_len = req->ssids[0].ssid_len; + goto out_sleep; } - mutex_lock(&wl->mutex); - - ret = wl1251_ps_elp_wakeup(wl); - if (ret < 0) - goto out; - - ret = wl1251_hw_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3); - +out_sleep: wl1251_ps_elp_sleep(wl); out: @@ -1093,9 +947,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_bss_conf *bss_conf, u32 changed) { - enum wl1251_cmd_ps_mode mode; struct wl1251 *wl = hw->priv; - struct sk_buff *beacon; + struct sk_buff *beacon, *skb; int ret; wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed"); @@ -1109,7 +962,17 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BSSID) { memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); - ret = wl1251_build_null_data(wl); + skb = ieee80211_nullfunc_get(wl->hw, wl->vif); + if (!skb) + goto out_sleep; + + ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, + skb->data, skb->len); + dev_kfree_skb(skb); + if (ret < 0) + goto out_sleep; + + ret = wl1251_build_qos_null_data(wl); if (ret < 0) goto out; @@ -1124,27 +987,21 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { wl->beacon_int = bss_conf->beacon_int; - wl->dtim_period = bss_conf->dtim_period; - ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, - wl->dtim_period); - wl->aid = bss_conf->aid; + skb = ieee80211_pspoll_get(wl->hw, wl->vif); + if (!skb) + goto out_sleep; - ret = wl1251_build_ps_poll(wl, wl->aid); + ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, + skb->data, + skb->len); + dev_kfree_skb(skb); if (ret < 0) goto out_sleep; - ret = wl1251_acx_aid(wl, wl->aid); + ret = wl1251_acx_aid(wl, bss_conf->aid); if (ret < 0) goto out_sleep; - - /* If we want to go in PSM but we're not there yet */ - if (wl->psm_requested && !wl->psm) { - mode = STATION_POWER_SAVE_MODE; - ret = wl1251_ps_set_mode(wl, mode); - if (ret < 0) - goto out_sleep; - } } else { /* use defaults when not associated */ wl->beacon_int = WL1251_DEFAULT_BEACON_INT; @@ -1176,7 +1033,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1251_acx_cts_protect(wl, CTSPROTECT_DISABLE); if (ret < 0) { wl1251_warning("Set ctsprotect failed %d", ret); - goto out; + goto out_sleep; } } @@ -1187,7 +1044,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) { dev_kfree_skb(beacon); - goto out; + goto out_sleep; } ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, beacon->data, @@ -1196,13 +1053,13 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, dev_kfree_skb(beacon); if (ret < 0) - goto out; + goto out_sleep; ret = wl1251_join(wl, wl->bss_type, wl->beacon_int, wl->channel, wl->dtim_period); if (ret < 0) - goto out; + goto out_sleep; } out_sleep: @@ -1273,6 +1130,49 @@ static struct ieee80211_channel wl1251_channels[] = { { .hw_value = 13, .center_freq = 2472}, }; +static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + enum wl1251_acx_ps_scheme ps_scheme; + struct wl1251 *wl = hw->priv; + int ret; + + mutex_lock(&wl->mutex); + + wl1251_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue); + + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + /* mac80211 uses units of 32 usec */ + ret = wl1251_acx_ac_cfg(wl, wl1251_tx_get_queue(queue), + params->cw_min, params->cw_max, + params->aifs, params->txop * 32); + if (ret < 0) + goto out_sleep; + + if (params->uapsd) + ps_scheme = WL1251_ACX_PS_SCHEME_UPSD_TRIGGER; + else + ps_scheme = WL1251_ACX_PS_SCHEME_LEGACY; + + ret = wl1251_acx_tid_cfg(wl, wl1251_tx_get_queue(queue), + CHANNEL_TYPE_EDCF, + wl1251_tx_get_queue(queue), ps_scheme, + WL1251_ACX_ACK_POLICY_LEGACY); + if (ret < 0) + goto out_sleep; + +out_sleep: + wl1251_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + /* can't be const, mac80211 writes to this */ static struct ieee80211_supported_band wl1251_band_2ghz = { .channels = wl1251_channels, @@ -1293,6 +1193,7 @@ static const struct ieee80211_ops wl1251_ops = { .hw_scan = wl1251_op_hw_scan, .bss_info_changed = wl1251_op_bss_info_changed, .set_rts_threshold = wl1251_op_set_rts_threshold, + .conf_tx = wl1251_op_conf_tx, }; static int wl1251_register_hw(struct wl1251 *wl) @@ -1332,12 +1233,15 @@ int wl1251_init_ieee80211(struct wl1251 *wl) wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_BEACON_FILTER; + IEEE80211_HW_BEACON_FILTER | + IEEE80211_HW_SUPPORTS_UAPSD; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1251_band_2ghz; + wl->hw->queues = 4; + ret = wl1251_register_hw(wl); if (ret) goto out; diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.c b/drivers/net/wireless/wl12xx/wl1251_ps.c index 9931b197ff77..851dfb65e474 100644 --- a/drivers/net/wireless/wl12xx/wl1251_ps.c +++ b/drivers/net/wireless/wl12xx/wl1251_ps.c @@ -26,7 +26,8 @@ #include "wl1251_cmd.h" #include "wl1251_io.h" -#define WL1251_WAKEUP_TIMEOUT 2000 +/* in ms */ +#define WL1251_WAKEUP_TIMEOUT 100 void wl1251_elp_work(struct work_struct *work) { @@ -67,7 +68,7 @@ void wl1251_ps_elp_sleep(struct wl1251 *wl) int wl1251_ps_elp_wakeup(struct wl1251 *wl) { - unsigned long timeout; + unsigned long timeout, start; u32 elp_reg; if (!wl->elp) @@ -75,6 +76,7 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl) wl1251_debug(DEBUG_PSM, "waking up chip from elp"); + start = jiffies; timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT); wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); @@ -95,8 +97,7 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl) } wl1251_debug(DEBUG_PSM, "wakeup time: %u ms", - jiffies_to_msecs(jiffies) - - (jiffies_to_msecs(timeout) - WL1251_WAKEUP_TIMEOUT)); + jiffies_to_msecs(jiffies - start)); wl->elp = false; diff --git a/drivers/net/wireless/wl12xx/wl1251_rx.c b/drivers/net/wireless/wl12xx/wl1251_rx.c index f84cc89cbffc..6f229e0990f4 100644 --- a/drivers/net/wireless/wl12xx/wl1251_rx.c +++ b/drivers/net/wireless/wl12xx/wl1251_rx.c @@ -23,6 +23,7 @@ */ #include <linux/skbuff.h> +#include <linux/gfp.h> #include <net/mac80211.h> #include "wl1251.h" @@ -126,7 +127,7 @@ static void wl1251_rx_body(struct wl1251 *wl, if (wl->rx_current_buffer) rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size; - skb = dev_alloc_skb(length); + skb = __dev_alloc_skb(length, GFP_KERNEL); if (!skb) { wl1251_error("Couldn't allocate RX frame"); return; diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c index 9cc8c323830f..3bfb59bd4635 100644 --- a/drivers/net/wireless/wl12xx/wl1251_spi.c +++ b/drivers/net/wireless/wl12xx/wl1251_spi.c @@ -23,6 +23,7 @@ #include <linux/irq.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/crc7.h> #include <linux/spi/spi.h> #include <linux/spi/wl12xx.h> diff --git a/drivers/net/wireless/wl12xx/wl1251_tx.c b/drivers/net/wireless/wl12xx/wl1251_tx.c index f85970615849..c8223185efd2 100644 --- a/drivers/net/wireless/wl12xx/wl1251_tx.c +++ b/drivers/net/wireless/wl12xx/wl1251_tx.c @@ -167,8 +167,7 @@ static int wl1251_tx_fill_hdr(struct wl1251 *wl, struct sk_buff *skb, tx_hdr->expiry_time = cpu_to_le32(1 << 16); tx_hdr->id = id; - /* FIXME: how to get the correct queue id? */ - tx_hdr->xmit_queue = 0; + tx_hdr->xmit_queue = wl1251_tx_get_queue(skb_get_queue_mapping(skb)); wl1251_tx_control(tx_hdr, control, fc); wl1251_tx_frag_block_num(tx_hdr); @@ -220,6 +219,7 @@ static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb, /* align the buffer on a 4-byte boundary */ skb_reserve(skb, offset); memmove(skb->data, src, skb->len); + tx_hdr = (struct tx_double_buffer_desc *) skb->data; } else { wl1251_info("No handler, fixme!"); return -EINVAL; @@ -237,8 +237,9 @@ static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb, wl1251_mem_write(wl, addr, skb->data, len); - wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x", - tx_hdr->id, skb, tx_hdr->length, tx_hdr->rate); + wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x " + "queue %d", tx_hdr->id, skb, tx_hdr->length, + tx_hdr->rate, tx_hdr->xmit_queue); return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1251_tx.h b/drivers/net/wireless/wl12xx/wl1251_tx.h index 7c1c1665c810..55856c6bb97a 100644 --- a/drivers/net/wireless/wl12xx/wl1251_tx.h +++ b/drivers/net/wireless/wl12xx/wl1251_tx.h @@ -26,6 +26,7 @@ #define __WL1251_TX_H__ #include <linux/bitops.h> +#include "wl1251_acx.h" /* * @@ -209,6 +210,22 @@ struct tx_result { u8 done_2; } __attribute__ ((packed)); +static inline int wl1251_tx_get_queue(int queue) +{ + switch (queue) { + case 0: + return QOS_AC_VO; + case 1: + return QOS_AC_VI; + case 2: + return QOS_AC_BE; + case 3: + return QOS_AC_BK; + default: + return QOS_AC_BE; + } +} + void wl1251_tx_work(struct work_struct *work); void wl1251_tx_complete(struct wl1251 *wl); void wl1251_tx_flush(struct wl1251 *wl); diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 94359b1a861f..97ea5096bc8c 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -43,7 +43,7 @@ enum { DEBUG_SPI = BIT(1), DEBUG_BOOT = BIT(2), DEBUG_MAILBOX = BIT(3), - DEBUG_NETLINK = BIT(4), + DEBUG_TESTMODE = BIT(4), DEBUG_EVENT = BIT(5), DEBUG_TX = BIT(6), DEBUG_RX = BIT(7), @@ -107,11 +107,36 @@ enum { CFG_RX_CTL_EN | CFG_RX_BCN_EN | \ CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN) -#define WL1271_DEFAULT_BASIC_RATE_SET (CONF_TX_RATE_MASK_ALL) - #define WL1271_FW_NAME "wl1271-fw.bin" #define WL1271_NVS_NAME "wl1271-nvs.bin" +/* NVS data structure */ +#define WL1271_NVS_SECTION_SIZE 468 + +#define WL1271_NVS_GENERAL_PARAMS_SIZE 57 +#define WL1271_NVS_GENERAL_PARAMS_SIZE_PADDED \ + (WL1271_NVS_GENERAL_PARAMS_SIZE + 1) +#define WL1271_NVS_STAT_RADIO_PARAMS_SIZE 17 +#define WL1271_NVS_STAT_RADIO_PARAMS_SIZE_PADDED \ + (WL1271_NVS_STAT_RADIO_PARAMS_SIZE + 1) +#define WL1271_NVS_DYN_RADIO_PARAMS_SIZE 65 +#define WL1271_NVS_DYN_RADIO_PARAMS_SIZE_PADDED \ + (WL1271_NVS_DYN_RADIO_PARAMS_SIZE + 1) +#define WL1271_NVS_FEM_COUNT 2 +#define WL1271_NVS_INI_SPARE_SIZE 124 + +struct wl1271_nvs_file { + /* NVS section */ + u8 nvs[WL1271_NVS_SECTION_SIZE]; + + /* INI section */ + u8 general_params[WL1271_NVS_GENERAL_PARAMS_SIZE_PADDED]; + u8 stat_radio_params[WL1271_NVS_STAT_RADIO_PARAMS_SIZE_PADDED]; + u8 dyn_radio_params[WL1271_NVS_FEM_COUNT] + [WL1271_NVS_DYN_RADIO_PARAMS_SIZE_PADDED]; + u8 ini_spare[WL1271_NVS_INI_SPARE_SIZE]; +} __attribute__ ((packed)); + /* * Enable/disable 802.11a support for WL1273 */ @@ -276,6 +301,7 @@ struct wl1271_debugfs { struct dentry *retry_count; struct dentry *excessive_retries; + struct dentry *gpio_power; }; #define NUM_TX_QUEUES 4 @@ -322,6 +348,17 @@ struct wl1271 { enum wl1271_state state; struct mutex mutex; +#define WL1271_FLAG_STA_RATES_CHANGED (0) +#define WL1271_FLAG_STA_ASSOCIATED (1) +#define WL1271_FLAG_JOINED (2) +#define WL1271_FLAG_GPIO_POWER (3) +#define WL1271_FLAG_TX_QUEUE_STOPPED (4) +#define WL1271_FLAG_SCANNING (5) +#define WL1271_FLAG_IN_ELP (6) +#define WL1271_FLAG_PSM (7) +#define WL1271_FLAG_PSM_REQUESTED (8) + unsigned long flags; + struct wl1271_partition_set part; struct wl1271_chip chip; @@ -331,8 +368,7 @@ struct wl1271 { u8 *fw; size_t fw_len; - u8 *nvs; - size_t nvs_len; + struct wl1271_nvs_file *nvs; u8 bssid[ETH_ALEN]; u8 mac_addr[ETH_ALEN]; @@ -359,7 +395,6 @@ struct wl1271 { /* Frames scheduled for transmission, not handled yet */ struct sk_buff_head tx_queue; - bool tx_queue_stopped; struct work_struct tx_work; @@ -387,14 +422,15 @@ struct wl1271 { u32 mbox_ptr[2]; /* Are we currently scanning */ - bool scanning; struct wl1271_scan scan; /* Our association ID */ u16 aid; /* currently configured rate set */ + u32 sta_rate_set; u32 basic_rate_set; + u32 rate_set; /* The current band */ enum ieee80211_band band; @@ -405,18 +441,9 @@ struct wl1271 { unsigned int rx_config; unsigned int rx_filter; - /* is firmware in elp mode */ - bool elp; - struct completion *elp_compl; struct delayed_work elp_work; - /* we can be in psm, but not in elp, we have to differentiate */ - bool psm; - - /* PSM mode requested */ - bool psm_requested; - /* retry counter for PSM entries */ u8 psm_entry_retry; @@ -435,9 +462,6 @@ struct wl1271 { struct ieee80211_vif *vif; - /* Used for a workaround to send disconnect before rejoining */ - bool joined; - /* Current chipset configuration */ struct conf_drv_settings conf; @@ -455,11 +479,14 @@ int wl1271_plt_stop(struct wl1271 *wl); #define WL1271_TX_QUEUE_MAX_LENGTH 20 -/* WL1271 needs a 200ms sleep after power on */ +/* WL1271 needs a 200ms sleep after power on, and a 20ms sleep before power + on in case is has been shut down shortly before */ +#define WL1271_PRE_POWER_ON_SLEEP 20 /* in miliseconds */ #define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */ static inline bool wl1271_11a_enabled(void) { + /* FIXME: this could be determined based on the NVS-INI file */ #ifdef WL1271_80211A_ENABLED return true; #else diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index 5cc89bbdac7a..308782421fce 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -27,6 +27,7 @@ #include <linux/platform_device.h> #include <linux/crc7.h> #include <linux/spi/spi.h> +#include <linux/slab.h> #include "wl1271.h" #include "wl12xx_80211.h" @@ -390,6 +391,35 @@ out: return ret; } +int wl1271_acx_dco_itrim_params(struct wl1271 *wl) +{ + struct acx_dco_itrim_params *dco; + struct conf_itrim_settings *c = &wl->conf.itrim; + int ret; + + wl1271_debug(DEBUG_ACX, "acx dco itrim parameters"); + + dco = kzalloc(sizeof(*dco), GFP_KERNEL); + if (!dco) { + ret = -ENOMEM; + goto out; + } + + dco->enable = c->enable; + dco->timeout = cpu_to_le32(c->timeout); + + ret = wl1271_cmd_configure(wl, ACX_SET_DCO_ITRIM_PARAMS, + dco, sizeof(*dco)); + if (ret < 0) { + wl1271_warning("failed to set dco itrim parameters: %d", ret); + goto out; + } + +out: + kfree(dco); + return ret; +} + int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter) { struct acx_beacon_filter_option *beacon_filter = NULL; @@ -758,10 +788,11 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats) return 0; } -int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates) +int wl1271_acx_rate_policies(struct wl1271 *wl) { struct acx_rate_policy *acx; struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf; + int idx = 0; int ret = 0; wl1271_debug(DEBUG_ACX, "acx rate policies"); @@ -773,12 +804,21 @@ int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates) goto out; } - /* configure one default (one-size-fits-all) rate class */ - acx->rate_class_cnt = cpu_to_le32(1); - acx->rate_class[0].enabled_rates = cpu_to_le32(enabled_rates); - acx->rate_class[0].short_retry_limit = c->short_retry_limit; - acx->rate_class[0].long_retry_limit = c->long_retry_limit; - acx->rate_class[0].aflags = c->aflags; + /* configure one basic rate class */ + idx = ACX_TX_BASIC_RATE; + acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->basic_rate_set); + acx->rate_class[idx].short_retry_limit = c->short_retry_limit; + acx->rate_class[idx].long_retry_limit = c->long_retry_limit; + acx->rate_class[idx].aflags = c->aflags; + + /* configure one AP supported rate class */ + idx = ACX_TX_AP_FULL_RATE; + acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->rate_set); + acx->rate_class[idx].short_retry_limit = c->short_retry_limit; + acx->rate_class[idx].long_retry_limit = c->long_retry_limit; + acx->rate_class[idx].aflags = c->aflags; + + acx->rate_class_cnt = cpu_to_le32(ACX_TX_RATE_POLICY_CNT); ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { @@ -791,12 +831,14 @@ out: return ret; } -int wl1271_acx_ac_cfg(struct wl1271 *wl) +int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max, + u8 aifsn, u16 txop) { struct acx_ac_cfg *acx; - int i, ret = 0; + int ret = 0; - wl1271_debug(DEBUG_ACX, "acx access category config"); + wl1271_debug(DEBUG_ACX, "acx ac cfg %d cw_ming %d cw_max %d " + "aifs %d txop %d", ac, cw_min, cw_max, aifsn, txop); acx = kzalloc(sizeof(*acx), GFP_KERNEL); @@ -805,21 +847,16 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl) goto out; } - for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { - struct conf_tx_ac_category *c = &(wl->conf.tx.ac_conf[i]); - acx->ac = c->ac; - acx->cw_min = c->cw_min; - acx->cw_max = cpu_to_le16(c->cw_max); - acx->aifsn = c->aifsn; - acx->reserved = 0; - acx->tx_op_limit = cpu_to_le16(c->tx_op_limit); + acx->ac = ac; + acx->cw_min = cw_min; + acx->cw_max = cpu_to_le16(cw_max); + acx->aifsn = aifsn; + acx->tx_op_limit = cpu_to_le16(txop); - ret = wl1271_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); - if (ret < 0) { - wl1271_warning("Setting of access category " - "config: %d", ret); - goto out; - } + ret = wl1271_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx ac cfg failed: %d", ret); + goto out; } out: @@ -827,10 +864,12 @@ out: return ret; } -int wl1271_acx_tid_cfg(struct wl1271 *wl) +int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type, + u8 tsid, u8 ps_scheme, u8 ack_policy, + u32 apsd_conf0, u32 apsd_conf1) { struct acx_tid_config *acx; - int i, ret = 0; + int ret = 0; wl1271_debug(DEBUG_ACX, "acx tid config"); @@ -841,21 +880,18 @@ int wl1271_acx_tid_cfg(struct wl1271 *wl) goto out; } - for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { - struct conf_tx_tid *c = &(wl->conf.tx.tid_conf[i]); - acx->queue_id = c->queue_id; - acx->channel_type = c->channel_type; - acx->tsid = c->tsid; - acx->ps_scheme = c->ps_scheme; - acx->ack_policy = c->ack_policy; - acx->apsd_conf[0] = cpu_to_le32(c->apsd_conf[0]); - acx->apsd_conf[1] = cpu_to_le32(c->apsd_conf[1]); + acx->queue_id = queue_id; + acx->channel_type = channel_type; + acx->tsid = tsid; + acx->ps_scheme = ps_scheme; + acx->ack_policy = ack_policy; + acx->apsd_conf[0] = cpu_to_le32(apsd_conf0); + acx->apsd_conf[1] = cpu_to_le32(apsd_conf1); - ret = wl1271_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); - if (ret < 0) { - wl1271_warning("Setting of tid config failed: %d", ret); - goto out; - } + ret = wl1271_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of tid config failed: %d", ret); + goto out; } out: @@ -1012,59 +1048,6 @@ out: return ret; } -int wl1271_acx_smart_reflex(struct wl1271 *wl) -{ - struct acx_smart_reflex_state *sr_state = NULL; - struct acx_smart_reflex_config_params *sr_param = NULL; - int i, ret; - - wl1271_debug(DEBUG_ACX, "acx smart reflex"); - - sr_param = kzalloc(sizeof(*sr_param), GFP_KERNEL); - if (!sr_param) { - ret = -ENOMEM; - goto out; - } - - for (i = 0; i < CONF_SR_ERR_TBL_COUNT; i++) { - struct conf_mart_reflex_err_table *e = - &(wl->conf.init.sr_err_tbl[i]); - - sr_param->error_table[i].len = e->len; - sr_param->error_table[i].upper_limit = e->upper_limit; - memcpy(sr_param->error_table[i].values, e->values, e->len); - } - - ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_PARAMS, - sr_param, sizeof(*sr_param)); - if (ret < 0) { - wl1271_warning("failed to set smart reflex params: %d", ret); - goto out; - } - - sr_state = kzalloc(sizeof(*sr_state), GFP_KERNEL); - if (!sr_state) { - ret = -ENOMEM; - goto out; - } - - /* enable smart reflex */ - sr_state->enable = wl->conf.init.sr_enable; - - ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_STATE, - sr_state, sizeof(*sr_state)); - if (ret < 0) { - wl1271_warning("failed to set smart reflex params: %d", ret); - goto out; - } - -out: - kfree(sr_state); - kfree(sr_param); - return ret; - -} - int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable) { struct wl1271_acx_bet_enable *acx = NULL; @@ -1132,3 +1115,31 @@ out: kfree(acx); return ret; } + +int wl1271_acx_pm_config(struct wl1271 *wl) +{ + struct wl1271_acx_pm_config *acx = NULL; + struct conf_pm_config_settings *c = &wl->conf.pm_config; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx pm config"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->host_clk_settling_time = cpu_to_le32(c->host_clk_settling_time); + acx->host_fast_wakeup_support = c->host_fast_wakeup_support; + + ret = wl1271_cmd_configure(wl, ACX_PM_CONFIG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx pm config failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index 2ce0a8128542..aeccc98581eb 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -2,7 +2,7 @@ * This file is part of wl1271 * * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. - * Copyright (C) 2008-2009 Nokia Corporation + * Copyright (C) 2008-2010 Nokia Corporation * * Contact: Luciano Coelho <luciano.coelho@nokia.com> * @@ -348,7 +348,7 @@ struct acx_beacon_filter_option { * ACXBeaconFilterEntry (not 221) * Byte Offset Size (Bytes) Definition * =========== ============ ========== - * 0 1 IE identifier + * 0 1 IE identifier * 1 1 Treatment bit mask * * ACXBeaconFilterEntry (221) @@ -381,8 +381,8 @@ struct acx_beacon_filter_ie_table { struct acx_header header; u8 num_ie; - u8 table[BEACON_FILTER_TABLE_MAX_SIZE]; u8 pad[3]; + u8 table[BEACON_FILTER_TABLE_MAX_SIZE]; } __attribute__ ((packed)); struct acx_conn_monit_params { @@ -415,23 +415,12 @@ struct acx_bt_wlan_coex { u8 pad[3]; } __attribute__ ((packed)); -struct acx_smart_reflex_state { +struct acx_dco_itrim_params { struct acx_header header; u8 enable; u8 padding[3]; -} __attribute__ ((packed)); - -struct smart_reflex_err_table { - u8 len; - s8 upper_limit; - s8 values[14]; -} __attribute__ ((packed)); - -struct acx_smart_reflex_config_params { - struct acx_header header; - - struct smart_reflex_err_table error_table[3]; + __le32 timeout; } __attribute__ ((packed)); #define PTA_ANTENNA_TYPE_DEF (0) @@ -837,6 +826,9 @@ struct acx_rate_class { u8 reserved; }; +#define ACX_TX_BASIC_RATE 0 +#define ACX_TX_AP_FULL_RATE 1 +#define ACX_TX_RATE_POLICY_CNT 2 struct acx_rate_policy { struct acx_header header; @@ -877,8 +869,8 @@ struct acx_tx_config_options { __le16 tx_compl_threshold; /* number of packets */ } __attribute__ ((packed)); -#define ACX_RX_MEM_BLOCKS 64 -#define ACX_TX_MIN_MEM_BLOCKS 64 +#define ACX_RX_MEM_BLOCKS 70 +#define ACX_TX_MIN_MEM_BLOCKS 40 #define ACX_TX_DESCRIPTORS 32 #define ACX_NUM_SSID_PROFILES 1 @@ -969,6 +961,13 @@ struct wl1271_acx_arp_filter { used. */ } __attribute__((packed)); +struct wl1271_acx_pm_config { + struct acx_header header; + + __le32 host_clk_settling_time; + u8 host_fast_wakeup_support; + u8 padding[3]; +} __attribute__ ((packed)); enum { ACX_WAKE_UP_CONDITIONS = 0x0002, @@ -1027,13 +1026,13 @@ enum { ACX_HT_BSS_OPERATION = 0x0058, ACX_COEX_ACTIVITY = 0x0059, ACX_SET_SMART_REFLEX_DEBUG = 0x005A, - ACX_SET_SMART_REFLEX_STATE = 0x005B, - ACX_SET_SMART_REFLEX_PARAMS = 0x005F, + ACX_SET_DCO_ITRIM_PARAMS = 0x0061, DOT11_RX_MSDU_LIFE_TIME = 0x1004, DOT11_CUR_TX_PWR = 0x100D, DOT11_RX_DOT11_MODE = 0x1012, DOT11_RTS_THRESHOLD = 0x1013, DOT11_GROUP_ADDRESS_TBL = 0x1014, + ACX_PM_CONFIG = 0x1016, MAX_DOT11_IE = DOT11_GROUP_ADDRESS_TBL, @@ -1056,6 +1055,7 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable, void *mc_list, u32 mc_list_len); int wl1271_acx_service_period_timeout(struct wl1271 *wl); int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold); +int wl1271_acx_dco_itrim_params(struct wl1271 *wl); int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl); int wl1271_acx_conn_monit_params(struct wl1271 *wl); @@ -1069,9 +1069,12 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble); int wl1271_acx_cts_protect(struct wl1271 *wl, enum acx_ctsprotect_type ctsprotect); int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats); -int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates); -int wl1271_acx_ac_cfg(struct wl1271 *wl); -int wl1271_acx_tid_cfg(struct wl1271 *wl); +int wl1271_acx_rate_policies(struct wl1271 *wl); +int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max, + u8 aifsn, u16 txop); +int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type, + u8 tsid, u8 ps_scheme, u8 ack_policy, + u32 apsd_conf0, u32 apsd_conf1); int wl1271_acx_frag_threshold(struct wl1271 *wl); int wl1271_acx_tx_config_options(struct wl1271 *wl); int wl1271_acx_mem_cfg(struct wl1271 *wl); @@ -1081,5 +1084,6 @@ int wl1271_acx_smart_reflex(struct wl1271 *wl); int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address, u8 version); +int wl1271_acx_pm_config(struct wl1271 *wl); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index b7c96454cca3..024356263065 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -22,11 +22,13 @@ */ #include <linux/gpio.h> +#include <linux/slab.h> #include "wl1271_acx.h" #include "wl1271_reg.h" #include "wl1271_boot.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #include "wl1271_event.h" static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { @@ -93,19 +95,19 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) u32 cpu_ctrl; /* 10.5.0 run the firmware (I) */ - cpu_ctrl = wl1271_spi_read32(wl, ACX_REG_ECPU_CONTROL); + cpu_ctrl = wl1271_read32(wl, ACX_REG_ECPU_CONTROL); /* 10.5.1 run the firmware (II) */ cpu_ctrl |= flag; - wl1271_spi_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); + wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); } static void wl1271_boot_fw_version(struct wl1271 *wl) { struct wl1271_static_data static_data; - wl1271_spi_read(wl, wl->cmd_box_addr, - &static_data, sizeof(static_data), false); + wl1271_read(wl, wl->cmd_box_addr, &static_data, sizeof(static_data), + false); strncpy(wl->chip.fw_ver, static_data.fw_version, sizeof(wl->chip.fw_ver)); @@ -164,7 +166,7 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, memcpy(chunk, p, CHUNK_SIZE); wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", p, addr); - wl1271_spi_write(wl, addr, chunk, CHUNK_SIZE, false); + wl1271_write(wl, addr, chunk, CHUNK_SIZE, false); chunk_num++; } @@ -175,7 +177,7 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, memcpy(chunk, p, fw_data_len % CHUNK_SIZE); wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x", fw_data_len % CHUNK_SIZE, p, addr); - wl1271_spi_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false); + wl1271_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false); kfree(chunk); return 0; @@ -219,23 +221,14 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) size_t nvs_len, burst_len; int i; u32 dest_addr, val; - u8 *nvs_ptr, *nvs, *nvs_aligned; + u8 *nvs_ptr, *nvs_aligned; - nvs = wl->nvs; - if (nvs == NULL) + if (wl->nvs == NULL) return -ENODEV; - nvs_ptr = nvs; - - nvs_len = wl->nvs_len; - - /* Update the device MAC address into the nvs */ - nvs[11] = wl->mac_addr[0]; - nvs[10] = wl->mac_addr[1]; - nvs[6] = wl->mac_addr[2]; - nvs[5] = wl->mac_addr[3]; - nvs[4] = wl->mac_addr[4]; - nvs[3] = wl->mac_addr[5]; + /* only the first part of the NVS needs to be uploaded */ + nvs_len = sizeof(wl->nvs->nvs); + nvs_ptr = (u8 *)wl->nvs->nvs; /* * Layout before the actual NVS tables: @@ -265,7 +258,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "nvs burst write 0x%x: 0x%x", dest_addr, val); - wl1271_spi_write32(wl, dest_addr, val); + wl1271_write32(wl, dest_addr, val); nvs_ptr += 4; dest_addr += 4; @@ -277,7 +270,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) * is 7 bytes further. */ nvs_ptr += 7; - nvs_len -= nvs_ptr - nvs; + nvs_len -= nvs_ptr - (u8 *)wl->nvs->nvs; nvs_len = ALIGN(nvs_len, 4); /* FIXME: The driver sets the partition here, but this is not needed, @@ -286,15 +279,20 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) wl1271_set_partition(wl, &part_table[PART_WORK]); /* Copy the NVS tables to a new block to ensure alignment */ - nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); - if (!nvs_aligned) - return -ENOMEM; + /* FIXME: We jump 3 more bytes before uploading the NVS. It seems + that our NVS files have three extra zeros here. I'm not sure whether + the problem is in our NVS generation or we should really jumpt these + 3 bytes here */ + nvs_ptr += 3; + + nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); if + (!nvs_aligned) return -ENOMEM; /* And finally we upload the NVS tables */ /* FIXME: In wl1271, we upload everything at once. No endianness handling needed here?! The ref driver doesn't do anything about it at this point */ - wl1271_spi_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false); + wl1271_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false); kfree(nvs_aligned); return 0; @@ -303,9 +301,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) static void wl1271_boot_enable_interrupts(struct wl1271 *wl) { enable_irq(wl->irq); - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, - WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); - wl1271_spi_write32(wl, HI_CFG, HI_CFG_DEF_VAL); + wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, + WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); + wl1271_write32(wl, HI_CFG, HI_CFG_DEF_VAL); } static int wl1271_boot_soft_reset(struct wl1271 *wl) @@ -314,13 +312,12 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) u32 boot_data; /* perform soft reset */ - wl1271_spi_write32(wl, ACX_REG_SLV_SOFT_RESET, - ACX_SLV_SOFT_RESET_BIT); + wl1271_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); /* SOFT_RESET is self clearing */ timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); while (1) { - boot_data = wl1271_spi_read32(wl, ACX_REG_SLV_SOFT_RESET); + boot_data = wl1271_read32(wl, ACX_REG_SLV_SOFT_RESET); wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) break; @@ -336,10 +333,10 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) } /* disable Rx/Tx */ - wl1271_spi_write32(wl, ENABLE, 0x0); + wl1271_write32(wl, ENABLE, 0x0); /* disable auto calibration on start*/ - wl1271_spi_write32(wl, SPARE_A2, 0xffff); + wl1271_write32(wl, SPARE_A2, 0xffff); return 0; } @@ -351,7 +348,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); - chip_id = wl1271_spi_read32(wl, CHIP_ID_B); + chip_id = wl1271_read32(wl, CHIP_ID_B); wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); @@ -364,8 +361,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) loop = 0; while (loop++ < INIT_LOOP) { udelay(INIT_LOOP_DELAY); - interrupt = wl1271_spi_read32(wl, - ACX_REG_INTERRUPT_NO_CLEAR); + interrupt = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); if (interrupt == 0xffffffff) { wl1271_error("error reading hardware complete " @@ -374,8 +370,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) } /* check that ACX_INTR_INIT_COMPLETE is enabled */ else if (interrupt & WL1271_ACX_INTR_INIT_COMPLETE) { - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK, - WL1271_ACX_INTR_INIT_COMPLETE); + wl1271_write32(wl, ACX_REG_INTERRUPT_ACK, + WL1271_ACX_INTR_INIT_COMPLETE); break; } } @@ -387,10 +383,10 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) } /* get hardware config command mail box */ - wl->cmd_box_addr = wl1271_spi_read32(wl, REG_COMMAND_MAILBOX_PTR); + wl->cmd_box_addr = wl1271_read32(wl, REG_COMMAND_MAILBOX_PTR); /* get hardware config event mail box */ - wl->event_box_addr = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR); + wl->event_box_addr = wl1271_read32(wl, REG_EVENT_MAILBOX_PTR); /* set the working partition to its "running" mode offset */ wl1271_set_partition(wl, &part_table[PART_WORK]); @@ -463,9 +459,9 @@ int wl1271_boot(struct wl1271 *wl) wl1271_top_reg_write(wl, OCP_REG_CLK_POLARITY, val); } - wl1271_spi_write32(wl, PLL_PARAMETERS, clk); + wl1271_write32(wl, PLL_PARAMETERS, clk); - pause = wl1271_spi_read32(wl, PLL_PARAMETERS); + pause = wl1271_read32(wl, PLL_PARAMETERS); wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause); @@ -474,10 +470,10 @@ int wl1271_boot(struct wl1271 *wl) * 0x3ff (magic number ). How does * this work?! */ pause |= WU_COUNTER_PAUSE_VAL; - wl1271_spi_write32(wl, WU_COUNTER_PAUSE, pause); + wl1271_write32(wl, WU_COUNTER_PAUSE, pause); /* Continue the ELP wake up sequence */ - wl1271_spi_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); + wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); udelay(500); wl1271_set_partition(wl, &part_table[PART_DRPW]); @@ -487,18 +483,18 @@ int wl1271_boot(struct wl1271 *wl) before taking DRPw out of reset */ wl1271_debug(DEBUG_BOOT, "DRPW_SCRATCH_START %08x", DRPW_SCRATCH_START); - clk = wl1271_spi_read32(wl, DRPW_SCRATCH_START); + clk = wl1271_read32(wl, DRPW_SCRATCH_START); wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); /* 2 */ clk |= (REF_CLOCK << 1) << 4; - wl1271_spi_write32(wl, DRPW_SCRATCH_START, clk); + wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_set_partition(wl, &part_table[PART_WORK]); /* Disable interrupts */ - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); ret = wl1271_boot_soft_reset(wl); if (ret < 0) @@ -513,23 +509,22 @@ int wl1271_boot(struct wl1271 *wl) * ACX_EEPROMLESS_IND_REG */ wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG"); - wl1271_spi_write32(wl, ACX_EEPROMLESS_IND_REG, - ACX_EEPROMLESS_IND_REG); + wl1271_write32(wl, ACX_EEPROMLESS_IND_REG, ACX_EEPROMLESS_IND_REG); - tmp = wl1271_spi_read32(wl, CHIP_ID_B); + tmp = wl1271_read32(wl, CHIP_ID_B); wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); /* 6. read the EEPROM parameters */ - tmp = wl1271_spi_read32(wl, SCR_PAD2); + tmp = wl1271_read32(wl, SCR_PAD2); ret = wl1271_boot_write_irq_polarity(wl); if (ret < 0) goto out; /* FIXME: Need to check whether this is really what we want */ - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, - WL1271_ACX_ALL_EVENTS_VECTOR); + wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, + WL1271_ACX_ALL_EVENTS_VECTOR); /* WL1271: The reference driver skips steps 7 to 10 (jumps directly * to upload_fw) */ diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index c3385b3d246c..e7832f3318eb 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -26,10 +26,12 @@ #include <linux/crc7.h> #include <linux/spi/spi.h> #include <linux/etherdevice.h> +#include <linux/slab.h> #include "wl1271.h" #include "wl1271_reg.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #include "wl1271_acx.h" #include "wl12xx_80211.h" #include "wl1271_cmd.h" @@ -57,13 +59,13 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, WARN_ON(len % 4 != 0); - wl1271_spi_write(wl, wl->cmd_box_addr, buf, len, false); + wl1271_write(wl, wl->cmd_box_addr, buf, len, false); - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); + wl1271_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); - intr = wl1271_spi_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); @@ -73,13 +75,13 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, msleep(1); - intr = wl1271_spi_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); } /* read back the status code of the command */ if (res_len == 0) res_len = sizeof(struct wl1271_cmd_header); - wl1271_spi_read(wl, wl->cmd_box_addr, cmd, res_len, false); + wl1271_read(wl, wl->cmd_box_addr, cmd, res_len, false); status = le16_to_cpu(cmd->status); if (status != CMD_STATUS_SUCCESS) { @@ -87,8 +89,8 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, ret = -EIO; } - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK, - WL1271_ACX_INTR_CMD_COMPLETE); + wl1271_write32(wl, ACX_REG_INTERRUPT_ACK, + WL1271_ACX_INTR_CMD_COMPLETE); out: return ret; @@ -191,23 +193,19 @@ static int wl1271_cmd_cal(struct wl1271 *wl) int wl1271_cmd_general_parms(struct wl1271 *wl) { struct wl1271_general_parms_cmd *gen_parms; - struct conf_general_parms *g = &wl->conf.init.genparam; int ret; + if (!wl->nvs) + return -ENODEV; + gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); if (!gen_parms) return -ENOMEM; gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM; - gen_parms->ref_clk = g->ref_clk; - gen_parms->settling_time = g->settling_time; - gen_parms->clk_valid_on_wakeup = g->clk_valid_on_wakeup; - gen_parms->dc2dcmode = g->dc2dcmode; - gen_parms->single_dual_band = g->single_dual_band; - gen_parms->tx_bip_fem_autodetect = g->tx_bip_fem_autodetect; - gen_parms->tx_bip_fem_manufacturer = g->tx_bip_fem_manufacturer; - gen_parms->settings = g->settings; + memcpy(gen_parms->params, wl->nvs->general_params, + WL1271_NVS_GENERAL_PARAMS_SIZE); ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0); if (ret < 0) @@ -220,8 +218,11 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) int wl1271_cmd_radio_parms(struct wl1271 *wl) { struct wl1271_radio_parms_cmd *radio_parms; - struct conf_radio_parms *r = &wl->conf.init.radioparam; - int i, ret; + struct conf_radio_parms *rparam = &wl->conf.init.radioparam; + int ret; + + if (!wl->nvs) + return -ENODEV; radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL); if (!radio_parms) @@ -229,60 +230,13 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl) radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; - /* Static radio parameters */ - radio_parms->rx_trace_loss = r->rx_trace_loss; - radio_parms->tx_trace_loss = r->tx_trace_loss; - memcpy(radio_parms->rx_rssi_and_proc_compens, - r->rx_rssi_and_proc_compens, - CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE); - - memcpy(radio_parms->rx_trace_loss_5, r->rx_trace_loss_5, - CONF_NUMBER_OF_SUB_BANDS_5); - memcpy(radio_parms->tx_trace_loss_5, r->tx_trace_loss_5, - CONF_NUMBER_OF_SUB_BANDS_5); - memcpy(radio_parms->rx_rssi_and_proc_compens_5, - r->rx_rssi_and_proc_compens_5, - CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE); - - /* Dynamic radio parameters */ - radio_parms->tx_ref_pd_voltage = cpu_to_le16(r->tx_ref_pd_voltage); - radio_parms->tx_ref_power = r->tx_ref_power; - radio_parms->tx_offset_db = r->tx_offset_db; - - memcpy(radio_parms->tx_rate_limits_normal, r->tx_rate_limits_normal, - CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->tx_rate_limits_degraded, r->tx_rate_limits_degraded, - CONF_NUMBER_OF_RATE_GROUPS); - - memcpy(radio_parms->tx_channel_limits_11b, r->tx_channel_limits_11b, - CONF_NUMBER_OF_CHANNELS_2_4); - memcpy(radio_parms->tx_channel_limits_ofdm, r->tx_channel_limits_ofdm, - CONF_NUMBER_OF_CHANNELS_2_4); - memcpy(radio_parms->tx_pdv_rate_offsets, r->tx_pdv_rate_offsets, - CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->tx_ibias, r->tx_ibias, CONF_NUMBER_OF_RATE_GROUPS); - - radio_parms->rx_fem_insertion_loss = r->rx_fem_insertion_loss; - - for (i = 0; i < CONF_NUMBER_OF_SUB_BANDS_5; i++) - radio_parms->tx_ref_pd_voltage_5[i] = - cpu_to_le16(r->tx_ref_pd_voltage_5[i]); - memcpy(radio_parms->tx_ref_power_5, r->tx_ref_power_5, - CONF_NUMBER_OF_SUB_BANDS_5); - memcpy(radio_parms->tx_offset_db_5, r->tx_offset_db_5, - CONF_NUMBER_OF_SUB_BANDS_5); - memcpy(radio_parms->tx_rate_limits_normal_5, - r->tx_rate_limits_normal_5, CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->tx_rate_limits_degraded_5, - r->tx_rate_limits_degraded_5, CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->tx_channel_limits_ofdm_5, - r->tx_channel_limits_ofdm_5, CONF_NUMBER_OF_CHANNELS_5); - memcpy(radio_parms->tx_pdv_rate_offsets_5, r->tx_pdv_rate_offsets_5, - CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->tx_ibias_5, r->tx_ibias_5, - CONF_NUMBER_OF_RATE_GROUPS); - memcpy(radio_parms->rx_fem_insertion_loss_5, - r->rx_fem_insertion_loss_5, CONF_NUMBER_OF_SUB_BANDS_5); + memcpy(radio_parms->stat_radio_params, wl->nvs->stat_radio_params, + WL1271_NVS_STAT_RADIO_PARAMS_SIZE); + memcpy(radio_parms->dyn_radio_params, + wl->nvs->dyn_radio_params[rparam->fem], + WL1271_NVS_DYN_RADIO_PARAMS_SIZE); + + /* FIXME: current NVS is missing 5GHz parameters */ wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", radio_parms, sizeof(*radio_parms)); @@ -311,19 +265,6 @@ int wl1271_cmd_join(struct wl1271 *wl) do_cal = false; } - /* FIXME: This is a workaround, because with the current stack, we - * cannot know when we have disassociated. So, if we have already - * joined, we disconnect before joining again. */ - if (wl->joined) { - ret = wl1271_cmd_disconnect(wl); - if (ret < 0) { - wl1271_error("failed to disconnect before rejoining"); - goto out; - } - - wl->joined = false; - } - join = kzalloc(sizeof(*join), GFP_KERNEL); if (!join) { ret = -ENOMEM; @@ -388,8 +329,6 @@ int wl1271_cmd_join(struct wl1271 *wl) goto out_free; } - wl->joined = true; - /* * ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to * simplify locking we just sleep instead, for now @@ -487,7 +426,7 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) return 0; } -int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) +int wl1271_cmd_data_path(struct wl1271 *wl, bool enable) { struct cmd_enabledisable_path *cmd; int ret; @@ -501,7 +440,8 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) goto out; } - cmd->channel = channel; + /* the channel here is only used for calibration, so hardcoded to 1 */ + cmd->channel = 1; if (enable) { cmd_rx = CMD_ENABLE_RX; @@ -514,29 +454,29 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("rx %s cmd for channel %d failed", - enable ? "start" : "stop", channel); + enable ? "start" : "stop", cmd->channel); goto out; } wl1271_debug(DEBUG_BOOT, "rx %s cmd channel %d", - enable ? "start" : "stop", channel); + enable ? "start" : "stop", cmd->channel); ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("tx %s cmd for channel %d failed", - enable ? "start" : "stop", channel); + enable ? "start" : "stop", cmd->channel); return ret; } wl1271_debug(DEBUG_BOOT, "tx %s cmd channel %d", - enable ? "start" : "stop", channel); + enable ? "start" : "stop", cmd->channel); out: kfree(cmd); return ret; } -int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) +int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send) { struct wl1271_cmd_ps_params *ps_params = NULL; int ret = 0; @@ -557,7 +497,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) } ps_params->ps_mode = ps_mode; - ps_params->send_null_data = 1; + ps_params->send_null_data = send; ps_params->retries = 5; ps_params->hang_over_period = 128; ps_params->null_data_rate = cpu_to_le32(1); /* 1 Mbps */ @@ -636,7 +576,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, channels = wl->hw->wiphy->bands[ieee_band]->channels; n_ch = wl->hw->wiphy->bands[ieee_band]->n_channels; - if (wl->scanning) + if (test_bit(WL1271_FLAG_SCANNING, &wl->flags)) return -EINVAL; params = kzalloc(sizeof(*params), GFP_KERNEL); @@ -711,7 +651,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, wl1271_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); - wl->scanning = true; + set_bit(WL1271_FLAG_SCANNING, &wl->flags); if (wl1271_11a_enabled()) { wl->scan.state = band; if (band == WL1271_SCAN_BAND_DUAL) { @@ -729,7 +669,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params), 0); if (ret < 0) { wl1271_error("SCAN failed"); - wl->scanning = false; + clear_bit(WL1271_FLAG_SCANNING, &wl->flags); goto out; } @@ -1003,7 +943,7 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_warning("could not set keys"); - goto out; + goto out; } out: diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h index b4fa4acb9229..2dc06c73532b 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -37,8 +37,8 @@ int wl1271_cmd_join(struct wl1271 *wl); int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); -int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable); -int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode); +int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); +int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, @@ -428,67 +428,24 @@ struct wl1271_general_parms_cmd { struct wl1271_cmd_test_header test; - u8 ref_clk; - u8 settling_time; - u8 clk_valid_on_wakeup; - u8 dc2dcmode; - u8 single_dual_band; - - u8 tx_bip_fem_autodetect; - u8 tx_bip_fem_manufacturer; - u8 settings; + u8 params[WL1271_NVS_GENERAL_PARAMS_SIZE]; + s8 reserved[23]; } __attribute__ ((packed)); +#define WL1271_STAT_RADIO_PARAMS_5_SIZE 29 +#define WL1271_DYN_RADIO_PARAMS_5_SIZE 104 + struct wl1271_radio_parms_cmd { struct wl1271_cmd_header header; struct wl1271_cmd_test_header test; - /* Static radio parameters */ - /* 2.4GHz */ - u8 rx_trace_loss; - u8 tx_trace_loss; - s8 rx_rssi_and_proc_compens[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* 5GHz */ - u8 rx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; - u8 tx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 rx_rssi_and_proc_compens_5[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* Dynamic radio parameters */ - /* 2.4GHz */ - __le16 tx_ref_pd_voltage; - s8 tx_ref_power; - s8 tx_offset_db; - - s8 tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4]; - s8 tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4]; - s8 tx_pdv_rate_offsets[CONF_NUMBER_OF_RATE_GROUPS]; - - u8 tx_ibias[CONF_NUMBER_OF_RATE_GROUPS]; - u8 rx_fem_insertion_loss; + u8 stat_radio_params[WL1271_NVS_STAT_RADIO_PARAMS_SIZE]; + u8 stat_radio_params_5[WL1271_STAT_RADIO_PARAMS_5_SIZE]; - u8 padding2; - - /* 5GHz */ - __le16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5]; - - s8 tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5]; - s8 tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS]; - - /* FIXME: this is inconsistent with the types for 2.4GHz */ - s8 tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS]; - s8 rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; - - u8 padding3[2]; + u8 dyn_radio_params[WL1271_NVS_DYN_RADIO_PARAMS_SIZE]; + u8 reserved; + u8 dyn_radio_params_5[WL1271_DYN_RADIO_PARAMS_5_SIZE]; } __attribute__ ((packed)); struct wl1271_cmd_cal_channel_tune { diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h index 565373ede265..6f9e75cc5640 100644 --- a/drivers/net/wireless/wl12xx/wl1271_conf.h +++ b/drivers/net/wireless/wl12xx/wl1271_conf.h @@ -258,7 +258,8 @@ struct conf_rx_settings { #define CONF_TX_MAX_RATE_CLASSES 8 #define CONF_TX_RATE_MASK_UNSPECIFIED 0 -#define CONF_TX_RATE_MASK_ALL 0x1eff +#define CONF_TX_RATE_MASK_BASIC (CONF_HW_BIT_RATE_1MBPS | \ + CONF_HW_BIT_RATE_2MBPS) #define CONF_TX_RATE_RETRY_LIMIT 10 struct conf_tx_rate_class { @@ -722,31 +723,6 @@ struct conf_conn_settings { u8 psm_entry_retries; }; -#define CONF_SR_ERR_TBL_MAX_VALUES 14 - -struct conf_mart_reflex_err_table { - /* - * Length of the error table values table. - * - * Range: 0 - CONF_SR_ERR_TBL_MAX_VALUES - */ - u8 len; - - /* - * Smart Reflex error table upper limit. - * - * Range: s8 - */ - s8 upper_limit; - - /* - * Smart Reflex error table values. - * - * Range: s8 - */ - s8 values[CONF_SR_ERR_TBL_MAX_VALUES]; -}; - enum { CONF_REF_CLK_19_2_E, CONF_REF_CLK_26_E, @@ -759,64 +735,6 @@ enum single_dual_band_enum { CONF_DUAL_BAND }; -struct conf_general_parms { - /* - * RF Reference Clock type / speed - * - * Range: CONF_REF_CLK_* - */ - u8 ref_clk; - - /* - * Settling time of the reference clock after boot. - * - * Range: u8 - */ - u8 settling_time; - - /* - * Flag defining whether clock is valid on wakeup. - * - * Range: 0 - not valid on wakeup, 1 - valid on wakeup - */ - u8 clk_valid_on_wakeup; - - /* - * DC-to-DC mode. - * - * Range: Unknown - */ - u8 dc2dcmode; - - /* - * Flag defining whether used as single or dual-band. - * - * Range: CONF_SINGLE_BAND, CONF_DUAL_BAND - */ - u8 single_dual_band; - - /* - * TX bip fem autodetect flag. - * - * Range: Unknown - */ - u8 tx_bip_fem_autodetect; - - /* - * TX bip gem manufacturer. - * - * Range: Unknown - */ - u8 tx_bip_fem_manufacturer; - - /* - * Settings flags. - * - * Range: Unknown - */ - u8 settings; -}; - #define CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE 15 #define CONF_NUMBER_OF_SUB_BANDS_5 7 #define CONF_NUMBER_OF_RATE_GROUPS 6 @@ -825,87 +743,43 @@ struct conf_general_parms { struct conf_radio_parms { /* - * Static radio parameters for 2.4GHz - * - * Range: unknown - */ - u8 rx_trace_loss; - u8 tx_trace_loss; - s8 rx_rssi_and_proc_compens[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* - * Static radio parameters for 5GHz - * - * Range: unknown - */ - u8 rx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; - u8 tx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 rx_rssi_and_proc_compens_5[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE]; - - /* - * Dynamic radio parameters for 2.4GHz + * FEM parameter set to use * - * Range: unknown + * Range: 0 or 1 */ - s16 tx_ref_pd_voltage; - s8 tx_ref_power; - s8 tx_offset_db; - - s8 tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4]; - s8 tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4]; - s8 tx_pdv_rate_offsets[CONF_NUMBER_OF_RATE_GROUPS]; - - u8 tx_ibias[CONF_NUMBER_OF_RATE_GROUPS]; - u8 rx_fem_insertion_loss; + u8 fem; +}; +struct conf_init_settings { /* - * Dynamic radio parameters for 5GHz - * - * Range: unknown + * Configure radio parameters. */ - s16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5]; - s8 tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5]; - - s8 tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS]; - s8 tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS]; - - s8 tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5]; - s8 tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS]; + struct conf_radio_parms radioparam; - /* FIXME: this is inconsistent with the types for 2.4GHz */ - s8 tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS]; - s8 rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5]; }; -#define CONF_SR_ERR_TBL_COUNT 3 +struct conf_itrim_settings { + /* enable dco itrim */ + u8 enable; -struct conf_init_settings { - /* - * Configure Smart Reflex error table values. - */ - struct conf_mart_reflex_err_table sr_err_tbl[CONF_SR_ERR_TBL_COUNT]; + /* moderation timeout in microsecs from the last TX */ + u32 timeout; +}; +struct conf_pm_config_settings { /* - * Smart Reflex enable flag. + * Host clock settling time * - * Range: 1 - Smart Reflex enabled, 0 - Smart Reflex disabled - */ - u8 sr_enable; - - /* - * Configure general parameters. + * Range: 0 - 30000 us */ - struct conf_general_parms genparam; + u32 host_clk_settling_time; /* - * Configure radio parameters. + * Host fast wakeup support + * + * Range: true, false */ - struct conf_radio_parms radioparam; - + bool host_fast_wakeup_support; }; struct conf_drv_settings { @@ -914,6 +788,8 @@ struct conf_drv_settings { struct conf_tx_settings tx; struct conf_conn_settings conn; struct conf_init_settings init; + struct conf_itrim_settings itrim; + struct conf_pm_config_settings pm_config; }; #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_debugfs.c b/drivers/net/wireless/wl12xx/wl1271_debugfs.c index c1805e5f8964..3f7ff8d0cf5a 100644 --- a/drivers/net/wireless/wl12xx/wl1271_debugfs.c +++ b/drivers/net/wireless/wl12xx/wl1271_debugfs.c @@ -24,6 +24,7 @@ #include "wl1271_debugfs.h" #include <linux/skbuff.h> +#include <linux/slab.h> #include "wl1271.h" #include "wl1271_acx.h" @@ -237,6 +238,64 @@ static const struct file_operations tx_queue_len_ops = { .open = wl1271_open_file_generic, }; +static ssize_t gpio_power_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + bool state = test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); + + int res; + char buf[10]; + + res = scnprintf(buf, sizeof(buf), "%d\n", state); + + return simple_read_from_buffer(user_buf, count, ppos, buf, res); +} + +static ssize_t gpio_power_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + char buf[10]; + size_t len; + unsigned long value; + int ret; + + mutex_lock(&wl->mutex); + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) { + ret = -EFAULT; + goto out; + } + buf[len] = '\0'; + + ret = strict_strtoul(buf, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value in gpio_power"); + goto out; + } + + if (value) { + wl->set_power(true); + set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); + } else { + wl->set_power(false); + clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); + } + +out: + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations gpio_power_ops = { + .read = gpio_power_read, + .write = gpio_power_write, + .open = wl1271_open_file_generic +}; + static void wl1271_debugfs_delete_files(struct wl1271 *wl) { DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow); @@ -333,6 +392,8 @@ static void wl1271_debugfs_delete_files(struct wl1271 *wl) DEBUGFS_DEL(tx_queue_len); DEBUGFS_DEL(retry_count); DEBUGFS_DEL(excessive_retries); + + DEBUGFS_DEL(gpio_power); } static int wl1271_debugfs_add_files(struct wl1271 *wl) @@ -434,6 +495,8 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl) DEBUGFS_ADD(retry_count, wl->debugfs.rootdir); DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir); + DEBUGFS_ADD(gpio_power, wl->debugfs.rootdir); + out: if (ret < 0) wl1271_debugfs_delete_files(wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c index d13fdd99c85c..7468ef10194b 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.c +++ b/drivers/net/wireless/wl12xx/wl1271_event.c @@ -24,6 +24,7 @@ #include "wl1271.h" #include "wl1271_reg.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #include "wl1271_event.h" #include "wl1271_ps.h" #include "wl12xx_80211.h" @@ -35,7 +36,7 @@ static int wl1271_event_scan_complete(struct wl1271 *wl, wl1271_debug(DEBUG_EVENT, "status: 0x%x", mbox->scheduled_scan_status); - if (wl->scanning) { + if (test_bit(WL1271_FLAG_SCANNING, &wl->flags)) { if (wl->scan.state == WL1271_SCAN_BAND_DUAL) { wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, size); @@ -43,7 +44,7 @@ static int wl1271_event_scan_complete(struct wl1271 *wl, * to the wl1271_cmd_scan function that we are not * scanning as it checks that. */ - wl->scanning = false; + clear_bit(WL1271_FLAG_SCANNING, &wl->flags); wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len, wl->scan.active, wl->scan.high_prio, @@ -62,7 +63,7 @@ static int wl1271_event_scan_complete(struct wl1271 *wl, mutex_unlock(&wl->mutex); ieee80211_scan_completed(wl->hw, false); mutex_lock(&wl->mutex); - wl->scanning = false; + clear_bit(WL1271_FLAG_SCANNING, &wl->flags); } } return 0; @@ -78,25 +79,61 @@ static int wl1271_event_ps_report(struct wl1271 *wl, switch (mbox->ps_status) { case EVENT_ENTER_POWER_SAVE_FAIL: - if (!wl->psm) { + wl1271_debug(DEBUG_PSM, "PSM entry failed"); + + if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) { + /* remain in active mode */ wl->psm_entry_retry = 0; break; } if (wl->psm_entry_retry < wl->conf.conn.psm_entry_retries) { wl->psm_entry_retry++; - ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, + true); } else { wl1271_error("PSM entry failed, giving up.\n"); + /* FIXME: this may need to be reconsidered. for now it + is not possible to indicate to the mac80211 + afterwards that PSM entry failed. To maximize + functionality (receiving data and remaining + associated) make sure that we are in sync with the + AP in regard of PSM mode. */ + ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, + false); wl->psm_entry_retry = 0; - *beacon_loss = true; } break; case EVENT_ENTER_POWER_SAVE_SUCCESS: wl->psm_entry_retry = 0; + + /* enable beacon filtering */ + ret = wl1271_acx_beacon_filter_opt(wl, true); + if (ret < 0) + break; + + /* enable beacon early termination */ + ret = wl1271_acx_bet_enable(wl, true); + if (ret < 0) + break; + + /* go to extremely low power mode */ + wl1271_ps_elp_sleep(wl); + if (ret < 0) + break; break; case EVENT_EXIT_POWER_SAVE_FAIL: - wl1271_info("PSM exit failed"); + wl1271_debug(DEBUG_PSM, "PSM exit failed"); + + if (test_bit(WL1271_FLAG_PSM, &wl->flags)) { + wl->psm_entry_retry = 0; + break; + } + + /* make sure the firmware goes to active mode - the frame to + be sent next will indicate to the AP, that we are active. */ + ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, + false); break; case EVENT_EXIT_POWER_SAVE_SUCCESS: default: @@ -136,7 +173,8 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) * filtering) is enabled. Without PSM, the stack will receive all * beacons and can detect beacon loss by itself. */ - if (vector & BSS_LOSE_EVENT_ID && wl->psm) { + if (vector & BSS_LOSE_EVENT_ID && + test_bit(WL1271_FLAG_PSM, &wl->flags)) { wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); /* indicate to the stack, that beacons have been lost */ @@ -150,7 +188,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) return ret; } - if (beacon_loss) { + if (wl->vif && beacon_loss) { /* Obviously, it's dangerous to release the mutex while we are holding many of the variables in the wl struct. That's why it's done last in the function, and care must @@ -177,14 +215,14 @@ int wl1271_event_unmask(struct wl1271 *wl) void wl1271_event_mbox_config(struct wl1271 *wl) { - wl->mbox_ptr[0] = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR); + wl->mbox_ptr[0] = wl1271_read32(wl, REG_EVENT_MAILBOX_PTR); wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); wl1271_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x", wl->mbox_ptr[0], wl->mbox_ptr[1]); } -int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack) +int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) { struct event_mailbox mbox; int ret; @@ -195,8 +233,8 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack) return -EINVAL; /* first we read the mbox descriptor */ - wl1271_spi_read(wl, wl->mbox_ptr[mbox_num], &mbox, - sizeof(struct event_mailbox), false); + wl1271_read(wl, wl->mbox_ptr[mbox_num], &mbox, + sizeof(struct event_mailbox), false); /* process the descriptor */ ret = wl1271_event_process(wl, &mbox); @@ -204,9 +242,7 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack) return ret; /* then we let the firmware know it can go on...*/ - if (do_ack) - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG, - INTR_TRIG_EVENT_ACK); + wl1271_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK); return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h index 4e3f55ebb1a8..278f9206aa56 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.h +++ b/drivers/net/wireless/wl12xx/wl1271_event.h @@ -112,6 +112,6 @@ struct event_mailbox { int wl1271_event_unmask(struct wl1271 *wl); void wl1271_event_mbox_config(struct wl1271 *wl); -int wl1271_event_handle(struct wl1271 *wl, u8 mbox, bool do_ack); +int wl1271_event_handle(struct wl1271 *wl, u8 mbox); #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 11249b436cf1..d189e8fe05a6 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include "wl1271_init.h" #include "wl12xx_80211.h" @@ -49,7 +50,7 @@ static int wl1271_init_hwenc_config(struct wl1271 *wl) return 0; } -static int wl1271_init_templates_config(struct wl1271 *wl) +int wl1271_init_templates_config(struct wl1271 *wl) { int ret; @@ -113,7 +114,7 @@ static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter) return 0; } -static int wl1271_init_phy_config(struct wl1271 *wl) +int wl1271_init_phy_config(struct wl1271 *wl) { int ret; @@ -156,7 +157,7 @@ static int wl1271_init_beacon_filter(struct wl1271 *wl) return 0; } -static int wl1271_init_pta(struct wl1271 *wl) +int wl1271_init_pta(struct wl1271 *wl) { int ret; @@ -171,7 +172,7 @@ static int wl1271_init_pta(struct wl1271 *wl) return 0; } -static int wl1271_init_energy_detection(struct wl1271 *wl) +int wl1271_init_energy_detection(struct wl1271 *wl) { int ret; @@ -195,7 +196,9 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl) int wl1271_hw_init(struct wl1271 *wl) { - int ret; + struct conf_tx_ac_category *conf_ac; + struct conf_tx_tid *conf_tid; + int ret, i; ret = wl1271_cmd_general_parms(wl); if (ret < 0) @@ -229,6 +232,10 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + ret = wl1271_acx_dco_itrim_params(wl); + if (ret < 0) + goto out_free_memmap; + /* Initialize connection monitoring thresholds */ ret = wl1271_acx_conn_monit_params(wl); if (ret < 0) @@ -270,22 +277,36 @@ int wl1271_hw_init(struct wl1271 *wl) goto out_free_memmap; /* Default TID configuration */ - ret = wl1271_acx_tid_cfg(wl); - if (ret < 0) - goto out_free_memmap; + for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { + conf_tid = &wl->conf.tx.tid_conf[i]; + ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id, + conf_tid->channel_type, + conf_tid->tsid, + conf_tid->ps_scheme, + conf_tid->ack_policy, + conf_tid->apsd_conf[0], + conf_tid->apsd_conf[1]); + if (ret < 0) + goto out_free_memmap; + } /* Default AC configuration */ - ret = wl1271_acx_ac_cfg(wl); - if (ret < 0) - goto out_free_memmap; + for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { + conf_ac = &wl->conf.tx.ac_conf[i]; + ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min, + conf_ac->cw_max, conf_ac->aifsn, + conf_ac->tx_op_limit); + if (ret < 0) + goto out_free_memmap; + } /* Configure TX rate classes */ - ret = wl1271_acx_rate_policies(wl, CONF_TX_RATE_MASK_ALL); + ret = wl1271_acx_rate_policies(wl); if (ret < 0) goto out_free_memmap; /* Enable data path */ - ret = wl1271_cmd_data_path(wl, wl->channel, 1); + ret = wl1271_cmd_data_path(wl, 1); if (ret < 0) goto out_free_memmap; @@ -299,8 +320,8 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; - /* Configure smart reflex */ - ret = wl1271_acx_smart_reflex(wl); + /* configure PM */ + ret = wl1271_acx_pm_config(wl); if (ret < 0) goto out_free_memmap; diff --git a/drivers/net/wireless/wl12xx/wl1271_init.h b/drivers/net/wireless/wl12xx/wl1271_init.h index 930677fbe852..bc26f8c53b91 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.h +++ b/drivers/net/wireless/wl12xx/wl1271_init.h @@ -27,6 +27,10 @@ #include "wl1271.h" int wl1271_hw_init_power_auth(struct wl1271 *wl); +int wl1271_init_templates_config(struct wl1271 *wl); +int wl1271_init_phy_config(struct wl1271 *wl); +int wl1271_init_pta(struct wl1271 *wl); +int wl1271_init_energy_detection(struct wl1271 *wl); int wl1271_hw_init(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_io.c b/drivers/net/wireless/wl12xx/wl1271_io.c new file mode 100644 index 000000000000..5cd94d5666c2 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_io.c @@ -0,0 +1,213 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2008-2010 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/crc7.h> +#include <linux/spi/spi.h> + +#include "wl1271.h" +#include "wl12xx_80211.h" +#include "wl1271_spi.h" +#include "wl1271_io.h" + +static int wl1271_translate_addr(struct wl1271 *wl, int addr) +{ + /* + * To translate, first check to which window of addresses the + * particular address belongs. Then subtract the starting address + * of that window from the address. Then, add offset of the + * translated region. + * + * The translated regions occur next to each other in physical device + * memory, so just add the sizes of the preceeding address regions to + * get the offset to the new region. + * + * Currently, only the two first regions are addressed, and the + * assumption is that all addresses will fall into either of those + * two. + */ + if ((addr >= wl->part.reg.start) && + (addr < wl->part.reg.start + wl->part.reg.size)) + return addr - wl->part.reg.start + wl->part.mem.size; + else + return addr - wl->part.mem.start; +} + +/* Set the SPI partitions to access the chip addresses + * + * To simplify driver code, a fixed (virtual) memory map is defined for + * register and memory addresses. Because in the chipset, in different stages + * of operation, those addresses will move around, an address translation + * mechanism is required. + * + * There are four partitions (three memory and one register partition), + * which are mapped to two different areas of the hardware memory. + * + * Virtual address + * space + * + * | | + * ...+----+--> mem.start + * Physical address ... | | + * space ... | | [PART_0] + * ... | | + * 00000000 <--+----+... ...+----+--> mem.start + mem.size + * | | ... | | + * |MEM | ... | | + * | | ... | | + * mem.size <--+----+... | | {unused area) + * | | ... | | + * |REG | ... | | + * mem.size | | ... | | + * + <--+----+... ...+----+--> reg.start + * reg.size | | ... | | + * |MEM2| ... | | [PART_1] + * | | ... | | + * ...+----+--> reg.start + reg.size + * | | + * + */ +int wl1271_set_partition(struct wl1271 *wl, + struct wl1271_partition_set *p) +{ + /* copy partition info */ + memcpy(&wl->part, p, sizeof(*p)); + + wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", + p->mem.start, p->mem.size); + wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", + p->reg.start, p->reg.size); + wl1271_debug(DEBUG_SPI, "mem2_start %08X mem2_size %08X", + p->mem2.start, p->mem2.size); + wl1271_debug(DEBUG_SPI, "mem3_start %08X mem3_size %08X", + p->mem3.start, p->mem3.size); + + /* write partition info to the chipset */ + wl1271_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); + wl1271_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); + wl1271_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); + wl1271_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); + wl1271_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); + wl1271_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); + wl1271_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); + + return 0; +} + +void wl1271_io_reset(struct wl1271 *wl) +{ + wl1271_spi_reset(wl); +} + +void wl1271_io_init(struct wl1271 *wl) +{ + wl1271_spi_init(wl); +} + +void wl1271_raw_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) +{ + wl1271_spi_raw_write(wl, addr, buf, len, fixed); +} + +void wl1271_raw_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) +{ + wl1271_spi_raw_read(wl, addr, buf, len, fixed); +} + +void wl1271_read(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed) +{ + int physical; + + physical = wl1271_translate_addr(wl, addr); + + wl1271_spi_raw_read(wl, physical, buf, len, fixed); +} + +void wl1271_write(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed) +{ + int physical; + + physical = wl1271_translate_addr(wl, addr); + + wl1271_spi_raw_write(wl, physical, buf, len, fixed); +} + +u32 wl1271_read32(struct wl1271 *wl, int addr) +{ + return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr)); +} + +void wl1271_write32(struct wl1271 *wl, int addr, u32 val) +{ + wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val); +} + +void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) +{ + /* write address >> 1 + 0x30000 to OCP_POR_CTR */ + addr = (addr >> 1) + 0x30000; + wl1271_write32(wl, OCP_POR_CTR, addr); + + /* write value to OCP_POR_WDATA */ + wl1271_write32(wl, OCP_DATA_WRITE, val); + + /* write 1 to OCP_CMD */ + wl1271_write32(wl, OCP_CMD, OCP_CMD_WRITE); +} + +u16 wl1271_top_reg_read(struct wl1271 *wl, int addr) +{ + u32 val; + int timeout = OCP_CMD_LOOP; + + /* write address >> 1 + 0x30000 to OCP_POR_CTR */ + addr = (addr >> 1) + 0x30000; + wl1271_write32(wl, OCP_POR_CTR, addr); + + /* write 2 to OCP_CMD */ + wl1271_write32(wl, OCP_CMD, OCP_CMD_READ); + + /* poll for data ready */ + do { + val = wl1271_read32(wl, OCP_DATA_READ); + } while (!(val & OCP_READY_MASK) && --timeout); + + if (!timeout) { + wl1271_warning("Top register access timed out."); + return 0xffff; + } + + /* check data status and return if OK */ + if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK) + return val & 0xffff; + else { + wl1271_warning("Top register access returned error."); + return 0xffff; + } +} + diff --git a/drivers/net/wireless/wl12xx/wl1271_io.h b/drivers/net/wireless/wl12xx/wl1271_io.h new file mode 100644 index 000000000000..fa9a0b35788f --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_io.h @@ -0,0 +1,68 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. + * Copyright (C) 2008-2010 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_IO_H__ +#define __WL1271_IO_H__ + +struct wl1271; + +void wl1271_io_reset(struct wl1271 *wl); +void wl1271_io_init(struct wl1271 *wl); + +/* Raw target IO, address is not translated */ +void wl1271_raw_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed); +void wl1271_raw_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed); + +/* Translated target IO */ +void wl1271_read(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed); +void wl1271_write(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed); +u32 wl1271_read32(struct wl1271 *wl, int addr); +void wl1271_write32(struct wl1271 *wl, int addr, u32 val); + +/* Top Register IO */ +void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val); +u16 wl1271_top_reg_read(struct wl1271 *wl, int addr); + +int wl1271_set_partition(struct wl1271 *wl, + struct wl1271_partition_set *p); + +static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr) +{ + wl1271_raw_read(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); + + return wl->buffer_32; +} + +static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val) +{ + wl->buffer_32 = val; + wl1271_raw_write(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); +} +#endif diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index b62c00ff42fe..65a1aeba2419 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1,7 +1,7 @@ /* * This file is part of wl1271 * - * Copyright (C) 2008-2009 Nokia Corporation + * Copyright (C) 2008-2010 Nokia Corporation * * Contact: Luciano Coelho <luciano.coelho@nokia.com> * @@ -33,11 +33,13 @@ #include <linux/vmalloc.h> #include <linux/spi/wl12xx.h> #include <linux/inetdevice.h> +#include <linux/slab.h> #include "wl1271.h" #include "wl12xx_80211.h" #include "wl1271_reg.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #include "wl1271_event.h" #include "wl1271_tx.h" #include "wl1271_rx.h" @@ -46,6 +48,9 @@ #include "wl1271_debugfs.h" #include "wl1271_cmd.h" #include "wl1271_boot.h" +#include "wl1271_testmode.h" + +#define WL1271_BOOT_RETRIES 3 static struct conf_drv_settings default_conf = { .sg = { @@ -67,16 +72,17 @@ static struct conf_drv_settings default_conf = { .ps_poll_timeout = 15, .upsd_timeout = 15, .rts_threshold = 2347, - .rx_cca_threshold = 0xFFEF, - .irq_blk_threshold = 0, - .irq_pkt_threshold = USHORT_MAX, - .irq_timeout = 5, + .rx_cca_threshold = 0, + .irq_blk_threshold = 0xFFFF, + .irq_pkt_threshold = 0, + .irq_timeout = 600, .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY, }, .tx = { .tx_energy_detection = 0, .rc_conf = { - .enabled_rates = CONF_TX_RATE_MASK_UNSPECIFIED, + .enabled_rates = CONF_HW_BIT_RATE_1MBPS | + CONF_HW_BIT_RATE_2MBPS, .short_retry_limit = 10, .long_retry_limit = 10, .aflags = 0 @@ -172,8 +178,8 @@ static struct conf_drv_settings default_conf = { } }, .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, - .tx_compl_timeout = 5, - .tx_compl_threshold = 5 + .tx_compl_timeout = 700, + .tx_compl_threshold = 4 }, .conn = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, @@ -186,12 +192,12 @@ static struct conf_drv_settings default_conf = { .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, } }, - .synch_fail_thold = 5, + .synch_fail_thold = 10, .bss_lose_timeout = 100, .beacon_rx_timeout = 10000, .broadcast_timeout = 20000, .rx_broadcast_in_ps = 1, - .ps_poll_threshold = 4, + .ps_poll_threshold = 20, .sig_trigger_count = 2, .sig_trigger = { [0] = { @@ -226,97 +232,17 @@ static struct conf_drv_settings default_conf = { .psm_entry_retries = 3 }, .init = { - .sr_err_tbl = { - [0] = { - .len = 7, - .upper_limit = 0x03, - .values = { - 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8, - 0x00 } - }, - [1] = { - .len = 7, - .upper_limit = 0x03, - .values = { - 0x18, 0x10, 0x05, 0xf6, 0xf0, 0xe8, - 0x00 } - }, - [2] = { - .len = 7, - .upper_limit = 0x03, - .values = { - 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8, - 0x00 } - } - }, - .sr_enable = 1, - .genparam = { - .ref_clk = CONF_REF_CLK_38_4_E, - .settling_time = 5, - .clk_valid_on_wakeup = 0, - .dc2dcmode = 0, - .single_dual_band = CONF_SINGLE_BAND, - .tx_bip_fem_autodetect = 0, - .tx_bip_fem_manufacturer = 1, - .settings = 1, - }, .radioparam = { - .rx_trace_loss = 10, - .tx_trace_loss = 10, - .rx_rssi_and_proc_compens = { - 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8, - 0xfc, 0x00, 0x08, 0x10, 0xf0, 0xf8, - 0x00, 0x0a, 0x14 }, - .rx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 }, - .tx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 }, - .rx_rssi_and_proc_compens_5 = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 }, - .tx_ref_pd_voltage = 0x24e, - .tx_ref_power = 0x78, - .tx_offset_db = 0x0, - .tx_rate_limits_normal = { - 0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 }, - .tx_rate_limits_degraded = { - 0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 }, - .tx_channel_limits_11b = { - 0x22, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x22, 0x50, - 0x22, 0x50 }, - .tx_channel_limits_ofdm = { - 0x20, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x20, 0x50, - 0x20, 0x50 }, - .tx_pdv_rate_offsets = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - .tx_ibias = { - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 }, - .rx_fem_insertion_loss = 0x14, - .tx_ref_pd_voltage_5 = { - 0x0190, 0x01a4, 0x01c3, 0x01d8, - 0x020a, 0x021c }, - .tx_ref_power_5 = { - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, - .tx_offset_db_5 = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - .tx_rate_limits_normal_5 = { - 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 }, - .tx_rate_limits_degraded_5 = { - 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 }, - .tx_channel_limits_ofdm_5 = { - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50 }, - .tx_pdv_rate_offsets_5 = { - 0x01, 0x02, 0x02, 0x02, 0x02, 0x00 }, - .tx_ibias_5 = { - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, - .rx_fem_insertion_loss_5 = { - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 } + .fem = 1, } + }, + .itrim = { + .enable = false, + .timeout = 50000, + }, + .pm_config = { + .host_clk_settling_time = 5000, + .host_fast_wakeup_support = false } }; @@ -337,15 +263,14 @@ static void wl1271_conf_init(struct wl1271 *wl) /* apply driver default configuration */ memcpy(&wl->conf, &default_conf, sizeof(default_conf)); - - if (wl1271_11a_enabled()) - wl->conf.init.genparam.single_dual_band = CONF_DUAL_BAND; } static int wl1271_plt_init(struct wl1271 *wl) { - int ret; + struct conf_tx_ac_category *conf_ac; + struct conf_tx_tid *conf_tid; + int ret, i; ret = wl1271_cmd_general_parms(wl); if (ret < 0) @@ -355,15 +280,89 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_init_mem_config(wl); + ret = wl1271_init_templates_config(wl); if (ret < 0) return ret; - ret = wl1271_cmd_data_path(wl, wl->channel, 1); + ret = wl1271_acx_init_mem_config(wl); if (ret < 0) return ret; + /* PHY layer config */ + ret = wl1271_init_phy_config(wl); + if (ret < 0) + goto out_free_memmap; + + ret = wl1271_acx_dco_itrim_params(wl); + if (ret < 0) + goto out_free_memmap; + + /* Initialize connection monitoring thresholds */ + ret = wl1271_acx_conn_monit_params(wl); + if (ret < 0) + goto out_free_memmap; + + /* Bluetooth WLAN coexistence */ + ret = wl1271_init_pta(wl); + if (ret < 0) + goto out_free_memmap; + + /* Energy detection */ + ret = wl1271_init_energy_detection(wl); + if (ret < 0) + goto out_free_memmap; + + /* Default fragmentation threshold */ + ret = wl1271_acx_frag_threshold(wl); + if (ret < 0) + goto out_free_memmap; + + /* Default TID configuration */ + for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { + conf_tid = &wl->conf.tx.tid_conf[i]; + ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id, + conf_tid->channel_type, + conf_tid->tsid, + conf_tid->ps_scheme, + conf_tid->ack_policy, + conf_tid->apsd_conf[0], + conf_tid->apsd_conf[1]); + if (ret < 0) + goto out_free_memmap; + } + + /* Default AC configuration */ + for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { + conf_ac = &wl->conf.tx.ac_conf[i]; + ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min, + conf_ac->cw_max, conf_ac->aifsn, + conf_ac->tx_op_limit); + if (ret < 0) + goto out_free_memmap; + } + + /* Enable data path */ + ret = wl1271_cmd_data_path(wl, 1); + if (ret < 0) + goto out_free_memmap; + + /* Configure for CAM power saving (ie. always active) */ + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); + if (ret < 0) + goto out_free_memmap; + + /* configure PM */ + ret = wl1271_acx_pm_config(wl); + if (ret < 0) + goto out_free_memmap; + return 0; + + out_free_memmap: + kfree(wl->target_mem_map); + wl->target_mem_map = NULL; + + return ret; } static void wl1271_disable_interrupts(struct wl1271 *wl) @@ -374,11 +373,13 @@ static void wl1271_disable_interrupts(struct wl1271 *wl) static void wl1271_power_off(struct wl1271 *wl) { wl->set_power(false); + clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); } static void wl1271_power_on(struct wl1271 *wl) { wl->set_power(true); + set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); } static void wl1271_fw_status(struct wl1271 *wl, @@ -387,8 +388,7 @@ static void wl1271_fw_status(struct wl1271 *wl, u32 total = 0; int i; - wl1271_spi_read(wl, FW_STATUS_ADDR, status, - sizeof(*status), false); + wl1271_read(wl, FW_STATUS_ADDR, status, sizeof(*status), false); wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", @@ -435,7 +435,7 @@ static void wl1271_irq_work(struct work_struct *work) if (ret < 0) goto out; - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); wl1271_fw_status(wl, wl->fw_status); intr = le32_to_cpu(wl->fw_status->intr); @@ -447,14 +447,13 @@ static void wl1271_irq_work(struct work_struct *work) intr &= WL1271_INTR_MASK; if (intr & WL1271_ACX_INTR_EVENT_A) { - bool do_ack = (intr & WL1271_ACX_INTR_EVENT_B) ? false : true; wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A"); - wl1271_event_handle(wl, 0, do_ack); + wl1271_event_handle(wl, 0); } if (intr & WL1271_ACX_INTR_EVENT_B) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B"); - wl1271_event_handle(wl, 1, true); + wl1271_event_handle(wl, 1); } if (intr & WL1271_ACX_INTR_INIT_COMPLETE) @@ -478,8 +477,8 @@ static void wl1271_irq_work(struct work_struct *work) } out_sleep: - wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, - WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); + wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, + WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); wl1271_ps_elp_sleep(wl); out: @@ -546,6 +545,40 @@ out: return ret; } +static int wl1271_update_mac_addr(struct wl1271 *wl) +{ + int ret = 0; + u8 *nvs_ptr = (u8 *)wl->nvs->nvs; + + /* get mac address from the NVS */ + wl->mac_addr[0] = nvs_ptr[11]; + wl->mac_addr[1] = nvs_ptr[10]; + wl->mac_addr[2] = nvs_ptr[6]; + wl->mac_addr[3] = nvs_ptr[5]; + wl->mac_addr[4] = nvs_ptr[4]; + wl->mac_addr[5] = nvs_ptr[3]; + + /* FIXME: if it is a zero-address, we should bail out. Now, instead, + we randomize an address */ + if (is_zero_ether_addr(wl->mac_addr)) { + static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; + memcpy(wl->mac_addr, nokia_oui, 3); + get_random_bytes(wl->mac_addr + 3, 3); + + /* update this address to the NVS */ + nvs_ptr[11] = wl->mac_addr[0]; + nvs_ptr[10] = wl->mac_addr[1]; + nvs_ptr[6] = wl->mac_addr[2]; + nvs_ptr[5] = wl->mac_addr[3]; + nvs_ptr[4] = wl->mac_addr[4]; + nvs_ptr[3] = wl->mac_addr[5]; + } + + SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); + + return ret; +} + static int wl1271_fetch_nvs(struct wl1271 *wl) { const struct firmware *fw; @@ -558,15 +591,14 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) return ret; } - if (fw->size % 4) { - wl1271_error("nvs size is not multiple of 32 bits: %zu", - fw->size); + if (fw->size != sizeof(struct wl1271_nvs_file)) { + wl1271_error("nvs size is not as expected: %zu != %zu", + fw->size, sizeof(struct wl1271_nvs_file)); ret = -EILSEQ; goto out; } - wl->nvs_len = fw->size; - wl->nvs = kmalloc(wl->nvs_len, GFP_KERNEL); + wl->nvs = kmalloc(sizeof(struct wl1271_nvs_file), GFP_KERNEL); if (!wl->nvs) { wl1271_error("could not allocate memory for the nvs file"); @@ -574,9 +606,9 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) goto out; } - memcpy(wl->nvs, fw->data, wl->nvs_len); + memcpy(wl->nvs, fw->data, sizeof(struct wl1271_nvs_file)); - ret = 0; + ret = wl1271_update_mac_addr(wl); out: release_firmware(fw); @@ -614,10 +646,11 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) struct wl1271_partition_set partition; int ret = 0; + msleep(WL1271_PRE_POWER_ON_SLEEP); wl1271_power_on(wl); msleep(WL1271_POWER_ON_SLEEP); - wl1271_spi_reset(wl); - wl1271_spi_init(wl); + wl1271_io_reset(wl); + wl1271_io_init(wl); /* We don't need a real memory partition here, because we only want * to use the registers at this point. */ @@ -632,7 +665,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) /* whal_FwCtrl_BootSm() */ /* 0. read chip id from CHIP_ID */ - wl->chip.id = wl1271_spi_read32(wl, CHIP_ID_B); + wl->chip.id = wl1271_read32(wl, CHIP_ID_B); /* 1. check if chip id is valid */ @@ -643,7 +676,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ret = wl1271_setup(wl); if (ret < 0) - goto out_power_off; + goto out; break; case CHIP_ID_1271_PG20: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", @@ -651,38 +684,34 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ret = wl1271_setup(wl); if (ret < 0) - goto out_power_off; + goto out; break; default: - wl1271_error("unsupported chip id: 0x%x", wl->chip.id); + wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); ret = -ENODEV; - goto out_power_off; + goto out; } if (wl->fw == NULL) { ret = wl1271_fetch_firmware(wl); if (ret < 0) - goto out_power_off; + goto out; } /* No NVS from netlink, try to get it from the filesystem */ if (wl->nvs == NULL) { ret = wl1271_fetch_nvs(wl); if (ret < 0) - goto out_power_off; + goto out; } - goto out; - -out_power_off: - wl1271_power_off(wl); - out: return ret; } int wl1271_plt_start(struct wl1271 *wl) { + int retries = WL1271_BOOT_RETRIES; int ret; mutex_lock(&wl->mutex); @@ -696,35 +725,43 @@ int wl1271_plt_start(struct wl1271 *wl) goto out; } - wl->state = WL1271_STATE_PLT; - - ret = wl1271_chip_wakeup(wl); - if (ret < 0) - goto out; - - ret = wl1271_boot(wl); - if (ret < 0) - goto out_power_off; - - wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver); - - ret = wl1271_plt_init(wl); - if (ret < 0) - goto out_irq_disable; + while (retries) { + retries--; + ret = wl1271_chip_wakeup(wl); + if (ret < 0) + goto power_off; - /* Make sure power saving is disabled */ - ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); - if (ret < 0) - goto out_irq_disable; + ret = wl1271_boot(wl); + if (ret < 0) + goto power_off; - goto out; + ret = wl1271_plt_init(wl); + if (ret < 0) + goto irq_disable; -out_irq_disable: - wl1271_disable_interrupts(wl); + wl->state = WL1271_STATE_PLT; + wl1271_notice("firmware booted in PLT mode (%s)", + wl->chip.fw_ver); + goto out; -out_power_off: - wl1271_power_off(wl); +irq_disable: + wl1271_disable_interrupts(wl); + mutex_unlock(&wl->mutex); + /* Unlocking the mutex in the middle of handling is + inherently unsafe. In this case we deem it safe to do, + because we need to let any possibly pending IRQ out of + the system (and while we are WL1271_STATE_OFF the IRQ + work function will not do anything.) Also, any other + possible concurrent operations will fail due to the + current state, hence the wl1271 struct should be safe. */ + cancel_work_sync(&wl->irq_work); + mutex_lock(&wl->mutex); +power_off: + wl1271_power_off(wl); + } + wl1271_error("firmware boot in PLT mode failed despite %d retries", + WL1271_BOOT_RETRIES); out: mutex_unlock(&wl->mutex); @@ -762,7 +799,20 @@ out: static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1271 *wl = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta = txinfo->control.sta; + unsigned long flags; + + /* peek into the rates configured in the STA entry */ + spin_lock_irqsave(&wl->wl_lock, flags); + if (sta && sta->supp_rates[conf->channel->band] != wl->sta_rate_set) { + wl->sta_rate_set = sta->supp_rates[conf->channel->band]; + set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags); + } + spin_unlock_irqrestore(&wl->wl_lock, flags); + /* queue the packet */ skb_queue_tail(&wl->tx_queue, skb); /* @@ -784,7 +834,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * protected. Maybe fix this by removing the stupid * variable altogether and checking the real queue state? */ - wl->tx_queue_stopped = true; + set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); } return NETDEV_TX_OK; @@ -880,6 +930,7 @@ static struct notifier_block wl1271_dev_notifier = { static int wl1271_op_start(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; + int retries = WL1271_BOOT_RETRIES; int ret = 0; wl1271_debug(DEBUG_MAC80211, "mac80211 start"); @@ -893,30 +944,42 @@ static int wl1271_op_start(struct ieee80211_hw *hw) goto out; } - ret = wl1271_chip_wakeup(wl); - if (ret < 0) - goto out; - - ret = wl1271_boot(wl); - if (ret < 0) - goto out_power_off; - - ret = wl1271_hw_init(wl); - if (ret < 0) - goto out_irq_disable; - - wl->state = WL1271_STATE_ON; + while (retries) { + retries--; + ret = wl1271_chip_wakeup(wl); + if (ret < 0) + goto power_off; - wl1271_info("firmware booted (%s)", wl->chip.fw_ver); + ret = wl1271_boot(wl); + if (ret < 0) + goto power_off; - goto out; + ret = wl1271_hw_init(wl); + if (ret < 0) + goto irq_disable; -out_irq_disable: - wl1271_disable_interrupts(wl); + wl->state = WL1271_STATE_ON; + wl1271_info("firmware booted (%s)", wl->chip.fw_ver); + goto out; -out_power_off: - wl1271_power_off(wl); +irq_disable: + wl1271_disable_interrupts(wl); + mutex_unlock(&wl->mutex); + /* Unlocking the mutex in the middle of handling is + inherently unsafe. In this case we deem it safe to do, + because we need to let any possibly pending IRQ out of + the system (and while we are WL1271_STATE_OFF the IRQ + work function will not do anything.) Also, any other + possible concurrent operations will fail due to the + current state, hence the wl1271 struct should be safe. */ + cancel_work_sync(&wl->irq_work); + mutex_lock(&wl->mutex); +power_off: + wl1271_power_off(wl); + } + wl1271_error("firmware boot failed despite %d retries", + WL1271_BOOT_RETRIES); out: mutex_unlock(&wl->mutex); @@ -944,11 +1007,10 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) WARN_ON(wl->state != WL1271_STATE_ON); - if (wl->scanning) { + if (test_and_clear_bit(WL1271_FLAG_SCANNING, &wl->flags)) { mutex_unlock(&wl->mutex); ieee80211_scan_completed(wl->hw, true); mutex_lock(&wl->mutex); - wl->scanning = false; } wl->state = WL1271_STATE_OFF; @@ -973,10 +1035,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl->band = IEEE80211_BAND_2GHZ; wl->rx_counter = 0; - wl->elp = false; - wl->psm = 0; wl->psm_entry_retry = 0; - wl->tx_queue_stopped = false; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->tx_blocks_available = 0; wl->tx_results_count = 0; @@ -986,7 +1045,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl->tx_security_seq_32 = 0; wl->time_offset = 0; wl->session_counter = 0; - wl->joined = false; + wl->rate_set = CONF_TX_RATE_MASK_BASIC; + wl->sta_rate_set = 0; + wl->flags = 0; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -996,13 +1057,13 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) } static int wl1271_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; int ret = 0; wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", - conf->type, conf->mac_addr); + vif->type, vif->addr); mutex_lock(&wl->mutex); if (wl->vif) { @@ -1010,9 +1071,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, goto out; } - wl->vif = conf->vif; + wl->vif = vif; - switch (conf->type) { + switch (vif->type) { case NL80211_IFTYPE_STATION: wl->bss_type = BSS_TYPE_STA_BSS; break; @@ -1032,7 +1093,7 @@ out: } static void wl1271_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; @@ -1109,6 +1170,51 @@ out: } #endif +static int wl1271_join_channel(struct wl1271 *wl, int channel) +{ + int ret = 0; + /* we need to use a dummy BSSID for now */ + static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde, + 0xad, 0xbe, 0xef }; + + /* the dummy join is not required for ad-hoc */ + if (wl->bss_type == BSS_TYPE_IBSS) + goto out; + + /* disable mac filter, so we hear everything */ + wl->rx_config &= ~CFG_BSSID_FILTER_EN; + + wl->channel = channel; + memcpy(wl->bssid, dummy_bssid, ETH_ALEN); + + ret = wl1271_cmd_join(wl); + if (ret < 0) + goto out; + + set_bit(WL1271_FLAG_JOINED, &wl->flags); + +out: + return ret; +} + +static int wl1271_unjoin_channel(struct wl1271 *wl) +{ + int ret; + + /* to stop listening to a channel, we disconnect */ + ret = wl1271_cmd_disconnect(wl); + if (ret < 0) + goto out; + + clear_bit(WL1271_FLAG_JOINED, &wl->flags); + wl->channel = 0; + memset(wl->bssid, 0, ETH_ALEN); + wl->rx_config = WL1271_DEFAULT_RX_CONFIG; + +out: + return ret; +} + static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1271 *wl = hw->priv; @@ -1117,10 +1223,11 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", + wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s", channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", - conf->power_level); + conf->power_level, + conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use"); mutex_lock(&wl->mutex); @@ -1130,35 +1237,55 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) if (ret < 0) goto out; - if (channel != wl->channel) { - /* - * We assume that the stack will configure the right channel - * before associating, so we don't need to send a join - * command here. We will join the right channel when the - * BSSID changes - */ - wl->channel = channel; + if (changed & IEEE80211_CONF_CHANGE_IDLE) { + if (conf->flags & IEEE80211_CONF_IDLE && + test_bit(WL1271_FLAG_JOINED, &wl->flags)) + wl1271_unjoin_channel(wl); + else if (!(conf->flags & IEEE80211_CONF_IDLE)) + wl1271_join_channel(wl, channel); + + if (conf->flags & IEEE80211_CONF_IDLE) { + wl->rate_set = CONF_TX_RATE_MASK_BASIC; + wl->sta_rate_set = 0; + wl1271_acx_rate_policies(wl); + } } - if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { - wl1271_info("psm enabled"); + /* if the channel changes while joined, join again */ + if (channel != wl->channel && + test_bit(WL1271_FLAG_JOINED, &wl->flags)) { + wl->channel = channel; + /* FIXME: maybe use CMD_CHANNEL_SWITCH for this? */ + ret = wl1271_cmd_join(wl); + if (ret < 0) + wl1271_warning("cmd join to update channel failed %d", + ret); + } else + wl->channel = channel; - wl->psm_requested = true; + if (conf->flags & IEEE80211_CONF_PS && + !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) { + set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags); /* * We enter PSM only if we're already associated. * If we're not, we'll enter it when joining an SSID, * through the bss_info_changed() hook. */ - ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) { + wl1271_info("psm enabled"); + ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, + true); + } } else if (!(conf->flags & IEEE80211_CONF_PS) && - wl->psm_requested) { + test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) { wl1271_info("psm disabled"); - wl->psm_requested = false; + clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags); - if (wl->psm) - ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); + if (test_bit(WL1271_FLAG_PSM, &wl->flags)) + ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, + true); } if (conf->power_level != wl->power_level) { @@ -1350,9 +1477,24 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, wl1271_error("Could not add or replace key"); goto out_sleep; } + + /* the default WEP key needs to be configured at least once */ + if (key_type == KEY_WEP) { + ret = wl1271_cmd_set_default_wep_key(wl, + wl->default_key); + if (ret < 0) + goto out_sleep; + } break; case DISABLE_KEY: + /* The wl1271 does not allow to remove unicast keys - they + will be cleared automatically on next CMD_JOIN. Ignore the + request silently, as we dont want the mac80211 to emit + an error message. */ + if (!is_broadcast_ether_addr(addr)) + break; + ret = wl1271_cmd_set_key(wl, KEY_REMOVE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, @@ -1440,20 +1582,21 @@ out: return ret; } -static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set) +static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon) { - struct ieee80211_supported_band *band; - u32 enabled_rates = 0; - int bit; - - band = wl->hw->wiphy->bands[wl->band]; - for (bit = 0; bit < band->n_bitrates; bit++) { - if (basic_rate_set & 0x1) - enabled_rates |= band->bitrates[bit].hw_value; - basic_rate_set >>= 1; + u8 *ptr = beacon->data + + offsetof(struct ieee80211_mgmt, u.beacon.variable); + + /* find the location of the ssid in the beacon */ + while (ptr < beacon->data + beacon->len) { + if (ptr[0] == WLAN_EID_SSID) { + wl->ssid_len = ptr[1]; + memcpy(wl->ssid, ptr+2, wl->ssid_len); + return; + } + ptr += ptr[1]; } - - return enabled_rates; + wl1271_error("ad-hoc beacon template has no SSID!\n"); } static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, @@ -1463,6 +1606,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, { enum wl1271_cmd_ps_mode mode; struct wl1271 *wl = hw->priv; + bool do_join = false; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed"); @@ -1473,9 +1617,67 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out; + if (wl->bss_type == BSS_TYPE_IBSS) { + /* FIXME: This implements rudimentary ad-hoc support - + proper templates are on the wish list and notification + on when they change. This patch will update the templates + on every call to this function. */ + struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); + + if (beacon) { + struct ieee80211_hdr *hdr; + + wl1271_ssid_set(wl, beacon); + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, + beacon->data, + beacon->len); + + if (ret < 0) { + dev_kfree_skb(beacon); + goto out_sleep; + } + + hdr = (struct ieee80211_hdr *) beacon->data; + hdr->frame_control = cpu_to_le16( + IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + + ret = wl1271_cmd_template_set(wl, + CMD_TEMPL_PROBE_RESPONSE, + beacon->data, + beacon->len); + dev_kfree_skb(beacon); + if (ret < 0) + goto out_sleep; + + /* Need to update the SSID (for filtering etc) */ + do_join = true; + } + } + + if ((changed & BSS_CHANGED_BSSID) && + /* + * Now we know the correct bssid, so we send a new join command + * and enable the BSSID filter + */ + memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { + wl->rx_config |= CFG_BSSID_FILTER_EN; + memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); + ret = wl1271_cmd_build_null_data(wl); + if (ret < 0) { + wl1271_warning("cmd buld null data failed %d", + ret); + goto out_sleep; + } + + /* Need to update the BSSID (for filtering etc) */ + do_join = true; + } + if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { wl->aid = bss_conf->aid; + set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags); /* * with wl1271, we don't need to update the @@ -1492,15 +1694,16 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, goto out_sleep; /* If we want to go in PSM but we're not there yet */ - if (wl->psm_requested && !wl->psm) { + if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) && + !test_bit(WL1271_FLAG_PSM, &wl->flags)) { mode = STATION_POWER_SAVE_MODE; - ret = wl1271_ps_set_mode(wl, mode); + ret = wl1271_ps_set_mode(wl, mode, true); if (ret < 0) goto out_sleep; } } else { /* use defaults when not associated */ - wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET; + clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags); wl->aid = 0; } @@ -1535,15 +1738,13 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, } } - if (changed & BSS_CHANGED_BASIC_RATES) { - wl->basic_rate_set = wl1271_enabled_rates_get( - wl, bss_conf->basic_rates); - - ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set); + if (do_join) { + ret = wl1271_cmd_join(wl); if (ret < 0) { - wl1271_warning("Set rate policies failed %d", ret); + wl1271_warning("cmd join failed %d", ret); goto out_sleep; } + set_bit(WL1271_FLAG_JOINED, &wl->flags); } out_sleep: @@ -1553,6 +1754,43 @@ out: mutex_unlock(&wl->mutex); } +static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct wl1271 *wl = hw->priv; + int ret; + + mutex_lock(&wl->mutex); + + wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue); + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue), + params->cw_min, params->cw_max, + params->aifs, params->txop); + if (ret < 0) + goto out_sleep; + + ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue), + CONF_CHANNEL_TYPE_EDCF, + wl1271_tx_get_queue(queue), + CONF_PS_SCHEME_LEGACY_PSPOLL, + CONF_ACK_POLICY_LEGACY, 0, 0); + if (ret < 0) + goto out_sleep; + +out_sleep: + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + /* can't be const, mac80211 writes to this */ static struct ieee80211_rate wl1271_rates[] = { @@ -1599,19 +1837,19 @@ static struct ieee80211_rate wl1271_rates[] = { /* can't be const, mac80211 writes to this */ static struct ieee80211_channel wl1271_channels[] = { - { .hw_value = 1, .center_freq = 2412}, - { .hw_value = 2, .center_freq = 2417}, - { .hw_value = 3, .center_freq = 2422}, - { .hw_value = 4, .center_freq = 2427}, - { .hw_value = 5, .center_freq = 2432}, - { .hw_value = 6, .center_freq = 2437}, - { .hw_value = 7, .center_freq = 2442}, - { .hw_value = 8, .center_freq = 2447}, - { .hw_value = 9, .center_freq = 2452}, - { .hw_value = 10, .center_freq = 2457}, - { .hw_value = 11, .center_freq = 2462}, - { .hw_value = 12, .center_freq = 2467}, - { .hw_value = 13, .center_freq = 2472}, + { .hw_value = 1, .center_freq = 2412, .max_power = 25 }, + { .hw_value = 2, .center_freq = 2417, .max_power = 25 }, + { .hw_value = 3, .center_freq = 2422, .max_power = 25 }, + { .hw_value = 4, .center_freq = 2427, .max_power = 25 }, + { .hw_value = 5, .center_freq = 2432, .max_power = 25 }, + { .hw_value = 6, .center_freq = 2437, .max_power = 25 }, + { .hw_value = 7, .center_freq = 2442, .max_power = 25 }, + { .hw_value = 8, .center_freq = 2447, .max_power = 25 }, + { .hw_value = 9, .center_freq = 2452, .max_power = 25 }, + { .hw_value = 10, .center_freq = 2457, .max_power = 25 }, + { .hw_value = 11, .center_freq = 2462, .max_power = 25 }, + { .hw_value = 12, .center_freq = 2467, .max_power = 25 }, + { .hw_value = 13, .center_freq = 2472, .max_power = 25 }, }; /* can't be const, mac80211 writes to this */ @@ -1718,6 +1956,8 @@ static const struct ieee80211_ops wl1271_ops = { .hw_scan = wl1271_op_hw_scan, .bss_info_changed = wl1271_op_bss_info_changed, .set_rts_threshold = wl1271_op_set_rts_threshold, + .conf_tx = wl1271_op_conf_tx, + CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; static int wl1271_register_hw(struct wl1271 *wl) @@ -1757,7 +1997,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_SUPPORTS_PS; - wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz; @@ -1785,24 +2026,17 @@ static struct platform_device wl1271_device = { }; #define WL1271_DEFAULT_CHANNEL 0 -static int __devinit wl1271_probe(struct spi_device *spi) + +static struct ieee80211_hw *wl1271_alloc_hw(void) { - struct wl12xx_platform_data *pdata; struct ieee80211_hw *hw; struct wl1271 *wl; - int ret, i; - static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; - - pdata = spi->dev.platform_data; - if (!pdata) { - wl1271_error("no platform data"); - return -ENODEV; - } + int i; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); if (!hw) { wl1271_error("could not alloc ieee80211_hw"); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } wl = hw->priv; @@ -1811,44 +2045,80 @@ static int __devinit wl1271_probe(struct spi_device *spi) INIT_LIST_HEAD(&wl->list); wl->hw = hw; - dev_set_drvdata(&spi->dev, wl); - wl->spi = spi; skb_queue_head_init(&wl->tx_queue); INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); wl->channel = WL1271_DEFAULT_CHANNEL; - wl->scanning = false; wl->default_key = 0; wl->rx_counter = 0; wl->rx_config = WL1271_DEFAULT_RX_CONFIG; wl->rx_filter = WL1271_DEFAULT_RX_FILTER; - wl->elp = false; - wl->psm = 0; - wl->psm_requested = false; wl->psm_entry_retry = 0; - wl->tx_queue_stopped = false; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; - wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET; + wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC; + wl->rate_set = CONF_TX_RATE_MASK_BASIC; + wl->sta_rate_set = 0; wl->band = IEEE80211_BAND_2GHZ; wl->vif = NULL; - wl->joined = false; + wl->flags = 0; for (i = 0; i < ACX_TX_DESCRIPTORS; i++) wl->tx_frames[i] = NULL; spin_lock_init(&wl->wl_lock); - /* - * In case our MAC address is not correctly set, - * we use a random but Nokia MAC. - */ - memcpy(wl->mac_addr, nokia_oui, 3); - get_random_bytes(wl->mac_addr + 3, 3); - wl->state = WL1271_STATE_OFF; mutex_init(&wl->mutex); + /* Apply default driver configuration. */ + wl1271_conf_init(wl); + + return hw; +} + +int wl1271_free_hw(struct wl1271 *wl) +{ + ieee80211_unregister_hw(wl->hw); + + wl1271_debugfs_exit(wl); + + kfree(wl->target_mem_map); + vfree(wl->fw); + wl->fw = NULL; + kfree(wl->nvs); + wl->nvs = NULL; + + kfree(wl->fw_status); + kfree(wl->tx_res_if); + + ieee80211_free_hw(wl->hw); + + return 0; +} + +static int __devinit wl1271_probe(struct spi_device *spi) +{ + struct wl12xx_platform_data *pdata; + struct ieee80211_hw *hw; + struct wl1271 *wl; + int ret; + + pdata = spi->dev.platform_data; + if (!pdata) { + wl1271_error("no platform data"); + return -ENODEV; + } + + hw = wl1271_alloc_hw(); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + wl = hw->priv; + + dev_set_drvdata(&spi->dev, wl); + wl->spi = spi; + /* This is the only SPI value that we need to set here, the rest * comes from the board-peripherals file */ spi->bits_per_word = 32; @@ -1890,9 +2160,6 @@ static int __devinit wl1271_probe(struct spi_device *spi) } dev_set_drvdata(&wl1271_device.dev, wl); - /* Apply default driver configuration. */ - wl1271_conf_init(wl); - ret = wl1271_init_ieee80211(wl); if (ret) goto out_platform; @@ -1923,21 +2190,10 @@ static int __devexit wl1271_remove(struct spi_device *spi) { struct wl1271 *wl = dev_get_drvdata(&spi->dev); - ieee80211_unregister_hw(wl->hw); - - wl1271_debugfs_exit(wl); platform_device_unregister(&wl1271_device); free_irq(wl->irq, wl); - kfree(wl->target_mem_map); - vfree(wl->fw); - wl->fw = NULL; - kfree(wl->nvs); - wl->nvs = NULL; - kfree(wl->fw_status); - kfree(wl->tx_res_if); - - ieee80211_free_hw(wl->hw); + wl1271_free_hw(wl); return 0; } diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c index 507cd91d7eed..e2b1ebf096e8 100644 --- a/drivers/net/wireless/wl12xx/wl1271_ps.c +++ b/drivers/net/wireless/wl12xx/wl1271_ps.c @@ -24,6 +24,7 @@ #include "wl1271_reg.h" #include "wl1271_ps.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #define WL1271_WAKEUP_TIMEOUT 500 @@ -39,12 +40,13 @@ void wl1271_elp_work(struct work_struct *work) mutex_lock(&wl->mutex); - if (wl->elp || !wl->psm) + if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags) || + !test_bit(WL1271_FLAG_PSM, &wl->flags)) goto out; wl1271_debug(DEBUG_PSM, "chip to elp"); wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); - wl->elp = true; + set_bit(WL1271_FLAG_IN_ELP, &wl->flags); out: mutex_unlock(&wl->mutex); @@ -55,7 +57,7 @@ out: /* Routines to toggle sleep mode while in ELP */ void wl1271_ps_elp_sleep(struct wl1271 *wl) { - if (wl->psm) { + if (test_bit(WL1271_FLAG_PSM, &wl->flags)) { cancel_delayed_work(&wl->elp_work); ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, msecs_to_jiffies(ELP_ENTRY_DELAY)); @@ -70,7 +72,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) u32 start_time = jiffies; bool pending = false; - if (!wl->elp) + if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags)) return 0; wl1271_debug(DEBUG_PSM, "waking up chip from elp"); @@ -101,7 +103,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) } } - wl->elp = false; + clear_bit(WL1271_FLAG_IN_ELP, &wl->flags); wl1271_debug(DEBUG_PSM, "wakeup time: %u ms", jiffies_to_msecs(jiffies - start_time)); @@ -117,7 +119,8 @@ out: return 0; } -int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) +int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, + bool send) { int ret; @@ -125,25 +128,11 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) case STATION_POWER_SAVE_MODE: wl1271_debug(DEBUG_PSM, "entering psm"); - /* enable beacon filtering */ - ret = wl1271_acx_beacon_filter_opt(wl, true); + ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE, send); if (ret < 0) return ret; - /* enable beacon early termination */ - ret = wl1271_acx_bet_enable(wl, true); - if (ret < 0) - return ret; - - ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); - if (ret < 0) - return ret; - - wl1271_ps_elp_sleep(wl); - if (ret < 0) - return ret; - - wl->psm = 1; + set_bit(WL1271_FLAG_PSM, &wl->flags); break; case STATION_ACTIVE_MODE: default: @@ -162,11 +151,11 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) if (ret < 0) return ret; - ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE); + ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE, send); if (ret < 0) return ret; - wl->psm = 0; + clear_bit(WL1271_FLAG_PSM, &wl->flags); break; } diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.h b/drivers/net/wireless/wl12xx/wl1271_ps.h index 779653d0ae85..940276f517a4 100644 --- a/drivers/net/wireless/wl12xx/wl1271_ps.h +++ b/drivers/net/wireless/wl12xx/wl1271_ps.h @@ -27,7 +27,8 @@ #include "wl1271.h" #include "wl1271_acx.h" -int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode); +int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, + bool send); void wl1271_ps_elp_sleep(struct wl1271 *wl); int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake); void wl1271_elp_work(struct work_struct *work); diff --git a/drivers/net/wireless/wl12xx/wl1271_reg.h b/drivers/net/wireless/wl12xx/wl1271_reg.h index 1f237389d1c7..990960771528 100644 --- a/drivers/net/wireless/wl12xx/wl1271_reg.h +++ b/drivers/net/wireless/wl12xx/wl1271_reg.h @@ -62,73 +62,10 @@ #define WL1271_SLV_REG_DATA (REGISTERS_BASE + 0x0008) #define WL1271_SLV_REG_ADATA (REGISTERS_BASE + 0x000c) #define WL1271_SLV_MEM_DATA (REGISTERS_BASE + 0x0018) -/* - * Interrupt registers. - * 64 bit interrupt sources registers ws ced. - * sme interupts were removed and new ones were added. - * Order was changed. - */ -#define FIQ_MASK (REGISTERS_BASE + 0x0400) -#define FIQ_MASK_L (REGISTERS_BASE + 0x0400) -#define FIQ_MASK_H (REGISTERS_BASE + 0x0404) -#define FIQ_MASK_SET (REGISTERS_BASE + 0x0408) -#define FIQ_MASK_SET_L (REGISTERS_BASE + 0x0408) -#define FIQ_MASK_SET_H (REGISTERS_BASE + 0x040C) -#define FIQ_MASK_CLR (REGISTERS_BASE + 0x0410) -#define FIQ_MASK_CLR_L (REGISTERS_BASE + 0x0410) -#define FIQ_MASK_CLR_H (REGISTERS_BASE + 0x0414) -#define IRQ_MASK (REGISTERS_BASE + 0x0418) -#define IRQ_MASK_L (REGISTERS_BASE + 0x0418) -#define IRQ_MASK_H (REGISTERS_BASE + 0x041C) -#define IRQ_MASK_SET (REGISTERS_BASE + 0x0420) -#define IRQ_MASK_SET_L (REGISTERS_BASE + 0x0420) -#define IRQ_MASK_SET_H (REGISTERS_BASE + 0x0424) -#define IRQ_MASK_CLR (REGISTERS_BASE + 0x0428) -#define IRQ_MASK_CLR_L (REGISTERS_BASE + 0x0428) -#define IRQ_MASK_CLR_H (REGISTERS_BASE + 0x042C) -#define ECPU_MASK (REGISTERS_BASE + 0x0448) -#define FIQ_STS_L (REGISTERS_BASE + 0x044C) -#define FIQ_STS_H (REGISTERS_BASE + 0x0450) -#define IRQ_STS_L (REGISTERS_BASE + 0x0454) -#define IRQ_STS_H (REGISTERS_BASE + 0x0458) -#define INT_STS_ND (REGISTERS_BASE + 0x0464) -#define INT_STS_RAW_L (REGISTERS_BASE + 0x0464) -#define INT_STS_RAW_H (REGISTERS_BASE + 0x0468) -#define INT_STS_CLR (REGISTERS_BASE + 0x04B4) -#define INT_STS_CLR_L (REGISTERS_BASE + 0x04B4) -#define INT_STS_CLR_H (REGISTERS_BASE + 0x04B8) -#define INT_ACK (REGISTERS_BASE + 0x046C) -#define INT_ACK_L (REGISTERS_BASE + 0x046C) -#define INT_ACK_H (REGISTERS_BASE + 0x0470) -#define INT_TRIG (REGISTERS_BASE + 0x0474) -#define INT_TRIG_L (REGISTERS_BASE + 0x0474) -#define INT_TRIG_H (REGISTERS_BASE + 0x0478) -#define HOST_STS_L (REGISTERS_BASE + 0x045C) -#define HOST_STS_H (REGISTERS_BASE + 0x0460) -#define HOST_MASK (REGISTERS_BASE + 0x0430) -#define HOST_MASK_L (REGISTERS_BASE + 0x0430) -#define HOST_MASK_H (REGISTERS_BASE + 0x0434) -#define HOST_MASK_SET (REGISTERS_BASE + 0x0438) -#define HOST_MASK_SET_L (REGISTERS_BASE + 0x0438) -#define HOST_MASK_SET_H (REGISTERS_BASE + 0x043C) -#define HOST_MASK_CLR (REGISTERS_BASE + 0x0440) -#define HOST_MASK_CLR_L (REGISTERS_BASE + 0x0440) -#define HOST_MASK_CLR_H (REGISTERS_BASE + 0x0444) #define ACX_REG_INTERRUPT_TRIG (REGISTERS_BASE + 0x0474) #define ACX_REG_INTERRUPT_TRIG_H (REGISTERS_BASE + 0x0478) -/* Host Interrupts*/ -#define HINT_MASK (REGISTERS_BASE + 0x0494) -#define HINT_MASK_SET (REGISTERS_BASE + 0x0498) -#define HINT_MASK_CLR (REGISTERS_BASE + 0x049C) -#define HINT_STS_ND_MASKED (REGISTERS_BASE + 0x04A0) -/*1150 spec calls this HINT_STS_RAW*/ -#define HINT_STS_ND (REGISTERS_BASE + 0x04B0) -#define HINT_STS_CLR (REGISTERS_BASE + 0x04A4) -#define HINT_ACK (REGISTERS_BASE + 0x04A8) -#define HINT_TRIG (REGISTERS_BASE + 0x04AC) - /*============================================= Host Interrupt Mask Register - 32bit (RW) ------------------------------------------ @@ -433,16 +370,6 @@ /*=============================================== - Phy regs - ===============================================*/ -#define ACX_PHY_ADDR_REG SBB_ADDR -#define ACX_PHY_DATA_REG SBB_DATA -#define ACX_PHY_CTRL_REG SBB_CTL -#define ACX_PHY_REG_WR_MASK 0x00000001ul -#define ACX_PHY_REG_RD_MASK 0x00000002ul - - -/*=============================================== EEPROM Read/Write Request 32bit RW ------------------------------------------ 1 EE_READ - EEPROM Read Request 1 - Setting this bit @@ -511,28 +438,6 @@ #define ACX_CONT_WIND_MIN_MASK 0x0000007f #define ACX_CONT_WIND_MAX 0x03ff0000 -/* - * Indirect slave register/memory registers - * ---------------------------------------- - */ -#define HW_SLAVE_REG_ADDR_REG 0x00000004 -#define HW_SLAVE_REG_DATA_REG 0x00000008 -#define HW_SLAVE_REG_CTRL_REG 0x0000000c - -#define SLAVE_AUTO_INC 0x00010000 -#define SLAVE_NO_AUTO_INC 0x00000000 -#define SLAVE_HOST_LITTLE_ENDIAN 0x00000000 - -#define HW_SLAVE_MEM_ADDR_REG SLV_MEM_ADDR -#define HW_SLAVE_MEM_DATA_REG SLV_MEM_DATA -#define HW_SLAVE_MEM_CTRL_REG SLV_MEM_CTL -#define HW_SLAVE_MEM_ENDIAN_REG SLV_END_CTL - -#define HW_FUNC_EVENT_INT_EN 0x8000 -#define HW_FUNC_EVENT_MASK_REG 0x00000034 - -#define ACX_MAC_TIMESTAMP_REG (MAC_TIMESTAMP) - /*=============================================== HI_CFG Interface Configuration Register Values ------------------------------------------ @@ -647,10 +552,6 @@ b12-b0 - Supported Rate indicator bits as defined below. ******************************************************************************/ -#define TNETW1251_CHIP_ID_PG1_0 0x07010101 -#define TNETW1251_CHIP_ID_PG1_1 0x07020101 -#define TNETW1251_CHIP_ID_PG1_2 0x07030101 - /************************************************************************* Interrupt Trigger Register (Host -> WiLink) diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c index ca645f38109b..c723d9c7e131 100644 --- a/drivers/net/wireless/wl12xx/wl1271_rx.c +++ b/drivers/net/wireless/wl12xx/wl1271_rx.c @@ -21,11 +21,14 @@ * */ +#include <linux/gfp.h> + #include "wl1271.h" #include "wl1271_acx.h" #include "wl1271_reg.h" #include "wl1271_rx.h" #include "wl1271_spi.h" +#include "wl1271_io.h" static u8 wl1271_rx_get_mem_block(struct wl1271_fw_status *status, u32 drv_rx_counter) @@ -166,7 +169,7 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) } buf = skb_put(skb, length); - wl1271_spi_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); + wl1271_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); /* the data read starts with the descriptor */ desc = (struct wl1271_rx_descriptor *) buf; @@ -210,15 +213,13 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) wl->rx_mem_pool_addr.addr + 4; /* Choose the block we want to read */ - wl1271_spi_write(wl, WL1271_SLV_REG_DATA, - &wl->rx_mem_pool_addr, - sizeof(wl->rx_mem_pool_addr), false); + wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr, + sizeof(wl->rx_mem_pool_addr), false); wl1271_rx_handle_data(wl, buf_size); wl->rx_counter++; drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; + wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); } - - wl1271_spi_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); } diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 02978a16e732..053c84aceb49 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -25,33 +25,12 @@ #include <linux/platform_device.h> #include <linux/crc7.h> #include <linux/spi/spi.h> +#include <linux/slab.h> #include "wl1271.h" #include "wl12xx_80211.h" #include "wl1271_spi.h" -static int wl1271_translate_addr(struct wl1271 *wl, int addr) -{ - /* - * To translate, first check to which window of addresses the - * particular address belongs. Then subtract the starting address - * of that window from the address. Then, add offset of the - * translated region. - * - * The translated regions occur next to each other in physical device - * memory, so just add the sizes of the preceeding address regions to - * get the offset to the new region. - * - * Currently, only the two first regions are addressed, and the - * assumption is that all addresses will fall into either of those - * two. - */ - if ((addr >= wl->part.reg.start) && - (addr < wl->part.reg.start + wl->part.reg.size)) - return addr - wl->part.reg.start + wl->part.mem.size; - else - return addr - wl->part.mem.start; -} void wl1271_spi_reset(struct wl1271 *wl) { @@ -133,67 +112,6 @@ void wl1271_spi_init(struct wl1271 *wl) wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); } -/* Set the SPI partitions to access the chip addresses - * - * To simplify driver code, a fixed (virtual) memory map is defined for - * register and memory addresses. Because in the chipset, in different stages - * of operation, those addresses will move around, an address translation - * mechanism is required. - * - * There are four partitions (three memory and one register partition), - * which are mapped to two different areas of the hardware memory. - * - * Virtual address - * space - * - * | | - * ...+----+--> mem.start - * Physical address ... | | - * space ... | | [PART_0] - * ... | | - * 00000000 <--+----+... ...+----+--> mem.start + mem.size - * | | ... | | - * |MEM | ... | | - * | | ... | | - * mem.size <--+----+... | | {unused area) - * | | ... | | - * |REG | ... | | - * mem.size | | ... | | - * + <--+----+... ...+----+--> reg.start - * reg.size | | ... | | - * |MEM2| ... | | [PART_1] - * | | ... | | - * ...+----+--> reg.start + reg.size - * | | - * - */ -int wl1271_set_partition(struct wl1271 *wl, - struct wl1271_partition_set *p) -{ - /* copy partition info */ - memcpy(&wl->part, p, sizeof(*p)); - - wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", - p->mem.start, p->mem.size); - wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", - p->reg.start, p->reg.size); - wl1271_debug(DEBUG_SPI, "mem2_start %08X mem2_size %08X", - p->mem2.start, p->mem2.size); - wl1271_debug(DEBUG_SPI, "mem3_start %08X mem3_size %08X", - p->mem3.start, p->mem3.size); - - /* write partition info to the chipset */ - wl1271_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); - wl1271_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); - wl1271_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); - wl1271_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); - wl1271_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); - wl1271_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); - wl1271_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); - - return 0; -} - #define WL1271_BUSY_WORD_TIMEOUT 1000 /* FIXME: Check busy words, removed due to SPI bug */ @@ -338,78 +256,3 @@ void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, wl1271_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); } - -void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed) -{ - int physical; - - physical = wl1271_translate_addr(wl, addr); - - wl1271_spi_raw_read(wl, physical, buf, len, fixed); -} - -void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed) -{ - int physical; - - physical = wl1271_translate_addr(wl, addr); - - wl1271_spi_raw_write(wl, physical, buf, len, fixed); -} - -u32 wl1271_spi_read32(struct wl1271 *wl, int addr) -{ - return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr)); -} - -void wl1271_spi_write32(struct wl1271 *wl, int addr, u32 val) -{ - wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val); -} - -void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) -{ - /* write address >> 1 + 0x30000 to OCP_POR_CTR */ - addr = (addr >> 1) + 0x30000; - wl1271_spi_write32(wl, OCP_POR_CTR, addr); - - /* write value to OCP_POR_WDATA */ - wl1271_spi_write32(wl, OCP_DATA_WRITE, val); - - /* write 1 to OCP_CMD */ - wl1271_spi_write32(wl, OCP_CMD, OCP_CMD_WRITE); -} - -u16 wl1271_top_reg_read(struct wl1271 *wl, int addr) -{ - u32 val; - int timeout = OCP_CMD_LOOP; - - /* write address >> 1 + 0x30000 to OCP_POR_CTR */ - addr = (addr >> 1) + 0x30000; - wl1271_spi_write32(wl, OCP_POR_CTR, addr); - - /* write 2 to OCP_CMD */ - wl1271_spi_write32(wl, OCP_CMD, OCP_CMD_READ); - - /* poll for data ready */ - do { - val = wl1271_spi_read32(wl, OCP_DATA_READ); - timeout--; - } while (!(val & OCP_READY_MASK) && timeout); - - if (!timeout) { - wl1271_warning("Top register access timed out."); - return 0xffff; - } - - /* check data status and return if OK */ - if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK) - return val & 0xffff; - else { - wl1271_warning("Top register access returned error."); - return 0xffff; - } -} diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.h b/drivers/net/wireless/wl12xx/wl1271_spi.h index cb7df1c56314..a803596dad4a 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.h +++ b/drivers/net/wireless/wl12xx/wl1271_spi.h @@ -90,37 +90,7 @@ void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf, void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed); -/* Translated target IO */ -void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed); -void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, size_t len, - bool fixed); -u32 wl1271_spi_read32(struct wl1271 *wl, int addr); -void wl1271_spi_write32(struct wl1271 *wl, int addr, u32 val); - -/* Top Register IO */ -void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val); -u16 wl1271_top_reg_read(struct wl1271 *wl, int addr); - /* INIT and RESET words */ void wl1271_spi_reset(struct wl1271 *wl); void wl1271_spi_init(struct wl1271 *wl); -int wl1271_set_partition(struct wl1271 *wl, - struct wl1271_partition_set *p); - -static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr) -{ - wl1271_spi_raw_read(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); - - return wl->buffer_32; -} - -static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val) -{ - wl->buffer_32 = val; - wl1271_spi_raw_write(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); -} - #endif /* __WL1271_SPI_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_testmode.c b/drivers/net/wireless/wl12xx/wl1271_testmode.c new file mode 100644 index 000000000000..5c1c4f565fd8 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_testmode.c @@ -0,0 +1,284 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#include "wl1271_testmode.h" + +#include <linux/slab.h> +#include <net/genetlink.h> + +#include "wl1271.h" +#include "wl1271_spi.h" +#include "wl1271_acx.h" + +#define WL1271_TM_MAX_DATA_LENGTH 1024 + +enum wl1271_tm_commands { + WL1271_TM_CMD_UNSPEC, + WL1271_TM_CMD_TEST, + WL1271_TM_CMD_INTERROGATE, + WL1271_TM_CMD_CONFIGURE, + WL1271_TM_CMD_NVS_PUSH, + WL1271_TM_CMD_SET_PLT_MODE, + + __WL1271_TM_CMD_AFTER_LAST +}; +#define WL1271_TM_CMD_MAX (__WL1271_TM_CMD_AFTER_LAST - 1) + +enum wl1271_tm_attrs { + WL1271_TM_ATTR_UNSPEC, + WL1271_TM_ATTR_CMD_ID, + WL1271_TM_ATTR_ANSWER, + WL1271_TM_ATTR_DATA, + WL1271_TM_ATTR_IE_ID, + WL1271_TM_ATTR_PLT_MODE, + + __WL1271_TM_ATTR_AFTER_LAST +}; +#define WL1271_TM_ATTR_MAX (__WL1271_TM_ATTR_AFTER_LAST - 1) + +static struct nla_policy wl1271_tm_policy[WL1271_TM_ATTR_MAX + 1] = { + [WL1271_TM_ATTR_CMD_ID] = { .type = NLA_U32 }, + [WL1271_TM_ATTR_ANSWER] = { .type = NLA_U8 }, + [WL1271_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = WL1271_TM_MAX_DATA_LENGTH }, + [WL1271_TM_ATTR_IE_ID] = { .type = NLA_U32 }, + [WL1271_TM_ATTR_PLT_MODE] = { .type = NLA_U32 }, +}; + + +static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[]) +{ + int buf_len, ret, len; + struct sk_buff *skb; + void *buf; + u8 answer = 0; + + wl1271_debug(DEBUG_TESTMODE, "testmode cmd test"); + + if (!tb[WL1271_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[WL1271_TM_ATTR_DATA]); + buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]); + + if (tb[WL1271_TM_ATTR_ANSWER]) + answer = nla_get_u8(tb[WL1271_TM_ATTR_ANSWER]); + + if (buf_len > sizeof(struct wl1271_command)) + return -EMSGSIZE; + + mutex_lock(&wl->mutex); + ret = wl1271_cmd_test(wl, buf, buf_len, answer); + mutex_unlock(&wl->mutex); + + if (ret < 0) { + wl1271_warning("testmode cmd test failed: %d", ret); + return ret; + } + + if (answer) { + len = nla_total_size(buf_len); + skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len); + if (!skb) + return -ENOMEM; + + NLA_PUT(skb, WL1271_TM_ATTR_DATA, buf_len, buf); + ret = cfg80211_testmode_reply(skb); + if (ret < 0) + return ret; + } + + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EMSGSIZE; +} + +static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[]) +{ + int ret; + struct wl1271_command *cmd; + struct sk_buff *skb; + u8 ie_id; + + wl1271_debug(DEBUG_TESTMODE, "testmode cmd interrogate"); + + if (!tb[WL1271_TM_ATTR_IE_ID]) + return -EINVAL; + + ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + mutex_lock(&wl->mutex); + ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd)); + mutex_unlock(&wl->mutex); + + if (ret < 0) { + wl1271_warning("testmode cmd interrogate failed: %d", ret); + return ret; + } + + skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + NLA_PUT(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd); + + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EMSGSIZE; +} + +static int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[]) +{ + int buf_len, ret; + void *buf; + u8 ie_id; + + wl1271_debug(DEBUG_TESTMODE, "testmode cmd configure"); + + if (!tb[WL1271_TM_ATTR_DATA]) + return -EINVAL; + if (!tb[WL1271_TM_ATTR_IE_ID]) + return -EINVAL; + + ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); + buf = nla_data(tb[WL1271_TM_ATTR_DATA]); + buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]); + + if (buf_len > sizeof(struct wl1271_command)) + return -EMSGSIZE; + + mutex_lock(&wl->mutex); + ret = wl1271_cmd_configure(wl, ie_id, buf, buf_len); + mutex_unlock(&wl->mutex); + + if (ret < 0) { + wl1271_warning("testmode cmd configure failed: %d", ret); + return ret; + } + + return 0; +} + +static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) +{ + int ret = 0; + size_t len; + void *buf; + + wl1271_debug(DEBUG_TESTMODE, "testmode cmd nvs push"); + + if (!tb[WL1271_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[WL1271_TM_ATTR_DATA]); + len = nla_len(tb[WL1271_TM_ATTR_DATA]); + + if (len != sizeof(struct wl1271_nvs_file)) { + wl1271_error("nvs size is not as expected: %zu != %zu", + len, sizeof(struct wl1271_nvs_file)); + return -EMSGSIZE; + } + + mutex_lock(&wl->mutex); + + kfree(wl->nvs); + + wl->nvs = kmalloc(sizeof(struct wl1271_nvs_file), GFP_KERNEL); + if (!wl->nvs) { + wl1271_error("could not allocate memory for the nvs file"); + ret = -ENOMEM; + goto out; + } + + memcpy(wl->nvs, buf, len); + + wl1271_debug(DEBUG_TESTMODE, "testmode pushed nvs"); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + +static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[]) +{ + u32 val; + int ret; + + wl1271_debug(DEBUG_TESTMODE, "testmode cmd set plt mode"); + + if (!tb[WL1271_TM_ATTR_PLT_MODE]) + return -EINVAL; + + val = nla_get_u32(tb[WL1271_TM_ATTR_PLT_MODE]); + + switch (val) { + case 0: + ret = wl1271_plt_stop(wl); + break; + case 1: + ret = wl1271_plt_start(wl); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len) +{ + struct wl1271 *wl = hw->priv; + struct nlattr *tb[WL1271_TM_ATTR_MAX + 1]; + int err; + + err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy); + if (err) + return err; + + if (!tb[WL1271_TM_ATTR_CMD_ID]) + return -EINVAL; + + switch (nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID])) { + case WL1271_TM_CMD_TEST: + return wl1271_tm_cmd_test(wl, tb); + case WL1271_TM_CMD_INTERROGATE: + return wl1271_tm_cmd_interrogate(wl, tb); + case WL1271_TM_CMD_CONFIGURE: + return wl1271_tm_cmd_configure(wl, tb); + case WL1271_TM_CMD_NVS_PUSH: + return wl1271_tm_cmd_nvs_push(wl, tb); + case WL1271_TM_CMD_SET_PLT_MODE: + return wl1271_tm_cmd_set_plt_mode(wl, tb); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/wireless/wl12xx/wl1271_testmode.h b/drivers/net/wireless/wl12xx/wl1271_testmode.h new file mode 100644 index 000000000000..c196d28f9d9d --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_testmode.h @@ -0,0 +1,31 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_TESTMODE_H__ +#define __WL1271_TESTMODE_H__ + +#include <net/mac80211.h> + +int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len); + +#endif /* __WL1271_TESTMODE_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c index 00af065c77c2..811e739d05bf 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.c +++ b/drivers/net/wireless/wl12xx/wl1271_tx.c @@ -26,6 +26,7 @@ #include "wl1271.h" #include "wl1271_spi.h" +#include "wl1271_io.h" #include "wl1271_reg.h" #include "wl1271_ps.h" #include "wl1271_tx.h" @@ -87,7 +88,7 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, u32 extra, struct ieee80211_tx_info *control) { struct wl1271_tx_hw_descr *desc; - int pad; + int pad, ac; u16 tx_attr; desc = (struct wl1271_tx_hw_descr *) skb->data; @@ -107,9 +108,11 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, /* configure the tx attributes */ tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; - /* FIXME: do we know the packet priority? can we identify mgmt - packets, and use max prio for them at least? */ - desc->tid = 0; + + /* queue */ + ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); + desc->tid = wl1271_tx_ac_to_tid(ac); + desc->aid = TX_HW_DEFAULT_AID; desc->reserved = 0; @@ -121,6 +124,11 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, pad = pad - skb->len; tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; + /* if the packets are destined for AP (have a STA entry) send them + with AP rate policies, otherwise use default basic rates */ + if (control->control.sta) + tx_attr |= ACX_TX_AP_FULL_RATE << TX_HW_ATTR_OFST_RATE_POLICY; + desc->tx_attr = cpu_to_le16(tx_attr); wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad); @@ -158,11 +166,11 @@ static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb, len = WL1271_TX_ALIGN(skb->len); /* perform a fixed address block write with the packet */ - wl1271_spi_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); + wl1271_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); /* write packet new counter into the write access register */ wl->tx_packets_count++; - wl1271_spi_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); + wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); desc = (struct wl1271_tx_hw_descr *) skb->data; wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)", @@ -196,6 +204,7 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb) ret = wl1271_cmd_set_default_wep_key(wl, idx); if (ret < 0) return ret; + wl->default_key = idx; } } @@ -214,18 +223,50 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb) return ret; } +static u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set) +{ + struct ieee80211_supported_band *band; + u32 enabled_rates = 0; + int bit; + + band = wl->hw->wiphy->bands[wl->band]; + for (bit = 0; bit < band->n_bitrates; bit++) { + if (rate_set & 0x1) + enabled_rates |= band->bitrates[bit].hw_value; + rate_set >>= 1; + } + + return enabled_rates; +} + void wl1271_tx_work(struct work_struct *work) { struct wl1271 *wl = container_of(work, struct wl1271, tx_work); struct sk_buff *skb; bool woken_up = false; + u32 sta_rates = 0; int ret; + /* check if the rates supported by the AP have changed */ + if (unlikely(test_and_clear_bit(WL1271_FLAG_STA_RATES_CHANGED, + &wl->flags))) { + unsigned long flags; + spin_lock_irqsave(&wl->wl_lock, flags); + sta_rates = wl->sta_rate_set; + spin_unlock_irqrestore(&wl->wl_lock, flags); + } + mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; + /* if rates have changed, re-configure the rate policy */ + if (unlikely(sta_rates)) { + wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates); + wl1271_acx_rate_policies(wl); + } + while ((skb = skb_dequeue(&wl->tx_queue))) { if (!woken_up) { ret = wl1271_ps_elp_wakeup(wl, false); @@ -240,18 +281,18 @@ void wl1271_tx_work(struct work_struct *work) wl1271_debug(DEBUG_TX, "tx_work: fw buffer full, " "stop queues"); ieee80211_stop_queues(wl->hw); - wl->tx_queue_stopped = true; + set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); skb_queue_head(&wl->tx_queue, skb); goto out; } else if (ret < 0) { dev_kfree_skb(skb); goto out; - } else if (wl->tx_queue_stopped) { + } else if (test_and_clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED, + &wl->flags)) { /* firmware buffer has space, restart queues */ wl1271_debug(DEBUG_TX, "complete_packet: waking queues"); ieee80211_wake_queues(wl->hw); - wl->tx_queue_stopped = false; } } @@ -335,8 +376,8 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count) wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); /* read the tx results from the chipset */ - wl1271_spi_read(wl, le32_to_cpu(memmap->tx_result), - wl->tx_res_if, sizeof(*wl->tx_res_if), false); + wl1271_read(wl, le32_to_cpu(memmap->tx_result), + wl->tx_res_if, sizeof(*wl->tx_res_if), false); /* verify that the result buffer is not getting overrun */ if (count > TX_HW_RESULT_QUEUE_LEN) { @@ -357,10 +398,10 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count) } /* write host counter to chipset (to ack) */ - wl1271_spi_write32(wl, le32_to_cpu(memmap->tx_result) + - offsetof(struct wl1271_tx_hw_res_if, - tx_result_host_counter), - le32_to_cpu(wl->tx_res_if->tx_result_fw_counter)); + wl1271_write32(wl, le32_to_cpu(memmap->tx_result) + + offsetof(struct wl1271_tx_hw_res_if, + tx_result_host_counter), + le32_to_cpu(wl->tx_res_if->tx_result_fw_counter)); } /* caller must hold wl->mutex */ diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h index 416396caf0a0..17e405a09caa 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.h +++ b/drivers/net/wireless/wl12xx/wl1271_tx.h @@ -123,6 +123,42 @@ struct wl1271_tx_hw_res_if { struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN]; } __attribute__ ((packed)); +static inline int wl1271_tx_get_queue(int queue) +{ + /* FIXME: use best effort until WMM is enabled */ + return CONF_TX_AC_BE; + + switch (queue) { + case 0: + return CONF_TX_AC_VO; + case 1: + return CONF_TX_AC_VI; + case 2: + return CONF_TX_AC_BE; + case 3: + return CONF_TX_AC_BK; + default: + return CONF_TX_AC_BE; + } +} + +/* wl1271 tx descriptor needs the tid and we need to convert it from ac */ +static inline int wl1271_tx_ac_to_tid(int ac) +{ + switch (ac) { + case 0: + return 0; + case 1: + return 2; + case 2: + return 4; + case 3: + return 6; + default: + return 0; + } +} + void wl1271_tx_work(struct work_struct *work); void wl1271_tx_complete(struct wl1271 *wl, u32 count); void wl1271_tx_flush(struct wl1271 *wl); |