Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,36 @@
# SPDX-License-Identifier: GPL-2.0-only
config WLCORE
tristate "TI wlcore support"
depends on MAC80211
select FW_LOADER
help
This module contains the main code for TI WLAN chips. It abstracts
hardware-specific differences among different chipset families.
Each chipset family needs to implement its own lower-level module
that will depend on this module for the common code.
If you choose to build a module, it will be called wlcore. Say N if
unsure.
config WLCORE_SPI
tristate "TI wlcore SPI support"
depends on WLCORE && SPI_MASTER && OF
select CRC7
help
This module adds support for the SPI interface of adapters using
TI WLAN chipsets. Select this if your platform is using
the SPI bus.
If you choose to build a module, it'll be called wlcore_spi.
Say N if unsure.
config WLCORE_SDIO
tristate "TI wlcore SDIO support"
depends on WLCORE && MMC
help
This module adds support for the SDIO interface of adapters using
TI WLAN chipsets. Select this if your platform is using
the SDIO bus.
If you choose to build a module, it'll be called wlcore_sdio.
Say N if unsure.
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
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 sysfs.o vendor_cmd.o
wlcore_spi-objs = spi.o
wlcore_sdio-objs = sdio.o
wlcore-$(CONFIG_NL80211_TESTMODE) += testmode.o
obj-$(CONFIG_WLCORE) += wlcore.o
obj-$(CONFIG_WLCORE_SPI) += wlcore_spi.o
obj-$(CONFIG_WLCORE_SDIO) += wlcore_sdio.o
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,525 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#include <linux/slab.h>
#include <linux/export.h>
#include "debug.h"
#include "acx.h"
#include "boot.h"
#include "io.h"
#include "event.h"
#include "rx.h"
#include "hw_ops.h"
static int wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
{
u32 cpu_ctrl;
int ret;
/* 10.5.0 run the firmware (I) */
ret = wlcore_read_reg(wl, REG_ECPU_CONTROL, &cpu_ctrl);
if (ret < 0)
goto out;
/* 10.5.1 run the firmware (II) */
cpu_ctrl |= flag;
ret = wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl);
out:
return ret;
}
static int wlcore_boot_parse_fw_ver(struct wl1271 *wl,
struct wl1271_static_data *static_data)
{
int ret;
strscpy(wl->chip.fw_ver_str, static_data->fw_version,
sizeof(wl->chip.fw_ver_str));
ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u",
&wl->chip.fw_ver[0], &wl->chip.fw_ver[1],
&wl->chip.fw_ver[2], &wl->chip.fw_ver[3],
&wl->chip.fw_ver[4]);
if (ret != 5) {
wl1271_warning("fw version incorrect value");
memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
ret = -EINVAL;
goto out;
}
ret = wlcore_identify_fw(wl);
if (ret < 0)
goto out;
out:
return ret;
}
static int wlcore_validate_fw_ver(struct wl1271 *wl)
{
unsigned int *fw_ver = wl->chip.fw_ver;
unsigned int *min_ver = (wl->fw_type == WL12XX_FW_TYPE_MULTI) ?
wl->min_mr_fw_ver : wl->min_sr_fw_ver;
char min_fw_str[32] = "";
int off = 0;
int i;
/* the chip must be exactly equal */
if ((min_ver[FW_VER_CHIP] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP]))
goto fail;
/* the firmware type must be equal */
if ((min_ver[FW_VER_IF_TYPE] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_IF_TYPE] != fw_ver[FW_VER_IF_TYPE]))
goto fail;
/* the project number must be equal */
if ((min_ver[FW_VER_SUBTYPE] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_SUBTYPE] != fw_ver[FW_VER_SUBTYPE]))
goto fail;
/* the API version must be greater or equal */
if ((min_ver[FW_VER_MAJOR] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR]))
goto fail;
/* if the API version is equal... */
if (((min_ver[FW_VER_MAJOR] == WLCORE_FW_VER_IGNORE) ||
(min_ver[FW_VER_MAJOR] == fw_ver[FW_VER_MAJOR])) &&
/* ...the minor must be greater or equal */
((min_ver[FW_VER_MINOR] != WLCORE_FW_VER_IGNORE) &&
(min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR])))
goto fail;
return 0;
fail:
for (i = 0; i < NUM_FW_VER && off < sizeof(min_fw_str); i++)
if (min_ver[i] == WLCORE_FW_VER_IGNORE)
off += snprintf(min_fw_str + off,
sizeof(min_fw_str) - off,
"*.");
else
off += snprintf(min_fw_str + off,
sizeof(min_fw_str) - off,
"%u.", min_ver[i]);
wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is invalid.\n"
"Please use at least FW %s\n"
"You can get the latest firmwares at:\n"
"git://git.ti.com/wilink8-wlan/wl18xx_fw.git",
fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE],
fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE],
fw_ver[FW_VER_MINOR], min_fw_str);
return -EINVAL;
}
static int wlcore_boot_static_data(struct wl1271 *wl)
{
struct wl1271_static_data *static_data;
size_t len = sizeof(*static_data) + wl->static_data_priv_len;
int ret;
static_data = kmalloc(len, GFP_KERNEL);
if (!static_data) {
ret = -ENOMEM;
goto out;
}
ret = wlcore_read(wl, wl->cmd_box_addr, static_data, len, false);
if (ret < 0)
goto out_free;
ret = wlcore_boot_parse_fw_ver(wl, static_data);
if (ret < 0)
goto out_free;
ret = wlcore_validate_fw_ver(wl);
if (ret < 0)
goto out_free;
ret = wlcore_handle_static_data(wl, static_data);
if (ret < 0)
goto out_free;
out_free:
kfree(static_data);
out:
return ret;
}
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
size_t fw_data_len, u32 dest)
{
struct wlcore_partition_set partition;
int addr, chunk_num, partition_limit;
u8 *p, *chunk;
int ret;
/* whal_FwCtrl_LoadFwImageSm() */
wl1271_debug(DEBUG_BOOT, "starting firmware upload");
wl1271_debug(DEBUG_BOOT, "fw_data_len %zd chunk_size %d",
fw_data_len, CHUNK_SIZE);
if ((fw_data_len % 4) != 0) {
wl1271_error("firmware length not multiple of four");
return -EIO;
}
chunk = kmalloc(CHUNK_SIZE, GFP_KERNEL);
if (!chunk) {
wl1271_error("allocation for firmware upload chunk failed");
return -ENOMEM;
}
memcpy(&partition, &wl->ptable[PART_DOWN], sizeof(partition));
partition.mem.start = dest;
ret = wlcore_set_partition(wl, &partition);
if (ret < 0)
goto out;
/* 10.1 set partition limit and chunk num */
chunk_num = 0;
partition_limit = wl->ptable[PART_DOWN].mem.size;
while (chunk_num < fw_data_len / CHUNK_SIZE) {
/* 10.2 update partition, if needed */
addr = dest + (chunk_num + 2) * CHUNK_SIZE;
if (addr > partition_limit) {
addr = dest + chunk_num * CHUNK_SIZE;
partition_limit = chunk_num * CHUNK_SIZE +
wl->ptable[PART_DOWN].mem.size;
partition.mem.start = addr;
ret = wlcore_set_partition(wl, &partition);
if (ret < 0)
goto out;
}
/* 10.3 upload the chunk */
addr = dest + chunk_num * CHUNK_SIZE;
p = buf + chunk_num * CHUNK_SIZE;
memcpy(chunk, p, CHUNK_SIZE);
wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x",
p, addr);
ret = wlcore_write(wl, addr, chunk, CHUNK_SIZE, false);
if (ret < 0)
goto out;
chunk_num++;
}
/* 10.4 upload the last chunk */
addr = dest + chunk_num * CHUNK_SIZE;
p = buf + chunk_num * CHUNK_SIZE;
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);
ret = wlcore_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false);
out:
kfree(chunk);
return ret;
}
int wlcore_boot_upload_firmware(struct wl1271 *wl)
{
u32 chunks, addr, len;
int ret = 0;
u8 *fw;
fw = wl->fw;
chunks = be32_to_cpup((__be32 *) fw);
fw += sizeof(u32);
wl1271_debug(DEBUG_BOOT, "firmware chunks to be uploaded: %u", chunks);
while (chunks--) {
addr = be32_to_cpup((__be32 *) fw);
fw += sizeof(u32);
len = be32_to_cpup((__be32 *) fw);
fw += sizeof(u32);
if (len > 300000) {
wl1271_info("firmware chunk too long: %u", len);
return -EINVAL;
}
wl1271_debug(DEBUG_BOOT, "chunk %d addr 0x%x len %u",
chunks, addr, len);
ret = wl1271_boot_upload_firmware_chunk(wl, fw, len, addr);
if (ret != 0)
break;
fw += len;
}
return ret;
}
EXPORT_SYMBOL_GPL(wlcore_boot_upload_firmware);
int wlcore_boot_upload_nvs(struct wl1271 *wl)
{
struct platform_device *pdev = wl->pdev;
struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
const char *nvs_name = "unknown";
size_t nvs_len, burst_len;
int i;
u32 dest_addr, val;
u8 *nvs_ptr, *nvs_aligned;
int ret;
if (wl->nvs == NULL) {
wl1271_error("NVS file is needed during boot");
return -ENODEV;
}
if (pdev_data && pdev_data->family)
nvs_name = pdev_data->family->nvs_name;
if (wl->quirks & WLCORE_QUIRK_LEGACY_NVS) {
struct wl1271_nvs_file *nvs =
(struct wl1271_nvs_file *)wl->nvs;
/*
* FIXME: the LEGACY NVS image support (NVS's missing the 5GHz
* band configurations) can be removed when those NVS files stop
* floating around.
*/
if (wl->nvs_len == sizeof(struct wl1271_nvs_file) ||
wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) {
if (nvs->general_params.dual_mode_select)
wl->enable_11a = true;
}
if (wl->nvs_len != sizeof(struct wl1271_nvs_file) &&
(wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE ||
wl->enable_11a)) {
wl1271_error("%s size is not as expected: %zu != %zu",
nvs_name, wl->nvs_len,
sizeof(struct wl1271_nvs_file));
kfree(wl->nvs);
wl->nvs = NULL;
wl->nvs_len = 0;
return -EILSEQ;
}
/* only the first part of the NVS needs to be uploaded */
nvs_len = sizeof(nvs->nvs);
nvs_ptr = (u8 *) nvs->nvs;
} else {
struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs;
if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) {
if (nvs->general_params.dual_mode_select)
wl->enable_11a = true;
} else {
wl1271_error("%s size is not as expected: %zu != %zu",
nvs_name, wl->nvs_len,
sizeof(struct wl128x_nvs_file));
kfree(wl->nvs);
wl->nvs = NULL;
wl->nvs_len = 0;
return -EILSEQ;
}
/* only the first part of the NVS needs to be uploaded */
nvs_len = sizeof(nvs->nvs);
nvs_ptr = (u8 *)nvs->nvs;
}
/* update current MAC address to NVS */
nvs_ptr[11] = wl->addresses[0].addr[0];
nvs_ptr[10] = wl->addresses[0].addr[1];
nvs_ptr[6] = wl->addresses[0].addr[2];
nvs_ptr[5] = wl->addresses[0].addr[3];
nvs_ptr[4] = wl->addresses[0].addr[4];
nvs_ptr[3] = wl->addresses[0].addr[5];
/*
* Layout before the actual NVS tables:
* 1 byte : burst length.
* 2 bytes: destination address.
* n bytes: data to burst copy.
*
* This is ended by a 0 length, then the NVS tables.
*/
/* FIXME: Do we need to check here whether the LSB is 1? */
while (nvs_ptr[0]) {
burst_len = nvs_ptr[0];
dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8));
/*
* Due to our new wl1271_translate_reg_addr function,
* we need to add the register partition start address
* to the destination
*/
dest_addr += wl->curr_part.reg.start;
/* We move our pointer to the data */
nvs_ptr += 3;
for (i = 0; i < burst_len; i++) {
if (nvs_ptr + 3 >= (u8 *) wl->nvs + nvs_len)
goto out_badnvs;
val = (nvs_ptr[0] | (nvs_ptr[1] << 8)
| (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24));
wl1271_debug(DEBUG_BOOT,
"nvs burst write 0x%x: 0x%x",
dest_addr, val);
ret = wlcore_write32(wl, dest_addr, val);
if (ret < 0)
return ret;
nvs_ptr += 4;
dest_addr += 4;
}
if (nvs_ptr >= (u8 *) wl->nvs + nvs_len)
goto out_badnvs;
}
/*
* We've reached the first zero length, the first NVS table
* is located at an aligned offset which is at least 7 bytes further.
* NOTE: The wl->nvs->nvs element must be first, in order to
* simplify the casting, we assume it is at the beginning of
* the wl->nvs structure.
*/
nvs_ptr = (u8 *)wl->nvs +
ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4);
if (nvs_ptr >= (u8 *) wl->nvs + nvs_len)
goto out_badnvs;
nvs_len -= nvs_ptr - (u8 *)wl->nvs;
/* Now we must set the partition correctly */
ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
if (ret < 0)
return ret;
/* 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;
/* And finally we upload the NVS tables */
ret = wlcore_write_data(wl, REG_CMD_MBOX_ADDRESS, nvs_aligned, nvs_len,
false);
kfree(nvs_aligned);
return ret;
out_badnvs:
wl1271_error("nvs data is malformed");
return -EILSEQ;
}
EXPORT_SYMBOL_GPL(wlcore_boot_upload_nvs);
int wlcore_boot_run_firmware(struct wl1271 *wl)
{
int loop, ret;
u32 chip_id, intr;
/* Make sure we have the boot partition */
ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
if (ret < 0)
return ret;
ret = wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT);
if (ret < 0)
return ret;
ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &chip_id);
if (ret < 0)
return ret;
wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id);
if (chip_id != wl->chip.id) {
wl1271_error("chip id doesn't match after firmware boot");
return -EIO;
}
/* wait for init to complete */
loop = 0;
while (loop++ < INIT_LOOP) {
udelay(INIT_LOOP_DELAY);
ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
if (ret < 0)
return ret;
if (intr == 0xffffffff) {
wl1271_error("error reading hardware complete "
"init indication");
return -EIO;
}
/* check that ACX_INTR_INIT_COMPLETE is enabled */
else if (intr & WL1271_ACX_INTR_INIT_COMPLETE) {
ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK,
WL1271_ACX_INTR_INIT_COMPLETE);
if (ret < 0)
return ret;
break;
}
}
if (loop > INIT_LOOP) {
wl1271_error("timeout waiting for the hardware to "
"complete initialization");
return -EIO;
}
/* get hardware config command mail box */
ret = wlcore_read_reg(wl, REG_COMMAND_MAILBOX_PTR, &wl->cmd_box_addr);
if (ret < 0)
return ret;
wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x", wl->cmd_box_addr);
/* get hardware config event mail box */
ret = wlcore_read_reg(wl, REG_EVENT_MAILBOX_PTR, &wl->mbox_ptr[0]);
if (ret < 0)
return ret;
wl->mbox_ptr[1] = wl->mbox_ptr[0] + wl->mbox_size;
wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x",
wl->mbox_ptr[0], wl->mbox_ptr[1]);
ret = wlcore_boot_static_data(wl);
if (ret < 0) {
wl1271_error("error getting static data");
return ret;
}
/*
* in case of full asynchronous mode the firmware event must be
* ready to receive event from the command mailbox
*/
/* unmask required mbox events */
ret = wl1271_event_unmask(wl);
if (ret < 0) {
wl1271_error("EVENT mask setting failed");
return ret;
}
/* set the working partition to its "running" mode offset */
ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
/* firmware startup completed */
return ret;
}
EXPORT_SYMBOL_GPL(wlcore_boot_run_firmware);
@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __BOOT_H__
#define __BOOT_H__
#include "wlcore.h"
int wlcore_boot_upload_firmware(struct wl1271 *wl);
int wlcore_boot_upload_nvs(struct wl1271 *wl);
int wlcore_boot_run_firmware(struct wl1271 *wl);
#define WL1271_NO_SUBBANDS 8
#define WL1271_NO_POWER_LEVELS 4
#define WL1271_FW_VERSION_MAX_LEN 20
struct wl1271_static_data {
u8 mac_address[ETH_ALEN];
u8 padding[2];
u8 fw_version[WL1271_FW_VERSION_MAX_LEN];
u32 hw_version;
u8 tx_power_table[WL1271_NO_SUBBANDS][WL1271_NO_POWER_LEVELS];
u8 priv[];
};
/* number of times we try to read the INIT interrupt */
#define INIT_LOOP 20000
/* delay between retries */
#define INIT_LOOP_DELAY 50
#define WU_COUNTER_PAUSE_VAL 0x3FF
#define WELP_ARM_COMMAND_VAL 0x4
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,710 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __CMD_H__
#define __CMD_H__
#include "wlcore.h"
struct acx_header;
int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
size_t res_len);
int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
u8 *role_id);
int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id);
int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum nl80211_band band, int channel);
int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
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 cmd_len, size_t res_len);
int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
size_t len, unsigned long valid_rets);
int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 ps_mode, u16 auto_ps_timeout);
int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
size_t len);
int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id,
u16 template_id, void *buf, size_t buf_len,
int index, u32 rates);
int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u16 aid);
int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 role_id, u8 band,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len, const u8 *common_ie,
size_t common_ie_len, bool sched_scan);
struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct sk_buff *skb);
int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif);
int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl12xx_cmd_set_default_wep_key(struct wl1271 *wl, u8 id, u8 hlid);
int wl1271_cmd_set_sta_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, const u8 *addr,
u32 tx_seq_32, u16 tx_seq_16);
int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
u16 tx_seq_16, bool is_pairwise);
int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 hlid);
int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
enum nl80211_band band, u8 channel);
int wl12xx_croc(struct wl1271 *wl, u8 role_id);
int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta, u8 hlid);
int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 hlid);
void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
enum nl80211_band band);
int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl);
int wlcore_cmd_generic_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 feature, u8 enable, u8 value);
int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
int wl12xx_cmd_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch);
int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 *hlid);
void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid);
int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
u32 mask, bool *timeout);
u8 wlcore_get_native_channel_type(u8 nl_channel_type);
enum wl1271_commands {
CMD_INTERROGATE = 1, /* use this to read information elements */
CMD_CONFIGURE = 2, /* use this to write information elements */
CMD_ENABLE_RX = 3,
CMD_ENABLE_TX = 4,
CMD_DISABLE_RX = 5,
CMD_DISABLE_TX = 6,
CMD_SCAN = 7,
CMD_STOP_SCAN = 8,
CMD_SET_KEYS = 9,
CMD_READ_MEMORY = 10,
CMD_WRITE_MEMORY = 11,
CMD_SET_TEMPLATE = 12,
CMD_TEST = 13,
CMD_NOISE_HIST = 14,
CMD_QUIET_ELEMENT_SET_STATE = 15,
CMD_SET_BCN_MODE = 16,
CMD_MEASUREMENT = 17,
CMD_STOP_MEASUREMENT = 18,
CMD_SET_PS_MODE = 19,
CMD_CHANNEL_SWITCH = 20,
CMD_STOP_CHANNEL_SWICTH = 21,
CMD_AP_DISCOVERY = 22,
CMD_STOP_AP_DISCOVERY = 23,
CMD_HEALTH_CHECK = 24,
CMD_DEBUG = 25,
CMD_TRIGGER_SCAN_TO = 26,
CMD_CONNECTION_SCAN_CFG = 27,
CMD_CONNECTION_SCAN_SSID_CFG = 28,
CMD_START_PERIODIC_SCAN = 29,
CMD_STOP_PERIODIC_SCAN = 30,
CMD_SET_PEER_STATE = 31,
CMD_REMAIN_ON_CHANNEL = 32,
CMD_CANCEL_REMAIN_ON_CHANNEL = 33,
CMD_CONFIG_FWLOGGER = 34,
CMD_START_FWLOGGER = 35,
CMD_STOP_FWLOGGER = 36,
/* Access point commands */
CMD_ADD_PEER = 37,
CMD_REMOVE_PEER = 38,
/* Role API */
CMD_ROLE_ENABLE = 39,
CMD_ROLE_DISABLE = 40,
CMD_ROLE_START = 41,
CMD_ROLE_STOP = 42,
/* DFS */
CMD_START_RADAR_DETECTION = 43,
CMD_STOP_RADAR_DETECTION = 44,
/* WIFI Direct */
CMD_WFD_START_DISCOVERY = 45,
CMD_WFD_STOP_DISCOVERY = 46,
CMD_WFD_ATTRIBUTE_CONFIG = 47,
CMD_GENERIC_CFG = 48,
CMD_NOP = 49,
/* start of 18xx specific commands */
CMD_DFS_CHANNEL_CONFIG = 60,
CMD_SMART_CONFIG_START = 61,
CMD_SMART_CONFIG_STOP = 62,
CMD_SMART_CONFIG_SET_GROUP_KEY = 63,
CMD_CAC_START = 64,
CMD_CAC_STOP = 65,
CMD_DFS_MASTER_RESTART = 66,
CMD_DFS_RADAR_DETECTION_DEBUG = 67,
MAX_COMMAND_ID = 0xFFFF,
};
#define MAX_CMD_PARAMS 572
enum cmd_templ {
CMD_TEMPL_NULL_DATA = 0,
CMD_TEMPL_BEACON,
CMD_TEMPL_CFG_PROBE_REQ_2_4,
CMD_TEMPL_CFG_PROBE_REQ_5,
CMD_TEMPL_PROBE_RESPONSE,
CMD_TEMPL_QOS_NULL_DATA,
CMD_TEMPL_PS_POLL,
CMD_TEMPL_KLV,
CMD_TEMPL_DISCONNECT,
CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY,
CMD_TEMPL_APP_PROBE_REQ_5_LEGACY,
CMD_TEMPL_BAR, /* for firmware internal use only */
CMD_TEMPL_CTS, /*
* For CTS-to-self (FastCTS) mechanism
* for BT/WLAN coexistence (SoftGemini). */
CMD_TEMPL_AP_BEACON,
CMD_TEMPL_AP_PROBE_RESPONSE,
CMD_TEMPL_ARP_RSP,
CMD_TEMPL_DEAUTH_AP,
CMD_TEMPL_TEMPORARY,
CMD_TEMPL_LINK_MEASUREMENT_REPORT,
CMD_TEMPL_PROBE_REQ_2_4_PERIODIC,
CMD_TEMPL_PROBE_REQ_5_PERIODIC,
CMD_TEMPL_MAX = 0xff
};
/* unit ms */
#define WL1271_COMMAND_TIMEOUT 2000
#define WL1271_CMD_TEMPL_DFLT_SIZE 252
#define WL1271_CMD_TEMPL_MAX_SIZE 512
#define WL1271_EVENT_TIMEOUT 5000
struct wl1271_cmd_header {
__le16 id;
__le16 status;
} __packed;
#define WL1271_CMD_MAX_PARAMS 572
struct wl1271_command {
struct wl1271_cmd_header header;
u8 parameters[WL1271_CMD_MAX_PARAMS];
} __packed;
enum {
CMD_MAILBOX_IDLE = 0,
CMD_STATUS_SUCCESS = 1,
CMD_STATUS_UNKNOWN_CMD = 2,
CMD_STATUS_UNKNOWN_IE = 3,
CMD_STATUS_REJECT_MEAS_SG_ACTIVE = 11,
CMD_STATUS_RX_BUSY = 13,
CMD_STATUS_INVALID_PARAM = 14,
CMD_STATUS_TEMPLATE_TOO_LARGE = 15,
CMD_STATUS_OUT_OF_MEMORY = 16,
CMD_STATUS_STA_TABLE_FULL = 17,
CMD_STATUS_RADIO_ERROR = 18,
CMD_STATUS_WRONG_NESTING = 19,
CMD_STATUS_TIMEOUT = 21, /* Driver internal use.*/
CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/
CMD_STATUS_TEMPLATE_OOM = 23,
CMD_STATUS_NO_RX_BA_SESSION = 24,
MAX_COMMAND_STATUS
};
#define CMDMBOX_HEADER_LEN 4
#define CMDMBOX_INFO_ELEM_HEADER_LEN 4
enum {
BSS_TYPE_IBSS = 0,
BSS_TYPE_STA_BSS = 2,
BSS_TYPE_AP_BSS = 3,
MAX_BSS_TYPE = 0xFF
};
#define WL1271_JOIN_CMD_CTRL_TX_FLUSH 0x80 /* Firmware flushes all Tx */
#define WL1271_JOIN_CMD_TX_SESSION_OFFSET 1
#define WL1271_JOIN_CMD_BSS_TYPE_5GHZ 0x10
struct wl12xx_cmd_role_enable {
struct wl1271_cmd_header header;
u8 role_id;
u8 role_type;
u8 mac_address[ETH_ALEN];
} __packed;
struct wl12xx_cmd_role_disable {
struct wl1271_cmd_header header;
u8 role_id;
u8 padding[3];
} __packed;
enum wlcore_band {
WLCORE_BAND_2_4GHZ = 0,
WLCORE_BAND_5GHZ = 1,
WLCORE_BAND_JAPAN_4_9_GHZ = 2,
WLCORE_BAND_DEFAULT = WLCORE_BAND_2_4GHZ,
WLCORE_BAND_INVALID = 0x7E,
WLCORE_BAND_MAX_RADIO = 0x7F,
};
enum wlcore_channel_type {
WLCORE_CHAN_NO_HT,
WLCORE_CHAN_HT20,
WLCORE_CHAN_HT40MINUS,
WLCORE_CHAN_HT40PLUS
};
struct wl12xx_cmd_role_start {
struct wl1271_cmd_header header;
u8 role_id;
u8 band;
u8 channel;
/* enum wlcore_channel_type */
u8 channel_type;
union {
struct {
u8 hlid;
u8 session;
u8 padding_1[54];
} __packed device;
/* sta & p2p_cli use the same struct */
struct {
u8 bssid[ETH_ALEN];
u8 hlid; /* data hlid */
u8 session;
__le32 remote_rates; /* remote supported rates */
/*
* The target uses this field to determine the rate at
* which to transmit control frame responses (such as
* ACK or CTS frames).
*/
__le32 basic_rate_set;
__le32 local_rates; /* local supported rates */
u8 ssid_type;
u8 ssid_len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
__le16 beacon_interval; /* in TBTTs */
} __packed sta;
struct {
u8 bssid[ETH_ALEN];
u8 hlid; /* data hlid */
u8 dtim_interval;
__le32 remote_rates; /* remote supported rates */
__le32 basic_rate_set;
__le32 local_rates; /* local supported rates */
u8 ssid_type;
u8 ssid_len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
__le16 beacon_interval; /* in TBTTs */
u8 padding_1[4];
} __packed ibss;
/* ap & p2p_go use the same struct */
struct {
__le16 aging_period; /* in secs */
u8 beacon_expiry; /* in ms */
u8 bss_index;
/* The host link id for the AP's global queue */
u8 global_hlid;
/* The host link id for the AP's broadcast queue */
u8 broadcast_hlid;
__le16 beacon_interval; /* in TBTTs */
__le32 basic_rate_set;
__le32 local_rates; /* local supported rates */
u8 dtim_interval;
u8 ssid_type;
u8 ssid_len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 reset_tsf;
/*
* ap supports wmm (note that there is additional
* per-sta wmm configuration)
*/
u8 wmm;
u8 bcast_session_id;
u8 global_session_id;
u8 padding_1[1];
} __packed ap;
};
} __packed;
struct wl12xx_cmd_role_stop {
struct wl1271_cmd_header header;
u8 role_id;
u8 disc_type; /* only STA and P2P_CLI */
__le16 reason; /* only STA and P2P_CLI */
} __packed;
struct cmd_enabledisable_path {
struct wl1271_cmd_header header;
u8 channel;
u8 padding[3];
} __packed;
#define WL1271_RATE_AUTOMATIC 0
struct wl1271_cmd_template_set {
struct wl1271_cmd_header header;
u8 role_id;
u8 template_type;
__le16 len;
u8 index; /* relevant only for KLV_TEMPLATE type */
u8 padding[3];
__le32 enabled_rates;
u8 short_retry_limit;
u8 long_retry_limit;
u8 aflags;
u8 reserved;
u8 template_data[WL1271_CMD_TEMPL_MAX_SIZE];
} __packed;
#define TIM_ELE_ID 5
#define PARTIAL_VBM_MAX 251
struct wl1271_tim {
u8 identity;
u8 length;
u8 dtim_count;
u8 dtim_period;
u8 bitmap_ctrl;
u8 pvb_field[PARTIAL_VBM_MAX]; /* Partial Virtual Bitmap */
} __packed;
enum wl1271_cmd_ps_mode {
STATION_AUTO_PS_MODE, /* Dynamic Power Save */
STATION_ACTIVE_MODE,
STATION_POWER_SAVE_MODE
};
struct wl1271_cmd_ps_params {
struct wl1271_cmd_header header;
u8 role_id;
u8 ps_mode; /* STATION_* */
u16 auto_ps_timeout;
} __packed;
/* HW encryption keys */
#define NUM_ACCESS_CATEGORIES_COPY 4
enum wl1271_cmd_key_action {
KEY_ADD_OR_REPLACE = 1,
KEY_REMOVE = 2,
KEY_SET_ID = 3,
MAX_KEY_ACTION = 0xffff,
};
enum wl1271_cmd_lid_key_type {
UNICAST_LID_TYPE = 0,
BROADCAST_LID_TYPE = 1,
WEP_DEFAULT_LID_TYPE = 2
};
enum wl1271_cmd_key_type {
KEY_NONE = 0,
KEY_WEP = 1,
KEY_TKIP = 2,
KEY_AES = 3,
KEY_GEM = 4,
KEY_IGTK = 5,
};
struct wl1271_cmd_set_keys {
struct wl1271_cmd_header header;
/*
* Indicates whether the HLID is a unicast key set
* or broadcast key set. A special value 0xFF is
* used to indicate that the HLID is on WEP-default
* (multi-hlids). of type wl1271_cmd_lid_key_type.
*/
u8 hlid;
/*
* In WEP-default network (hlid == 0xFF) used to
* indicate which network STA/IBSS/AP role should be
* changed
*/
u8 lid_key_type;
/*
* Key ID - For TKIP and AES key types, this field
* indicates the value that should be inserted into
* the KeyID field of frames transmitted using this
* key entry. For broadcast keys the index use as a
* marker for TX/RX key.
* For WEP default network (HLID=0xFF), this field
* indicates the ID of the key to add or remove.
*/
u8 key_id;
u8 reserved_1;
/* key_action_e */
__le16 key_action;
/* key size in bytes */
u8 key_size;
/* key_type_e */
u8 key_type;
/* This field holds the security key data to add to the STA table */
u8 key[MAX_KEY_SIZE];
__le16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY];
__le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
} __packed;
struct wl1271_cmd_test_header {
u8 id;
u8 padding[3];
} __packed;
enum wl1271_channel_tune_bands {
WL1271_CHANNEL_TUNE_BAND_2_4,
WL1271_CHANNEL_TUNE_BAND_5,
WL1271_CHANNEL_TUNE_BAND_4_9
};
#define WL1271_PD_REFERENCE_POINT_BAND_B_G 0
/*
* There are three types of disconnections:
*
* DISCONNECT_IMMEDIATE: the fw doesn't send any frames
* DISCONNECT_DEAUTH: the fw generates a DEAUTH request with the reason
* we have passed
* DISCONNECT_DISASSOC: the fw generates a DESASSOC request with the reason
* we have passed
*/
enum wl1271_disconnect_type {
DISCONNECT_IMMEDIATE,
DISCONNECT_DEAUTH,
DISCONNECT_DISASSOC
};
#define WL1271_CMD_STA_STATE_CONNECTED 1
struct wl12xx_cmd_set_peer_state {
struct wl1271_cmd_header header;
u8 hlid;
u8 state;
/*
* wmm is relevant for sta role only.
* ap role configures the per-sta wmm params in
* the add_peer command.
*/
u8 wmm;
u8 padding[1];
} __packed;
struct wl12xx_cmd_roc {
struct wl1271_cmd_header header;
u8 role_id;
u8 channel;
u8 band;
u8 padding;
};
struct wl12xx_cmd_croc {
struct wl1271_cmd_header header;
u8 role_id;
u8 padding[3];
};
enum wl12xx_ssid_type {
WL12XX_SSID_TYPE_PUBLIC = 0,
WL12XX_SSID_TYPE_HIDDEN = 1,
WL12XX_SSID_TYPE_ANY = 2,
};
enum wl1271_psd_type {
WL1271_PSD_LEGACY = 0,
WL1271_PSD_UPSD_TRIGGER = 1,
WL1271_PSD_LEGACY_PSPOLL = 2,
WL1271_PSD_SAPSD = 3
};
struct wl12xx_cmd_add_peer {
struct wl1271_cmd_header header;
u8 addr[ETH_ALEN];
u8 hlid;
u8 aid;
u8 psd_type[NUM_ACCESS_CATEGORIES_COPY];
__le32 supported_rates;
u8 bss_index;
u8 sp_len;
u8 wmm;
u8 session_id;
u8 role_id;
u8 padding[3];
} __packed;
struct wl12xx_cmd_remove_peer {
struct wl1271_cmd_header header;
u8 hlid;
u8 reason_opcode;
u8 send_deauth_flag;
u8 role_id;
} __packed;
/*
* Continuous mode - packets are transferred to the host periodically
* via the data path.
* On demand - Log messages are stored in a cyclic buffer in the
* firmware, and only transferred to the host when explicitly requested
*/
enum wl12xx_fwlogger_log_mode {
WL12XX_FWLOG_CONTINUOUS,
};
/* Include/exclude timestamps from the log messages */
enum wl12xx_fwlogger_timestamp {
WL12XX_FWLOG_TIMESTAMP_DISABLED,
WL12XX_FWLOG_TIMESTAMP_ENABLED
};
/*
* Logs can be routed to the debug pinouts (where available), to the host bus
* (SDIO/SPI), or dropped
*/
enum wl12xx_fwlogger_output {
WL12XX_FWLOG_OUTPUT_NONE,
WL12XX_FWLOG_OUTPUT_DBG_PINS,
WL12XX_FWLOG_OUTPUT_HOST,
};
struct wl12xx_cmd_regdomain_dfs_config {
struct wl1271_cmd_header header;
__le32 ch_bit_map1;
__le32 ch_bit_map2;
u8 dfs_region;
u8 padding[3];
} __packed;
enum wlcore_generic_cfg_feature {
WLCORE_CFG_FEATURE_RADAR_DEBUG = 2,
};
struct wlcore_cmd_generic_cfg {
struct wl1271_cmd_header header;
u8 role_id;
u8 feature;
u8 enable;
u8 value;
} __packed;
struct wl12xx_cmd_config_fwlog {
struct wl1271_cmd_header header;
/* See enum wl12xx_fwlogger_log_mode */
u8 logger_mode;
/* Minimum log level threshold */
u8 log_severity;
/* Include/exclude timestamps from the log messages */
u8 timestamp;
/* See enum wl1271_fwlogger_output */
u8 output;
/* Regulates the frequency of log messages */
u8 threshold;
u8 padding[3];
} __packed;
struct wl12xx_cmd_start_fwlog {
struct wl1271_cmd_header header;
} __packed;
struct wl12xx_cmd_stop_fwlog {
struct wl1271_cmd_header header;
} __packed;
struct wl12xx_cmd_stop_channel_switch {
struct wl1271_cmd_header header;
u8 role_id;
u8 padding[3];
} __packed;
/* Used to check radio status after calibration */
#define MAX_TLV_LENGTH 500
#define TEST_CMD_P2G_CAL 2 /* TX BiP */
struct wl1271_cmd_cal_p2g {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
__le32 ver;
__le16 len;
u8 buf[MAX_TLV_LENGTH];
u8 type;
u8 padding;
__le16 radio_status;
u8 sub_band_mask;
u8 padding2;
} __packed;
#endif /* __WL1271_CMD_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,98 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl12xx
*
* Copyright (C) 2011 Texas Instruments. All rights reserved.
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <coelho@ti.com>
*/
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include <linux/bitops.h>
#include <linux/printk.h>
#define DRIVER_NAME "wlcore"
#define DRIVER_PREFIX DRIVER_NAME ": "
enum {
DEBUG_NONE = 0,
DEBUG_IRQ = BIT(0),
DEBUG_SPI = BIT(1),
DEBUG_BOOT = BIT(2),
DEBUG_MAILBOX = BIT(3),
DEBUG_TESTMODE = BIT(4),
DEBUG_EVENT = BIT(5),
DEBUG_TX = BIT(6),
DEBUG_RX = BIT(7),
DEBUG_SCAN = BIT(8),
DEBUG_CRYPT = BIT(9),
DEBUG_PSM = BIT(10),
DEBUG_MAC80211 = BIT(11),
DEBUG_CMD = BIT(12),
DEBUG_ACX = BIT(13),
DEBUG_SDIO = BIT(14),
DEBUG_FILTERS = BIT(15),
DEBUG_ADHOC = BIT(16),
DEBUG_AP = BIT(17),
DEBUG_PROBE = BIT(18),
DEBUG_IO = BIT(19),
DEBUG_MASTER = (DEBUG_ADHOC | DEBUG_AP),
DEBUG_ALL = ~0,
};
extern u32 wl12xx_debug_level;
#define DEBUG_DUMP_LIMIT 1024
#define wl1271_error(fmt, arg...) \
pr_err(DRIVER_PREFIX "ERROR " fmt "\n", ##arg)
#define wl1271_warning(fmt, arg...) \
pr_warn(DRIVER_PREFIX "WARNING " fmt "\n", ##arg)
#define wl1271_notice(fmt, arg...) \
pr_info(DRIVER_PREFIX fmt "\n", ##arg)
#define wl1271_info(fmt, arg...) \
pr_info(DRIVER_PREFIX fmt "\n", ##arg)
/* define the debug macro differently if dynamic debug is supported */
#if defined(CONFIG_DYNAMIC_DEBUG)
#define wl1271_debug(level, fmt, arg...) \
do { \
if (unlikely(level & wl12xx_debug_level)) \
dynamic_pr_debug(DRIVER_PREFIX fmt "\n", ##arg); \
} while (0)
#else
#define wl1271_debug(level, fmt, arg...) \
do { \
if (unlikely(level & wl12xx_debug_level)) \
printk(KERN_DEBUG pr_fmt(DRIVER_PREFIX fmt "\n"), \
##arg); \
} while (0)
#endif
#define wl1271_dump(level, prefix, buf, len) \
do { \
if (level & wl12xx_debug_level) \
print_hex_dump_debug(DRIVER_PREFIX prefix, \
DUMP_PREFIX_OFFSET, 16, 1, \
buf, \
min_t(size_t, len, DEBUG_DUMP_LIMIT), \
0); \
} while (0)
#define wl1271_dump_ascii(level, prefix, buf, len) \
do { \
if (level & wl12xx_debug_level) \
print_hex_dump_debug(DRIVER_PREFIX prefix, \
DUMP_PREFIX_OFFSET, 16, 1, \
buf, \
min_t(size_t, len, DEBUG_DUMP_LIMIT), \
true); \
} while (0)
#endif /* __DEBUG_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __DEBUGFS_H__
#define __DEBUGFS_H__
#include "wlcore.h"
__printf(4, 5) int wl1271_format_buffer(char __user *userbuf, size_t count,
loff_t *ppos, char *fmt, ...);
int wl1271_debugfs_init(struct wl1271 *wl);
void wl1271_debugfs_exit(struct wl1271 *wl);
void wl1271_debugfs_reset(struct wl1271 *wl);
void wl1271_debugfs_update_stats(struct wl1271 *wl);
#define DEBUGFS_FORMAT_BUFFER_SIZE 256
#define DEBUGFS_READONLY_FILE(name, fmt, value...) \
static ssize_t name## _read(struct file *file, char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wl1271 *wl = file->private_data; \
return wl1271_format_buffer(userbuf, count, ppos, \
fmt "\n", ##value); \
} \
\
static const struct file_operations name## _ops = { \
.read = name## _read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_ADD(name, parent) \
do { \
debugfs_create_file(#name, 0400, parent, \
wl, &name## _ops); \
} while (0)
#define DEBUGFS_ADD_PREFIX(prefix, name, parent) \
do { \
debugfs_create_file(#name, 0400, parent, \
wl, &prefix## _## name## _ops); \
} while (0)
#define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type) \
static ssize_t sub## _ ##name## _read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wl1271 *wl = file->private_data; \
struct struct_type *stats = wl->stats.fw_stats; \
\
wl1271_debugfs_update_stats(wl); \
\
return wl1271_format_buffer(userbuf, count, ppos, fmt "\n", \
stats->sub.name); \
} \
\
static const struct file_operations sub## _ ##name## _ops = { \
.read = sub## _ ##name## _read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_FWSTATS_FILE_ARRAY(sub, name, len, struct_type) \
static ssize_t sub## _ ##name## _read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wl1271 *wl = file->private_data; \
struct struct_type *stats = wl->stats.fw_stats; \
char buf[DEBUGFS_FORMAT_BUFFER_SIZE] = ""; \
int pos = 0; \
int i; \
\
wl1271_debugfs_update_stats(wl); \
\
for (i = 0; i < len && pos < sizeof(buf); i++) \
pos += snprintf(buf + pos, sizeof(buf) - pos, \
"[%d] = %d\n", i, stats->sub.name[i]); \
\
return wl1271_format_buffer(userbuf, count, ppos, "%s", buf); \
} \
\
static const struct file_operations sub## _ ##name## _ops = { \
.read = sub## _ ##name## _read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_FWSTATS_ADD(sub, name) \
DEBUGFS_ADD(sub## _ ##name, stats)
#endif /* WL1271_DEBUGFS_H */
@@ -0,0 +1,389 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#include "wlcore.h"
#include "debug.h"
#include "io.h"
#include "event.h"
#include "ps.h"
#include "scan.h"
#include "wl12xx_80211.h"
#include "hw_ops.h"
#define WL18XX_LOGGER_SDIO_BUFF_MAX (0x1020)
#define WL18XX_DATA_RAM_BASE_ADDRESS (0x20000000)
#define WL18XX_LOGGER_SDIO_BUFF_ADDR (0x40159c)
#define WL18XX_LOGGER_BUFF_OFFSET (sizeof(struct fw_logger_information))
#define WL18XX_LOGGER_READ_POINT_OFFSET (12)
int wlcore_event_fw_logger(struct wl1271 *wl)
{
int ret;
struct fw_logger_information fw_log;
u8 *buffer;
u32 internal_fw_addrbase = WL18XX_DATA_RAM_BASE_ADDRESS;
u32 addr = WL18XX_LOGGER_SDIO_BUFF_ADDR;
u32 addr_ptr;
u32 buff_start_ptr;
u32 buff_read_ptr;
u32 buff_end_ptr;
u32 available_len;
u32 actual_len;
u32 clear_ptr;
size_t len;
u32 start_loc;
buffer = kzalloc(WL18XX_LOGGER_SDIO_BUFF_MAX, GFP_KERNEL);
if (!buffer) {
wl1271_error("Fail to allocate fw logger memory");
actual_len = 0;
goto out;
}
ret = wlcore_read(wl, addr, buffer, WL18XX_LOGGER_SDIO_BUFF_MAX,
false);
if (ret < 0) {
wl1271_error("Fail to read logger buffer, error_id = %d",
ret);
actual_len = 0;
goto free_out;
}
memcpy(&fw_log, buffer, sizeof(fw_log));
actual_len = le32_to_cpu(fw_log.actual_buff_size);
if (actual_len == 0)
goto free_out;
/* Calculate the internal pointer to the fwlog structure */
addr_ptr = internal_fw_addrbase + addr;
/* Calculate the internal pointers to the start and end of log buffer */
buff_start_ptr = addr_ptr + WL18XX_LOGGER_BUFF_OFFSET;
buff_end_ptr = buff_start_ptr + le32_to_cpu(fw_log.max_buff_size);
/* Read the read pointer and validate it */
buff_read_ptr = le32_to_cpu(fw_log.buff_read_ptr);
if (buff_read_ptr < buff_start_ptr ||
buff_read_ptr >= buff_end_ptr) {
wl1271_error("buffer read pointer out of bounds: %x not in (%x-%x)\n",
buff_read_ptr, buff_start_ptr, buff_end_ptr);
goto free_out;
}
start_loc = buff_read_ptr - addr_ptr;
available_len = buff_end_ptr - buff_read_ptr;
/* Copy initial part up to the end of ring buffer */
len = min(actual_len, available_len);
wl12xx_copy_fwlog(wl, &buffer[start_loc], len);
clear_ptr = addr_ptr + start_loc + len;
if (clear_ptr == buff_end_ptr)
clear_ptr = buff_start_ptr;
/* Copy any remaining part from beginning of ring buffer */
len = actual_len - len;
if (len) {
wl12xx_copy_fwlog(wl,
&buffer[WL18XX_LOGGER_BUFF_OFFSET],
len);
clear_ptr = addr_ptr + WL18XX_LOGGER_BUFF_OFFSET + len;
}
/* Update the read pointer */
ret = wlcore_write32(wl, addr + WL18XX_LOGGER_READ_POINT_OFFSET,
clear_ptr);
free_out:
kfree(buffer);
out:
return actual_len;
}
EXPORT_SYMBOL_GPL(wlcore_event_fw_logger);
void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr)
{
struct wl12xx_vif *wlvif;
struct ieee80211_vif *vif;
enum nl80211_cqm_rssi_threshold_event event;
s8 metric = metric_arr[0];
wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
/* TODO: check actual multi-role support */
wl12xx_for_each_wlvif_sta(wl, wlvif) {
if (metric <= wlvif->rssi_thold)
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
else
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
vif = wl12xx_wlvif_to_vif(wlvif);
if (event != wlvif->last_rssi_event)
ieee80211_cqm_rssi_notify(vif, event, metric,
GFP_KERNEL);
wlvif->last_rssi_event = event;
}
}
EXPORT_SYMBOL_GPL(wlcore_event_rssi_trigger);
static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
u8 hlid = wlvif->sta.hlid;
if (!wl->links[hlid].ba_bitmap)
return;
ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap,
vif->bss_conf.bssid);
} else {
u8 hlid;
struct wl1271_link *lnk;
for_each_set_bit(hlid, wlvif->ap.sta_hlid_map,
wl->num_links) {
lnk = &wl->links[hlid];
if (!lnk->ba_bitmap)
continue;
ieee80211_stop_rx_ba_session(vif,
lnk->ba_bitmap,
lnk->addr);
}
}
}
void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable)
{
struct wl12xx_vif *wlvif;
if (enable) {
set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
} else {
clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
wl12xx_for_each_wlvif_sta(wl, wlvif) {
wl1271_recalc_rx_streaming(wl, wlvif);
}
}
}
EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense);
void wlcore_event_sched_scan_completed(struct wl1271 *wl,
u8 status)
{
wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)",
status);
if (wl->sched_vif) {
ieee80211_sched_scan_stopped(wl->hw);
wl->sched_vif = NULL;
}
}
EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed);
void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
unsigned long roles_bitmap,
unsigned long allowed_bitmap)
{
struct wl12xx_vif *wlvif;
wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx",
__func__, roles_bitmap, allowed_bitmap);
wl12xx_for_each_wlvif(wl, wlvif) {
if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
!test_bit(wlvif->role_id , &roles_bitmap))
continue;
wlvif->ba_allowed = !!test_bit(wlvif->role_id,
&allowed_bitmap);
if (!wlvif->ba_allowed)
wl1271_stop_ba_event(wl, wlvif);
}
}
EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint);
void wlcore_event_channel_switch(struct wl1271 *wl,
unsigned long roles_bitmap,
bool success)
{
struct wl12xx_vif *wlvif;
struct ieee80211_vif *vif;
wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d",
__func__, roles_bitmap, success);
wl12xx_for_each_wlvif(wl, wlvif) {
if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
!test_bit(wlvif->role_id , &roles_bitmap))
continue;
if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
&wlvif->flags))
continue;
vif = wl12xx_wlvif_to_vif(wlvif);
if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
ieee80211_chswitch_done(vif, success, 0);
cancel_delayed_work(&wlvif->channel_switch_work);
} else {
set_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags);
ieee80211_csa_finish(vif, 0);
}
}
}
EXPORT_SYMBOL_GPL(wlcore_event_channel_switch);
void wlcore_event_dummy_packet(struct wl1271 *wl)
{
if (wl->plt) {
wl1271_info("Got DUMMY_PACKET event in PLT mode. FW bug, ignoring.");
return;
}
wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
wl1271_tx_dummy_packet(wl);
}
EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet);
static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap)
{
u32 num_packets = wl->conf.tx.max_tx_retries;
struct wl12xx_vif *wlvif;
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
const u8 *addr;
int h;
for_each_set_bit(h, &sta_bitmap, wl->num_links) {
bool found = false;
/* find the ap vif connected to this sta */
wl12xx_for_each_wlvif_ap(wl, wlvif) {
if (!test_bit(h, wlvif->ap.sta_hlid_map))
continue;
found = true;
break;
}
if (!found)
continue;
vif = wl12xx_wlvif_to_vif(wlvif);
addr = wl->links[h].addr;
rcu_read_lock();
sta = ieee80211_find_sta(vif, addr);
if (sta) {
wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
ieee80211_report_low_ack(sta, num_packets);
}
rcu_read_unlock();
}
}
void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap)
{
wl1271_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID");
wlcore_disconnect_sta(wl, sta_bitmap);
}
EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure);
void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap)
{
wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
wlcore_disconnect_sta(wl, sta_bitmap);
}
EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta);
void wlcore_event_roc_complete(struct wl1271 *wl)
{
wl1271_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID");
if (wl->roc_vif)
ieee80211_ready_on_channel(wl->hw);
}
EXPORT_SYMBOL_GPL(wlcore_event_roc_complete);
void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap)
{
/*
* We are HW_MONITOR device. On beacon loss - queue
* connection loss work. Cancel it on REGAINED event.
*/
struct wl12xx_vif *wlvif;
struct ieee80211_vif *vif;
int delay = wl->conf.conn.synch_fail_thold *
wl->conf.conn.bss_lose_timeout;
wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap);
wl12xx_for_each_wlvif_sta(wl, wlvif) {
if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
!test_bit(wlvif->role_id , &roles_bitmap))
continue;
vif = wl12xx_wlvif_to_vif(wlvif);
/* don't attempt roaming in case of p2p */
if (wlvif->p2p) {
ieee80211_connection_loss(vif);
continue;
}
/*
* if the work is already queued, it should take place.
* We don't want to delay the connection loss
* indication any more.
*/
ieee80211_queue_delayed_work(wl->hw,
&wlvif->connection_loss_work,
msecs_to_jiffies(delay));
ieee80211_cqm_beacon_loss_notify(vif, GFP_KERNEL);
}
}
EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss);
int wl1271_event_unmask(struct wl1271 *wl)
{
int ret;
wl1271_debug(DEBUG_EVENT, "unmasking event_mask 0x%x", wl->event_mask);
ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask));
if (ret < 0)
return ret;
return 0;
}
int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
{
int ret;
wl1271_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num);
if (mbox_num > 1)
return -EINVAL;
/* first we read the mbox descriptor */
ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox,
wl->mbox_size, false);
if (ret < 0)
return ret;
/* process the descriptor */
ret = wl->ops->process_mailbox_events(wl);
if (ret < 0)
return ret;
/*
* TODO: we just need this because one bit is in a different
* place. Is there any better way?
*/
ret = wl->ops->ack_event(wl);
return ret;
}
@@ -0,0 +1,82 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __EVENT_H__
#define __EVENT_H__
/*
* Mbox events
*
* The event mechanism is based on a pair of event buffers (buffers A and
* B) at fixed locations in the target's memory. The host processes one
* buffer while the other buffer continues to collect events. If the host
* is not processing events, an interrupt is issued to signal that a buffer
* is ready. Once the host is done with processing events from one buffer,
* it signals the target (with an ACK interrupt) that the event buffer is
* free.
*/
enum {
RSSI_SNR_TRIGGER_0_EVENT_ID = BIT(0),
RSSI_SNR_TRIGGER_1_EVENT_ID = BIT(1),
RSSI_SNR_TRIGGER_2_EVENT_ID = BIT(2),
RSSI_SNR_TRIGGER_3_EVENT_ID = BIT(3),
RSSI_SNR_TRIGGER_4_EVENT_ID = BIT(4),
RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5),
RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6),
RSSI_SNR_TRIGGER_7_EVENT_ID = BIT(7),
EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff,
};
/* events the driver might want to wait for */
enum wlcore_wait_event {
WLCORE_EVENT_ROLE_STOP_COMPLETE,
WLCORE_EVENT_PEER_REMOVE_COMPLETE,
WLCORE_EVENT_DFS_CONFIG_COMPLETE
};
enum {
EVENT_ENTER_POWER_SAVE_FAIL = 0,
EVENT_ENTER_POWER_SAVE_SUCCESS,
};
#define NUM_OF_RSSI_SNR_TRIGGERS 8
struct fw_logger_information {
__le32 max_buff_size;
__le32 actual_buff_size;
__le32 num_trace_drop;
__le32 buff_read_ptr;
__le32 buff_write_ptr;
} __packed;
struct wl1271;
int wl1271_event_unmask(struct wl1271 *wl);
int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable);
void wlcore_event_sched_scan_completed(struct wl1271 *wl,
u8 status);
void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
unsigned long roles_bitmap,
unsigned long allowed_bitmap);
void wlcore_event_channel_switch(struct wl1271 *wl,
unsigned long roles_bitmap,
bool success);
void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap);
void wlcore_event_dummy_packet(struct wl1271 *wl);
void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap);
void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap);
void wlcore_event_roc_complete(struct wl1271 *wl);
void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr);
int wlcore_event_fw_logger(struct wl1271 *wl);
#endif
@@ -0,0 +1,318 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wlcore
*
* Copyright (C) 2011 Texas Instruments Inc.
*/
#ifndef __WLCORE_HW_OPS_H__
#define __WLCORE_HW_OPS_H__
#include "wlcore.h"
#include "rx.h"
static inline u32
wlcore_hw_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks)
{
if (!wl->ops->calc_tx_blocks)
BUG_ON(1);
return wl->ops->calc_tx_blocks(wl, len, spare_blks);
}
static inline void
wlcore_hw_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
u32 blks, u32 spare_blks)
{
if (!wl->ops->set_tx_desc_blocks)
BUG_ON(1);
return wl->ops->set_tx_desc_blocks(wl, desc, blks, spare_blks);
}
static inline void
wlcore_hw_set_tx_desc_data_len(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb)
{
if (!wl->ops->set_tx_desc_data_len)
BUG_ON(1);
wl->ops->set_tx_desc_data_len(wl, desc, skb);
}
static inline enum wl_rx_buf_align
wlcore_hw_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc)
{
if (!wl->ops->get_rx_buf_align)
BUG_ON(1);
return wl->ops->get_rx_buf_align(wl, rx_desc);
}
static inline int
wlcore_hw_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
{
if (wl->ops->prepare_read)
return wl->ops->prepare_read(wl, rx_desc, len);
return 0;
}
static inline u32
wlcore_hw_get_rx_packet_len(struct wl1271 *wl, void *rx_data, u32 data_len)
{
if (!wl->ops->get_rx_packet_len)
BUG_ON(1);
return wl->ops->get_rx_packet_len(wl, rx_data, data_len);
}
static inline int wlcore_hw_tx_delayed_compl(struct wl1271 *wl)
{
if (wl->ops->tx_delayed_compl)
return wl->ops->tx_delayed_compl(wl);
return 0;
}
static inline void wlcore_hw_tx_immediate_compl(struct wl1271 *wl)
{
if (wl->ops->tx_immediate_compl)
wl->ops->tx_immediate_compl(wl);
}
static inline int
wlcore_hw_init_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
if (wl->ops->init_vif)
return wl->ops->init_vif(wl, wlvif);
return 0;
}
static inline void
wlcore_hw_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
struct wl_fw_status *fw_status)
{
BUG_ON(!wl->ops->convert_fw_status);
wl->ops->convert_fw_status(wl, raw_fw_status, fw_status);
}
static inline u32
wlcore_hw_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
if (!wl->ops->sta_get_ap_rate_mask)
BUG_ON(1);
return wl->ops->sta_get_ap_rate_mask(wl, wlvif);
}
static inline int wlcore_identify_fw(struct wl1271 *wl)
{
if (wl->ops->identify_fw)
return wl->ops->identify_fw(wl);
return 0;
}
static inline void
wlcore_hw_set_tx_desc_csum(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb)
{
if (!wl->ops->set_tx_desc_csum)
BUG_ON(1);
wl->ops->set_tx_desc_csum(wl, desc, skb);
}
static inline void
wlcore_hw_set_rx_csum(struct wl1271 *wl,
struct wl1271_rx_descriptor *desc,
struct sk_buff *skb)
{
if (wl->ops->set_rx_csum)
wl->ops->set_rx_csum(wl, desc, skb);
}
static inline u32
wlcore_hw_ap_get_mimo_wide_rate_mask(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
if (wl->ops->ap_get_mimo_wide_rate_mask)
return wl->ops->ap_get_mimo_wide_rate_mask(wl, wlvif);
return 0;
}
static inline int
wlcore_debugfs_init(struct wl1271 *wl, struct dentry *rootdir)
{
if (wl->ops->debugfs_init)
return wl->ops->debugfs_init(wl, rootdir);
return 0;
}
static inline int
wlcore_handle_static_data(struct wl1271 *wl, void *static_data)
{
if (wl->ops->handle_static_data)
return wl->ops->handle_static_data(wl, static_data);
return 0;
}
static inline int
wlcore_hw_get_spare_blocks(struct wl1271 *wl, bool is_gem)
{
if (!wl->ops->get_spare_blocks)
BUG_ON(1);
return wl->ops->get_spare_blocks(wl, is_gem);
}
static inline int
wlcore_hw_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf)
{
if (!wl->ops->set_key)
BUG_ON(1);
return wl->ops->set_key(wl, cmd, vif, sta, key_conf);
}
static inline u32
wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len)
{
if (wl->ops->pre_pkt_send)
return wl->ops->pre_pkt_send(wl, buf_offset, last_len);
return buf_offset;
}
static inline void
wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
if (wl->ops->sta_rc_update)
wl->ops->sta_rc_update(wl, wlvif);
}
static inline int
wlcore_hw_interrupt_notify(struct wl1271 *wl, bool action)
{
if (wl->ops->interrupt_notify)
return wl->ops->interrupt_notify(wl, action);
return 0;
}
static inline int
wlcore_hw_rx_ba_filter(struct wl1271 *wl, bool action)
{
if (wl->ops->rx_ba_filter)
return wl->ops->rx_ba_filter(wl, action);
return 0;
}
static inline int
wlcore_hw_ap_sleep(struct wl1271 *wl)
{
if (wl->ops->ap_sleep)
return wl->ops->ap_sleep(wl);
return 0;
}
static inline int
wlcore_hw_set_peer_cap(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap,
bool allow_ht_operation,
u32 rate_set, u8 hlid)
{
if (wl->ops->set_peer_cap)
return wl->ops->set_peer_cap(wl, ht_cap, allow_ht_operation,
rate_set, hlid);
return 0;
}
static inline u32
wlcore_hw_convert_hwaddr(struct wl1271 *wl, u32 hwaddr)
{
if (!wl->ops->convert_hwaddr)
BUG_ON(1);
return wl->ops->convert_hwaddr(wl, hwaddr);
}
static inline bool
wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk)
{
if (!wl->ops->lnk_high_prio)
BUG_ON(1);
return wl->ops->lnk_high_prio(wl, hlid, lnk);
}
static inline bool
wlcore_hw_lnk_low_prio(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk)
{
if (!wl->ops->lnk_low_prio)
BUG_ON(1);
return wl->ops->lnk_low_prio(wl, hlid, lnk);
}
static inline int
wlcore_smart_config_start(struct wl1271 *wl, u32 group_bitmap)
{
if (!wl->ops->smart_config_start)
return -EINVAL;
return wl->ops->smart_config_start(wl, group_bitmap);
}
static inline int
wlcore_smart_config_stop(struct wl1271 *wl)
{
if (!wl->ops->smart_config_stop)
return -EINVAL;
return wl->ops->smart_config_stop(wl);
}
static inline int
wlcore_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
u8 key_len, u8 *key)
{
if (!wl->ops->smart_config_set_group_key)
return -EINVAL;
return wl->ops->smart_config_set_group_key(wl, group_id, key_len, key);
}
static inline int
wlcore_hw_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start)
{
if (!wl->ops->set_cac)
return -EINVAL;
return wl->ops->set_cac(wl, wlvif, start);
}
static inline int
wlcore_hw_dfs_master_restart(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
if (!wl->ops->dfs_master_restart)
return -EINVAL;
return wl->ops->dfs_master_restart(wl, wlvif);
}
#endif
@@ -0,0 +1,218 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __INI_H__
#define __INI_H__
#define GENERAL_SETTINGS_DRPW_LPD 0xc0
#define SCRATCH_ENABLE_LPD BIT(25)
#define WL1271_INI_MAX_SMART_REFLEX_PARAM 16
struct wl1271_ini_general_params {
u8 ref_clock;
u8 settling_time;
u8 clk_valid_on_wakeup;
u8 dc2dc_mode;
u8 dual_mode_select;
u8 tx_bip_fem_auto_detect;
u8 tx_bip_fem_manufacturer;
u8 general_settings;
u8 sr_state;
u8 srf1[WL1271_INI_MAX_SMART_REFLEX_PARAM];
u8 srf2[WL1271_INI_MAX_SMART_REFLEX_PARAM];
u8 srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM];
} __packed;
#define WL128X_INI_MAX_SETTINGS_PARAM 4
struct wl128x_ini_general_params {
u8 ref_clock;
u8 settling_time;
u8 clk_valid_on_wakeup;
u8 tcxo_ref_clock;
u8 tcxo_settling_time;
u8 tcxo_valid_on_wakeup;
u8 tcxo_ldo_voltage;
u8 xtal_itrim_val;
u8 platform_conf;
u8 dual_mode_select;
u8 tx_bip_fem_auto_detect;
u8 tx_bip_fem_manufacturer;
u8 general_settings[WL128X_INI_MAX_SETTINGS_PARAM];
u8 sr_state;
u8 srf1[WL1271_INI_MAX_SMART_REFLEX_PARAM];
u8 srf2[WL1271_INI_MAX_SMART_REFLEX_PARAM];
u8 srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM];
} __packed;
#define WL1271_INI_RSSI_PROCESS_COMPENS_SIZE 15
struct wl1271_ini_band_params_2 {
u8 rx_trace_insertion_loss;
u8 tx_trace_loss;
u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
} __packed;
#define WL1271_INI_CHANNEL_COUNT_2 14
struct wl128x_ini_band_params_2 {
u8 rx_trace_insertion_loss;
u8 tx_trace_loss[WL1271_INI_CHANNEL_COUNT_2];
u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
} __packed;
#define WL1271_INI_RATE_GROUP_COUNT 6
struct wl1271_ini_fem_params_2 {
__le16 tx_bip_ref_pd_voltage;
u8 tx_bip_ref_power;
u8 tx_bip_ref_offset;
u8 tx_per_rate_pwr_limits_normal[WL1271_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_degraded[WL1271_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_extreme[WL1271_INI_RATE_GROUP_COUNT];
u8 tx_per_chan_pwr_limits_11b[WL1271_INI_CHANNEL_COUNT_2];
u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_2];
u8 tx_pd_vs_rate_offsets[WL1271_INI_RATE_GROUP_COUNT];
u8 tx_ibias[WL1271_INI_RATE_GROUP_COUNT];
u8 rx_fem_insertion_loss;
u8 degraded_low_to_normal_thr;
u8 normal_to_degraded_high_thr;
} __packed;
#define WL128X_INI_RATE_GROUP_COUNT 7
/* low and high temperatures */
#define WL128X_INI_PD_VS_TEMPERATURE_RANGES 2
struct wl128x_ini_fem_params_2 {
__le16 tx_bip_ref_pd_voltage;
u8 tx_bip_ref_power;
u8 tx_bip_ref_offset;
u8 tx_per_rate_pwr_limits_normal[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_degraded[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_extreme[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_chan_pwr_limits_11b[WL1271_INI_CHANNEL_COUNT_2];
u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_2];
u8 tx_pd_vs_rate_offsets[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_ibias[WL128X_INI_RATE_GROUP_COUNT + 1];
u8 tx_pd_vs_chan_offsets[WL1271_INI_CHANNEL_COUNT_2];
u8 tx_pd_vs_temperature[WL128X_INI_PD_VS_TEMPERATURE_RANGES];
u8 rx_fem_insertion_loss;
u8 degraded_low_to_normal_thr;
u8 normal_to_degraded_high_thr;
} __packed;
#define WL1271_INI_CHANNEL_COUNT_5 35
#define WL1271_INI_SUB_BAND_COUNT_5 7
struct wl1271_ini_band_params_5 {
u8 rx_trace_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_trace_loss[WL1271_INI_SUB_BAND_COUNT_5];
u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
} __packed;
struct wl128x_ini_band_params_5 {
u8 rx_trace_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_trace_loss[WL1271_INI_CHANNEL_COUNT_5];
u8 rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE];
} __packed;
struct wl1271_ini_fem_params_5 {
__le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_bip_ref_offset[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_per_rate_pwr_limits_normal[WL1271_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_degraded[WL1271_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_extreme[WL1271_INI_RATE_GROUP_COUNT];
u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_5];
u8 tx_pd_vs_rate_offsets[WL1271_INI_RATE_GROUP_COUNT];
u8 tx_ibias[WL1271_INI_RATE_GROUP_COUNT];
u8 rx_fem_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5];
u8 degraded_low_to_normal_thr;
u8 normal_to_degraded_high_thr;
} __packed;
struct wl128x_ini_fem_params_5 {
__le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_bip_ref_offset[WL1271_INI_SUB_BAND_COUNT_5];
u8 tx_per_rate_pwr_limits_normal[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_degraded[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_rate_pwr_limits_extreme[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_5];
u8 tx_pd_vs_rate_offsets[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_ibias[WL128X_INI_RATE_GROUP_COUNT];
u8 tx_pd_vs_chan_offsets[WL1271_INI_CHANNEL_COUNT_5];
u8 tx_pd_vs_temperature[WL1271_INI_SUB_BAND_COUNT_5 *
WL128X_INI_PD_VS_TEMPERATURE_RANGES];
u8 rx_fem_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5];
u8 degraded_low_to_normal_thr;
u8 normal_to_degraded_high_thr;
} __packed;
/* NVS data structure */
#define WL1271_INI_NVS_SECTION_SIZE 468
/* We have four FEM module types: 0-RFMD, 1-TQS, 2-SKW, 3-TQS_HP */
#define WL1271_INI_FEM_MODULE_COUNT 4
/*
* In NVS we only store two FEM module entries -
* FEM modules 0,2,3 are stored in entry 0
* FEM module 1 is stored in entry 1
*/
#define WL12XX_NVS_FEM_MODULE_COUNT 2
#define WL12XX_FEM_TO_NVS_ENTRY(ini_fem_module) \
((ini_fem_module) == 1 ? 1 : 0)
#define WL1271_INI_LEGACY_NVS_FILE_SIZE 800
struct wl1271_nvs_file {
/* NVS section - must be first! */
u8 nvs[WL1271_INI_NVS_SECTION_SIZE];
/* INI section */
struct wl1271_ini_general_params general_params;
u8 padding1;
struct wl1271_ini_band_params_2 stat_radio_params_2;
u8 padding2;
struct {
struct wl1271_ini_fem_params_2 params;
u8 padding;
} dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT];
struct wl1271_ini_band_params_5 stat_radio_params_5;
u8 padding3;
struct {
struct wl1271_ini_fem_params_5 params;
u8 padding;
} dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT];
} __packed;
struct wl128x_nvs_file {
/* NVS section - must be first! */
u8 nvs[WL1271_INI_NVS_SECTION_SIZE];
/* INI section */
struct wl128x_ini_general_params general_params;
u8 fem_vendor_and_options;
struct wl128x_ini_band_params_2 stat_radio_params_2;
u8 padding2;
struct {
struct wl128x_ini_fem_params_2 params;
u8 padding;
} dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT];
struct wl128x_ini_band_params_5 stat_radio_params_5;
u8 padding3;
struct {
struct wl128x_ini_fem_params_5 params;
u8 padding;
} dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT];
} __packed;
#endif
@@ -0,0 +1,751 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "debug.h"
#include "init.h"
#include "wl12xx_80211.h"
#include "acx.h"
#include "cmd.h"
#include "tx.h"
#include "io.h"
#include "hw_ops.h"
int wl1271_init_templates_config(struct wl1271 *wl)
{
int ret, i;
size_t max_size;
/* send empty templates for fw memory reservation */
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
wl->scan_templ_id_2_4, NULL,
WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
wl->scan_templ_id_5,
NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0,
WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) {
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
wl->sched_scan_templ_id_2_4,
NULL,
WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
wl->sched_scan_templ_id_5,
NULL,
WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
}
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_NULL_DATA, NULL,
sizeof(struct wl12xx_null_data_template),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_PS_POLL, NULL,
sizeof(struct wl12xx_ps_poll_template),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_QOS_NULL_DATA, NULL,
sizeof
(struct ieee80211_qos_hdr),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_PROBE_RESPONSE, NULL,
WL1271_CMD_TEMPL_DFLT_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_BEACON, NULL,
WL1271_CMD_TEMPL_DFLT_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
max_size = sizeof(struct wl12xx_arp_rsp_template) +
WL1271_EXTRA_SPACE_MAX;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_ARP_RSP, NULL,
max_size,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
/*
* Put very large empty placeholders for all templates. These
* reserve memory for later.
*/
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_AP_PROBE_RESPONSE, NULL,
WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_AP_BEACON, NULL,
WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_DEAUTH_AP, NULL,
sizeof
(struct wl12xx_disconn_template),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
for (i = 0; i < WLCORE_MAX_KLV_TEMPLATES; i++) {
ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
CMD_TEMPL_KLV, NULL,
sizeof(struct ieee80211_qos_hdr),
i, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
}
return 0;
}
static int wl1271_ap_init_deauth_template(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
struct wl12xx_disconn_template *tmpl;
int ret;
u32 rate;
tmpl = kzalloc_obj(*tmpl);
if (!tmpl) {
ret = -ENOMEM;
goto out;
}
tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_DEAUTH);
rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
ret = wl1271_cmd_template_set(wl, wlvif->role_id,
CMD_TEMPL_DEAUTH_AP,
tmpl, sizeof(*tmpl), 0, rate);
out:
kfree(tmpl);
return ret;
}
static int wl1271_ap_init_null_template(struct wl1271 *wl,
struct ieee80211_vif *vif)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct ieee80211_hdr_3addr *nullfunc;
int ret;
u32 rate;
nullfunc = kzalloc_obj(*nullfunc);
if (!nullfunc) {
ret = -ENOMEM;
goto out;
}
nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_NULLFUNC |
IEEE80211_FCTL_FROMDS);
/* nullfunc->addr1 is filled by FW */
memcpy(nullfunc->addr2, vif->addr, ETH_ALEN);
memcpy(nullfunc->addr3, vif->addr, ETH_ALEN);
rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
ret = wl1271_cmd_template_set(wl, wlvif->role_id,
CMD_TEMPL_NULL_DATA, nullfunc,
sizeof(*nullfunc), 0, rate);
out:
kfree(nullfunc);
return ret;
}
static int wl1271_ap_init_qos_null_template(struct wl1271 *wl,
struct ieee80211_vif *vif)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct ieee80211_qos_hdr *qosnull;
int ret;
u32 rate;
qosnull = kzalloc_obj(*qosnull);
if (!qosnull) {
ret = -ENOMEM;
goto out;
}
qosnull->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_QOS_NULLFUNC |
IEEE80211_FCTL_FROMDS);
/* qosnull->addr1 is filled by FW */
memcpy(qosnull->addr2, vif->addr, ETH_ALEN);
memcpy(qosnull->addr3, vif->addr, ETH_ALEN);
rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
ret = wl1271_cmd_template_set(wl, wlvif->role_id,
CMD_TEMPL_QOS_NULL_DATA, qosnull,
sizeof(*qosnull), 0, rate);
out:
kfree(qosnull);
return ret;
}
static int wl12xx_init_rx_config(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_rx_msdu_life_time(wl);
if (ret < 0)
return ret;
return 0;
}
static int wl12xx_init_phy_vif_config(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
int ret;
ret = wl1271_acx_slot(wl, wlvif, DEFAULT_SLOT_TIME);
if (ret < 0)
return ret;
ret = wl1271_acx_service_period_timeout(wl, wlvif);
if (ret < 0)
return ret;
ret = wl1271_acx_rts_threshold(wl, wlvif, wl->hw->wiphy->rts_threshold);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_sta_beacon_filter(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
int ret;
ret = wl1271_acx_beacon_filter_table(wl, wlvif);
if (ret < 0)
return ret;
/* disable beacon filtering until we get the first beacon */
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
if (ret < 0)
return ret;
return 0;
}
int wl1271_init_pta(struct wl1271 *wl)
{
int ret;
ret = wl12xx_acx_sg_cfg(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_sg_enable(wl, wl->sg_enabled);
if (ret < 0)
return ret;
return 0;
}
int wl1271_init_energy_detection(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_cca_threshold(wl);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_beacon_broadcast(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
int ret;
ret = wl1271_acx_bcn_dtim_options(wl, wlvif);
if (ret < 0)
return ret;
return 0;
}
static int wl12xx_init_fwlog(struct wl1271 *wl)
{
int ret;
if (wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED)
return 0;
ret = wl12xx_cmd_config_fwlog(wl);
if (ret < 0)
return ret;
return 0;
}
/* generic sta initialization (non vif-specific) */
int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
/* PS config */
ret = wl12xx_acx_config_ps(wl, wlvif);
if (ret < 0)
return ret;
/* FM WLAN coexistence */
ret = wl1271_acx_fm_coex(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl,
struct ieee80211_vif *vif)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret;
/* disable the keep-alive feature */
ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
if (ret < 0)
return ret;
return 0;
}
/* generic ap initialization (non vif-specific) */
static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
ret = wl1271_init_ap_rates(wl, wlvif);
if (ret < 0)
return ret;
/* configure AP sleep, if enabled */
ret = wlcore_hw_ap_sleep(wl);
if (ret < 0)
return ret;
return 0;
}
int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret;
ret = wl1271_ap_init_deauth_template(wl, wlvif);
if (ret < 0)
return ret;
ret = wl1271_ap_init_null_template(wl, vif);
if (ret < 0)
return ret;
ret = wl1271_ap_init_qos_null_template(wl, vif);
if (ret < 0)
return ret;
/*
* when operating as AP we want to receive external beacons for
* configuring ERP protection.
*/
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl,
struct ieee80211_vif *vif)
{
return wl1271_ap_init_templates(wl, vif);
}
int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int i, ret;
struct conf_tx_rate_class rc;
u32 supported_rates;
wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x",
wlvif->basic_rate_set);
if (wlvif->basic_rate_set == 0)
return -EINVAL;
rc.enabled_rates = wlvif->basic_rate_set;
rc.long_retry_limit = 10;
rc.short_retry_limit = 10;
rc.aflags = 0;
ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.mgmt_rate_idx);
if (ret < 0)
return ret;
/* use the min basic rate for AP broadcast/multicast */
rc.enabled_rates = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
rc.short_retry_limit = 10;
rc.long_retry_limit = 10;
rc.aflags = 0;
ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.bcast_rate_idx);
if (ret < 0)
return ret;
/*
* If the basic rates contain OFDM rates, use OFDM only
* rates for unicast TX as well. Else use all supported rates.
*/
if (wl->ofdm_only_ap && (wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
supported_rates = CONF_TX_OFDM_RATES;
else
supported_rates = CONF_TX_ENABLED_RATES;
/* unconditionally enable HT rates */
supported_rates |= CONF_TX_MCS_RATES;
/* get extra MIMO or wide-chan rates where the HW supports it */
supported_rates |= wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
/* configure unicast TX rate classes */
for (i = 0; i < wl->conf.tx.ac_conf_count; i++) {
rc.enabled_rates = supported_rates;
rc.short_retry_limit = 10;
rc.long_retry_limit = 10;
rc.aflags = 0;
ret = wl1271_acx_ap_rate_policy(wl, &rc,
wlvif->ap.ucast_rate_idx[i]);
if (ret < 0)
return ret;
}
return 0;
}
static int wl1271_set_ba_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
/* Reset the BA RX indicators */
wlvif->ba_allowed = true;
wl->ba_rx_session_count = 0;
/* BA is supported in STA/AP modes */
if (wlvif->bss_type != BSS_TYPE_AP_BSS &&
wlvif->bss_type != BSS_TYPE_STA_BSS) {
wlvif->ba_support = false;
return 0;
}
wlvif->ba_support = true;
/* 802.11n initiator BA session setting */
return wl12xx_acx_set_ba_initiator_policy(wl, wlvif);
}
/* vif-specifc initialization */
static int wl12xx_init_sta_role(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
ret = wl1271_acx_group_address_tbl(wl, wlvif, true, NULL, 0);
if (ret < 0)
return ret;
/* Initialize connection monitoring thresholds */
ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
if (ret < 0)
return ret;
/* Beacon filtering */
ret = wl1271_init_sta_beacon_filter(wl, wlvif);
if (ret < 0)
return ret;
/* Beacons and broadcast settings */
ret = wl1271_init_beacon_broadcast(wl, wlvif);
if (ret < 0)
return ret;
/* Configure rssi/snr averaging weights */
ret = wl1271_acx_rssi_snr_avg_weights(wl, wlvif);
if (ret < 0)
return ret;
return 0;
}
/* vif-specific initialization */
static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
ret = wl1271_acx_ap_max_tx_retry(wl, wlvif);
if (ret < 0)
return ret;
/* initialize Tx power */
ret = wl1271_acx_tx_power(wl, wlvif, wlvif->power_level);
if (ret < 0)
return ret;
if (wl->radar_debug_mode)
wlcore_cmd_generic_cfg(wl, wlvif,
WLCORE_CFG_FEATURE_RADAR_DEBUG,
wl->radar_debug_mode, 0);
return 0;
}
int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct conf_tx_ac_category *conf_ac;
struct conf_tx_tid *conf_tid;
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
int ret, i;
/* consider all existing roles before configuring psm. */
if (wl->ap_count == 0 && is_ap) { /* first AP */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
if (ret < 0)
return ret;
/* unmask ap events */
wl->event_mask |= wl->ap_event_mask;
ret = wl1271_event_unmask(wl);
if (ret < 0)
return ret;
/* first STA, no APs */
} else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) {
u8 sta_auth = wl->conf.conn.sta_sleep_auth;
/* Configure for power according to debugfs */
if (sta_auth != WL1271_PSM_ILLEGAL)
ret = wl1271_acx_sleep_auth(wl, sta_auth);
/* Configure for ELP power saving */
else
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
if (ret < 0)
return ret;
}
/* Mode specific init */
if (is_ap) {
ret = wl1271_ap_hw_init(wl, wlvif);
if (ret < 0)
return ret;
ret = wl12xx_init_ap_role(wl, wlvif);
if (ret < 0)
return ret;
} else {
ret = wl1271_sta_hw_init(wl, wlvif);
if (ret < 0)
return ret;
ret = wl12xx_init_sta_role(wl, wlvif);
if (ret < 0)
return ret;
}
wl12xx_init_phy_vif_config(wl, wlvif);
/* Default TID/AC configuration */
BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
conf_ac = &wl->conf.tx.ac_conf[i];
ret = wl1271_acx_ac_cfg(wl, wlvif, conf_ac->ac,
conf_ac->cw_min, conf_ac->cw_max,
conf_ac->aifsn, conf_ac->tx_op_limit);
if (ret < 0)
return ret;
conf_tid = &wl->conf.tx.tid_conf[i];
ret = wl1271_acx_tid_cfg(wl, wlvif,
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)
return ret;
}
/* Configure HW encryption */
ret = wl1271_acx_feature_cfg(wl, wlvif);
if (ret < 0)
return ret;
/* Mode specific init - post mem init */
if (is_ap)
ret = wl1271_ap_hw_init_post_mem(wl, vif);
else
ret = wl1271_sta_hw_init_post_mem(wl, vif);
if (ret < 0)
return ret;
/* Configure initiator BA sessions policies */
ret = wl1271_set_ba_policies(wl, wlvif);
if (ret < 0)
return ret;
ret = wlcore_hw_init_vif(wl, wlvif);
if (ret < 0)
return ret;
return 0;
}
int wl1271_hw_init(struct wl1271 *wl)
{
int ret;
/* Chip-specific hw init */
ret = wl->ops->hw_init(wl);
if (ret < 0)
return ret;
/* Init templates */
ret = wl1271_init_templates_config(wl);
if (ret < 0)
return ret;
ret = wl12xx_acx_mem_cfg(wl);
if (ret < 0)
return ret;
/* Configure the FW logger */
ret = wl12xx_init_fwlog(wl);
if (ret < 0)
return ret;
ret = wlcore_cmd_regdomain_config_locked(wl);
if (ret < 0)
return ret;
/* Bluetooth WLAN coexistence */
ret = wl1271_init_pta(wl);
if (ret < 0)
return ret;
/* Default memory configuration */
ret = wl1271_acx_init_mem_config(wl);
if (ret < 0)
return ret;
/* RX config */
ret = wl12xx_init_rx_config(wl);
if (ret < 0)
goto out_free_memmap;
ret = wl1271_acx_dco_itrim_params(wl);
if (ret < 0)
goto out_free_memmap;
/* Configure TX patch complete interrupt behavior */
ret = wl1271_acx_tx_config_options(wl);
if (ret < 0)
goto out_free_memmap;
/* RX complete interrupt pacing */
ret = wl1271_acx_init_rx_interrupt(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, wl->hw->wiphy->frag_threshold);
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 PM */
ret = wl1271_acx_pm_config(wl);
if (ret < 0)
goto out_free_memmap;
ret = wl12xx_acx_set_rate_mgmt_params(wl);
if (ret < 0)
goto out_free_memmap;
/* configure hangover */
ret = wl12xx_acx_config_hangover(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;
}
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __INIT_H__
#define __INIT_H__
#include "wlcore.h"
int wl1271_hw_init_power_auth(struct wl1271 *wl);
int wl1271_init_templates_config(struct wl1271 *wl);
int wl1271_init_pta(struct wl1271 *wl);
int wl1271_init_energy_detection(struct wl1271 *wl);
int wl1271_chip_specific_init(struct wl1271 *wl);
int wl1271_hw_init(struct wl1271 *wl);
int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif);
int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif);
int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif);
#endif
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/interrupt.h>
#include "wlcore.h"
#include "debug.h"
#include "wl12xx_80211.h"
#include "io.h"
#include "tx.h"
bool wl1271_set_block_size(struct wl1271 *wl)
{
if (wl->if_ops->set_block_size) {
wl->if_ops->set_block_size(wl->dev, WL12XX_BUS_BLOCK_SIZE);
return true;
}
return false;
}
void wlcore_disable_interrupts(struct wl1271 *wl)
{
disable_irq(wl->irq);
}
EXPORT_SYMBOL_GPL(wlcore_disable_interrupts);
void wlcore_disable_interrupts_nosync(struct wl1271 *wl)
{
disable_irq_nosync(wl->irq);
}
EXPORT_SYMBOL_GPL(wlcore_disable_interrupts_nosync);
void wlcore_enable_interrupts(struct wl1271 *wl)
{
enable_irq(wl->irq);
}
EXPORT_SYMBOL_GPL(wlcore_enable_interrupts);
void wlcore_synchronize_interrupts(struct wl1271 *wl)
{
synchronize_irq(wl->irq);
}
EXPORT_SYMBOL_GPL(wlcore_synchronize_interrupts);
int wlcore_translate_addr(struct wl1271 *wl, int addr)
{
struct wlcore_partition_set *part = &wl->curr_part;
/*
* 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 preceding address regions to
* get the offset to the new region.
*/
if ((addr >= part->mem.start) &&
(addr < part->mem.start + part->mem.size))
return addr - part->mem.start;
else if ((addr >= part->reg.start) &&
(addr < part->reg.start + part->reg.size))
return addr - part->reg.start + part->mem.size;
else if ((addr >= part->mem2.start) &&
(addr < part->mem2.start + part->mem2.size))
return addr - part->mem2.start + part->mem.size +
part->reg.size;
else if ((addr >= part->mem3.start) &&
(addr < part->mem3.start + part->mem3.size))
return addr - part->mem3.start + part->mem.size +
part->reg.size + part->mem2.size;
WARN(1, "HW address 0x%x out of range", addr);
return 0;
}
EXPORT_SYMBOL_GPL(wlcore_translate_addr);
/* Set the 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 wlcore_set_partition(struct wl1271 *wl,
const struct wlcore_partition_set *p)
{
int ret;
/* copy partition info */
memcpy(&wl->curr_part, p, sizeof(*p));
wl1271_debug(DEBUG_IO, "mem_start %08X mem_size %08X",
p->mem.start, p->mem.size);
wl1271_debug(DEBUG_IO, "reg_start %08X reg_size %08X",
p->reg.start, p->reg.size);
wl1271_debug(DEBUG_IO, "mem2_start %08X mem2_size %08X",
p->mem2.start, p->mem2.size);
wl1271_debug(DEBUG_IO, "mem3_start %08X mem3_size %08X",
p->mem3.start, p->mem3.size);
ret = wlcore_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start);
if (ret < 0)
goto out;
ret = wlcore_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size);
if (ret < 0)
goto out;
ret = wlcore_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start);
if (ret < 0)
goto out;
ret = wlcore_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size);
if (ret < 0)
goto out;
ret = wlcore_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start);
if (ret < 0)
goto out;
ret = wlcore_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size);
if (ret < 0)
goto out;
/* wl12xx only: We don't need the size of the last partition,
* as it is automatically calculated based on the total memory
* size and the sizes of the previous partitions.
*
* wl18xx re-defines the HW_PART3 addresses for logger over
* SDIO support. wl12xx is expecting the write to
* HW_PART3_START_ADDR at offset 24. This creates conflict
* between the addresses.
* In order to fix this the expected value is written to
* HW_PART3_SIZE_ADDR instead which is at offset 24 after changes.
*/
ret = wlcore_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start);
if (ret < 0)
goto out;
ret = wlcore_raw_write32(wl, HW_PART3_SIZE_ADDR, p->mem3.size);
if (ret < 0)
goto out;
out:
return ret;
}
EXPORT_SYMBOL_GPL(wlcore_set_partition);
void wl1271_io_reset(struct wl1271 *wl)
{
if (wl->if_ops->reset)
wl->if_ops->reset(wl->dev);
}
void wl1271_io_init(struct wl1271 *wl)
{
if (wl->if_ops->init)
wl->if_ops->init(wl->dev);
}
@@ -0,0 +1,229 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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>
*/
#ifndef __IO_H__
#define __IO_H__
#include <linux/irqreturn.h>
#define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0
#define HW_PARTITION_REGISTERS_ADDR 0x1FFC0
#define HW_PART0_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR)
#define HW_PART0_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 4)
#define HW_PART1_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 8)
#define HW_PART1_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 12)
#define HW_PART2_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 16)
#define HW_PART2_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 20)
#define HW_PART3_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 24)
#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 28)
#define HW_ACCESS_REGISTER_SIZE 4
#define HW_ACCESS_PRAM_MAX_RANGE 0x3c000
struct wl1271;
void wlcore_disable_interrupts(struct wl1271 *wl);
void wlcore_disable_interrupts_nosync(struct wl1271 *wl);
void wlcore_enable_interrupts(struct wl1271 *wl);
void wlcore_synchronize_interrupts(struct wl1271 *wl);
void wl1271_io_reset(struct wl1271 *wl);
void wl1271_io_init(struct wl1271 *wl);
int wlcore_translate_addr(struct wl1271 *wl, int addr);
/* Raw target IO, address is not translated */
static inline int __must_check wlcore_raw_write(struct wl1271 *wl, int addr,
void *buf, size_t len,
bool fixed)
{
int ret;
if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags) ||
WARN_ON((test_bit(WL1271_FLAG_IN_ELP, &wl->flags) &&
addr != HW_ACCESS_ELP_CTRL_REG)))
return -EIO;
ret = wl->if_ops->write(wl->dev, addr, buf, len, fixed);
if (ret && wl->state != WLCORE_STATE_OFF)
set_bit(WL1271_FLAG_IO_FAILED, &wl->flags);
return ret;
}
static inline int __must_check wlcore_raw_read(struct wl1271 *wl, int addr,
void *buf, size_t len,
bool fixed)
{
int ret;
if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags) ||
WARN_ON((test_bit(WL1271_FLAG_IN_ELP, &wl->flags) &&
addr != HW_ACCESS_ELP_CTRL_REG)))
return -EIO;
ret = wl->if_ops->read(wl->dev, addr, buf, len, fixed);
if (ret && wl->state != WLCORE_STATE_OFF)
set_bit(WL1271_FLAG_IO_FAILED, &wl->flags);
return ret;
}
static inline int __must_check wlcore_raw_read_data(struct wl1271 *wl, int reg,
void *buf, size_t len,
bool fixed)
{
return wlcore_raw_read(wl, wl->rtable[reg], buf, len, fixed);
}
static inline int __must_check wlcore_raw_write_data(struct wl1271 *wl, int reg,
void *buf, size_t len,
bool fixed)
{
return wlcore_raw_write(wl, wl->rtable[reg], buf, len, fixed);
}
static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr,
u32 *val)
{
int ret;
ret = wlcore_raw_read(wl, addr, wl->buffer_32,
sizeof(*wl->buffer_32), false);
if (ret < 0)
return ret;
if (val)
*val = le32_to_cpu(*wl->buffer_32);
return 0;
}
static inline int __must_check wlcore_raw_write32(struct wl1271 *wl, int addr,
u32 val)
{
*wl->buffer_32 = cpu_to_le32(val);
return wlcore_raw_write(wl, addr, wl->buffer_32,
sizeof(*wl->buffer_32), false);
}
static inline int __must_check wlcore_read(struct wl1271 *wl, int addr,
void *buf, size_t len, bool fixed)
{
int physical;
physical = wlcore_translate_addr(wl, addr);
return wlcore_raw_read(wl, physical, buf, len, fixed);
}
static inline int __must_check wlcore_write(struct wl1271 *wl, int addr,
void *buf, size_t len, bool fixed)
{
int physical;
physical = wlcore_translate_addr(wl, addr);
return wlcore_raw_write(wl, physical, buf, len, fixed);
}
static inline int __must_check wlcore_write_data(struct wl1271 *wl, int reg,
void *buf, size_t len,
bool fixed)
{
return wlcore_write(wl, wl->rtable[reg], buf, len, fixed);
}
static inline int __must_check wlcore_read_data(struct wl1271 *wl, int reg,
void *buf, size_t len,
bool fixed)
{
return wlcore_read(wl, wl->rtable[reg], buf, len, fixed);
}
static inline int __must_check wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr,
void *buf, size_t len,
bool fixed)
{
int physical;
int addr;
/* Convert from FW internal address which is chip arch dependent */
addr = wl->ops->convert_hwaddr(wl, hwaddr);
physical = wlcore_translate_addr(wl, addr);
return wlcore_raw_read(wl, physical, buf, len, fixed);
}
static inline int __must_check wlcore_read32(struct wl1271 *wl, int addr,
u32 *val)
{
return wlcore_raw_read32(wl, wlcore_translate_addr(wl, addr), val);
}
static inline int __must_check wlcore_write32(struct wl1271 *wl, int addr,
u32 val)
{
return wlcore_raw_write32(wl, wlcore_translate_addr(wl, addr), val);
}
static inline int __must_check wlcore_read_reg(struct wl1271 *wl, int reg,
u32 *val)
{
return wlcore_raw_read32(wl,
wlcore_translate_addr(wl, wl->rtable[reg]),
val);
}
static inline int __must_check wlcore_write_reg(struct wl1271 *wl, int reg,
u32 val)
{
return wlcore_raw_write32(wl,
wlcore_translate_addr(wl, wl->rtable[reg]),
val);
}
static inline void wl1271_power_off(struct wl1271 *wl)
{
int ret = 0;
if (!test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags))
return;
if (wl->if_ops->power)
ret = wl->if_ops->power(wl->dev, false);
if (!ret)
clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
}
static inline int wl1271_power_on(struct wl1271 *wl)
{
int ret = 0;
if (wl->if_ops->power)
ret = wl->if_ops->power(wl->dev, true);
if (ret == 0)
set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
return ret;
}
int wlcore_set_partition(struct wl1271 *wl,
const struct wlcore_partition_set *p);
bool wl1271_set_block_size(struct wl1271 *wl);
/* Functions from wl1271_main.c */
int wl1271_tx_dummy_packet(struct wl1271 *wl);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#include "ps.h"
#include "io.h"
#include "tx.h"
#include "debug.h"
int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum wl1271_cmd_ps_mode mode)
{
int ret;
u16 timeout = wl->conf.conn.dynamic_ps_timeout;
switch (mode) {
case STATION_AUTO_PS_MODE:
case STATION_POWER_SAVE_MODE:
wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)",
mode, timeout);
ret = wl1271_acx_wake_up_conditions(wl, wlvif,
wl->conf.conn.wake_up_event,
wl->conf.conn.listen_interval);
if (ret < 0) {
wl1271_error("couldn't set wake up conditions");
return ret;
}
ret = wl1271_cmd_ps_mode(wl, wlvif, mode, timeout);
if (ret < 0)
return ret;
set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
/*
* enable beacon early termination.
* Not relevant for 5GHz and for high rates.
*/
if ((wlvif->band == NL80211_BAND_2GHZ) &&
(wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
ret = wl1271_acx_bet_enable(wl, wlvif, true);
if (ret < 0)
return ret;
}
break;
case STATION_ACTIVE_MODE:
wl1271_debug(DEBUG_PSM, "leaving psm");
/* disable beacon early termination */
if ((wlvif->band == NL80211_BAND_2GHZ) &&
(wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
ret = wl1271_acx_bet_enable(wl, wlvif, false);
if (ret < 0)
return ret;
}
ret = wl1271_cmd_ps_mode(wl, wlvif, mode, 0);
if (ret < 0)
return ret;
clear_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
break;
default:
wl1271_warning("trying to set ps to unsupported mode %d", mode);
ret = -EINVAL;
}
return ret;
}
static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
{
int i;
struct sk_buff *skb;
struct ieee80211_tx_info *info;
unsigned long flags;
int filtered[NUM_TX_QUEUES];
struct wl1271_link *lnk = &wl->links[hlid];
/* filter all frames currently in the low level queues for this hlid */
for (i = 0; i < NUM_TX_QUEUES; i++) {
filtered[i] = 0;
while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
filtered[i]++;
if (WARN_ON(wl12xx_is_dummy_packet(wl, skb)))
continue;
info = IEEE80211_SKB_CB(skb);
info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
info->status.rates[0].idx = -1;
ieee80211_tx_status_ni(wl->hw, skb);
}
}
spin_lock_irqsave(&wl->wl_lock, flags);
for (i = 0; i < NUM_TX_QUEUES; i++) {
wl->tx_queue_count[i] -= filtered[i];
if (lnk->wlvif)
lnk->wlvif->tx_queue_count[i] -= filtered[i];
}
spin_unlock_irqrestore(&wl->wl_lock, flags);
wl1271_handle_tx_low_watermark(wl);
}
void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 hlid, bool clean_queues)
{
struct ieee80211_sta *sta;
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
return;
if (!test_bit(hlid, wlvif->ap.sta_hlid_map) ||
test_bit(hlid, &wl->ap_ps_map))
return;
wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d pkts %d "
"clean_queues %d", hlid, wl->links[hlid].allocated_pkts,
clean_queues);
rcu_read_lock();
sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
if (!sta) {
wl1271_error("could not find sta %pM for starting ps",
wl->links[hlid].addr);
rcu_read_unlock();
return;
}
ieee80211_sta_ps_transition_ni(sta, true);
rcu_read_unlock();
/* do we want to filter all frames from this link's queues? */
if (clean_queues)
wl1271_ps_filter_frames(wl, hlid);
__set_bit(hlid, &wl->ap_ps_map);
}
void wl12xx_ps_link_end(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
{
struct ieee80211_sta *sta;
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
if (!test_bit(hlid, &wl->ap_ps_map))
return;
wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid);
__clear_bit(hlid, &wl->ap_ps_map);
rcu_read_lock();
sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
if (!sta) {
wl1271_error("could not find sta %pM for ending ps",
wl->links[hlid].addr);
goto end;
}
ieee80211_sta_ps_transition_ni(sta, false);
end:
rcu_read_unlock();
}
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __PS_H__
#define __PS_H__
#include "wlcore.h"
#include "acx.h"
int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum wl1271_cmd_ps_mode mode);
void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 hlid, bool clean_queues);
void wl12xx_ps_link_end(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
#define WL1271_PS_COMPLETE_TIMEOUT 500
#endif /* __WL1271_PS_H__ */
@@ -0,0 +1,342 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#include <linux/gfp.h>
#include <linux/sched.h>
#include "wlcore.h"
#include "debug.h"
#include "acx.h"
#include "rx.h"
#include "tx.h"
#include "io.h"
#include "hw_ops.h"
/*
* TODO: this is here just for now, it must be removed when the data
* operations are in place.
*/
#include "../wl12xx/reg.h"
static u32 wlcore_rx_get_buf_size(struct wl1271 *wl,
u32 rx_pkt_desc)
{
if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN)
return (rx_pkt_desc & ALIGNED_RX_BUF_SIZE_MASK) >>
ALIGNED_RX_BUF_SIZE_SHIFT;
return (rx_pkt_desc & RX_BUF_SIZE_MASK) >> RX_BUF_SIZE_SHIFT_DIV;
}
static u32 wlcore_rx_get_align_buf_size(struct wl1271 *wl, u32 pkt_len)
{
if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN)
return ALIGN(pkt_len, WL12XX_BUS_BLOCK_SIZE);
return pkt_len;
}
static void wl1271_rx_status(struct wl1271 *wl,
struct wl1271_rx_descriptor *desc,
struct ieee80211_rx_status *status,
u8 beacon, u8 probe_rsp)
{
memset(status, 0, sizeof(struct ieee80211_rx_status));
if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG)
status->band = NL80211_BAND_2GHZ;
else
status->band = NL80211_BAND_5GHZ;
status->rate_idx = wlcore_rate_to_idx(wl, desc->rate, status->band);
/* 11n support */
if (desc->rate <= wl->hw_min_ht_rate)
status->encoding = RX_ENC_HT;
/*
* Read the signal level and antenna diversity indication.
* The msb in the signal level is always set as it is a
* negative number.
* The antenna indication is the msb of the rssi.
*/
status->signal = ((desc->rssi & RSSI_LEVEL_BITMASK) | BIT(7));
status->antenna = ((desc->rssi & ANT_DIVERSITY_BITMASK) >> 7);
/*
* FIXME: In wl1251, the SNR should be divided by two. In wl1271 we
* need to divide by two for now, but TI has been discussing about
* changing it. This needs to be rechecked.
*/
wl->noise = desc->rssi - (desc->snr >> 1);
status->freq = ieee80211_channel_to_frequency(desc->channel,
status->band);
if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) {
u8 desc_err_code = desc->status & WL1271_RX_DESC_STATUS_MASK;
status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED |
RX_FLAG_DECRYPTED;
if (unlikely(desc_err_code & WL1271_RX_DESC_MIC_FAIL)) {
status->flag |= RX_FLAG_MMIC_ERROR;
wl1271_warning("Michael MIC error. Desc: 0x%x",
desc_err_code);
}
}
if (beacon || probe_rsp)
status->boottime_ns = ktime_get_boottime_ns();
if (beacon)
wlcore_set_pending_regdomain_ch(wl, (u16)desc->channel,
status->band);
}
static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
enum wl_rx_buf_align rx_align, u8 *hlid)
{
struct wl1271_rx_descriptor *desc;
struct sk_buff *skb;
struct ieee80211_hdr *hdr;
u8 beacon = 0;
u8 is_data = 0;
u8 reserved = 0, offset_to_data = 0;
u16 seq_num;
u32 pkt_data_len;
/*
* In PLT mode we seem to get frames and mac80211 warns about them,
* workaround this by not retrieving them at all.
*/
if (unlikely(wl->plt))
return -EINVAL;
pkt_data_len = wlcore_hw_get_rx_packet_len(wl, data, length);
if (!pkt_data_len) {
wl1271_error("Invalid packet arrived from HW. length %d",
length);
return -EINVAL;
}
if (rx_align == WLCORE_RX_BUF_UNALIGNED)
reserved = RX_BUF_ALIGN;
else if (rx_align == WLCORE_RX_BUF_PADDED)
offset_to_data = RX_BUF_ALIGN;
/* the data read starts with the descriptor */
desc = (struct wl1271_rx_descriptor *) data;
if (desc->packet_class == WL12XX_RX_CLASS_LOGGER) {
size_t len = length - sizeof(*desc);
wl12xx_copy_fwlog(wl, data + sizeof(*desc), len);
return 0;
}
/* discard corrupted packets */
if (desc->status & WL1271_RX_DESC_DECRYPT_FAIL) {
hdr = (void *)(data + sizeof(*desc) + offset_to_data);
wl1271_warning("corrupted packet in RX: status: 0x%x len: %d",
desc->status & WL1271_RX_DESC_STATUS_MASK,
pkt_data_len);
wl1271_dump((DEBUG_RX|DEBUG_CMD), "PKT: ", data + sizeof(*desc),
min(pkt_data_len,
ieee80211_hdrlen(hdr->frame_control)));
return -EINVAL;
}
/* skb length not including rx descriptor */
skb = __dev_alloc_skb(pkt_data_len + reserved, GFP_KERNEL);
if (!skb) {
wl1271_error("Couldn't allocate RX frame");
return -ENOMEM;
}
/* reserve the unaligned payload(if any) */
skb_reserve(skb, reserved);
/*
* Copy packets from aggregation buffer to the skbs without rx
* descriptor and with packet payload aligned care. In case of unaligned
* packets copy the packets in offset of 2 bytes guarantee IP header
* payload aligned to 4 bytes.
*/
skb_put_data(skb, data + sizeof(*desc), pkt_data_len);
if (rx_align == WLCORE_RX_BUF_PADDED)
skb_pull(skb, RX_BUF_ALIGN);
*hlid = desc->hlid;
hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_beacon(hdr->frame_control))
beacon = 1;
if (ieee80211_is_data_present(hdr->frame_control))
is_data = 1;
wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon,
ieee80211_is_probe_resp(hdr->frame_control));
wlcore_hw_set_rx_csum(wl, desc, skb);
seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d hlid %d", skb,
skb->len - desc->pad_len,
beacon ? "beacon" : "",
seq_num, *hlid);
skb_queue_tail(&wl->deferred_rx_queue, skb);
queue_work(wl->freezable_wq, &wl->netstack_work);
return is_data;
}
int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status)
{
unsigned long active_hlids[BITS_TO_LONGS(WLCORE_MAX_LINKS)] = {0};
u32 buf_size;
u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc;
u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc;
u32 rx_counter;
u32 pkt_len, align_pkt_len;
u32 pkt_offset, des;
u8 hlid;
enum wl_rx_buf_align rx_align;
int ret = 0;
/* update rates per link */
hlid = status->counters.hlid;
if (hlid < WLCORE_MAX_LINKS)
wl->links[hlid].fw_rate_mbps =
status->counters.tx_last_rate_mbps;
while (drv_rx_counter != fw_rx_counter) {
buf_size = 0;
rx_counter = drv_rx_counter;
while (rx_counter != fw_rx_counter) {
des = le32_to_cpu(status->rx_pkt_descs[rx_counter]);
pkt_len = wlcore_rx_get_buf_size(wl, des);
align_pkt_len = wlcore_rx_get_align_buf_size(wl,
pkt_len);
if (buf_size + align_pkt_len > wl->aggr_buf_size)
break;
buf_size += align_pkt_len;
rx_counter++;
rx_counter %= wl->num_rx_desc;
}
if (buf_size == 0) {
wl1271_warning("received empty data");
break;
}
/* Read all available packets at once */
des = le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]);
ret = wlcore_hw_prepare_read(wl, des, buf_size);
if (ret < 0)
goto out;
ret = wlcore_read_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf,
buf_size, true);
if (ret < 0)
goto out;
/* Split data into separate packets */
pkt_offset = 0;
while (pkt_offset < buf_size) {
des = le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]);
pkt_len = wlcore_rx_get_buf_size(wl, des);
rx_align = wlcore_hw_get_rx_buf_align(wl, des);
/*
* the handle data call can only fail in memory-outage
* conditions, in that case the received frame will just
* be dropped.
*/
if (wl1271_rx_handle_data(wl,
wl->aggr_buf + pkt_offset,
pkt_len, rx_align,
&hlid) == 1) {
if (hlid < wl->num_links)
__set_bit(hlid, active_hlids);
else
WARN(1,
"hlid (%d) exceeded MAX_LINKS\n",
hlid);
}
wl->rx_counter++;
drv_rx_counter++;
drv_rx_counter %= wl->num_rx_desc;
pkt_offset += wlcore_rx_get_align_buf_size(wl, pkt_len);
}
}
/*
* Write the driver's packet counter to the FW. This is only required
* for older hardware revisions
*/
if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) {
ret = wlcore_write32(wl, WL12XX_REG_RX_DRIVER_COUNTER,
wl->rx_counter);
if (ret < 0)
goto out;
}
wl12xx_rearm_rx_streaming(wl, active_hlids);
out:
return ret;
}
#ifdef CONFIG_PM
int wl1271_rx_filter_enable(struct wl1271 *wl,
int index, bool enable,
struct wl12xx_rx_filter *filter)
{
int ret;
if (!!test_bit(index, wl->rx_filter_enabled) == enable) {
wl1271_warning("Request to enable an already "
"enabled rx filter %d", index);
return 0;
}
ret = wl1271_acx_set_rx_filter(wl, index, enable, filter);
if (ret) {
wl1271_error("Failed to %s rx data filter %d (err=%d)",
enable ? "enable" : "disable", index, ret);
return ret;
}
if (enable)
__set_bit(index, wl->rx_filter_enabled);
else
__clear_bit(index, wl->rx_filter_enabled);
return 0;
}
int wl1271_rx_filter_clear_all(struct wl1271 *wl)
{
int i, ret = 0;
for (i = 0; i < WL1271_MAX_RX_FILTERS; i++) {
if (!test_bit(i, wl->rx_filter_enabled))
continue;
ret = wl1271_rx_filter_enable(wl, i, 0, NULL);
if (ret)
goto out;
}
out:
return ret;
}
#endif /* CONFIG_PM */
@@ -0,0 +1,141 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __RX_H__
#define __RX_H__
#include <linux/bitops.h>
#define WL1271_RX_MAX_RSSI -30
#define WL1271_RX_MIN_RSSI -95
#define RSSI_LEVEL_BITMASK 0x7F
#define ANT_DIVERSITY_BITMASK BIT(7)
#define SHORT_PREAMBLE_BIT BIT(0)
#define OFDM_RATE_BIT BIT(6)
#define PBCC_RATE_BIT BIT(7)
#define PLCP_HEADER_LENGTH 8
#define RX_DESC_PACKETID_SHIFT 11
#define RX_MAX_PACKET_ID 3
#define RX_DESC_VALID_FCS 0x0001
#define RX_DESC_MATCH_RXADDR1 0x0002
#define RX_DESC_MCAST 0x0004
#define RX_DESC_STAINTIM 0x0008
#define RX_DESC_VIRTUAL_BM 0x0010
#define RX_DESC_BCAST 0x0020
#define RX_DESC_MATCH_SSID 0x0040
#define RX_DESC_MATCH_BSSID 0x0080
#define RX_DESC_ENCRYPTION_MASK 0x0300
#define RX_DESC_MEASURMENT 0x0400
#define RX_DESC_SEQNUM_MASK 0x1800
#define RX_DESC_MIC_FAIL 0x2000
#define RX_DESC_DECRYPT_FAIL 0x4000
/*
* RX Descriptor flags:
*
* Bits 0-1 - band
* Bit 2 - STBC
* Bit 3 - A-MPDU
* Bit 4 - HT
* Bits 5-7 - encryption
*/
#define WL1271_RX_DESC_BAND_MASK 0x03
#define WL1271_RX_DESC_ENCRYPT_MASK 0xE0
#define WL1271_RX_DESC_BAND_BG 0x00
#define WL1271_RX_DESC_BAND_J 0x01
#define WL1271_RX_DESC_BAND_A 0x02
#define WL1271_RX_DESC_STBC BIT(2)
#define WL1271_RX_DESC_A_MPDU BIT(3)
#define WL1271_RX_DESC_HT BIT(4)
#define WL1271_RX_DESC_ENCRYPT_WEP 0x20
#define WL1271_RX_DESC_ENCRYPT_TKIP 0x40
#define WL1271_RX_DESC_ENCRYPT_AES 0x60
#define WL1271_RX_DESC_ENCRYPT_GEM 0x80
/*
* RX Descriptor status
*
* Bits 0-2 - error code
* Bits 3-5 - process_id tag (AP mode FW)
* Bits 6-7 - reserved
*/
#define WL1271_RX_DESC_STATUS_MASK 0x07
#define WL1271_RX_DESC_SUCCESS 0x00
#define WL1271_RX_DESC_DECRYPT_FAIL 0x01
#define WL1271_RX_DESC_MIC_FAIL 0x02
#define RX_MEM_BLOCK_MASK 0xFF
#define RX_BUF_SIZE_MASK 0xFFF00
#define RX_BUF_SIZE_SHIFT_DIV 6
#define ALIGNED_RX_BUF_SIZE_MASK 0xFFFF00
#define ALIGNED_RX_BUF_SIZE_SHIFT 8
/* If set, the start of IP payload is not 4 bytes aligned */
#define RX_BUF_UNALIGNED_PAYLOAD BIT(20)
/* If set, the buffer was padded by the FW to be 4 bytes aligned */
#define RX_BUF_PADDED_PAYLOAD BIT(30)
/*
* Account for the padding inserted by the FW in case of RX_ALIGNMENT
* or for fixing alignment in case the packet wasn't aligned.
*/
#define RX_BUF_ALIGN 2
/* Describes the alignment state of a Rx buffer */
enum wl_rx_buf_align {
WLCORE_RX_BUF_ALIGNED,
WLCORE_RX_BUF_UNALIGNED,
WLCORE_RX_BUF_PADDED,
};
enum {
WL12XX_RX_CLASS_UNKNOWN,
WL12XX_RX_CLASS_MANAGEMENT,
WL12XX_RX_CLASS_DATA,
WL12XX_RX_CLASS_QOS_DATA,
WL12XX_RX_CLASS_BCN_PRBRSP,
WL12XX_RX_CLASS_EAPOL,
WL12XX_RX_CLASS_BA_EVENT,
WL12XX_RX_CLASS_AMSDU,
WL12XX_RX_CLASS_LOGGER,
};
struct wl1271_rx_descriptor {
__le16 length;
u8 status;
u8 flags;
u8 rate;
u8 channel;
s8 rssi;
u8 snr;
__le32 timestamp;
u8 packet_class;
u8 hlid;
u8 pad_len;
u8 reserved;
} __packed;
int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status);
u8 wl1271_rate_to_idx(int rate, enum nl80211_band band);
int wl1271_rx_filter_enable(struct wl1271 *wl,
int index, bool enable,
struct wl12xx_rx_filter *filter);
int wl1271_rx_filter_clear_all(struct wl1271 *wl);
#endif
@@ -0,0 +1,477 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wl1271
*
* Copyright (C) 2009-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#include <linux/ieee80211.h>
#include <linux/pm_runtime.h>
#include "wlcore.h"
#include "debug.h"
#include "cmd.h"
#include "scan.h"
#include "acx.h"
#include "tx.h"
void wl1271_scan_complete_work(struct work_struct *work)
{
struct delayed_work *dwork;
struct wl1271 *wl;
struct wl12xx_vif *wlvif;
struct cfg80211_scan_info info = {
.aborted = false,
};
int ret;
dwork = to_delayed_work(work);
wl = container_of(dwork, struct wl1271, scan_complete_work);
wl1271_debug(DEBUG_SCAN, "Scanning complete");
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON))
goto out;
if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
goto out;
wlvif = wl->scan_wlvif;
/*
* Rearm the tx watchdog just before idling scan. This
* prevents just-finished scans from triggering the watchdog
*/
wl12xx_rearm_tx_watchdog_locked(wl);
wl->scan.state = WL1271_SCAN_STATE_IDLE;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan.req = NULL;
wl->scan_wlvif = NULL;
ret = pm_runtime_resume_and_get(wl->dev);
if (ret < 0)
goto out;
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
/* restore hardware connection monitoring template */
wl1271_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq);
}
if (wl->scan.failed) {
wl1271_info("Scan completed due to error.");
wl12xx_queue_recovery_work(wl);
}
wlcore_cmd_regdomain_config_locked(wl);
pm_runtime_put_autosuspend(wl->dev);
ieee80211_scan_completed(wl->hw, &info);
out:
mutex_unlock(&wl->mutex);
}
static void wlcore_started_vifs_iter(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
bool active = false;
int *count = (int *)data;
/*
* count active interfaces according to interface type.
* checking only bss_conf.idle is bad for some cases, e.g.
* we don't want to count sta in p2p_find as active interface.
*/
switch (wlvif->bss_type) {
case BSS_TYPE_STA_BSS:
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
active = true;
break;
case BSS_TYPE_AP_BSS:
if (wlvif->wl->active_sta_count > 0)
active = true;
break;
default:
break;
}
if (active)
(*count)++;
}
static int wlcore_count_started_vifs(struct wl1271 *wl)
{
int count = 0;
ieee80211_iterate_active_interfaces_atomic(wl->hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
wlcore_started_vifs_iter, &count);
return count;
}
static int
wlcore_scan_get_channels(struct wl1271 *wl,
struct ieee80211_channel *req_channels[],
u32 n_channels,
u32 n_ssids,
struct conn_scan_ch_params *channels,
u32 band, bool radar, bool passive,
int start, int max_channels,
u8 *n_pactive_ch,
int scan_type)
{
int i, j;
u32 flags;
bool force_passive = !n_ssids;
u32 min_dwell_time_active, max_dwell_time_active;
u32 dwell_time_passive, dwell_time_dfs;
/* configure dwell times according to scan type */
if (scan_type == SCAN_TYPE_SEARCH) {
struct conf_scan_settings *c = &wl->conf.scan;
bool active_vif_exists = !!wlcore_count_started_vifs(wl);
min_dwell_time_active = active_vif_exists ?
c->min_dwell_time_active :
c->min_dwell_time_active_long;
max_dwell_time_active = active_vif_exists ?
c->max_dwell_time_active :
c->max_dwell_time_active_long;
dwell_time_passive = c->dwell_time_passive;
dwell_time_dfs = c->dwell_time_dfs;
} else {
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
u32 delta_per_probe;
if (band == NL80211_BAND_5GHZ)
delta_per_probe = c->dwell_time_delta_per_probe_5;
else
delta_per_probe = c->dwell_time_delta_per_probe;
min_dwell_time_active = c->base_dwell_time +
n_ssids * c->num_probe_reqs * delta_per_probe;
max_dwell_time_active = min_dwell_time_active +
c->max_dwell_time_delta;
dwell_time_passive = c->dwell_time_passive;
dwell_time_dfs = c->dwell_time_dfs;
}
min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000);
max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000);
dwell_time_passive = DIV_ROUND_UP(dwell_time_passive, 1000);
dwell_time_dfs = DIV_ROUND_UP(dwell_time_dfs, 1000);
for (i = 0, j = start;
i < n_channels && j < max_channels;
i++) {
flags = req_channels[i]->flags;
if (force_passive)
flags |= IEEE80211_CHAN_NO_IR;
if ((req_channels[i]->band == band) &&
!(flags & IEEE80211_CHAN_DISABLED) &&
(!!(flags & IEEE80211_CHAN_RADAR) == radar) &&
/* if radar is set, we ignore the passive flag */
(radar ||
!!(flags & IEEE80211_CHAN_NO_IR) == passive)) {
if (flags & IEEE80211_CHAN_RADAR) {
channels[j].flags |= SCAN_CHANNEL_FLAGS_DFS;
channels[j].passive_duration =
cpu_to_le16(dwell_time_dfs);
} else {
channels[j].passive_duration =
cpu_to_le16(dwell_time_passive);
}
channels[j].min_duration =
cpu_to_le16(min_dwell_time_active);
channels[j].max_duration =
cpu_to_le16(max_dwell_time_active);
channels[j].tx_power_att = req_channels[i]->max_power;
channels[j].channel = req_channels[i]->hw_value;
if (n_pactive_ch &&
(band == NL80211_BAND_2GHZ) &&
(channels[j].channel >= 12) &&
(channels[j].channel <= 14) &&
(flags & IEEE80211_CHAN_NO_IR) &&
!force_passive) {
/* pactive channels treated as DFS */
channels[j].flags = SCAN_CHANNEL_FLAGS_DFS;
/*
* n_pactive_ch is counted down from the end of
* the passive channel list
*/
(*n_pactive_ch)++;
wl1271_debug(DEBUG_SCAN, "n_pactive_ch = %d",
*n_pactive_ch);
}
wl1271_debug(DEBUG_SCAN, "freq %d, ch. %d, flags 0x%x, power %d, min/max_dwell %d/%d%s%s",
req_channels[i]->center_freq,
req_channels[i]->hw_value,
req_channels[i]->flags,
req_channels[i]->max_power,
min_dwell_time_active,
max_dwell_time_active,
flags & IEEE80211_CHAN_RADAR ?
", DFS" : "",
flags & IEEE80211_CHAN_NO_IR ?
", NO-IR" : "");
j++;
}
}
return j - start;
}
bool
wlcore_set_scan_chan_params(struct wl1271 *wl,
struct wlcore_scan_channels *cfg,
struct ieee80211_channel *channels[],
u32 n_channels,
u32 n_ssids,
int scan_type)
{
u8 n_pactive_ch = 0;
cfg->passive[0] =
wlcore_scan_get_channels(wl,
channels,
n_channels,
n_ssids,
cfg->channels_2,
NL80211_BAND_2GHZ,
false, true, 0,
MAX_CHANNELS_2GHZ,
&n_pactive_ch,
scan_type);
cfg->active[0] =
wlcore_scan_get_channels(wl,
channels,
n_channels,
n_ssids,
cfg->channels_2,
NL80211_BAND_2GHZ,
false, false,
cfg->passive[0],
MAX_CHANNELS_2GHZ,
&n_pactive_ch,
scan_type);
cfg->passive[1] =
wlcore_scan_get_channels(wl,
channels,
n_channels,
n_ssids,
cfg->channels_5,
NL80211_BAND_5GHZ,
false, true, 0,
wl->max_channels_5,
&n_pactive_ch,
scan_type);
cfg->dfs =
wlcore_scan_get_channels(wl,
channels,
n_channels,
n_ssids,
cfg->channels_5,
NL80211_BAND_5GHZ,
true, true,
cfg->passive[1],
wl->max_channels_5,
&n_pactive_ch,
scan_type);
cfg->active[1] =
wlcore_scan_get_channels(wl,
channels,
n_channels,
n_ssids,
cfg->channels_5,
NL80211_BAND_5GHZ,
false, false,
cfg->passive[1] + cfg->dfs,
wl->max_channels_5,
&n_pactive_ch,
scan_type);
/* 802.11j channels are not supported yet */
cfg->passive[2] = 0;
cfg->active[2] = 0;
cfg->passive_active = n_pactive_ch;
wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d",
cfg->active[0], cfg->passive[0]);
wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d",
cfg->active[1], cfg->passive[1]);
wl1271_debug(DEBUG_SCAN, " DFS: %d", cfg->dfs);
return cfg->passive[0] || cfg->active[0] ||
cfg->passive[1] || cfg->active[1] || cfg->dfs ||
cfg->passive[2] || cfg->active[2];
}
EXPORT_SYMBOL_GPL(wlcore_set_scan_chan_params);
int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len,
struct cfg80211_scan_request *req)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
/*
* cfg80211 should guarantee that we don't get more channels
* than what we have registered.
*/
BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
return -EBUSY;
wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
if (ssid_len && ssid) {
wl->scan.ssid_len = ssid_len;
memcpy(wl->scan.ssid, ssid, ssid_len);
} else {
wl->scan.ssid_len = 0;
}
wl->scan_wlvif = wlvif;
wl->scan.req = req;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
/* we assume failure so that timeout scenarios are handled correctly */
wl->scan.failed = true;
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
wl->ops->scan_start(wl, wlvif, req);
return 0;
}
/* Returns the scan type to be used or a negative value on error */
int
wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req)
{
struct wl1271_cmd_sched_scan_ssid_list *cmd = NULL;
struct cfg80211_match_set *sets = req->match_sets;
struct cfg80211_ssid *ssids = req->ssids;
int ret = 0, type, i, j, n_match_ssids = 0;
wl1271_debug((DEBUG_CMD | DEBUG_SCAN), "cmd sched scan ssid list");
/* count the match sets that contain SSIDs */
for (i = 0; i < req->n_match_sets; i++)
if (sets[i].ssid.ssid_len > 0)
n_match_ssids++;
/* No filter, no ssids or only bcast ssid */
if (!n_match_ssids &&
(!req->n_ssids ||
(req->n_ssids == 1 && req->ssids[0].ssid_len == 0))) {
type = SCAN_SSID_FILTER_ANY;
goto out;
}
cmd = kzalloc_obj(*cmd);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->role_id = wlvif->role_id;
if (!n_match_ssids) {
/* No filter, with ssids */
type = SCAN_SSID_FILTER_DISABLED;
for (i = 0; i < req->n_ssids; i++) {
cmd->ssids[cmd->n_ssids].type = (ssids[i].ssid_len) ?
SCAN_SSID_TYPE_HIDDEN : SCAN_SSID_TYPE_PUBLIC;
cmd->ssids[cmd->n_ssids].len = ssids[i].ssid_len;
memcpy(cmd->ssids[cmd->n_ssids].ssid, ssids[i].ssid,
ssids[i].ssid_len);
cmd->n_ssids++;
}
} else {
type = SCAN_SSID_FILTER_LIST;
/* Add all SSIDs from the filters */
for (i = 0; i < req->n_match_sets; i++) {
/* ignore sets without SSIDs */
if (!sets[i].ssid.ssid_len)
continue;
cmd->ssids[cmd->n_ssids].type = SCAN_SSID_TYPE_PUBLIC;
cmd->ssids[cmd->n_ssids].len = sets[i].ssid.ssid_len;
memcpy(cmd->ssids[cmd->n_ssids].ssid,
sets[i].ssid.ssid, sets[i].ssid.ssid_len);
cmd->n_ssids++;
}
if ((req->n_ssids > 1) ||
(req->n_ssids == 1 && req->ssids[0].ssid_len > 0)) {
/*
* Mark all the SSIDs passed in the SSID list as HIDDEN,
* so they're used in probe requests.
*/
for (i = 0; i < req->n_ssids; i++) {
if (!req->ssids[i].ssid_len)
continue;
for (j = 0; j < cmd->n_ssids; j++)
if ((req->ssids[i].ssid_len ==
cmd->ssids[j].len) &&
!memcmp(req->ssids[i].ssid,
cmd->ssids[j].ssid,
req->ssids[i].ssid_len)) {
cmd->ssids[j].type =
SCAN_SSID_TYPE_HIDDEN;
break;
}
/* Fail if SSID isn't present in the filters */
if (j == cmd->n_ssids) {
ret = -EINVAL;
goto out_free;
}
}
}
}
ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_SSID_CFG, cmd,
sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("cmd sched scan ssid list failed");
goto out_free;
}
out_free:
kfree(cmd);
out:
if (ret < 0)
return ret;
return type;
}
EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_ssid_list);
void wlcore_scan_sched_scan_results(struct wl1271 *wl)
{
wl1271_debug(DEBUG_SCAN, "got periodic scan results");
ieee80211_sched_scan_results(wl->hw);
}
EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_results);
@@ -0,0 +1,164 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 2009-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __SCAN_H__
#define __SCAN_H__
#include "wlcore.h"
int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len,
struct cfg80211_scan_request *req);
int wl1271_scan_build_probe_req(struct wl1271 *wl,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len, u8 band);
void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl1271_scan_complete_work(struct work_struct *work);
int wl1271_scan_sched_scan_config(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_scan_ies *ies);
int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wlcore_scan_sched_scan_results(struct wl1271 *wl);
#define WL1271_SCAN_MAX_CHANNELS 24
#define WL1271_SCAN_DEFAULT_TAG 1
#define WL1271_SCAN_CURRENT_TX_PWR 0
#define WL1271_SCAN_OPT_ACTIVE 0
#define WL1271_SCAN_OPT_PASSIVE 1
#define WL1271_SCAN_OPT_SPLIT_SCAN 2
#define WL1271_SCAN_OPT_PRIORITY_HIGH 4
/* scan even if we fail to enter psm */
#define WL1271_SCAN_OPT_FORCE 8
#define WL1271_SCAN_BAND_2_4_GHZ 0
#define WL1271_SCAN_BAND_5_GHZ 1
#define WL1271_SCAN_TIMEOUT 30000 /* msec */
enum {
WL1271_SCAN_STATE_IDLE,
WL1271_SCAN_STATE_2GHZ_ACTIVE,
WL1271_SCAN_STATE_2GHZ_PASSIVE,
WL1271_SCAN_STATE_5GHZ_ACTIVE,
WL1271_SCAN_STATE_5GHZ_PASSIVE,
WL1271_SCAN_STATE_DONE
};
struct wl1271_cmd_trigger_scan_to {
struct wl1271_cmd_header header;
__le32 timeout;
} __packed;
#define MAX_CHANNELS_2GHZ 14
#define MAX_CHANNELS_4GHZ 4
/*
* This max value here is used only for the struct definition of
* wlcore_scan_channels. This struct is used by both 12xx
* and 18xx (which have different max 5ghz channels value).
* In order to make sure this is large enough, just use the
* max possible 5ghz channels.
*/
#define MAX_CHANNELS_5GHZ 42
#define SCAN_MAX_CYCLE_INTERVALS 16
/* The FW intervals can take up to 16 entries.
* The 1st entry isn't used (scan is immediate). The last
* entry should be used for the long_interval
*/
#define SCAN_MAX_SHORT_INTERVALS (SCAN_MAX_CYCLE_INTERVALS - 2)
#define SCAN_MAX_BANDS 3
enum {
SCAN_SSID_FILTER_ANY = 0,
SCAN_SSID_FILTER_SPECIFIC = 1,
SCAN_SSID_FILTER_LIST = 2,
SCAN_SSID_FILTER_DISABLED = 3
};
enum {
SCAN_BSS_TYPE_INDEPENDENT,
SCAN_BSS_TYPE_INFRASTRUCTURE,
SCAN_BSS_TYPE_ANY,
};
#define SCAN_CHANNEL_FLAGS_DFS BIT(0) /* channel is passive until an
activity is detected on it */
#define SCAN_CHANNEL_FLAGS_DFS_ENABLED BIT(1)
struct conn_scan_ch_params {
__le16 min_duration;
__le16 max_duration;
__le16 passive_duration;
u8 channel;
u8 tx_power_att;
/* bit 0: DFS channel; bit 1: DFS enabled */
u8 flags;
u8 padding[3];
} __packed;
#define SCHED_SCAN_MAX_SSIDS 16
enum {
SCAN_SSID_TYPE_PUBLIC = 0,
SCAN_SSID_TYPE_HIDDEN = 1,
};
struct wl1271_ssid {
u8 type;
u8 len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
/* u8 padding[2]; */
} __packed;
struct wl1271_cmd_sched_scan_ssid_list {
struct wl1271_cmd_header header;
u8 n_ssids;
struct wl1271_ssid ssids[SCHED_SCAN_MAX_SSIDS];
u8 role_id;
u8 padding[2];
} __packed;
struct wlcore_scan_channels {
u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
u8 active[SCAN_MAX_BANDS]; /* number of active scan channels */
u8 dfs; /* number of dfs channels in 5ghz */
u8 passive_active; /* number of passive before active channels 2.4ghz */
struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
};
enum {
SCAN_TYPE_SEARCH = 0,
SCAN_TYPE_PERIODIC = 1,
SCAN_TYPE_TRACKING = 2,
};
bool
wlcore_set_scan_chan_params(struct wl1271 *wl,
struct wlcore_scan_channels *cfg,
struct ieee80211_channel *channels[],
u32 n_channels,
u32 n_ssids,
int scan_type);
int
wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req);
#endif /* __WL1271_SCAN_H__ */
@@ -0,0 +1,443 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wl1271
*
* Copyright (C) 2009-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/platform_device.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/pm_runtime.h>
#include <linux/printk.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include "wlcore.h"
#include "wl12xx_80211.h"
#include "io.h"
static bool dump;
struct wl12xx_sdio_glue {
struct device *dev;
struct platform_device *core;
};
static const struct sdio_device_id wl1271_devices[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) },
{}
};
MODULE_DEVICE_TABLE(sdio, wl1271_devices);
static void wl1271_sdio_set_block_size(struct device *child,
unsigned int blksz)
{
struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
struct sdio_func *func = dev_to_sdio_func(glue->dev);
sdio_claim_host(func);
sdio_set_block_size(func, blksz);
sdio_release_host(func);
}
static int __must_check wl12xx_sdio_raw_read(struct device *child, int addr,
void *buf, size_t len, bool fixed)
{
int ret;
struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
struct sdio_func *func = dev_to_sdio_func(glue->dev);
sdio_claim_host(func);
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n",
addr, ((u8 *)buf)[0]);
} else {
if (fixed)
ret = sdio_readsb(func, buf, addr, len);
else
ret = sdio_memcpy_fromio(func, buf, addr, len);
dev_dbg(child->parent, "sdio read 53 addr 0x%x, %zu bytes\n",
addr, len);
}
sdio_release_host(func);
if (ret)
dev_err_ratelimited(child->parent, "sdio read failed (%d)\n", ret);
if (unlikely(dump)) {
printk(KERN_DEBUG "wlcore_sdio: READ from 0x%04x\n", addr);
print_hex_dump(KERN_DEBUG, "wlcore_sdio: READ ",
DUMP_PREFIX_OFFSET, 16, 1,
buf, len, false);
}
return ret;
}
static int __must_check wl12xx_sdio_raw_write(struct device *child, int addr,
void *buf, size_t len, bool fixed)
{
int ret;
struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
struct sdio_func *func = dev_to_sdio_func(glue->dev);
sdio_claim_host(func);
if (unlikely(dump)) {
printk(KERN_DEBUG "wlcore_sdio: WRITE to 0x%04x\n", addr);
print_hex_dump(KERN_DEBUG, "wlcore_sdio: WRITE ",
DUMP_PREFIX_OFFSET, 16, 1,
buf, len, false);
}
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n",
addr, ((u8 *)buf)[0]);
} else {
dev_dbg(child->parent, "sdio write 53 addr 0x%x, %zu bytes\n",
addr, len);
if (fixed)
ret = sdio_writesb(func, addr, buf, len);
else
ret = sdio_memcpy_toio(func, addr, buf, len);
}
sdio_release_host(func);
if (ret)
dev_err_ratelimited(child->parent, "sdio write failed (%d)\n", ret);
return ret;
}
static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue)
{
int ret;
struct sdio_func *func = dev_to_sdio_func(glue->dev);
struct mmc_card *card = func->card;
ret = pm_runtime_resume_and_get(&card->dev);
if (ret < 0) {
dev_err(glue->dev, "%s: failed to get_sync(%d)\n",
__func__, ret);
return ret;
}
sdio_claim_host(func);
/*
* To guarantee that the SDIO card is power cycled, as required to make
* the FW programming to succeed, let's do a brute force HW reset.
*/
mmc_hw_reset(card);
sdio_enable_func(func);
sdio_release_host(func);
return 0;
}
static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
{
struct sdio_func *func = dev_to_sdio_func(glue->dev);
struct mmc_card *card = func->card;
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
/* Let runtime PM know the card is powered off */
pm_runtime_put(&card->dev);
return 0;
}
static int wl12xx_sdio_set_power(struct device *child, bool enable)
{
struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
if (enable)
return wl12xx_sdio_power_on(glue);
else
return wl12xx_sdio_power_off(glue);
}
static struct wl1271_if_operations sdio_ops = {
.read = wl12xx_sdio_raw_read,
.write = wl12xx_sdio_raw_write,
.power = wl12xx_sdio_set_power,
.set_block_size = wl1271_sdio_set_block_size,
};
#ifdef CONFIG_OF
static const struct wilink_family_data wl127x_data = {
.name = "wl127x",
.nvs_name = "ti-connectivity/wl127x-nvs.bin",
};
static const struct wilink_family_data wl128x_data = {
.name = "wl128x",
.nvs_name = "ti-connectivity/wl128x-nvs.bin",
};
static const struct wilink_family_data wl18xx_data = {
.name = "wl18xx",
.cfg_name = "ti-connectivity/wl18xx-conf.bin",
.nvs_name = "ti-connectivity/wl1271-nvs.bin",
};
static const struct of_device_id wlcore_sdio_of_match_table[] = {
{ .compatible = "ti,wl1271", .data = &wl127x_data },
{ .compatible = "ti,wl1273", .data = &wl127x_data },
{ .compatible = "ti,wl1281", .data = &wl128x_data },
{ .compatible = "ti,wl1283", .data = &wl128x_data },
{ .compatible = "ti,wl1285", .data = &wl128x_data },
{ .compatible = "ti,wl1801", .data = &wl18xx_data },
{ .compatible = "ti,wl1805", .data = &wl18xx_data },
{ .compatible = "ti,wl1807", .data = &wl18xx_data },
{ .compatible = "ti,wl1831", .data = &wl18xx_data },
{ .compatible = "ti,wl1835", .data = &wl18xx_data },
{ .compatible = "ti,wl1837", .data = &wl18xx_data },
{ }
};
static int wlcore_probe_of(struct device *dev, int *irq, int *wakeirq,
struct wlcore_platdev_data *pdev_data)
{
struct device_node *np = dev->of_node;
const struct of_device_id *of_id;
of_id = of_match_node(wlcore_sdio_of_match_table, np);
if (!of_id)
return -ENODEV;
pdev_data->family = of_id->data;
*irq = irq_of_parse_and_map(np, 0);
if (!*irq) {
dev_err(dev, "No irq in platform data\n");
return -EINVAL;
}
*wakeirq = irq_of_parse_and_map(np, 1);
/* optional clock frequency params */
of_property_read_u32(np, "ref-clock-frequency",
&pdev_data->ref_clock_freq);
of_property_read_u32(np, "tcxo-clock-frequency",
&pdev_data->tcxo_clock_freq);
return 0;
}
#else
static int wlcore_probe_of(struct device *dev, int *irq, int *wakeirq,
struct wlcore_platdev_data *pdev_data)
{
return -ENODATA;
}
#endif
static int wl1271_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct wlcore_platdev_data *pdev_data;
struct wl12xx_sdio_glue *glue;
struct resource res[2];
mmc_pm_flag_t mmcflags;
int ret = -ENOMEM;
int irq, wakeirq, num_irqs;
const char *chip_family;
/* We are only able to handle the wlan function */
if (func->num != 0x02)
return -ENODEV;
pdev_data = devm_kzalloc(&func->dev, sizeof(*pdev_data), GFP_KERNEL);
if (!pdev_data)
return -ENOMEM;
pdev_data->if_ops = &sdio_ops;
glue = devm_kzalloc(&func->dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
return -ENOMEM;
glue->dev = &func->dev;
/* Grab access to FN0 for ELP reg. */
func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
/* Use block mode for transferring over one block size of data */
func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
ret = wlcore_probe_of(&func->dev, &irq, &wakeirq, pdev_data);
if (ret)
goto out;
/* if sdio can keep power while host is suspended, enable wow */
mmcflags = sdio_get_host_pm_caps(func);
dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
if (mmcflags & MMC_PM_KEEP_POWER)
pdev_data->pwr_in_suspend = true;
sdio_set_drvdata(func, glue);
/* Tell PM core that we don't need the card to be powered now */
pm_runtime_put_noidle(&func->dev);
/*
* Due to a hardware bug, we can't differentiate wl18xx from
* wl12xx, because both report the same device ID. The only
* way to differentiate is by checking the SDIO revision,
* which is 3.00 on the wl18xx chips.
*/
if (func->card->cccr.sdio_vsn == SDIO_SDIO_REV_3_00)
chip_family = "wl18xx";
else
chip_family = "wl12xx";
glue->core = platform_device_alloc(chip_family, PLATFORM_DEVID_AUTO);
if (!glue->core) {
dev_err(glue->dev, "can't allocate platform_device");
ret = -ENOMEM;
goto out;
}
glue->core->dev.parent = &func->dev;
memset(res, 0x00, sizeof(res));
res[0] = DEFINE_RES_IRQ_NAMED(irq, "irq");
res[0].flags |= irq_get_trigger_type(irq);
if (wakeirq > 0) {
res[1] = DEFINE_RES_IRQ_NAMED(wakeirq, "wakeirq");
res[1].flags |= irq_get_trigger_type(wakeirq);
num_irqs = 2;
} else {
num_irqs = 1;
}
ret = platform_device_add_resources(glue->core, res, num_irqs);
if (ret) {
dev_err(glue->dev, "can't add resources\n");
goto out_dev_put;
}
ret = platform_device_add_data(glue->core, pdev_data,
sizeof(*pdev_data));
if (ret) {
dev_err(glue->dev, "can't add platform data\n");
goto out_dev_put;
}
ret = platform_device_add(glue->core);
if (ret) {
dev_err(glue->dev, "can't add platform device\n");
goto out_dev_put;
}
return 0;
out_dev_put:
platform_device_put(glue->core);
out:
return ret;
}
static void wl1271_remove(struct sdio_func *func)
{
struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
/* Undo decrement done above in wl1271_probe */
pm_runtime_get_noresume(&func->dev);
platform_device_unregister(glue->core);
}
static int wl1271_suspend(struct device *dev)
{
/* Tell MMC/SDIO core it's OK to power down the card
* (if it isn't already), but not to remove it completely */
struct sdio_func *func = dev_to_sdio_func(dev);
struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
struct wl1271 *wl = platform_get_drvdata(glue->core);
mmc_pm_flag_t sdio_flags;
int ret = 0;
if (!wl) {
dev_err(dev, "no wilink module was probed\n");
goto out;
}
dev_dbg(dev, "wl1271 suspend. wow_enabled: %d\n",
wl->wow_enabled);
/* check whether sdio should keep power */
if (wl->wow_enabled) {
sdio_flags = sdio_get_host_pm_caps(func);
if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
dev_err(dev, "can't keep power while host "
"is suspended\n");
ret = -EINVAL;
goto out;
}
/* keep power while host suspended */
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
if (ret) {
dev_err(dev, "error while trying to keep power\n");
goto out;
}
}
out:
return ret;
}
static int wl1271_resume(struct device *dev)
{
dev_dbg(dev, "wl1271 resume\n");
return 0;
}
static const struct dev_pm_ops wl1271_sdio_pm_ops = {
.suspend = wl1271_suspend,
.resume = wl1271_resume,
};
static struct sdio_driver wl1271_sdio_driver = {
.name = "wl1271_sdio",
.id_table = wl1271_devices,
.probe = wl1271_probe,
.remove = wl1271_remove,
.drv = {
.pm = pm_ptr(&wl1271_sdio_pm_ops),
},
};
module_sdio_driver(wl1271_sdio_driver);
module_param(dump, bool, 0600);
MODULE_PARM_DESC(dump, "Enable sdio read/write dumps.");
MODULE_DESCRIPTION("TI WLAN SDIO helpers");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
@@ -0,0 +1,569 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/swab.h>
#include <linux/crc7.h>
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <linux/of_irq.h>
#include <linux/regulator/consumer.h>
#include "wlcore.h"
#include "wl12xx_80211.h"
#include "io.h"
#define WSPI_CMD_READ 0x40000000
#define WSPI_CMD_WRITE 0x00000000
#define WSPI_CMD_FIXED 0x20000000
#define WSPI_CMD_BYTE_LENGTH 0x1FFE0000
#define WSPI_CMD_BYTE_LENGTH_OFFSET 17
#define WSPI_CMD_BYTE_ADDR 0x0001FFFF
#define WSPI_INIT_CMD_CRC_LEN 5
#define WSPI_INIT_CMD_START 0x00
#define WSPI_INIT_CMD_TX 0x40
/* the extra bypass bit is sampled by the TNET as '1' */
#define WSPI_INIT_CMD_BYPASS_BIT 0x80
#define WSPI_INIT_CMD_FIXEDBUSY_LEN 0x07
#define WSPI_INIT_CMD_EN_FIXEDBUSY 0x80
#define WSPI_INIT_CMD_DIS_FIXEDBUSY 0x00
#define WSPI_INIT_CMD_IOD 0x40
#define WSPI_INIT_CMD_IP 0x20
#define WSPI_INIT_CMD_CS 0x10
#define WSPI_INIT_CMD_WS 0x08
#define WSPI_INIT_CMD_WSPI 0x01
#define WSPI_INIT_CMD_END 0x01
#define WSPI_INIT_CMD_LEN 8
#define HW_ACCESS_WSPI_FIXED_BUSY_LEN \
((WL1271_BUSY_WORD_LEN - 4) / sizeof(u32))
#define HW_ACCESS_WSPI_INIT_CMD_MASK 0
/* HW limitation: maximum possible chunk size is 4095 bytes */
#define WSPI_MAX_CHUNK_SIZE 4092
/*
* wl18xx driver aggregation buffer size is (13 * 4K) compared to
* (4 * 4K) for wl12xx, so use the larger buffer needed for wl18xx
*/
#define SPI_AGGR_BUFFER_SIZE (13 * SZ_4K)
/* Maximum number of SPI write chunks */
#define WSPI_MAX_NUM_OF_CHUNKS \
((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
static const struct wilink_family_data wl127x_data = {
.name = "wl127x",
.nvs_name = "ti-connectivity/wl127x-nvs.bin",
};
static const struct wilink_family_data wl128x_data = {
.name = "wl128x",
.nvs_name = "ti-connectivity/wl128x-nvs.bin",
};
static const struct wilink_family_data wl18xx_data = {
.name = "wl18xx",
.cfg_name = "ti-connectivity/wl18xx-conf.bin",
.nvs_name = "ti-connectivity/wl1271-nvs.bin",
};
struct wl12xx_spi_glue {
struct device *dev;
struct platform_device *core;
struct regulator *reg; /* Power regulator */
};
static void wl12xx_spi_reset(struct device *child)
{
struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
u8 *cmd;
struct spi_transfer t;
struct spi_message m;
cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
if (!cmd) {
dev_err(child->parent,
"could not allocate cmd for spi reset\n");
return;
}
memset(&t, 0, sizeof(t));
spi_message_init(&m);
memset(cmd, 0xff, WSPI_INIT_CMD_LEN);
t.tx_buf = cmd;
t.len = WSPI_INIT_CMD_LEN;
spi_message_add_tail(&t, &m);
spi_sync(to_spi_device(glue->dev), &m);
kfree(cmd);
}
static void wl12xx_spi_init(struct device *child)
{
struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
struct spi_transfer t;
struct spi_message m;
struct spi_device *spi = to_spi_device(glue->dev);
u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
if (!cmd) {
dev_err(child->parent,
"could not allocate cmd for spi init\n");
return;
}
memset(&t, 0, sizeof(t));
spi_message_init(&m);
/*
* Set WSPI_INIT_COMMAND
* the data is being send from the MSB to LSB
*/
cmd[0] = 0xff;
cmd[1] = 0xff;
cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
cmd[3] = 0;
cmd[4] = 0;
cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
| WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0)
cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY;
else
cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END;
/*
* The above is the logical order; it must actually be stored
* in the buffer byte-swapped.
*/
__swab32s((u32 *)cmd);
__swab32s((u32 *)cmd+1);
t.tx_buf = cmd;
t.len = WSPI_INIT_CMD_LEN;
spi_message_add_tail(&t, &m);
spi_sync(to_spi_device(glue->dev), &m);
/* Send extra clocks with inverted CS (high). this is required
* by the wilink family in order to successfully enter WSPI mode.
*/
spi->mode ^= SPI_CS_HIGH;
memset(&m, 0, sizeof(m));
spi_message_init(&m);
cmd[0] = 0xff;
cmd[1] = 0xff;
cmd[2] = 0xff;
cmd[3] = 0xff;
__swab32s((u32 *)cmd);
t.tx_buf = cmd;
t.len = 4;
spi_message_add_tail(&t, &m);
spi_sync(to_spi_device(glue->dev), &m);
/* Restore chip select configuration to normal */
spi->mode ^= SPI_CS_HIGH;
kfree(cmd);
}
#define WL1271_BUSY_WORD_TIMEOUT 1000
static int wl12xx_spi_read_busy(struct device *child)
{
struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
struct wl1271 *wl = dev_get_drvdata(child);
struct spi_transfer t[1];
struct spi_message m;
u32 *busy_buf;
int num_busy_bytes = 0;
/*
* Read further busy words from SPI until a non-busy word is
* encountered, then read the data itself into the buffer.
*/
num_busy_bytes = WL1271_BUSY_WORD_TIMEOUT;
busy_buf = wl->buffer_busyword;
while (num_busy_bytes) {
num_busy_bytes--;
spi_message_init(&m);
memset(t, 0, sizeof(t));
t[0].rx_buf = busy_buf;
t[0].len = sizeof(u32);
t[0].cs_change = true;
spi_message_add_tail(&t[0], &m);
spi_sync(to_spi_device(glue->dev), &m);
if (*busy_buf & 0x1)
return 0;
}
/* The SPI bus is unresponsive, the read failed. */
dev_err(child->parent, "SPI read busy-word timeout!\n");
return -ETIMEDOUT;
}
static int __must_check wl12xx_spi_raw_read(struct device *child, int addr,
void *buf, size_t len, bool fixed)
{
struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
struct wl1271 *wl = dev_get_drvdata(child);
struct spi_transfer t[2];
struct spi_message m;
u32 *busy_buf;
u32 *cmd;
u32 chunk_len;
while (len > 0) {
chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
cmd = &wl->buffer_cmd;
busy_buf = wl->buffer_busyword;
*cmd = 0;
*cmd |= WSPI_CMD_READ;
*cmd |= (chunk_len << WSPI_CMD_BYTE_LENGTH_OFFSET) &
WSPI_CMD_BYTE_LENGTH;
*cmd |= addr & WSPI_CMD_BYTE_ADDR;
if (fixed)
*cmd |= WSPI_CMD_FIXED;
spi_message_init(&m);
memset(t, 0, sizeof(t));
t[0].tx_buf = cmd;
t[0].len = 4;
t[0].cs_change = true;
spi_message_add_tail(&t[0], &m);
/* Busy and non busy words read */
t[1].rx_buf = busy_buf;
t[1].len = WL1271_BUSY_WORD_LEN;
t[1].cs_change = true;
spi_message_add_tail(&t[1], &m);
spi_sync(to_spi_device(glue->dev), &m);
if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1) &&
wl12xx_spi_read_busy(child)) {
memset(buf, 0, chunk_len);
return 0;
}
spi_message_init(&m);
memset(t, 0, sizeof(t));
t[0].rx_buf = buf;
t[0].len = chunk_len;
t[0].cs_change = true;
spi_message_add_tail(&t[0], &m);
spi_sync(to_spi_device(glue->dev), &m);
if (!fixed)
addr += chunk_len;
buf += chunk_len;
len -= chunk_len;
}
return 0;
}
static int __wl12xx_spi_raw_write(struct device *child, int addr,
void *buf, size_t len, bool fixed)
{
struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
struct spi_transfer *t;
struct spi_message m;
u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
u32 *cmd;
u32 chunk_len;
int i;
/* SPI write buffers - 2 for each chunk */
t = kzalloc(sizeof(*t) * 2 * WSPI_MAX_NUM_OF_CHUNKS, GFP_KERNEL);
if (!t)
return -ENOMEM;
WARN_ON(len > SPI_AGGR_BUFFER_SIZE);
spi_message_init(&m);
cmd = &commands[0];
i = 0;
while (len > 0) {
chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len);
*cmd = 0;
*cmd |= WSPI_CMD_WRITE;
*cmd |= (chunk_len << WSPI_CMD_BYTE_LENGTH_OFFSET) &
WSPI_CMD_BYTE_LENGTH;
*cmd |= addr & WSPI_CMD_BYTE_ADDR;
if (fixed)
*cmd |= WSPI_CMD_FIXED;
t[i].tx_buf = cmd;
t[i].len = sizeof(*cmd);
spi_message_add_tail(&t[i++], &m);
t[i].tx_buf = buf;
t[i].len = chunk_len;
spi_message_add_tail(&t[i++], &m);
if (!fixed)
addr += chunk_len;
buf += chunk_len;
len -= chunk_len;
cmd++;
}
spi_sync(to_spi_device(glue->dev), &m);
kfree(t);
return 0;
}
static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
void *buf, size_t len, bool fixed)
{
/* The ELP wakeup write may fail the first time due to internal
* hardware latency. It is safer to send the wakeup command twice to
* avoid unexpected failures.
*/
if (addr == HW_ACCESS_ELP_CTRL_REG)
__wl12xx_spi_raw_write(child, addr, buf, len, fixed);
return __wl12xx_spi_raw_write(child, addr, buf, len, fixed);
}
/**
* wl12xx_spi_set_power - power on/off the wl12xx unit
* @child: wl12xx device handle.
* @enable: true/false to power on/off the unit.
*
* use the WiFi enable regulator to enable/disable the WiFi unit.
*/
static int wl12xx_spi_set_power(struct device *child, bool enable)
{
int ret = 0;
struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
WARN_ON(!glue->reg);
/* Update regulator state */
if (enable) {
ret = regulator_enable(glue->reg);
if (ret)
dev_err(child, "Power enable failure\n");
} else {
ret = regulator_disable(glue->reg);
if (ret)
dev_err(child, "Power disable failure\n");
}
return ret;
}
/*
* wl12xx_spi_set_block_size
*
* This function is not needed for spi mode, but need to be present.
* Without it defined the wlcore fallback to use the wrong packet
* allignment on tx.
*/
static void wl12xx_spi_set_block_size(struct device *child,
unsigned int blksz)
{
}
static struct wl1271_if_operations spi_ops = {
.read = wl12xx_spi_raw_read,
.write = wl12xx_spi_raw_write,
.reset = wl12xx_spi_reset,
.init = wl12xx_spi_init,
.power = wl12xx_spi_set_power,
.set_block_size = wl12xx_spi_set_block_size,
};
static const struct of_device_id wlcore_spi_of_match_table[] = {
{ .compatible = "ti,wl1271", .data = &wl127x_data},
{ .compatible = "ti,wl1273", .data = &wl127x_data},
{ .compatible = "ti,wl1281", .data = &wl128x_data},
{ .compatible = "ti,wl1283", .data = &wl128x_data},
{ .compatible = "ti,wl1285", .data = &wl128x_data},
{ .compatible = "ti,wl1801", .data = &wl18xx_data},
{ .compatible = "ti,wl1805", .data = &wl18xx_data},
{ .compatible = "ti,wl1807", .data = &wl18xx_data},
{ .compatible = "ti,wl1831", .data = &wl18xx_data},
{ .compatible = "ti,wl1835", .data = &wl18xx_data},
{ .compatible = "ti,wl1837", .data = &wl18xx_data},
{ }
};
MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table);
/**
* wlcore_probe_of - DT node parsing.
* @spi: SPI slave device parameters.
* @glue: wl12xx SPI bus to slave device glue parameters.
* @pdev_data: wlcore device parameters
*/
static int wlcore_probe_of(struct spi_device *spi, struct wl12xx_spi_glue *glue,
struct wlcore_platdev_data *pdev_data)
{
struct device_node *dt_node = spi->dev.of_node;
const struct of_device_id *of_id;
of_id = of_match_node(wlcore_spi_of_match_table, dt_node);
if (!of_id)
return -ENODEV;
pdev_data->family = of_id->data;
dev_info(&spi->dev, "selected chip family is %s\n",
pdev_data->family->name);
pdev_data->ref_clock_xtal = of_property_read_bool(dt_node, "clock-xtal");
/* optional clock frequency params */
of_property_read_u32(dt_node, "ref-clock-frequency",
&pdev_data->ref_clock_freq);
of_property_read_u32(dt_node, "tcxo-clock-frequency",
&pdev_data->tcxo_clock_freq);
return 0;
}
static int wl1271_probe(struct spi_device *spi)
{
struct wl12xx_spi_glue *glue;
struct wlcore_platdev_data *pdev_data;
struct resource res[1];
int ret;
pdev_data = devm_kzalloc(&spi->dev, sizeof(*pdev_data), GFP_KERNEL);
if (!pdev_data)
return -ENOMEM;
pdev_data->if_ops = &spi_ops;
glue = devm_kzalloc(&spi->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&spi->dev, "can't allocate glue\n");
return -ENOMEM;
}
glue->dev = &spi->dev;
spi_set_drvdata(spi, glue);
/* 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;
glue->reg = devm_regulator_get(&spi->dev, "vwlan");
if (IS_ERR(glue->reg))
return dev_err_probe(glue->dev, PTR_ERR(glue->reg),
"can't get regulator\n");
ret = wlcore_probe_of(spi, glue, pdev_data);
if (ret) {
dev_err(glue->dev,
"can't get device tree parameters (%d)\n", ret);
return ret;
}
ret = spi_setup(spi);
if (ret < 0) {
dev_err(glue->dev, "spi_setup failed\n");
return ret;
}
glue->core = platform_device_alloc(pdev_data->family->name,
PLATFORM_DEVID_AUTO);
if (!glue->core) {
dev_err(glue->dev, "can't allocate platform_device\n");
return -ENOMEM;
}
glue->core->dev.parent = &spi->dev;
memset(res, 0x00, sizeof(res));
res[0].start = spi->irq;
res[0].flags = IORESOURCE_IRQ | irq_get_trigger_type(spi->irq);
res[0].name = "irq";
ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res));
if (ret) {
dev_err(glue->dev, "can't add resources\n");
goto out_dev_put;
}
ret = platform_device_add_data(glue->core, pdev_data,
sizeof(*pdev_data));
if (ret) {
dev_err(glue->dev, "can't add platform data\n");
goto out_dev_put;
}
ret = platform_device_add(glue->core);
if (ret) {
dev_err(glue->dev, "can't register platform device\n");
goto out_dev_put;
}
return 0;
out_dev_put:
platform_device_put(glue->core);
return ret;
}
static void wl1271_remove(struct spi_device *spi)
{
struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
platform_device_unregister(glue->core);
}
static struct spi_driver wl1271_spi_driver = {
.driver = {
.name = "wl1271_spi",
.of_match_table = wlcore_spi_of_match_table,
},
.probe = wl1271_probe,
.remove = wl1271_remove,
};
module_spi_driver(wl1271_spi_driver);
MODULE_DESCRIPTION("TI WLAN SPI helpers");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_ALIAS("spi:wl1271");
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wlcore
*
* Copyright (C) 2013 Texas Instruments Inc.
*/
#include <linux/pm_runtime.h>
#include "acx.h"
#include "wlcore.h"
#include "debug.h"
#include "sysfs.h"
static ssize_t bt_coex_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct wl1271 *wl = dev_get_drvdata(dev);
ssize_t len;
mutex_lock(&wl->mutex);
len = sysfs_emit(buf, "%d\n\n0 - off\n1 - on\n", wl->sg_enabled);
mutex_unlock(&wl->mutex);
return len;
}
static ssize_t bt_coex_state_store(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 = pm_runtime_resume_and_get(wl->dev);
if (ret < 0)
goto out;
wl1271_acx_sg_enable(wl, wl->sg_enabled);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
return count;
}
static DEVICE_ATTR_RW(bt_coex_state);
static ssize_t hw_pg_ver_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct wl1271 *wl = dev_get_drvdata(dev);
ssize_t len;
mutex_lock(&wl->mutex);
if (wl->hw_pg_ver >= 0)
len = sysfs_emit(buf, "%d\n", wl->hw_pg_ver);
else
len = sysfs_emit(buf, "n/a\n");
mutex_unlock(&wl->mutex);
return len;
}
static DEVICE_ATTR_RO(hw_pg_ver);
static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct wl1271 *wl = dev_get_drvdata(dev);
ssize_t len;
int ret;
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_t(size_t, count, 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 const struct bin_attribute fwlog_attr = {
.attr = { .name = "fwlog", .mode = 0400 },
.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);
}
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wlcore
*
* Copyright (C) 2013 Texas Instruments Inc.
*/
#ifndef __SYSFS_H__
#define __SYSFS_H__
int wlcore_sysfs_init(struct wl1271 *wl);
void wlcore_sysfs_free(struct wl1271 *wl);
#endif
@@ -0,0 +1,384 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wl1271
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#include "testmode.h"
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <net/genetlink.h>
#include "wlcore.h"
#include "debug.h"
#include "acx.h"
#include "io.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, /* Not in use. Keep to not break ABI */
WL1271_TM_CMD_SET_PLT_MODE,
WL1271_TM_CMD_RECOVER, /* Not in use. Keep to not break ABI */
WL1271_TM_CMD_GET_MAC,
__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 const 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);
if (unlikely(wl->state != WLCORE_STATE_ON)) {
ret = -EINVAL;
goto out;
}
ret = pm_runtime_resume_and_get(wl->dev);
if (ret < 0)
goto out;
ret = wl1271_cmd_test(wl, buf, buf_len, answer);
if (ret < 0) {
wl1271_warning("testmode cmd test failed: %d", ret);
goto out_sleep;
}
if (answer) {
/* If we got bip calibration answer print radio status */
struct wl1271_cmd_cal_p2g *params =
(struct wl1271_cmd_cal_p2g *) buf;
s16 radio_status = (s16) le16_to_cpu(params->radio_status);
if (params->test.id == TEST_CMD_P2G_CAL &&
radio_status < 0)
wl1271_warning("testmode cmd: radio status=%d",
radio_status);
else
wl1271_info("testmode cmd: radio status=%d",
radio_status);
len = nla_total_size(buf_len);
skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
if (!skb) {
ret = -ENOMEM;
goto out_sleep;
}
if (nla_put(skb, WL1271_TM_ATTR_DATA, buf_len, buf)) {
kfree_skb(skb);
ret = -EMSGSIZE;
goto out_sleep;
}
ret = cfg80211_testmode_reply(skb);
if (ret < 0)
goto out_sleep;
}
out_sleep:
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
return ret;
}
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]);
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON)) {
ret = -EINVAL;
goto out;
}
ret = pm_runtime_resume_and_get(wl->dev);
if (ret < 0)
goto out;
cmd = kzalloc_obj(*cmd);
if (!cmd) {
ret = -ENOMEM;
goto out_sleep;
}
ret = wl1271_cmd_interrogate(wl, ie_id, cmd,
sizeof(struct acx_header), sizeof(*cmd));
if (ret < 0) {
wl1271_warning("testmode cmd interrogate failed: %d", ret);
goto out_free;
}
skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd));
if (!skb) {
ret = -ENOMEM;
goto out_free;
}
if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd)) {
kfree_skb(skb);
ret = -EMSGSIZE;
goto out_free;
}
ret = cfg80211_testmode_reply(skb);
if (ret < 0)
goto out_free;
out_free:
kfree(cmd);
out_sleep:
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
return ret;
}
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_detect_fem(struct wl1271 *wl, struct nlattr *tb[])
{
/* return FEM type */
int ret, len;
struct sk_buff *skb;
ret = wl1271_plt_start(wl, PLT_FEM_DETECT);
if (ret < 0)
goto out;
mutex_lock(&wl->mutex);
len = nla_total_size(sizeof(wl->fem_manuf));
skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
if (!skb) {
ret = -ENOMEM;
goto out_mutex;
}
if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(wl->fem_manuf),
&wl->fem_manuf)) {
kfree_skb(skb);
ret = -EMSGSIZE;
goto out_mutex;
}
ret = cfg80211_testmode_reply(skb);
out_mutex:
mutex_unlock(&wl->mutex);
/* We always stop plt after DETECT mode */
wl1271_plt_stop(wl);
out:
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 PLT_OFF:
ret = wl1271_plt_stop(wl);
break;
case PLT_ON:
case PLT_CHIP_AWAKE:
ret = wl1271_plt_start(wl, val);
break;
case PLT_FEM_DETECT:
ret = wl1271_tm_detect_fem(wl, tb);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int wl12xx_tm_cmd_get_mac(struct wl1271 *wl, struct nlattr *tb[])
{
struct sk_buff *skb;
u8 mac_addr[ETH_ALEN];
int ret = 0;
mutex_lock(&wl->mutex);
if (!wl->plt) {
ret = -EINVAL;
goto out;
}
if (wl->fuse_oui_addr == 0 && wl->fuse_nic_addr == 0) {
ret = -EOPNOTSUPP;
goto out;
}
mac_addr[0] = (u8)(wl->fuse_oui_addr >> 16);
mac_addr[1] = (u8)(wl->fuse_oui_addr >> 8);
mac_addr[2] = (u8) wl->fuse_oui_addr;
mac_addr[3] = (u8)(wl->fuse_nic_addr >> 16);
mac_addr[4] = (u8)(wl->fuse_nic_addr >> 8);
mac_addr[5] = (u8) wl->fuse_nic_addr;
skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, ETH_ALEN);
if (!skb) {
ret = -ENOMEM;
goto out;
}
if (nla_put(skb, WL1271_TM_ATTR_DATA, ETH_ALEN, mac_addr)) {
kfree_skb(skb);
ret = -EMSGSIZE;
goto out;
}
ret = cfg80211_testmode_reply(skb);
if (ret < 0)
goto out;
out:
mutex_unlock(&wl->mutex);
return ret;
}
int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len)
{
struct wl1271 *wl = hw->priv;
struct nlattr *tb[WL1271_TM_ATTR_MAX + 1];
u32 nla_cmd;
int err;
err = nla_parse_deprecated(tb, WL1271_TM_ATTR_MAX, data, len,
wl1271_tm_policy, NULL);
if (err)
return err;
if (!tb[WL1271_TM_ATTR_CMD_ID])
return -EINVAL;
nla_cmd = nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID]);
/* Only SET_PLT_MODE is allowed in case of mode PLT_CHIP_AWAKE */
if (wl->plt_mode == PLT_CHIP_AWAKE &&
nla_cmd != WL1271_TM_CMD_SET_PLT_MODE)
return -EOPNOTSUPP;
switch (nla_cmd) {
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_SET_PLT_MODE:
return wl1271_tm_cmd_set_plt_mode(wl, tb);
case WL1271_TM_CMD_GET_MAC:
return wl12xx_tm_cmd_get_mac(wl, tb);
default:
return -EOPNOTSUPP;
}
}
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __TESTMODE_H__
#define __TESTMODE_H__
#include <net/mac80211.h>
int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len);
#endif /* __WL1271_TESTMODE_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,273 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __TX_H__
#define __TX_H__
#define TX_HW_MGMT_PKT_LIFETIME_TU 2000
#define TX_HW_AP_MODE_PKT_LIFETIME_TU 8000
#define TX_HW_ATTR_SAVE_RETRIES BIT(0)
#define TX_HW_ATTR_HEADER_PAD BIT(1)
#define TX_HW_ATTR_SESSION_COUNTER (BIT(2) | BIT(3) | BIT(4))
#define TX_HW_ATTR_RATE_POLICY (BIT(5) | BIT(6) | BIT(7) | \
BIT(8) | BIT(9))
#define TX_HW_ATTR_LAST_WORD_PAD (BIT(10) | BIT(11))
#define TX_HW_ATTR_TX_CMPLT_REQ BIT(12)
#define TX_HW_ATTR_TX_DUMMY_REQ BIT(13)
#define TX_HW_ATTR_HOST_ENCRYPT BIT(14)
#define TX_HW_ATTR_EAPOL_FRAME BIT(15)
#define TX_HW_ATTR_OFST_SAVE_RETRIES 0
#define TX_HW_ATTR_OFST_HEADER_PAD 1
#define TX_HW_ATTR_OFST_SESSION_COUNTER 2
#define TX_HW_ATTR_OFST_RATE_POLICY 5
#define TX_HW_ATTR_OFST_LAST_WORD_PAD 10
#define TX_HW_ATTR_OFST_TX_CMPLT_REQ 12
#define TX_HW_RESULT_QUEUE_LEN 16
#define TX_HW_RESULT_QUEUE_LEN_MASK 0xf
#define WL1271_TX_ALIGN_TO 4
#define WL1271_EXTRA_SPACE_TKIP 4
#define WL1271_EXTRA_SPACE_AES 8
#define WL1271_EXTRA_SPACE_MAX 8
/* Used for management frames and dummy packets */
#define WL1271_TID_MGMT 7
/* stop a ROC for pending authentication reply after this time (ms) */
#define WLCORE_PEND_AUTH_ROC_TIMEOUT 1000
struct wl127x_tx_mem {
/*
* Number of extra memory blocks to allocate for this packet
* in addition to the number of blocks derived from the packet
* length.
*/
u8 extra_blocks;
/*
* Total number of memory blocks allocated by the host for
* this packet. Must be equal or greater than the actual
* blocks number allocated by HW.
*/
u8 total_mem_blocks;
} __packed;
struct wl128x_tx_mem {
/*
* Total number of memory blocks allocated by the host for
* this packet.
*/
u8 total_mem_blocks;
/*
* Number of extra bytes, at the end of the frame. the host
* uses this padding to complete each frame to integer number
* of SDIO blocks.
*/
u8 extra_bytes;
} __packed;
struct wl18xx_tx_mem {
/*
* Total number of memory blocks allocated by the host for
* this packet.
*/
u8 total_mem_blocks;
/*
* control bits
*/
u8 ctrl;
} __packed;
/*
* On wl128x based devices, when TX packets are aggregated, each packet
* size must be aligned to the SDIO block size. The maximum block size
* is bounded by the type of the padded bytes field that is sent to the
* FW. Currently the type is u8, so the maximum block size is 256 bytes.
*/
#define WL12XX_BUS_BLOCK_SIZE min(512u, \
(1u << (8 * sizeof(((struct wl128x_tx_mem *) 0)->extra_bytes))))
struct wl1271_tx_hw_descr {
/* Length of packet in words, including descriptor+header+data */
__le16 length;
union {
struct wl127x_tx_mem wl127x_mem;
struct wl128x_tx_mem wl128x_mem;
struct wl18xx_tx_mem wl18xx_mem;
} __packed;
/* Device time (in us) when the packet arrived to the driver */
__le32 start_time;
/*
* Max delay in TUs until transmission. The last device time the
* packet can be transmitted is: start_time + (1024 * life_time)
*/
__le16 life_time;
/* Bitwise fields - see TX_ATTR... definitions above. */
__le16 tx_attr;
/* Packet identifier used also in the Tx-Result. */
u8 id;
/* The packet TID value (as User-Priority) */
u8 tid;
/* host link ID (HLID) */
u8 hlid;
union {
u8 wl12xx_reserved;
/*
* bit 0 -> 0 = udp, 1 = tcp
* bit 1:7 -> IP header offset
*/
u8 wl18xx_checksum_data;
} __packed;
} __packed;
enum wl1271_tx_hw_res_status {
TX_SUCCESS = 0,
TX_HW_ERROR = 1,
TX_DISABLED = 2,
TX_RETRY_EXCEEDED = 3,
TX_TIMEOUT = 4,
TX_KEY_NOT_FOUND = 5,
TX_PEER_NOT_FOUND = 6,
TX_SESSION_MISMATCH = 7,
TX_LINK_NOT_VALID = 8,
};
struct wl1271_tx_hw_res_descr {
/* Packet Identifier - same value used in the Tx descriptor.*/
u8 id;
/* The status of the transmission, indicating success or one of
several possible reasons for failure. */
u8 status;
/* Total air access duration including all retrys and overheads.*/
__le16 medium_usage;
/* The time passed from host xfer to Tx-complete.*/
__le32 fw_handling_time;
/* Total media delay
(from 1st EDCA AIFS counter until TX Complete). */
__le32 medium_delay;
/* LS-byte of last TKIP seq-num (saved per AC for recovery). */
u8 tx_security_sequence_number_lsb;
/* Retry count - number of transmissions without successful ACK.*/
u8 ack_failures;
/* The rate that succeeded getting ACK
(Valid only if status=SUCCESS). */
u8 rate_class_index;
/* for 4-byte alignment. */
u8 spare;
} __packed;
struct wl1271_tx_hw_res_if {
__le32 tx_result_fw_counter;
__le32 tx_result_host_counter;
struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN];
} __packed;
enum wlcore_queue_stop_reason {
WLCORE_QUEUE_STOP_REASON_WATERMARK,
WLCORE_QUEUE_STOP_REASON_FW_RESTART,
WLCORE_QUEUE_STOP_REASON_FLUSH,
WLCORE_QUEUE_STOP_REASON_SPARE_BLK, /* 18xx specific */
};
static inline int wl1271_tx_get_queue(int queue)
{
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;
}
}
static inline
int wlcore_tx_get_mac80211_queue(struct wl12xx_vif *wlvif, int queue)
{
int mac_queue = wlvif->hw_queue_base;
switch (queue) {
case CONF_TX_AC_VO:
return mac_queue + 0;
case CONF_TX_AC_VI:
return mac_queue + 1;
case CONF_TX_AC_BE:
return mac_queue + 2;
case CONF_TX_AC_BK:
return mac_queue + 3;
default:
return mac_queue + 2;
}
}
static inline int wl1271_tx_total_queue_count(struct wl1271 *wl)
{
int i, count = 0;
for (i = 0; i < NUM_TX_QUEUES; i++)
count += wl->tx_queue_count[i];
return count;
}
void wl1271_tx_work(struct work_struct *work);
int wlcore_tx_work_locked(struct wl1271 *wl);
int wlcore_tx_complete(struct wl1271 *wl);
void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl12xx_tx_reset(struct wl1271 *wl);
void wl1271_tx_flush(struct wl1271 *wl);
u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum nl80211_band band);
u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
enum nl80211_band rate_band);
u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set);
u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb, struct ieee80211_sta *sta);
void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid);
void wl1271_handle_tx_low_watermark(struct wl1271 *wl);
bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb);
void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
unsigned int packet_length);
void wl1271_free_tx_id(struct wl1271 *wl, int id);
void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 queue, enum wlcore_queue_stop_reason reason);
void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
enum wlcore_queue_stop_reason reason);
void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
enum wlcore_queue_stop_reason reason);
void wlcore_stop_queues(struct wl1271 *wl,
enum wlcore_queue_stop_reason reason);
void wlcore_wake_queues(struct wl1271 *wl,
enum wlcore_queue_stop_reason reason);
bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl,
struct wl12xx_vif *wlvif, u8 queue,
enum wlcore_queue_stop_reason reason);
bool
wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
u8 queue,
enum wlcore_queue_stop_reason reason);
bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 queue);
/* from main.c */
void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl);
#endif
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file is part of wlcore
*
* Copyright (C) 2014 Texas Instruments. All rights reserved.
*/
#include <linux/pm_runtime.h>
#include <net/mac80211.h>
#include <net/netlink.h>
#include "wlcore.h"
#include "debug.h"
#include "hw_ops.h"
#include "vendor_cmd.h"
static const
struct nla_policy wlcore_vendor_attr_policy[NUM_WLCORE_VENDOR_ATTR] = {
[WLCORE_VENDOR_ATTR_FREQ] = { .type = NLA_U32 },
[WLCORE_VENDOR_ATTR_GROUP_ID] = { .type = NLA_U32 },
[WLCORE_VENDOR_ATTR_GROUP_KEY] = { .type = NLA_BINARY,
.len = WLAN_MAX_KEY_LEN },
};
static int
wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct wl1271 *wl = hw->priv;
struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
int ret;
wl1271_debug(DEBUG_CMD, "vendor cmd smart config start");
if (!data)
return -EINVAL;
ret = nla_parse_deprecated(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
wlcore_vendor_attr_policy, NULL);
if (ret)
return ret;
if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID])
return -EINVAL;
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON)) {
ret = -EINVAL;
goto out;
}
ret = pm_runtime_resume_and_get(wl->dev);
if (ret < 0)
goto out;
ret = wlcore_smart_config_start(wl,
nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]));
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
return ret;
}
static int
wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct wl1271 *wl = hw->priv;
int ret;
wl1271_debug(DEBUG_CMD, "testmode cmd smart config stop");
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON)) {
ret = -EINVAL;
goto out;
}
ret = pm_runtime_resume_and_get(wl->dev);
if (ret < 0)
goto out;
ret = wlcore_smart_config_stop(wl);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
return ret;
}
static int
wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct wl1271 *wl = hw->priv;
struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
int ret;
wl1271_debug(DEBUG_CMD, "testmode cmd smart config set group key");
if (!data)
return -EINVAL;
ret = nla_parse_deprecated(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
wlcore_vendor_attr_policy, NULL);
if (ret)
return ret;
if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID] ||
!tb[WLCORE_VENDOR_ATTR_GROUP_KEY])
return -EINVAL;
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON)) {
ret = -EINVAL;
goto out;
}
ret = pm_runtime_resume_and_get(wl->dev);
if (ret < 0)
goto out;
ret = wlcore_smart_config_set_group_key(wl,
nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]),
nla_len(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]),
nla_data(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]));
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
return ret;
}
static const struct wiphy_vendor_command wlcore_vendor_commands[] = {
{
.info = {
.vendor_id = TI_OUI,
.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_START,
},
.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlcore_vendor_cmd_smart_config_start,
.policy = wlcore_vendor_attr_policy,
},
{
.info = {
.vendor_id = TI_OUI,
.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
},
.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlcore_vendor_cmd_smart_config_stop,
.policy = wlcore_vendor_attr_policy,
},
{
.info = {
.vendor_id = TI_OUI,
.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
},
.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlcore_vendor_cmd_smart_config_set_group_key,
.policy = wlcore_vendor_attr_policy,
},
};
static const struct nl80211_vendor_cmd_info wlcore_vendor_events[] = {
{
.vendor_id = TI_OUI,
.subcmd = WLCORE_VENDOR_EVENT_SC_SYNC,
},
{
.vendor_id = TI_OUI,
.subcmd = WLCORE_VENDOR_EVENT_SC_DECODE,
},
};
void wlcore_set_vendor_commands(struct wiphy *wiphy)
{
wiphy->vendor_commands = wlcore_vendor_commands;
wiphy->n_vendor_commands = ARRAY_SIZE(wlcore_vendor_commands);
wiphy->vendor_events = wlcore_vendor_events;
wiphy->n_vendor_events = ARRAY_SIZE(wlcore_vendor_events);
}
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wlcore
*
* Copyright (C) 2014 Texas Instruments. All rights reserved.
*/
#ifndef __WLCORE_VENDOR_H__
#define __WLCORE_VENDOR_H__
#ifdef __KERNEL__
void wlcore_set_vendor_commands(struct wiphy *wiphy);
#endif
#define TI_OUI 0x080028
enum wlcore_vendor_commands {
WLCORE_VENDOR_CMD_SMART_CONFIG_START,
WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
NUM_WLCORE_VENDOR_CMD,
MAX_WLCORE_VENDOR_CMD = NUM_WLCORE_VENDOR_CMD - 1
};
enum wlcore_vendor_attributes {
WLCORE_VENDOR_ATTR_FREQ,
WLCORE_VENDOR_ATTR_PSK,
WLCORE_VENDOR_ATTR_SSID,
WLCORE_VENDOR_ATTR_GROUP_ID,
WLCORE_VENDOR_ATTR_GROUP_KEY,
NUM_WLCORE_VENDOR_ATTR,
MAX_WLCORE_VENDOR_ATTR = NUM_WLCORE_VENDOR_ATTR - 1
};
enum wlcore_vendor_events {
WLCORE_VENDOR_EVENT_SC_SYNC,
WLCORE_VENDOR_EVENT_SC_DECODE,
};
#endif /* __WLCORE_VENDOR_H__ */
@@ -0,0 +1,137 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __WL12XX_80211_H__
#define __WL12XX_80211_H__
#include <linux/if_ether.h> /* ETH_ALEN */
#include <linux/if_arp.h>
/* RATES */
#define IEEE80211_CCK_RATE_1MB 0x02
#define IEEE80211_CCK_RATE_2MB 0x04
#define IEEE80211_CCK_RATE_5MB 0x0B
#define IEEE80211_CCK_RATE_11MB 0x16
#define IEEE80211_OFDM_RATE_6MB 0x0C
#define IEEE80211_OFDM_RATE_9MB 0x12
#define IEEE80211_OFDM_RATE_12MB 0x18
#define IEEE80211_OFDM_RATE_18MB 0x24
#define IEEE80211_OFDM_RATE_24MB 0x30
#define IEEE80211_OFDM_RATE_36MB 0x48
#define IEEE80211_OFDM_RATE_48MB 0x60
#define IEEE80211_OFDM_RATE_54MB 0x6C
#define IEEE80211_BASIC_RATE_MASK 0x80
#define IEEE80211_CCK_RATE_1MB_MASK (1<<0)
#define IEEE80211_CCK_RATE_2MB_MASK (1<<1)
#define IEEE80211_CCK_RATE_5MB_MASK (1<<2)
#define IEEE80211_CCK_RATE_11MB_MASK (1<<3)
#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4)
#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5)
#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6)
#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7)
#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8)
#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9)
#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10)
#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11)
#define IEEE80211_CCK_RATES_MASK 0x0000000F
#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \
IEEE80211_CCK_RATE_2MB_MASK)
#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \
IEEE80211_CCK_RATE_5MB_MASK | \
IEEE80211_CCK_RATE_11MB_MASK)
#define IEEE80211_OFDM_RATES_MASK 0x00000FF0
#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \
IEEE80211_OFDM_RATE_12MB_MASK | \
IEEE80211_OFDM_RATE_24MB_MASK)
#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \
IEEE80211_OFDM_RATE_9MB_MASK | \
IEEE80211_OFDM_RATE_18MB_MASK | \
IEEE80211_OFDM_RATE_36MB_MASK | \
IEEE80211_OFDM_RATE_48MB_MASK | \
IEEE80211_OFDM_RATE_54MB_MASK)
#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \
IEEE80211_CCK_DEFAULT_RATES_MASK)
/* This really should be 8, but not for our firmware */
#define MAX_SUPPORTED_RATES 32
#define MAX_COUNTRY_TRIPLETS 32
/* Headers */
struct ieee80211_header {
__le16 frame_ctl;
__le16 duration_id;
u8 da[ETH_ALEN];
u8 sa[ETH_ALEN];
u8 bssid[ETH_ALEN];
__le16 seq_ctl;
} __packed;
struct wl12xx_ie_header {
u8 id;
u8 len;
} __packed;
/* IEs */
struct wl12xx_ie_ssid {
struct wl12xx_ie_header header;
char ssid[IEEE80211_MAX_SSID_LEN];
} __packed;
struct wl12xx_ie_rates {
struct wl12xx_ie_header header;
u8 rates[MAX_SUPPORTED_RATES];
} __packed;
struct wl12xx_ie_ds_params {
struct wl12xx_ie_header header;
u8 channel;
} __packed;
struct country_triplet {
u8 channel;
u8 num_channels;
u8 max_tx_power;
} __packed;
struct wl12xx_ie_country {
struct wl12xx_ie_header header;
u8 country_string[IEEE80211_COUNTRY_STRING_LEN];
struct country_triplet triplets[MAX_COUNTRY_TRIPLETS];
} __packed;
/* Templates */
struct wl12xx_null_data_template {
struct ieee80211_header header;
} __packed;
struct wl12xx_ps_poll_template {
__le16 fc;
__le16 aid;
u8 bssid[ETH_ALEN];
u8 ta[ETH_ALEN];
} __packed;
struct wl12xx_arp_rsp_template {
/* not including ieee80211 header */
u8 llc_hdr[sizeof(rfc1042_header)];
__be16 llc_type;
struct arphdr arp_hdr;
u8 sender_hw[ETH_ALEN];
__be32 sender_ip;
u8 target_hw[ETH_ALEN];
__be32 target_ip;
} __packed;
struct wl12xx_disconn_template {
struct ieee80211_header header;
__le16 disconn_reason;
} __packed;
#endif
@@ -0,0 +1,640 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wlcore
*
* Copyright (C) 2011 Texas Instruments Inc.
*/
#ifndef __WLCORE_H__
#define __WLCORE_H__
#include <linux/platform_device.h>
#include "wlcore_i.h"
#include "event.h"
#include "boot.h"
/* The maximum number of Tx descriptors in all chip families */
#define WLCORE_MAX_TX_DESCRIPTORS 32
/*
* We always allocate this number of mac addresses. If we don't
* have enough allocated addresses, the LAA bit is used
*/
#define WLCORE_NUM_MAC_ADDRESSES 3
/* wl12xx/wl18xx maximum transmission power (in dBm) */
#define WLCORE_MAX_TXPWR 25
/* Texas Instruments pre assigned OUI */
#define WLCORE_TI_OUI_ADDRESS 0x080028
/* forward declaration */
struct wl1271_tx_hw_descr;
enum wl_rx_buf_align;
struct wl1271_rx_descriptor;
struct wlcore_ops {
int (*setup)(struct wl1271 *wl);
int (*identify_chip)(struct wl1271 *wl);
int (*identify_fw)(struct wl1271 *wl);
int (*boot)(struct wl1271 *wl);
int (*plt_init)(struct wl1271 *wl);
int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr,
void *buf, size_t len);
int (*ack_event)(struct wl1271 *wl);
int (*wait_for_event)(struct wl1271 *wl, enum wlcore_wait_event event,
bool *timeout);
int (*process_mailbox_events)(struct wl1271 *wl);
u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks);
void (*set_tx_desc_blocks)(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
u32 blks, u32 spare_blks);
void (*set_tx_desc_data_len)(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb);
enum wl_rx_buf_align (*get_rx_buf_align)(struct wl1271 *wl,
u32 rx_desc);
int (*prepare_read)(struct wl1271 *wl, u32 rx_desc, u32 len);
u32 (*get_rx_packet_len)(struct wl1271 *wl, void *rx_data,
u32 data_len);
int (*tx_delayed_compl)(struct wl1271 *wl);
void (*tx_immediate_compl)(struct wl1271 *wl);
int (*hw_init)(struct wl1271 *wl);
int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void (*convert_fw_status)(struct wl1271 *wl, void *raw_fw_status,
struct wl_fw_status *fw_status);
u32 (*sta_get_ap_rate_mask)(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int (*get_pg_ver)(struct wl1271 *wl, s8 *ver);
int (*get_mac)(struct wl1271 *wl);
void (*set_tx_desc_csum)(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb);
void (*set_rx_csum)(struct wl1271 *wl,
struct wl1271_rx_descriptor *desc,
struct sk_buff *skb);
u32 (*ap_get_mimo_wide_rate_mask)(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir);
int (*handle_static_data)(struct wl1271 *wl,
struct wl1271_static_data *static_data);
int (*scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_scan_request *req);
int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_scan_ies *ies);
void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf);
int (*channel_switch)(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch);
u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int (*set_peer_cap)(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap,
bool allow_ht_operation,
u32 rate_set, u8 hlid);
u32 (*convert_hwaddr)(struct wl1271 *wl, u32 hwaddr);
bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk);
bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
struct wl1271_link *lnk);
int (*interrupt_notify)(struct wl1271 *wl, bool action);
int (*rx_ba_filter)(struct wl1271 *wl, bool action);
int (*ap_sleep)(struct wl1271 *wl);
int (*smart_config_start)(struct wl1271 *wl, u32 group_bitmap);
int (*smart_config_stop)(struct wl1271 *wl);
int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id,
u8 key_len, u8 *key);
int (*set_cac)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool start);
int (*dfs_master_restart)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
};
enum wlcore_partitions {
PART_DOWN,
PART_WORK,
PART_BOOT,
PART_DRPW,
PART_TOP_PRCM_ELP_SOC,
PART_PHY_INIT,
PART_TABLE_LEN,
};
struct wlcore_partition {
u32 size;
u32 start;
};
struct wlcore_partition_set {
struct wlcore_partition mem;
struct wlcore_partition reg;
struct wlcore_partition mem2;
struct wlcore_partition mem3;
};
enum wlcore_registers {
/* register addresses, used with partition translation */
REG_ECPU_CONTROL,
REG_INTERRUPT_NO_CLEAR,
REG_INTERRUPT_ACK,
REG_COMMAND_MAILBOX_PTR,
REG_EVENT_MAILBOX_PTR,
REG_INTERRUPT_TRIG,
REG_INTERRUPT_MASK,
REG_PC_ON_RECOVERY,
REG_CHIP_ID_B,
REG_CMD_MBOX_ADDRESS,
/* data access memory addresses, used with partition translation */
REG_SLV_MEM_DATA,
REG_SLV_REG_DATA,
/* raw data access memory addresses */
REG_RAW_FW_STATUS_ADDR,
REG_TABLE_LEN,
};
struct wl1271_stats {
void *fw_stats;
unsigned long fw_stats_update;
size_t fw_stats_len;
unsigned int retry_count;
unsigned int excessive_retries;
};
struct wl1271 {
bool initialized;
struct ieee80211_hw *hw;
bool mac80211_registered;
struct device *dev;
struct platform_device *pdev;
void *if_priv;
struct wl1271_if_operations *if_ops;
int irq;
int wakeirq;
int irq_flags;
int wakeirq_flags;
spinlock_t wl_lock;
enum wlcore_state state;
enum wl12xx_fw_type fw_type;
bool plt;
enum plt_mode plt_mode;
u8 fem_manuf;
u8 last_vif_count;
struct mutex mutex;
unsigned long flags;
struct wlcore_partition_set curr_part;
struct wl1271_chip chip;
int cmd_box_addr;
u8 *fw;
size_t fw_len;
void *nvs;
size_t nvs_len;
s8 hw_pg_ver;
/* address read from the fuse ROM */
u32 fuse_oui_addr;
u32 fuse_nic_addr;
/* we have up to 2 MAC addresses */
struct mac_address addresses[WLCORE_NUM_MAC_ADDRESSES];
int channel;
u8 system_hlid;
unsigned long links_map[BITS_TO_LONGS(WLCORE_MAX_LINKS)];
unsigned long roles_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
unsigned long roc_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
unsigned long rate_policies_map[
BITS_TO_LONGS(WL12XX_MAX_RATE_POLICIES)];
unsigned long klv_templates_map[
BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)];
u8 session_ids[WLCORE_MAX_LINKS];
struct list_head wlvif_list;
u8 sta_count;
u8 ap_count;
struct wl1271_acx_mem_map *target_mem_map;
/* Accounting for allocated / available TX blocks on HW */
u32 tx_blocks_freed;
u32 tx_blocks_available;
u32 tx_allocated_blocks;
u32 tx_results_count;
/* Accounting for allocated / available Tx packets in HW */
u32 tx_pkts_freed[NUM_TX_QUEUES];
u32 tx_allocated_pkts[NUM_TX_QUEUES];
/* Transmitted TX packets counter for chipset interface */
u32 tx_packets_count;
/* Time-offset between host and chipset clocks */
s64 time_offset;
/* Frames scheduled for transmission, not handled yet */
int tx_queue_count[NUM_TX_QUEUES];
unsigned long queue_stop_reasons[
NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES];
/* Frames received, not handled yet by mac80211 */
struct sk_buff_head deferred_rx_queue;
/* Frames sent, not returned yet to mac80211 */
struct sk_buff_head deferred_tx_queue;
struct work_struct tx_work;
struct workqueue_struct *freezable_wq;
/* Pending TX frames */
unsigned long tx_frames_map[BITS_TO_LONGS(WLCORE_MAX_TX_DESCRIPTORS)];
struct sk_buff *tx_frames[WLCORE_MAX_TX_DESCRIPTORS];
int tx_frames_cnt;
/* FW Rx counter */
u32 rx_counter;
/* Intermediate buffer, used for packet aggregation */
u8 *aggr_buf;
u32 aggr_buf_size;
/* Reusable dummy packet template */
struct sk_buff *dummy_packet;
/* Network stack work */
struct work_struct netstack_work;
/* FW log buffer */
u8 *fwlog;
/* Number of valid bytes in the FW log buffer */
ssize_t fwlog_size;
/* FW log end marker */
u32 fwlog_end;
/* FW memory block size */
u32 fw_mem_block_size;
/* Hardware recovery work */
struct work_struct recovery_work;
bool watchdog_recovery;
/* Reg domain last configuration */
DECLARE_BITMAP(reg_ch_conf_last, 64);
/* Reg domain pending configuration */
DECLARE_BITMAP(reg_ch_conf_pending, 64);
/* Pointer that holds DMA-friendly block for the mailbox */
void *mbox;
/* The mbox event mask */
u32 event_mask;
/* events to unmask only when ap interface is up */
u32 ap_event_mask;
/* Mailbox pointers */
u32 mbox_size;
u32 mbox_ptr[2];
/* Are we currently scanning */
struct wl12xx_vif *scan_wlvif;
struct wl1271_scan scan;
struct delayed_work scan_complete_work;
struct ieee80211_vif *roc_vif;
struct delayed_work roc_complete_work;
struct wl12xx_vif *sched_vif;
/* The current band */
enum nl80211_band band;
struct completion *elp_compl;
/* in dBm */
int power_level;
struct wl1271_stats stats;
__le32 *buffer_32;
u32 buffer_cmd;
u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
void *raw_fw_status;
struct wl_fw_status *fw_status;
struct wl1271_tx_hw_res_if *tx_res_if;
/* Current chipset configuration */
struct wlcore_conf conf;
bool sg_enabled;
bool enable_11a;
int recovery_count;
/* Most recently reported noise in dBm */
s8 noise;
/* bands supported by this instance of wl12xx */
struct ieee80211_supported_band bands[WLCORE_NUM_BANDS];
/*
* wowlan trigger was configured during suspend.
* (currently, only "ANY" trigger is supported)
*/
bool wow_enabled;
bool irq_wake_enabled;
/*
* AP-mode - links indexed by HLID. The global and broadcast links
* are always active.
*/
struct wl1271_link links[WLCORE_MAX_LINKS];
/* number of currently active links */
int active_link_count;
/* Fast/slow links bitmap according to FW */
unsigned long fw_fast_lnk_map;
/* AP-mode - a bitmap of links currently in PS mode according to FW */
unsigned long ap_fw_ps_map;
/* AP-mode - a bitmap of links currently in PS mode in mac80211 */
unsigned long ap_ps_map;
/* Quirks of specific hardware revisions */
unsigned int quirks;
/* number of currently active RX BA sessions */
int ba_rx_session_count;
/* Maximum number of supported RX BA sessions */
int ba_rx_session_count_max;
/* AP-mode - number of currently connected stations */
int active_sta_count;
/* Flag determining whether AP should broadcast OFDM-only rates */
bool ofdm_only_ap;
/* last wlvif we transmitted from */
struct wl12xx_vif *last_wlvif;
/* work to fire when Tx is stuck */
struct delayed_work tx_watchdog_work;
struct wlcore_ops *ops;
/* pointer to the lower driver partition table */
const struct wlcore_partition_set *ptable;
/* pointer to the lower driver register table */
const int *rtable;
/* name of the firmwares to load - for PLT, single role, multi-role */
const char *plt_fw_name;
const char *sr_fw_name;
const char *mr_fw_name;
u8 scan_templ_id_2_4;
u8 scan_templ_id_5;
u8 sched_scan_templ_id_2_4;
u8 sched_scan_templ_id_5;
u8 max_channels_5;
/* per-chip-family private structure */
void *priv;
/* number of TX descriptors the HW supports. */
u32 num_tx_desc;
/* number of RX descriptors the HW supports. */
u32 num_rx_desc;
/* number of links the HW supports */
u8 num_links;
/* max stations a single AP can support */
u8 max_ap_stations;
/* translate HW Tx rates to standard rate-indices */
const u8 **band_rate_to_idx;
/* size of table for HW rates that can be received from chip */
u8 hw_tx_rate_tbl_size;
/* this HW rate and below are considered HT rates for this chip */
u8 hw_min_ht_rate;
/* HW HT (11n) capabilities */
struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS];
/* the current dfs region */
enum nl80211_dfs_regions dfs_region;
bool radar_debug_mode;
/* size of the private FW status data */
size_t fw_status_len;
size_t fw_status_priv_len;
/* RX Data filter rule state - enabled/disabled */
unsigned long rx_filter_enabled[BITS_TO_LONGS(WL1271_MAX_RX_FILTERS)];
/* size of the private static data */
size_t static_data_priv_len;
/* the current channel type */
enum nl80211_channel_type channel_type;
/* mutex for protecting the tx_flush function */
struct mutex flush_mutex;
/* sleep auth value currently configured to FW */
int sleep_auth;
/* the number of allocated MAC addresses in this chip */
int num_mac_addr;
/* minimum FW version required for the driver to work in single-role */
unsigned int min_sr_fw_ver[NUM_FW_VER];
/* minimum FW version required for the driver to work in multi-role */
unsigned int min_mr_fw_ver[NUM_FW_VER];
struct completion nvs_loading_complete;
/* interface combinations supported by the hw */
const struct ieee80211_iface_combination *iface_combinations;
u8 n_iface_combinations;
/* dynamic fw traces */
u32 dynamic_fw_traces;
/* time sync zone master */
u8 zone_master_mac_addr[ETH_ALEN];
};
int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
void wlcore_remove(struct platform_device *pdev);
struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
u32 mbox_size);
int wlcore_free_hw(struct wl1271 *wl);
int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf);
void wlcore_regdomain_config(struct wl1271 *wl);
void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct wl1271_station *wl_sta, bool in_conn);
static inline void
wlcore_set_ht_cap(struct wl1271 *wl, enum nl80211_band band,
struct ieee80211_sta_ht_cap *ht_cap)
{
memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap));
}
/* Tell wlcore not to care about this element when checking the version */
#define WLCORE_FW_VER_IGNORE -1
static inline void
wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
unsigned int iftype_sr, unsigned int major_sr,
unsigned int subtype_sr, unsigned int minor_sr,
unsigned int iftype_mr, unsigned int major_mr,
unsigned int subtype_mr, unsigned int minor_mr)
{
wl->min_sr_fw_ver[FW_VER_CHIP] = chip;
wl->min_sr_fw_ver[FW_VER_IF_TYPE] = iftype_sr;
wl->min_sr_fw_ver[FW_VER_MAJOR] = major_sr;
wl->min_sr_fw_ver[FW_VER_SUBTYPE] = subtype_sr;
wl->min_sr_fw_ver[FW_VER_MINOR] = minor_sr;
wl->min_mr_fw_ver[FW_VER_CHIP] = chip;
wl->min_mr_fw_ver[FW_VER_IF_TYPE] = iftype_mr;
wl->min_mr_fw_ver[FW_VER_MAJOR] = major_mr;
wl->min_mr_fw_ver[FW_VER_SUBTYPE] = subtype_mr;
wl->min_mr_fw_ver[FW_VER_MINOR] = minor_mr;
}
/* Firmware image load chunk size */
#define CHUNK_SIZE 16384
/* Quirks */
/* Each RX/TX transaction requires an end-of-transaction transfer */
#define WLCORE_QUIRK_END_OF_TRANSACTION BIT(0)
/* wl127x and SPI don't support SDIO block size alignment */
#define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN BIT(2)
/* means aggregated Rx packets are aligned to a SDIO block */
#define WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN BIT(3)
/* Older firmwares did not implement the FW logger over bus feature */
#define WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED BIT(4)
/* Older firmwares use an old NVS format */
#define WLCORE_QUIRK_LEGACY_NVS BIT(5)
/* pad only the last frame in the aggregate buffer */
#define WLCORE_QUIRK_TX_PAD_LAST_FRAME BIT(7)
/* extra header space is required for TKIP */
#define WLCORE_QUIRK_TKIP_HEADER_SPACE BIT(8)
/* Some firmwares not support sched scans while connected */
#define WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN BIT(9)
/* separate probe response templates for one-shot and sched scans */
#define WLCORE_QUIRK_DUAL_PROBE_TMPL BIT(10)
/* Firmware requires reg domain configuration for active calibration */
#define WLCORE_QUIRK_REGDOMAIN_CONF BIT(11)
/* The FW only support a zero session id for AP */
#define WLCORE_QUIRK_AP_ZERO_SESSION_ID BIT(12)
/* TODO: move all these common registers and values elsewhere */
#define HW_ACCESS_ELP_CTRL_REG 0x1FFFC
/* ELP register commands */
#define ELPCTRL_WAKE_UP 0x1
#define ELPCTRL_WAKE_UP_WLAN_READY 0x5
#define ELPCTRL_SLEEP 0x0
/* ELP WLAN_READY bit */
#define ELPCTRL_WLAN_READY 0x2
/*************************************************************************
Interrupt Trigger Register (Host -> WiLink)
**************************************************************************/
/* Hardware to Embedded CPU Interrupts - first 32-bit register set */
/*
* The host sets this bit to inform the Wlan
* FW that a TX packet is in the XFER
* Buffer #0.
*/
#define INTR_TRIG_TX_PROC0 BIT(2)
/*
* The host sets this bit to inform the FW
* that it read a packet from RX XFER
* Buffer #0.
*/
#define INTR_TRIG_RX_PROC0 BIT(3)
#define INTR_TRIG_DEBUG_ACK BIT(4)
#define INTR_TRIG_STATE_CHANGED BIT(5)
/* Hardware to Embedded CPU Interrupts - second 32-bit register set */
/*
* The host sets this bit to inform the FW
* that it read a packet from RX XFER
* Buffer #1.
*/
#define INTR_TRIG_RX_PROC1 BIT(17)
/*
* The host sets this bit to inform the Wlan
* hardware that a TX packet is in the XFER
* Buffer #1.
*/
#define INTR_TRIG_TX_PROC1 BIT(18)
#define ACX_SLV_SOFT_RESET_BIT BIT(1)
#define SOFT_RESET_MAX_TIME 1000000
#define SOFT_RESET_STALL_TIME 1000
#define ECPU_CONTROL_HALT 0x00000101
#define WELP_ARM_COMMAND_VAL 0x4
#endif /* __WLCORE_H__ */
@@ -0,0 +1,572 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* This file is part of wl1271
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
#ifndef __WLCORE_I_H__
#define __WLCORE_I_H__
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/bitops.h>
#include <net/mac80211.h>
#include "conf.h"
#include "ini.h"
struct wilink_family_data {
const char *name;
const char *nvs_name; /* wl12xx nvs file */
const char *cfg_name; /* wl18xx cfg file */
};
#define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff))
#define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff))
#define WL1271_TX_SQN_POST_RECOVERY_PADDING 0xff
/* Use smaller padding for GEM, as some APs have issues when it's too big */
#define WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM 0x20
#define WL1271_CIPHER_SUITE_GEM 0x00147201
#define WL1271_BUSY_WORD_CNT 1
#define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32))
#define WL1271_ELP_HW_STATE_ASLEEP 0
#define WL1271_ELP_HW_STATE_IRQ 1
#define WL1271_DEFAULT_BEACON_INT 100
#define WL1271_DEFAULT_DTIM_PERIOD 1
#define WL12XX_MAX_ROLES 4
#define WL12XX_INVALID_ROLE_ID 0xff
#define WL12XX_INVALID_LINK_ID 0xff
/*
* max number of links allowed by all HWs.
* this is NOT the actual max links supported by the current hw.
*/
#define WLCORE_MAX_LINKS 16
/* the driver supports the 2.4Ghz and 5Ghz bands */
#define WLCORE_NUM_BANDS 2
#define WL12XX_MAX_RATE_POLICIES 16
#define WLCORE_MAX_KLV_TEMPLATES 4
/* Defined by FW as 0. Will not be freed or allocated. */
#define WL12XX_SYSTEM_HLID 0
/*
* When in AP-mode, we allow (at least) this number of packets
* to be transmitted to FW for a STA in PS-mode. Only when packets are
* present in the FW buffers it will wake the sleeping STA. We want to put
* enough packets for the driver to transmit all of its buffered data before
* the STA goes to sleep again. But we don't want to take too much memory
* as it might hurt the throughput of active STAs.
*/
#define WL1271_PS_STA_MAX_PACKETS 2
#define WL1271_AP_BSS_INDEX 0
#define WL1271_AP_DEF_BEACON_EXP 20
enum wlcore_state {
WLCORE_STATE_OFF,
WLCORE_STATE_RESTARTING,
WLCORE_STATE_ON,
};
enum wl12xx_fw_type {
WL12XX_FW_TYPE_NONE,
WL12XX_FW_TYPE_NORMAL,
WL12XX_FW_TYPE_MULTI,
WL12XX_FW_TYPE_PLT,
};
struct wl1271;
enum {
FW_VER_CHIP,
FW_VER_IF_TYPE,
FW_VER_MAJOR,
FW_VER_SUBTYPE,
FW_VER_MINOR,
NUM_FW_VER
};
struct wl1271_chip {
u32 id;
char fw_ver_str[ETHTOOL_FWVERS_LEN];
unsigned int fw_ver[NUM_FW_VER];
char phy_fw_ver_str[ETHTOOL_FWVERS_LEN];
};
#define NUM_TX_QUEUES 4
struct wl_fw_status {
u32 intr;
u8 fw_rx_counter;
u8 drv_rx_counter;
u8 tx_results_counter;
__le32 *rx_pkt_descs;
u32 fw_localtime;
/*
* A bitmap (where each bit represents a single HLID)
* to indicate if the station is in PS mode.
*/
u32 link_ps_bitmap;
/*
* A bitmap (where each bit represents a single HLID) to indicate
* if the station is in Fast mode
*/
u32 link_fast_bitmap;
/* Cumulative counter of total released mem blocks since FW-reset */
u32 total_released_blks;
/* Size (in Memory Blocks) of TX pool */
u32 tx_total;
struct {
/*
* Cumulative counter of released packets per AC
* (length of the array is NUM_TX_QUEUES)
*/
u8 *tx_released_pkts;
/*
* Cumulative counter of freed packets per HLID
* (length of the array is wl->num_links)
*/
u8 *tx_lnk_free_pkts;
/* PN16 of last TKIP/AES seq-num per HLID */
__le16 *tx_lnk_sec_pn16;
/* Cumulative counter of released Voice memory blocks */
u8 tx_voice_released_blks;
/* Tx rate of the last transmitted packet */
u8 tx_last_rate;
/* Tx rate or Tx rate estimate pre calculated by fw in mbps */
u8 tx_last_rate_mbps;
/* hlid for which the rates were reported */
u8 hlid;
} counters;
u32 log_start_addr;
/* Private status to be used by the lower drivers */
void *priv;
};
#define WL1271_MAX_CHANNELS 64
struct wl1271_scan {
struct cfg80211_scan_request *req;
unsigned long scanned_ch[BITS_TO_LONGS(WL1271_MAX_CHANNELS)];
bool failed;
u8 state;
u8 ssid[IEEE80211_MAX_SSID_LEN+1];
size_t ssid_len;
};
struct wl1271_if_operations {
int __must_check (*read)(struct device *child, int addr, void *buf,
size_t len, bool fixed);
int __must_check (*write)(struct device *child, int addr, void *buf,
size_t len, bool fixed);
void (*reset)(struct device *child);
void (*init)(struct device *child);
int (*power)(struct device *child, bool enable);
void (*set_block_size) (struct device *child, unsigned int blksz);
};
struct wlcore_platdev_data {
struct wl1271_if_operations *if_ops;
const struct wilink_family_data *family;
bool ref_clock_xtal; /* specify whether the clock is XTAL or not */
u32 ref_clock_freq; /* in Hertz */
u32 tcxo_clock_freq; /* in Hertz, tcxo is always XTAL */
bool pwr_in_suspend;
};
#define MAX_NUM_KEYS 14
#define MAX_KEY_SIZE 32
struct wl1271_ap_key {
u8 id;
u8 key_type;
u8 key_size;
u8 key[MAX_KEY_SIZE];
u8 hlid;
u32 tx_seq_32;
u16 tx_seq_16;
bool is_pairwise;
};
enum wl12xx_flags {
WL1271_FLAG_GPIO_POWER,
WL1271_FLAG_TX_QUEUE_STOPPED,
WL1271_FLAG_TX_PENDING,
WL1271_FLAG_IN_ELP,
WL1271_FLAG_IRQ_RUNNING,
WL1271_FLAG_FW_TX_BUSY,
WL1271_FLAG_DUMMY_PACKET_PENDING,
WL1271_FLAG_SUSPENDED,
WL1271_FLAG_PENDING_WORK,
WL1271_FLAG_SOFT_GEMINI,
WL1271_FLAG_RECOVERY_IN_PROGRESS,
WL1271_FLAG_VIF_CHANGE_IN_PROGRESS,
WL1271_FLAG_INTENDED_FW_RECOVERY,
WL1271_FLAG_IO_FAILED,
WL1271_FLAG_REINIT_TX_WDOG,
};
enum wl12xx_vif_flags {
WLVIF_FLAG_INITIALIZED,
WLVIF_FLAG_STA_ASSOCIATED,
WLVIF_FLAG_STA_AUTHORIZED,
WLVIF_FLAG_IBSS_JOINED,
WLVIF_FLAG_AP_STARTED,
WLVIF_FLAG_IN_PS,
WLVIF_FLAG_STA_STATE_SENT,
WLVIF_FLAG_RX_STREAMING_STARTED,
WLVIF_FLAG_PSPOLL_FAILURE,
WLVIF_FLAG_CS_PROGRESS,
WLVIF_FLAG_AP_PROBE_RESP_SET,
WLVIF_FLAG_IN_USE,
WLVIF_FLAG_ACTIVE,
WLVIF_FLAG_BEACON_DISABLED,
};
struct wl12xx_vif;
struct wl1271_link {
/* AP-mode - TX queue per AC in link */
struct sk_buff_head tx_queue[NUM_TX_QUEUES];
/* accounting for allocated / freed packets in FW */
u8 allocated_pkts;
u8 prev_freed_pkts;
u16 prev_sec_pn16;
u8 addr[ETH_ALEN];
/* bitmap of TIDs where RX BA sessions are active for this link */
u8 ba_bitmap;
/* the last fw rate index we used for this link */
u8 fw_rate_idx;
/* the last fw rate [Mbps] we used for this link */
u8 fw_rate_mbps;
/* The wlvif this link belongs to. Might be null for global links */
struct wl12xx_vif *wlvif;
/*
* total freed FW packets on the link - used for tracking the
* AES/TKIP PN across recoveries. Re-initialized each time
* from the wl1271_station structure.
*/
u64 total_freed_pkts;
};
#define WL1271_MAX_RX_FILTERS 5
#define WL1271_RX_FILTER_MAX_FIELDS 8
#define WL1271_RX_FILTER_ETH_HEADER_SIZE 14
#define WL1271_RX_FILTER_MAX_FIELDS_SIZE 95
#define RX_FILTER_FIELD_OVERHEAD \
(sizeof(struct wl12xx_rx_filter_field) - sizeof(u8 *))
#define WL1271_RX_FILTER_MAX_PATTERN_SIZE \
(WL1271_RX_FILTER_MAX_FIELDS_SIZE - RX_FILTER_FIELD_OVERHEAD)
#define WL1271_RX_FILTER_FLAG_MASK BIT(0)
#define WL1271_RX_FILTER_FLAG_IP_HEADER 0
#define WL1271_RX_FILTER_FLAG_ETHERNET_HEADER BIT(1)
enum rx_filter_action {
FILTER_DROP = 0,
FILTER_SIGNAL = 1,
FILTER_FW_HANDLE = 2
};
enum plt_mode {
PLT_OFF = 0,
PLT_ON = 1,
PLT_FEM_DETECT = 2,
PLT_CHIP_AWAKE = 3
};
struct wl12xx_rx_filter_field {
__le16 offset;
u8 len;
u8 flags;
u8 *pattern;
} __packed;
struct wl12xx_rx_filter {
u8 action;
int num_fields;
struct wl12xx_rx_filter_field fields[WL1271_RX_FILTER_MAX_FIELDS];
};
struct wl1271_station {
u8 hlid;
bool fw_added;
bool in_connection;
/*
* total freed FW packets on the link to the STA - used for tracking the
* AES/TKIP PN across recoveries. Re-initialized each time from the
* wl1271_station structure.
* Used in both AP and STA mode.
*/
u64 total_freed_pkts;
};
static inline struct wl1271_station *wl1271_station(struct ieee80211_sta *sta)
{
return (struct wl1271_station *)sta->drv_priv;
}
struct wl12xx_vif {
struct wl1271 *wl;
struct list_head list;
unsigned long flags;
u8 bss_type;
u8 p2p; /* we are using p2p role */
u8 role_id;
/* sta/ibss specific */
u8 dev_role_id;
u8 dev_hlid;
union {
struct {
u8 hlid;
u8 basic_rate_idx;
u8 ap_rate_idx;
u8 p2p_rate_idx;
u8 klv_template_id;
bool qos;
/* channel type we started the STA role with */
enum nl80211_channel_type role_chan_type;
} sta;
struct {
u8 global_hlid;
u8 bcast_hlid;
/* HLIDs bitmap of associated stations */
unsigned long sta_hlid_map[BITS_TO_LONGS(
WLCORE_MAX_LINKS)];
/* recoreded keys - set here before AP startup */
struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS];
u8 mgmt_rate_idx;
u8 bcast_rate_idx;
u8 ucast_rate_idx[CONF_TX_MAX_AC_COUNT];
} ap;
};
/* the hlid of the last transmitted skb */
int last_tx_hlid;
/* counters of packets per AC, across all links in the vif */
int tx_queue_count[NUM_TX_QUEUES];
unsigned long links_map[BITS_TO_LONGS(WLCORE_MAX_LINKS)];
u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
u8 ssid_len;
/* The current band */
enum nl80211_band band;
int channel;
enum nl80211_channel_type channel_type;
u32 bitrate_masks[WLCORE_NUM_BANDS];
u32 basic_rate_set;
/*
* currently configured rate set:
* bits 0-15 - 802.11abg rates
* bits 16-23 - 802.11n MCS index mask
* support only 1 stream, thus only 8 bits for the MCS rates (0-7).
*/
u32 basic_rate;
u32 rate_set;
/* probe-req template for the current AP */
struct sk_buff *probereq;
/* Beaconing interval (needed for ad-hoc) */
u32 beacon_int;
/* Default key (for WEP) */
u32 default_key;
/* Our association ID */
u16 aid;
/* retry counter for PSM entries */
u8 psm_entry_retry;
/* in dBm */
int power_level;
int rssi_thold;
int last_rssi_event;
/* save the current encryption type for auto-arp config */
u8 encryption_type;
__be32 ip_addr;
/* RX BA constraint value */
bool ba_support;
bool ba_allowed;
bool wmm_enabled;
bool radar_enabled;
/* Rx Streaming */
struct work_struct rx_streaming_enable_work;
struct work_struct rx_streaming_disable_work;
struct timer_list rx_streaming_timer;
struct delayed_work channel_switch_work;
struct delayed_work connection_loss_work;
/* number of in connection stations */
int inconn_count;
/*
* This vif's queues are mapped to mac80211 HW queues as:
* VO - hw_queue_base
* VI - hw_queue_base + 1
* BE - hw_queue_base + 2
* BK - hw_queue_base + 3
*/
int hw_queue_base;
/* do we have a pending auth reply? (and ROC) */
bool ap_pending_auth_reply;
/* time when we sent the pending auth reply */
unsigned long pending_auth_reply_time;
/* work for canceling ROC after pending auth reply */
struct delayed_work pending_auth_complete_work;
/* update rate conrol */
enum ieee80211_sta_rx_bandwidth rc_update_bw;
struct ieee80211_sta_ht_cap rc_ht_cap;
struct work_struct rc_update_work;
/*
* total freed FW packets on the link.
* For STA this holds the PN of the link to the AP.
* For AP this holds the PN of the broadcast link.
*/
u64 total_freed_pkts;
/*
* This struct must be last!
* data that has to be saved acrossed reconfigs (e.g. recovery)
* should be declared in this struct.
*/
struct {
u8 persistent[0];
};
};
static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif)
{
WARN_ON(!vif);
return (struct wl12xx_vif *)vif->drv_priv;
}
static inline
struct ieee80211_vif *wl12xx_wlvif_to_vif(struct wl12xx_vif *wlvif)
{
return container_of((void *)wlvif, struct ieee80211_vif, drv_priv);
}
static inline bool wlcore_is_p2p_mgmt(struct wl12xx_vif *wlvif)
{
return wl12xx_wlvif_to_vif(wlvif)->type == NL80211_IFTYPE_P2P_DEVICE;
}
#define wl12xx_for_each_wlvif(wl, wlvif) \
list_for_each_entry(wlvif, &wl->wlvif_list, list)
#define wl12xx_for_each_wlvif_continue(wl, wlvif) \
list_for_each_entry_continue(wlvif, &wl->wlvif_list, list)
#define wl12xx_for_each_wlvif_bss_type(wl, wlvif, _bss_type) \
wl12xx_for_each_wlvif(wl, wlvif) \
if (wlvif->bss_type == _bss_type)
#define wl12xx_for_each_wlvif_sta(wl, wlvif) \
wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_STA_BSS)
#define wl12xx_for_each_wlvif_ap(wl, wlvif) \
wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_AP_BSS)
int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode);
int wl1271_plt_stop(struct wl1271 *wl);
int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl12xx_queue_recovery_work(struct wl1271 *wl);
size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter,
u16 offset, u8 flags,
const u8 *pattern, u8 len);
void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter);
struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void);
int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter);
void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
u8 *buf);
#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */
#define SESSION_COUNTER_MAX 6 /* maximum value for the session counter */
#define SESSION_COUNTER_INVALID 7 /* used with dummy_packet */
#define WL1271_DEFAULT_POWER_LEVEL 0
#define WL1271_TX_QUEUE_LOW_WATERMARK 32
#define WL1271_TX_QUEUE_HIGH_WATERMARK 256
#define WL1271_DEFERRED_QUEUE_LIMIT 64
/* 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 milliseconds */
#define WL1271_POWER_ON_SLEEP 200 /* in milliseconds */
/* Macros to handle wl1271.sta_rate_set */
#define HW_BG_RATES_MASK 0xffff
#define HW_HT_RATES_OFFSET 16
#define HW_MIMO_RATES_OFFSET 24
#endif /* __WLCORE_I_H__ */