Merge branch 'for-linville' of git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx

This commit is contained in:
John W. Linville 2013-06-18 14:45:21 -04:00
commit 9e49a9d3d6
10 changed files with 367 additions and 254 deletions

View File

@ -93,8 +93,7 @@ static void wl1251_spi_wake(struct wl1251 *wl)
memset(&t, 0, sizeof(t));
spi_message_init(&m);
/*
* Set WSPI_INIT_COMMAND
/* Set WSPI_INIT_COMMAND
* the data is being send from the MSB to LSB
*/
cmd[2] = 0xff;
@ -262,7 +261,8 @@ static int wl1251_spi_probe(struct spi_device *spi)
wl->if_ops = &wl1251_spi_ops;
/* This is the only SPI value that we need to set here, the rest
* comes from the board-peripherals file */
* comes from the board-peripherals file
*/
spi->bits_per_word = 32;
ret = spi_setup(spi);
@ -329,29 +329,7 @@ static struct spi_driver wl1251_spi_driver = {
.remove = wl1251_spi_remove,
};
static int __init wl1251_spi_init(void)
{
int ret;
ret = spi_register_driver(&wl1251_spi_driver);
if (ret < 0) {
wl1251_error("failed to register spi driver: %d", ret);
goto out;
}
out:
return ret;
}
static void __exit wl1251_spi_exit(void)
{
spi_unregister_driver(&wl1251_spi_driver);
wl1251_notice("unloaded");
}
module_init(wl1251_spi_init);
module_exit(wl1251_spi_exit);
module_spi_driver(wl1251_spi_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");

View File

@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/ip.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include "../wlcore/wlcore.h"
#include "../wlcore/debug.h"
@ -594,8 +595,8 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
.mem3 = { .start = 0x00000000, .size = 0x00000000 },
},
[PART_PHY_INIT] = {
.mem = { .start = 0x80926000,
.size = sizeof(struct wl18xx_mac_and_phy_params) },
.mem = { .start = WL18XX_PHY_INIT_MEM_ADDR,
.size = WL18XX_PHY_INIT_MEM_SIZE },
.reg = { .start = 0x00000000, .size = 0x00000000 },
.mem2 = { .start = 0x00000000, .size = 0x00000000 },
.mem3 = { .start = 0x00000000, .size = 0x00000000 },
@ -799,6 +800,9 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
u32 tmp;
int ret;
BUILD_BUG_ON(sizeof(struct wl18xx_mac_and_phy_params) >
WL18XX_PHY_INIT_MEM_SIZE);
ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
if (ret < 0)
goto out;
@ -815,6 +819,35 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp);
if (ret < 0)
goto out;
/*
* Workaround for FDSP code RAM corruption (needed for PG2.1
* and newer; for older chips it's a NOP). Change FDSP clock
* settings so that it's muxed to the ATGP clock instead of
* its own clock.
*/
ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]);
if (ret < 0)
goto out;
/* disable FDSP clock */
ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
MEM_FDSP_CLK_120_DISABLE);
if (ret < 0)
goto out;
/* set ATPG clock toward FDSP Code RAM rather than its own clock */
ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
MEM_FDSP_CODERAM_FUNC_CLK_SEL);
if (ret < 0)
goto out;
/* re-enable FDSP clock */
ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
MEM_FDSP_CLK_120_ENABLE);
out:
return ret;
@ -1286,6 +1319,16 @@ static int wl18xx_get_mac(struct wl1271 *wl)
((mac1 & 0xff000000) >> 24);
wl->fuse_nic_addr = (mac1 & 0xffffff);
if (!wl->fuse_oui_addr && !wl->fuse_nic_addr) {
u8 mac[ETH_ALEN];
eth_random_addr(mac);
wl->fuse_oui_addr = (mac[0] << 16) + (mac[1] << 8) + mac[2];
wl->fuse_nic_addr = (mac[3] << 16) + (mac[4] << 8) + mac[5];
wl1271_warning("MAC address from fuse not available, using random locally administered addresses.");
}
ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
out:

View File

@ -38,6 +38,9 @@
#define WL18XX_REG_BOOT_PART_SIZE 0x00014578
#define WL18XX_PHY_INIT_MEM_ADDR 0x80926000
#define WL18XX_PHY_END_MEM_ADDR 0x8093CA44
#define WL18XX_PHY_INIT_MEM_SIZE \
(WL18XX_PHY_END_MEM_ADDR - WL18XX_PHY_INIT_MEM_ADDR)
#define WL18XX_SDIO_WSPI_BASE (WL18XX_REGISTERS_BASE)
#define WL18XX_REG_CONFIG_BASE (WL18XX_REGISTERS_BASE + 0x02000)
@ -217,4 +220,16 @@ static const char * const rdl_names[] = {
[RDL_4_SP] = "1897 MIMO",
};
/* FPGA_SPARE_1 register - used to change the PHY ATPG clock at boot time */
#define WL18XX_PHY_FPGA_SPARE_1 0x8093CA40
/* command to disable FDSP clock */
#define MEM_FDSP_CLK_120_DISABLE 0x80000000
/* command to set ATPG clock toward FDSP Code RAM rather than its own clock */
#define MEM_FDSP_CODERAM_FUNC_CLK_SEL 0xC0000000
/* command to re-enable FDSP clock */
#define MEM_FDSP_CLK_120_ENABLE 0x40000000
#endif /* __REG_H__ */

View File

@ -1,5 +1,5 @@
wlcore-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
boot.o init.o debugfs.o scan.o
boot.o init.o debugfs.o scan.o sysfs.o
wlcore_spi-objs = spi.o
wlcore_sdio-objs = sdio.o

View File

@ -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);
wlcore_sysfs_free(wl);
device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
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);

View File

@ -110,7 +110,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
DECLARE_COMPLETION_ONSTACK(compl);
unsigned long flags;
int ret;
u32 start_time = jiffies;
unsigned long start_time = jiffies;
bool pending = false;
/*

View File

@ -434,19 +434,7 @@ static struct spi_driver wl1271_spi_driver = {
.remove = wl1271_remove,
};
static int __init wl1271_init(void)
{
return spi_register_driver(&wl1271_spi_driver);
}
static void __exit wl1271_exit(void)
{
spi_unregister_driver(&wl1271_spi_driver);
}
module_init(wl1271_init);
module_exit(wl1271_exit);
module_spi_driver(wl1271_spi_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");

View File

@ -0,0 +1,216 @@
/*
* This file is part of wlcore
*
* Copyright (C) 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
* 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 "wlcore.h"
#include "debug.h"
#include "ps.h"
#include "sysfs.h"
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,
};
int wlcore_sysfs_init(struct wl1271 *wl)
{
int ret;
/* 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");
goto out;
}
/* 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;
}
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:
return ret;
}
void wlcore_sysfs_free(struct wl1271 *wl)
{
device_remove_bin_file(wl->dev, &fwlog_attr);
device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
device_remove_file(wl->dev, &dev_attr_bt_coex_state);
}

View File

@ -0,0 +1,28 @@
/*
* This file is part of wlcore
*
* Copyright (C) 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
* 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 __SYSFS_H__
#define __SYSFS_H__
int wlcore_sysfs_init(struct wl1271 *wl);
void wlcore_sysfs_free(struct wl1271 *wl);
#endif

View File

@ -386,7 +386,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
(cipher == WLAN_CIPHER_SUITE_WEP104);
if (unlikely(is_wep && wlvif->default_key != idx)) {
if (WARN_ON(is_wep && wlvif->default_key != idx)) {
ret = wl1271_set_default_wep_key(wl, wlvif, idx);
if (ret < 0)
return ret;