|
@@ -1,10 +1,9 @@
|
|
|
|
|
|
/*
|
|
|
- * This file is part of wl1271
|
|
|
+ * This file is part of wlcore
|
|
|
*
|
|
|
* Copyright (C) 2008-2010 Nokia Corporation
|
|
|
- *
|
|
|
- * Contact: Luciano Coelho <luciano.coelho@nokia.com>
|
|
|
+ * Copyright (C) 2011-2013 Texas Instruments Inc.
|
|
|
*
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
* modify it under the terms of the GNU General Public License
|
|
@@ -24,34 +23,23 @@
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/firmware.h>
|
|
|
-#include <linux/delay.h>
|
|
|
-#include <linux/spi/spi.h>
|
|
|
-#include <linux/crc32.h>
|
|
|
#include <linux/etherdevice.h>
|
|
|
#include <linux/vmalloc.h>
|
|
|
-#include <linux/platform_device.h>
|
|
|
-#include <linux/slab.h>
|
|
|
#include <linux/wl12xx.h>
|
|
|
-#include <linux/sched.h>
|
|
|
#include <linux/interrupt.h>
|
|
|
|
|
|
#include "wlcore.h"
|
|
|
#include "debug.h"
|
|
|
#include "wl12xx_80211.h"
|
|
|
#include "io.h"
|
|
|
-#include "event.h"
|
|
|
#include "tx.h"
|
|
|
-#include "rx.h"
|
|
|
#include "ps.h"
|
|
|
#include "init.h"
|
|
|
#include "debugfs.h"
|
|
|
-#include "cmd.h"
|
|
|
-#include "boot.h"
|
|
|
#include "testmode.h"
|
|
|
#include "scan.h"
|
|
|
#include "hw_ops.h"
|
|
|
-
|
|
|
-#define WL1271_BOOT_RETRIES 3
|
|
|
+#include "sysfs.h"
|
|
|
|
|
|
#define WL1271_BOOT_RETRIES 3
|
|
|
|
|
@@ -65,8 +53,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
|
|
|
static void wlcore_op_stop_locked(struct wl1271 *wl);
|
|
|
static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
|
|
|
|
|
|
-static int wl12xx_set_authorized(struct wl1271 *wl,
|
|
|
- struct wl12xx_vif *wlvif)
|
|
|
+static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
@@ -983,7 +970,7 @@ static int wlcore_fw_wakeup(struct wl1271 *wl)
|
|
|
|
|
|
static int wl1271_setup(struct wl1271 *wl)
|
|
|
{
|
|
|
- wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
|
|
|
+ wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
|
|
|
sizeof(*wl->fw_status_2) +
|
|
|
wl->fw_status_priv_len, GFP_KERNEL);
|
|
|
if (!wl->fw_status_1)
|
|
@@ -993,7 +980,7 @@ static int wl1271_setup(struct wl1271 *wl)
|
|
|
(((u8 *) wl->fw_status_1) +
|
|
|
WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
|
|
|
|
|
|
- wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
|
|
|
+ wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
|
|
|
if (!wl->tx_res_if) {
|
|
|
kfree(wl->fw_status_1);
|
|
|
return -ENOMEM;
|
|
@@ -1668,8 +1655,7 @@ static int wl1271_configure_suspend(struct wl1271 *wl,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void wl1271_configure_resume(struct wl1271 *wl,
|
|
|
- struct wl12xx_vif *wlvif)
|
|
|
+static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|
|
{
|
|
|
int ret = 0;
|
|
|
bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
|
|
@@ -2603,6 +2589,7 @@ unlock:
|
|
|
cancel_work_sync(&wlvif->rx_streaming_enable_work);
|
|
|
cancel_work_sync(&wlvif->rx_streaming_disable_work);
|
|
|
cancel_delayed_work_sync(&wlvif->connection_loss_work);
|
|
|
+ cancel_delayed_work_sync(&wlvif->channel_switch_work);
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
}
|
|
@@ -3210,14 +3197,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- /* the default WEP key needs to be configured at least once */
|
|
|
- if (key_type == KEY_WEP) {
|
|
|
- ret = wl12xx_cmd_set_default_wep_key(wl,
|
|
|
- wlvif->default_key,
|
|
|
- wlvif->sta.hlid);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -3374,6 +3353,46 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(wlcore_set_key);
|
|
|
|
|
|
+static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
|
|
|
+ struct ieee80211_vif *vif,
|
|
|
+ int key_idx)
|
|
|
+{
|
|
|
+ struct wl1271 *wl = hw->priv;
|
|
|
+ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d",
|
|
|
+ key_idx);
|
|
|
+
|
|
|
+ mutex_lock(&wl->mutex);
|
|
|
+
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
|
|
+ ret = -EAGAIN;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wl1271_ps_elp_wakeup(wl);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ wlvif->default_key = key_idx;
|
|
|
+
|
|
|
+ /* the default WEP key needs to be configured at least once */
|
|
|
+ if (wlvif->encryption_type == KEY_WEP) {
|
|
|
+ ret = wl12xx_cmd_set_default_wep_key(wl,
|
|
|
+ key_idx,
|
|
|
+ wlvif->sta.hlid);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out_sleep;
|
|
|
+ }
|
|
|
+
|
|
|
+out_sleep:
|
|
|
+ wl1271_ps_elp_sleep(wl);
|
|
|
+
|
|
|
+out_unlock:
|
|
|
+ mutex_unlock(&wl->mutex);
|
|
|
+}
|
|
|
+
|
|
|
void wlcore_regdomain_config(struct wl1271 *wl)
|
|
|
{
|
|
|
int ret;
|
|
@@ -3782,8 +3801,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
|
|
|
struct ieee80211_hdr *hdr;
|
|
|
u32 min_rate;
|
|
|
int ret;
|
|
|
- int ieoffset = offsetof(struct ieee80211_mgmt,
|
|
|
- u.beacon.variable);
|
|
|
+ int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
|
|
|
struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
|
|
|
u16 tmpl_id;
|
|
|
|
|
@@ -4230,8 +4248,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
|
|
|
}
|
|
|
|
|
|
/* Handle new association with HT. Do this after join. */
|
|
|
- if (sta_exists &&
|
|
|
- (changed & BSS_CHANGED_HT)) {
|
|
|
+ if (sta_exists) {
|
|
|
bool enabled =
|
|
|
bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
|
|
|
|
|
@@ -5368,6 +5385,7 @@ static const struct ieee80211_ops wl1271_ops = {
|
|
|
.ampdu_action = wl1271_op_ampdu_action,
|
|
|
.tx_frames_pending = wl1271_tx_frames_pending,
|
|
|
.set_bitrate_mask = wl12xx_set_bitrate_mask,
|
|
|
+ .set_default_unicast_key = wl1271_op_set_default_key_idx,
|
|
|
.channel_switch = wl12xx_op_channel_switch,
|
|
|
.flush = wlcore_op_flush,
|
|
|
.remain_on_channel = wlcore_op_remain_on_channel,
|
|
@@ -5403,151 +5421,6 @@ u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band)
|
|
|
return idx;
|
|
|
}
|
|
|
|
|
|
-static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
|
|
|
- struct device_attribute *attr,
|
|
|
- char *buf)
|
|
|
-{
|
|
|
- struct wl1271 *wl = dev_get_drvdata(dev);
|
|
|
- ssize_t len;
|
|
|
-
|
|
|
- len = PAGE_SIZE;
|
|
|
-
|
|
|
- mutex_lock(&wl->mutex);
|
|
|
- len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
|
|
|
- wl->sg_enabled);
|
|
|
- mutex_unlock(&wl->mutex);
|
|
|
-
|
|
|
- return len;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
|
|
|
- struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
-{
|
|
|
- struct wl1271 *wl = dev_get_drvdata(dev);
|
|
|
- unsigned long res;
|
|
|
- int ret;
|
|
|
-
|
|
|
- ret = kstrtoul(buf, 10, &res);
|
|
|
- if (ret < 0) {
|
|
|
- wl1271_warning("incorrect value written to bt_coex_mode");
|
|
|
- return count;
|
|
|
- }
|
|
|
-
|
|
|
- mutex_lock(&wl->mutex);
|
|
|
-
|
|
|
- res = !!res;
|
|
|
-
|
|
|
- if (res == wl->sg_enabled)
|
|
|
- goto out;
|
|
|
-
|
|
|
- wl->sg_enabled = res;
|
|
|
-
|
|
|
- if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
- goto out;
|
|
|
-
|
|
|
- ret = wl1271_ps_elp_wakeup(wl);
|
|
|
- if (ret < 0)
|
|
|
- goto out;
|
|
|
-
|
|
|
- wl1271_acx_sg_enable(wl, wl->sg_enabled);
|
|
|
- wl1271_ps_elp_sleep(wl);
|
|
|
-
|
|
|
- out:
|
|
|
- mutex_unlock(&wl->mutex);
|
|
|
- return count;
|
|
|
-}
|
|
|
-
|
|
|
-static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
|
|
|
- wl1271_sysfs_show_bt_coex_state,
|
|
|
- wl1271_sysfs_store_bt_coex_state);
|
|
|
-
|
|
|
-static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
|
|
|
- struct device_attribute *attr,
|
|
|
- char *buf)
|
|
|
-{
|
|
|
- struct wl1271 *wl = dev_get_drvdata(dev);
|
|
|
- ssize_t len;
|
|
|
-
|
|
|
- len = PAGE_SIZE;
|
|
|
-
|
|
|
- mutex_lock(&wl->mutex);
|
|
|
- if (wl->hw_pg_ver >= 0)
|
|
|
- len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
|
|
|
- else
|
|
|
- len = snprintf(buf, len, "n/a\n");
|
|
|
- mutex_unlock(&wl->mutex);
|
|
|
-
|
|
|
- return len;
|
|
|
-}
|
|
|
-
|
|
|
-static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
|
|
|
- wl1271_sysfs_show_hw_pg_ver, NULL);
|
|
|
-
|
|
|
-static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
|
|
|
- struct bin_attribute *bin_attr,
|
|
|
- char *buffer, loff_t pos, size_t count)
|
|
|
-{
|
|
|
- struct device *dev = container_of(kobj, struct device, kobj);
|
|
|
- struct wl1271 *wl = dev_get_drvdata(dev);
|
|
|
- ssize_t len;
|
|
|
- int ret;
|
|
|
-
|
|
|
- ret = mutex_lock_interruptible(&wl->mutex);
|
|
|
- if (ret < 0)
|
|
|
- return -ERESTARTSYS;
|
|
|
-
|
|
|
- /* Let only one thread read the log at a time, blocking others */
|
|
|
- while (wl->fwlog_size == 0) {
|
|
|
- DEFINE_WAIT(wait);
|
|
|
-
|
|
|
- prepare_to_wait_exclusive(&wl->fwlog_waitq,
|
|
|
- &wait,
|
|
|
- TASK_INTERRUPTIBLE);
|
|
|
-
|
|
|
- if (wl->fwlog_size != 0) {
|
|
|
- finish_wait(&wl->fwlog_waitq, &wait);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- mutex_unlock(&wl->mutex);
|
|
|
-
|
|
|
- schedule();
|
|
|
- finish_wait(&wl->fwlog_waitq, &wait);
|
|
|
-
|
|
|
- if (signal_pending(current))
|
|
|
- return -ERESTARTSYS;
|
|
|
-
|
|
|
- ret = mutex_lock_interruptible(&wl->mutex);
|
|
|
- if (ret < 0)
|
|
|
- return -ERESTARTSYS;
|
|
|
- }
|
|
|
-
|
|
|
- /* Check if the fwlog is still valid */
|
|
|
- if (wl->fwlog_size < 0) {
|
|
|
- mutex_unlock(&wl->mutex);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- /* Seeking is not supported - old logs are not kept. Disregard pos. */
|
|
|
- len = min(count, (size_t)wl->fwlog_size);
|
|
|
- wl->fwlog_size -= len;
|
|
|
- memcpy(buffer, wl->fwlog, len);
|
|
|
-
|
|
|
- /* Make room for new messages */
|
|
|
- memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
|
|
|
-
|
|
|
- mutex_unlock(&wl->mutex);
|
|
|
-
|
|
|
- return len;
|
|
|
-}
|
|
|
-
|
|
|
-static struct bin_attribute fwlog_attr = {
|
|
|
- .attr = {.name = "fwlog", .mode = S_IRUSR},
|
|
|
- .read = wl1271_sysfs_read_fwlog,
|
|
|
-};
|
|
|
-
|
|
|
static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
|
|
|
{
|
|
|
int i;
|
|
@@ -5827,8 +5700,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#define WL1271_DEFAULT_CHANNEL 0
|
|
|
-
|
|
|
struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
|
|
|
u32 mbox_size)
|
|
|
{
|
|
@@ -5881,7 +5752,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
|
|
|
goto err_hw;
|
|
|
}
|
|
|
|
|
|
- wl->channel = WL1271_DEFAULT_CHANNEL;
|
|
|
+ wl->channel = 0;
|
|
|
wl->rx_counter = 0;
|
|
|
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
|
|
|
wl->band = IEEE80211_BAND_2GHZ;
|
|
@@ -5988,11 +5859,8 @@ int wlcore_free_hw(struct wl1271 *wl)
|
|
|
wake_up_interruptible_all(&wl->fwlog_waitq);
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
- device_remove_bin_file(wl->dev, &fwlog_attr);
|
|
|
-
|
|
|
- device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
|
|
|
+ wlcore_sysfs_free(wl);
|
|
|
|
|
|
- device_remove_file(wl->dev, &dev_attr_bt_coex_state);
|
|
|
kfree(wl->buffer_32);
|
|
|
kfree(wl->mbox);
|
|
|
free_page((unsigned long)wl->fwlog);
|
|
@@ -6104,36 +5972,13 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
|
|
|
if (ret)
|
|
|
goto out_irq;
|
|
|
|
|
|
- /* Create sysfs file to control bt coex state */
|
|
|
- ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
|
|
|
- if (ret < 0) {
|
|
|
- wl1271_error("failed to create sysfs file bt_coex_state");
|
|
|
+ ret = wlcore_sysfs_init(wl);
|
|
|
+ if (ret)
|
|
|
goto out_unreg;
|
|
|
- }
|
|
|
-
|
|
|
- /* Create sysfs file to get HW PG version */
|
|
|
- ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
|
|
|
- if (ret < 0) {
|
|
|
- wl1271_error("failed to create sysfs file hw_pg_ver");
|
|
|
- goto out_bt_coex_state;
|
|
|
- }
|
|
|
-
|
|
|
- /* Create sysfs file for the FW log */
|
|
|
- ret = device_create_bin_file(wl->dev, &fwlog_attr);
|
|
|
- if (ret < 0) {
|
|
|
- wl1271_error("failed to create sysfs file fwlog");
|
|
|
- goto out_hw_pg_ver;
|
|
|
- }
|
|
|
|
|
|
wl->initialized = true;
|
|
|
goto out;
|
|
|
|
|
|
-out_hw_pg_ver:
|
|
|
- device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
|
|
|
-
|
|
|
-out_bt_coex_state:
|
|
|
- device_remove_file(wl->dev, &dev_attr_bt_coex_state);
|
|
|
-
|
|
|
out_unreg:
|
|
|
wl1271_unregister_hw(wl);
|
|
|
|