wireless/wl12xx/wl1251: move TI WLAN modules to a common ti subdirectory

Move wl12xx and wl1251 modules into a new drivers/net/wireless/ti
directory.  Add a TI WLAN Kconfig option and Makefile to support this
change.

Signed-off-by: Luciano Coelho <coelho@ti.com>
Cc: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Luciano Coelho
2011-11-20 21:40:41 +02:00
parent 16f3eb530f
commit 9092101460
70 changed files with 26 additions and 22 deletions

View File

@@ -0,0 +1,11 @@
menuconfig WL_TI
bool "TI Wireless LAN support"
---help---
This section contains support for all the wireless drivers
for Texas Instruments WLAN chips, such as wl1251 and the wl12xx
family.
if WL_TI
source "drivers/net/wireless/ti/wl1251/Kconfig"
source "drivers/net/wireless/ti/wl12xx/Kconfig"
endif # WL_TI

View File

@@ -0,0 +1,3 @@
obj-$(CONFIG_WL12XX) += wl12xx/
obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx/
obj-$(CONFIG_WL1251) += wl1251/

View File

@@ -0,0 +1,33 @@
menuconfig WL1251
tristate "TI wl1251 driver support"
depends on MAC80211 && EXPERIMENTAL && GENERIC_HARDIRQS
select FW_LOADER
select CRC7
---help---
This will enable TI wl1251 driver support. The drivers make
use of the mac80211 stack.
If you choose to build a module, it'll be called wl1251. Say
N if unsure.
config WL1251_SPI
tristate "TI wl1251 SPI support"
depends on WL1251 && SPI_MASTER
---help---
This module adds support for the SPI interface of adapters using
TI wl1251 chipset. Select this if your platform is using
the SPI bus.
If you choose to build a module, it'll be called wl1251_spi.
Say N if unsure.
config WL1251_SDIO
tristate "TI wl1251 SDIO support"
depends on WL1251 && MMC
---help---
This module adds support for the SDIO interface of adapters using
TI wl1251 chipset. Select this if your platform is using
the SDIO bus.
If you choose to build a module, it'll be called
wl1251_sdio. Say N if unsure.

View File

@@ -0,0 +1,10 @@
wl1251-objs = main.o event.o tx.o rx.o ps.o cmd.o \
acx.o boot.o init.o debugfs.o io.o
wl1251_spi-objs += spi.o
wl1251_sdio-objs += sdio.o
obj-$(CONFIG_WL1251) += wl1251.o
obj-$(CONFIG_WL1251_SPI) += wl1251_spi.o
obj-$(CONFIG_WL1251_SDIO) += wl1251_sdio.o
ccflags-y += -D__CHECK_ENDIAN__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,554 @@
/*
* This file is part of wl1251
*
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/slab.h>
#include "reg.h"
#include "boot.h"
#include "io.h"
#include "spi.h"
#include "event.h"
#include "acx.h"
void wl1251_boot_target_enable_interrupts(struct wl1251 *wl)
{
wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask));
wl1251_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL);
}
int wl1251_boot_soft_reset(struct wl1251 *wl)
{
unsigned long timeout;
u32 boot_data;
/* perform soft reset */
wl1251_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT);
/* SOFT_RESET is self clearing */
timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME);
while (1) {
boot_data = wl1251_reg_read32(wl, ACX_REG_SLV_SOFT_RESET);
wl1251_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data);
if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0)
break;
if (time_after(jiffies, timeout)) {
/* 1.2 check pWhalBus->uSelfClearTime if the
* timeout was reached */
wl1251_error("soft reset timeout");
return -1;
}
udelay(SOFT_RESET_STALL_TIME);
}
/* disable Rx/Tx */
wl1251_reg_write32(wl, ENABLE, 0x0);
/* disable auto calibration on start*/
wl1251_reg_write32(wl, SPARE_A2, 0xffff);
return 0;
}
int wl1251_boot_init_seq(struct wl1251 *wl)
{
u32 scr_pad6, init_data, tmp, elp_cmd, ref_freq;
/*
* col #1: INTEGER_DIVIDER
* col #2: FRACTIONAL_DIVIDER
* col #3: ATTN_BB
* col #4: ALPHA_BB
* col #5: STOP_TIME_BB
* col #6: BB_PLL_LOOP_FILTER
*/
static const u32 LUT[REF_FREQ_NUM][LUT_PARAM_NUM] = {
{ 83, 87381, 0xB, 5, 0xF00, 3}, /* REF_FREQ_19_2*/
{ 61, 141154, 0xB, 5, 0x1450, 2}, /* REF_FREQ_26_0*/
{ 41, 174763, 0xC, 6, 0x2D00, 1}, /* REF_FREQ_38_4*/
{ 40, 0, 0xC, 6, 0x2EE0, 1}, /* REF_FREQ_40_0*/
{ 47, 162280, 0xC, 6, 0x2760, 1} /* REF_FREQ_33_6 */
};
/* read NVS params */
scr_pad6 = wl1251_reg_read32(wl, SCR_PAD6);
wl1251_debug(DEBUG_BOOT, "scr_pad6 0x%x", scr_pad6);
/* read ELP_CMD */
elp_cmd = wl1251_reg_read32(wl, ELP_CMD);
wl1251_debug(DEBUG_BOOT, "elp_cmd 0x%x", elp_cmd);
/* set the BB calibration time to be 300 usec (PLL_CAL_TIME) */
ref_freq = scr_pad6 & 0x000000FF;
wl1251_debug(DEBUG_BOOT, "ref_freq 0x%x", ref_freq);
wl1251_reg_write32(wl, PLL_CAL_TIME, 0x9);
/*
* PG 1.2: set the clock buffer time to be 210 usec (CLK_BUF_TIME)
*/
wl1251_reg_write32(wl, CLK_BUF_TIME, 0x6);
/*
* set the clock detect feature to work in the restart wu procedure
* (ELP_CFG_MODE[14]) and Select the clock source type
* (ELP_CFG_MODE[13:12])
*/
tmp = ((scr_pad6 & 0x0000FF00) << 4) | 0x00004000;
wl1251_reg_write32(wl, ELP_CFG_MODE, tmp);
/* PG 1.2: enable the BB PLL fix. Enable the PLL_LIMP_CLK_EN_CMD */
elp_cmd |= 0x00000040;
wl1251_reg_write32(wl, ELP_CMD, elp_cmd);
/* PG 1.2: Set the BB PLL stable time to be 1000usec
* (PLL_STABLE_TIME) */
wl1251_reg_write32(wl, CFG_PLL_SYNC_CNT, 0x20);
/* PG 1.2: read clock request time */
init_data = wl1251_reg_read32(wl, CLK_REQ_TIME);
/*
* PG 1.2: set the clock request time to be ref_clk_settling_time -
* 1ms = 4ms
*/
if (init_data > 0x21)
tmp = init_data - 0x21;
else
tmp = 0;
wl1251_reg_write32(wl, CLK_REQ_TIME, tmp);
/* set BB PLL configurations in RF AFE */
wl1251_reg_write32(wl, 0x003058cc, 0x4B5);
/* set RF_AFE_REG_5 */
wl1251_reg_write32(wl, 0x003058d4, 0x50);
/* set RF_AFE_CTRL_REG_2 */
wl1251_reg_write32(wl, 0x00305948, 0x11c001);
/*
* change RF PLL and BB PLL divider for VCO clock and adjust VCO
* bais current(RF_AFE_REG_13)
*/
wl1251_reg_write32(wl, 0x003058f4, 0x1e);
/* set BB PLL configurations */
tmp = LUT[ref_freq][LUT_PARAM_INTEGER_DIVIDER] | 0x00017000;
wl1251_reg_write32(wl, 0x00305840, tmp);
/* set fractional divider according to Appendix C-BB PLL
* Calculations
*/
tmp = LUT[ref_freq][LUT_PARAM_FRACTIONAL_DIVIDER];
wl1251_reg_write32(wl, 0x00305844, tmp);
/* set the initial data for the sigma delta */
wl1251_reg_write32(wl, 0x00305848, 0x3039);
/*
* set the accumulator attenuation value, calibration loop1
* (alpha), calibration loop2 (beta), calibration loop3 (gamma) and
* the VCO gain
*/
tmp = (LUT[ref_freq][LUT_PARAM_ATTN_BB] << 16) |
(LUT[ref_freq][LUT_PARAM_ALPHA_BB] << 12) | 0x1;
wl1251_reg_write32(wl, 0x00305854, tmp);
/*
* set the calibration stop time after holdoff time expires and set
* settling time HOLD_OFF_TIME_BB
*/
tmp = LUT[ref_freq][LUT_PARAM_STOP_TIME_BB] | 0x000A0000;
wl1251_reg_write32(wl, 0x00305858, tmp);
/*
* set BB PLL Loop filter capacitor3- BB_C3[2:0] and set BB PLL
* constant leakage current to linearize PFD to 0uA -
* BB_ILOOPF[7:3]
*/
tmp = LUT[ref_freq][LUT_PARAM_BB_PLL_LOOP_FILTER] | 0x00000030;
wl1251_reg_write32(wl, 0x003058f8, tmp);
/*
* set regulator output voltage for n divider to
* 1.35-BB_REFDIV[1:0], set charge pump current- BB_CPGAIN[4:2],
* set BB PLL Loop filter capacitor2- BB_C2[7:5], set gain of BB
* PLL auto-call to normal mode- BB_CALGAIN_3DB[8]
*/
wl1251_reg_write32(wl, 0x003058f0, 0x29);
/* enable restart wakeup sequence (ELP_CMD[0]) */
wl1251_reg_write32(wl, ELP_CMD, elp_cmd | 0x1);
/* restart sequence completed */
udelay(2000);
return 0;
}
static void wl1251_boot_set_ecpu_ctrl(struct wl1251 *wl, u32 flag)
{
u32 cpu_ctrl;
/* 10.5.0 run the firmware (I) */
cpu_ctrl = wl1251_reg_read32(wl, ACX_REG_ECPU_CONTROL);
/* 10.5.1 run the firmware (II) */
cpu_ctrl &= ~flag;
wl1251_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl);
}
int wl1251_boot_run_firmware(struct wl1251 *wl)
{
int loop, ret;
u32 chip_id, acx_intr;
wl1251_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT);
chip_id = wl1251_reg_read32(wl, CHIP_ID_B);
wl1251_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id);
if (chip_id != wl->chip_id) {
wl1251_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);
acx_intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
if (acx_intr == 0xffffffff) {
wl1251_error("error reading hardware complete "
"init indication");
return -EIO;
}
/* check that ACX_INTR_INIT_COMPLETE is enabled */
else if (acx_intr & WL1251_ACX_INTR_INIT_COMPLETE) {
wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK,
WL1251_ACX_INTR_INIT_COMPLETE);
break;
}
}
if (loop > INIT_LOOP) {
wl1251_error("timeout waiting for the hardware to "
"complete initialization");
return -EIO;
}
/* get hardware config command mail box */
wl->cmd_box_addr = wl1251_reg_read32(wl, REG_COMMAND_MAILBOX_PTR);
/* get hardware config event mail box */
wl->event_box_addr = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR);
/* set the working partition to its "running" mode offset */
wl1251_set_partition(wl, WL1251_PART_WORK_MEM_START,
WL1251_PART_WORK_MEM_SIZE,
WL1251_PART_WORK_REG_START,
WL1251_PART_WORK_REG_SIZE);
wl1251_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x",
wl->cmd_box_addr, wl->event_box_addr);
wl1251_acx_fw_version(wl, wl->fw_ver, sizeof(wl->fw_ver));
/*
* in case of full asynchronous mode the firmware event must be
* ready to receive event from the command mailbox
*/
/* enable gpio interrupts */
wl1251_enable_interrupts(wl);
/* Enable target's interrupts */
wl->intr_mask = WL1251_ACX_INTR_RX0_DATA |
WL1251_ACX_INTR_RX1_DATA |
WL1251_ACX_INTR_TX_RESULT |
WL1251_ACX_INTR_EVENT_A |
WL1251_ACX_INTR_EVENT_B |
WL1251_ACX_INTR_INIT_COMPLETE;
wl1251_boot_target_enable_interrupts(wl);
wl->event_mask = SCAN_COMPLETE_EVENT_ID | BSS_LOSE_EVENT_ID |
SYNCHRONIZATION_TIMEOUT_EVENT_ID |
ROAMING_TRIGGER_LOW_RSSI_EVENT_ID |
ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID |
REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID |
BT_PTA_PREDICTION_EVENT_ID | JOIN_EVENT_COMPLETE_ID;
ret = wl1251_event_unmask(wl);
if (ret < 0) {
wl1251_error("EVENT mask setting failed");
return ret;
}
wl1251_event_mbox_config(wl);
/* firmware startup completed */
return 0;
}
static int wl1251_boot_upload_firmware(struct wl1251 *wl)
{
int addr, chunk_num, partition_limit;
size_t fw_data_len, len;
u8 *p, *buf;
/* whal_FwCtrl_LoadFwImageSm() */
wl1251_debug(DEBUG_BOOT, "chip id before fw upload: 0x%x",
wl1251_reg_read32(wl, CHIP_ID_B));
/* 10.0 check firmware length and set partition */
fw_data_len = (wl->fw[4] << 24) | (wl->fw[5] << 16) |
(wl->fw[6] << 8) | (wl->fw[7]);
wl1251_debug(DEBUG_BOOT, "fw_data_len %zu chunk_size %d", fw_data_len,
CHUNK_SIZE);
if ((fw_data_len % 4) != 0) {
wl1251_error("firmware length not multiple of four");
return -EIO;
}
buf = kmalloc(CHUNK_SIZE, GFP_KERNEL);
if (!buf) {
wl1251_error("allocation for firmware upload chunk failed");
return -ENOMEM;
}
wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START,
WL1251_PART_DOWN_MEM_SIZE,
WL1251_PART_DOWN_REG_START,
WL1251_PART_DOWN_REG_SIZE);
/* 10.1 set partition limit and chunk num */
chunk_num = 0;
partition_limit = WL1251_PART_DOWN_MEM_SIZE;
while (chunk_num < fw_data_len / CHUNK_SIZE) {
/* 10.2 update partition, if needed */
addr = WL1251_PART_DOWN_MEM_START +
(chunk_num + 2) * CHUNK_SIZE;
if (addr > partition_limit) {
addr = WL1251_PART_DOWN_MEM_START +
chunk_num * CHUNK_SIZE;
partition_limit = chunk_num * CHUNK_SIZE +
WL1251_PART_DOWN_MEM_SIZE;
wl1251_set_partition(wl,
addr,
WL1251_PART_DOWN_MEM_SIZE,
WL1251_PART_DOWN_REG_START,
WL1251_PART_DOWN_REG_SIZE);
}
/* 10.3 upload the chunk */
addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE;
p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE;
wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x",
p, addr);
/* need to copy the chunk for dma */
len = CHUNK_SIZE;
memcpy(buf, p, len);
wl1251_mem_write(wl, addr, buf, len);
chunk_num++;
}
/* 10.4 upload the last chunk */
addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE;
p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE;
/* need to copy the chunk for dma */
len = fw_data_len % CHUNK_SIZE;
memcpy(buf, p, len);
wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x",
len, p, addr);
wl1251_mem_write(wl, addr, buf, len);
kfree(buf);
return 0;
}
static int wl1251_boot_upload_nvs(struct wl1251 *wl)
{
size_t nvs_len, nvs_bytes_written, burst_len;
int nvs_start, i;
u32 dest_addr, val;
u8 *nvs_ptr, *nvs;
nvs = wl->nvs;
if (nvs == NULL)
return -ENODEV;
nvs_ptr = nvs;
nvs_len = wl->nvs_len;
nvs_start = wl->fw_len;
/*
* 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.
*/
while (nvs_ptr[0]) {
burst_len = nvs_ptr[0];
dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8));
/* We move our pointer to the data */
nvs_ptr += 3;
for (i = 0; i < burst_len; i++) {
val = (nvs_ptr[0] | (nvs_ptr[1] << 8)
| (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24));
wl1251_debug(DEBUG_BOOT,
"nvs burst write 0x%x: 0x%x",
dest_addr, val);
wl1251_mem_write32(wl, dest_addr, val);
nvs_ptr += 4;
dest_addr += 4;
}
}
/*
* We've reached the first zero length, the first NVS table
* is 7 bytes further.
*/
nvs_ptr += 7;
nvs_len -= nvs_ptr - nvs;
nvs_len = ALIGN(nvs_len, 4);
/* Now we must set the partition correctly */
wl1251_set_partition(wl, nvs_start,
WL1251_PART_DOWN_MEM_SIZE,
WL1251_PART_DOWN_REG_START,
WL1251_PART_DOWN_REG_SIZE);
/* And finally we upload the NVS tables */
nvs_bytes_written = 0;
while (nvs_bytes_written < nvs_len) {
val = (nvs_ptr[0] | (nvs_ptr[1] << 8)
| (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24));
wl1251_debug(DEBUG_BOOT,
"nvs write table 0x%x: 0x%x",
nvs_start, val);
wl1251_mem_write32(wl, nvs_start, val);
nvs_ptr += 4;
nvs_bytes_written += 4;
nvs_start += 4;
}
return 0;
}
int wl1251_boot(struct wl1251 *wl)
{
int ret = 0, minor_minor_e2_ver;
u32 tmp, boot_data;
/* halt embedded ARM CPU while loading firmware */
wl1251_reg_write32(wl, ACX_REG_ECPU_CONTROL, ECPU_CONTROL_HALT);
ret = wl1251_boot_soft_reset(wl);
if (ret < 0)
goto out;
/* 2. start processing NVS file */
if (wl->use_eeprom) {
wl1251_reg_write32(wl, ACX_REG_EE_START, START_EEPROM_MGR);
/* Wait for EEPROM NVS burst read to complete */
msleep(40);
wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, USE_EEPROM);
} else {
ret = wl1251_boot_upload_nvs(wl);
if (ret < 0)
goto out;
/* write firmware's last address (ie. it's length) to
* ACX_EEPROMLESS_IND_REG */
wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len);
}
/* 6. read the EEPROM parameters */
tmp = wl1251_reg_read32(wl, SCR_PAD2);
/* 7. read bootdata */
wl->boot_attr.radio_type = (tmp & 0x0000FF00) >> 8;
wl->boot_attr.major = (tmp & 0x00FF0000) >> 16;
tmp = wl1251_reg_read32(wl, SCR_PAD3);
/* 8. check bootdata and call restart sequence */
wl->boot_attr.minor = (tmp & 0x00FF0000) >> 16;
minor_minor_e2_ver = (tmp & 0xFF000000) >> 24;
wl1251_debug(DEBUG_BOOT, "radioType 0x%x majorE2Ver 0x%x "
"minorE2Ver 0x%x minor_minor_e2_ver 0x%x",
wl->boot_attr.radio_type, wl->boot_attr.major,
wl->boot_attr.minor, minor_minor_e2_ver);
ret = wl1251_boot_init_seq(wl);
if (ret < 0)
goto out;
/* 9. NVS processing done */
boot_data = wl1251_reg_read32(wl, ACX_REG_ECPU_CONTROL);
wl1251_debug(DEBUG_BOOT, "halt boot_data 0x%x", boot_data);
/* 10. check that ECPU_CONTROL_HALT bits are set in
* pWhalBus->uBootData and start uploading firmware
*/
if ((boot_data & ECPU_CONTROL_HALT) == 0) {
wl1251_error("boot failed, ECPU_CONTROL_HALT not set");
ret = -EIO;
goto out;
}
ret = wl1251_boot_upload_firmware(wl);
if (ret < 0)
goto out;
/* 10.5 start firmware */
ret = wl1251_boot_run_firmware(wl);
if (ret < 0)
goto out;
out:
return ret;
}

View File

@@ -0,0 +1,39 @@
/*
* This file is part of wl1251
*
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __BOOT_H__
#define __BOOT_H__
#include "wl1251.h"
int wl1251_boot_soft_reset(struct wl1251 *wl);
int wl1251_boot_init_seq(struct wl1251 *wl);
int wl1251_boot_run_firmware(struct wl1251 *wl);
void wl1251_boot_target_enable_interrupts(struct wl1251 *wl);
int wl1251_boot(struct wl1251 *wl);
/* number of times we try to read the INIT interrupt */
#define INIT_LOOP 20000
/* delay between retries */
#define INIT_LOOP_DELAY 50
#endif

View File

@@ -0,0 +1,496 @@
#include "cmd.h"
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/crc7.h>
#include "wl1251.h"
#include "reg.h"
#include "io.h"
#include "ps.h"
#include "acx.h"
/**
* send command to firmware
*
* @wl: wl struct
* @id: command id
* @buf: buffer containing the command, must work with dma
* @len: length of the buffer
*/
int wl1251_cmd_send(struct wl1251 *wl, u16 id, void *buf, size_t len)
{
struct wl1251_cmd_header *cmd;
unsigned long timeout;
u32 intr;
int ret = 0;
cmd = buf;
cmd->id = id;
cmd->status = 0;
WARN_ON(len % 4 != 0);
wl1251_mem_write(wl, wl->cmd_box_addr, buf, len);
wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD);
timeout = jiffies + msecs_to_jiffies(WL1251_COMMAND_TIMEOUT);
intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
while (!(intr & WL1251_ACX_INTR_CMD_COMPLETE)) {
if (time_after(jiffies, timeout)) {
wl1251_error("command complete timeout");
ret = -ETIMEDOUT;
goto out;
}
msleep(1);
intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
}
wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK,
WL1251_ACX_INTR_CMD_COMPLETE);
out:
return ret;
}
/**
* send test command to firmware
*
* @wl: wl struct
* @buf: buffer containing the command, with all headers, must work with dma
* @len: length of the buffer
* @answer: is answer needed
*/
int wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer)
{
int ret;
wl1251_debug(DEBUG_CMD, "cmd test");
ret = wl1251_cmd_send(wl, CMD_TEST, buf, buf_len);
if (ret < 0) {
wl1251_warning("TEST command failed");
return ret;
}
if (answer) {
struct wl1251_command *cmd_answer;
/*
* The test command got in, we can read the answer.
* The answer would be a wl1251_command, where the
* parameter array contains the actual answer.
*/
wl1251_mem_read(wl, wl->cmd_box_addr, buf, buf_len);
cmd_answer = buf;
if (cmd_answer->header.status != CMD_STATUS_SUCCESS)
wl1251_error("TEST command answer error: %d",
cmd_answer->header.status);
}
return 0;
}
/**
* read acx from firmware
*
* @wl: wl struct
* @id: acx id
* @buf: buffer for the response, including all headers, must work with dma
* @len: length of buf
*/
int wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len)
{
struct acx_header *acx = buf;
int ret;
wl1251_debug(DEBUG_CMD, "cmd interrogate");
acx->id = id;
/* payload length, does not include any headers */
acx->len = len - sizeof(*acx);
ret = wl1251_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx));
if (ret < 0) {
wl1251_error("INTERROGATE command failed");
goto out;
}
/* the interrogate command got in, we can read the answer */
wl1251_mem_read(wl, wl->cmd_box_addr, buf, len);
acx = buf;
if (acx->cmd.status != CMD_STATUS_SUCCESS)
wl1251_error("INTERROGATE command error: %d",
acx->cmd.status);
out:
return ret;
}
/**
* write acx value to firmware
*
* @wl: wl struct
* @id: acx id
* @buf: buffer containing acx, including all headers, must work with dma
* @len: length of buf
*/
int wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len)
{
struct acx_header *acx = buf;
int ret;
wl1251_debug(DEBUG_CMD, "cmd configure");
acx->id = id;
/* payload length, does not include any headers */
acx->len = len - sizeof(*acx);
ret = wl1251_cmd_send(wl, CMD_CONFIGURE, acx, len);
if (ret < 0) {
wl1251_warning("CONFIGURE command NOK");
return ret;
}
return 0;
}
int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity,
void *bitmap, u16 bitmap_len, u8 bitmap_control)
{
struct wl1251_cmd_vbm_update *vbm;
int ret;
wl1251_debug(DEBUG_CMD, "cmd vbm");
vbm = kzalloc(sizeof(*vbm), GFP_KERNEL);
if (!vbm) {
ret = -ENOMEM;
goto out;
}
/* Count and period will be filled by the target */
vbm->tim.bitmap_ctrl = bitmap_control;
if (bitmap_len > PARTIAL_VBM_MAX) {
wl1251_warning("cmd vbm len is %d B, truncating to %d",
bitmap_len, PARTIAL_VBM_MAX);
bitmap_len = PARTIAL_VBM_MAX;
}
memcpy(vbm->tim.pvb_field, bitmap, bitmap_len);
vbm->tim.identity = identity;
vbm->tim.length = bitmap_len + 3;
vbm->len = cpu_to_le16(bitmap_len + 5);
ret = wl1251_cmd_send(wl, CMD_VBM, vbm, sizeof(*vbm));
if (ret < 0) {
wl1251_error("VBM command failed");
goto out;
}
out:
kfree(vbm);
return ret;
}
int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable)
{
struct cmd_enabledisable_path *cmd;
int ret;
u16 cmd_rx, cmd_tx;
wl1251_debug(DEBUG_CMD, "cmd data path");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->channel = channel;
if (enable) {
cmd_rx = CMD_ENABLE_RX;
cmd_tx = CMD_ENABLE_TX;
} else {
cmd_rx = CMD_DISABLE_RX;
cmd_tx = CMD_DISABLE_TX;
}
ret = wl1251_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd));
if (ret < 0) {
wl1251_error("rx %s cmd for channel %d failed",
enable ? "start" : "stop", channel);
goto out;
}
wl1251_debug(DEBUG_BOOT, "rx %s cmd channel %d",
enable ? "start" : "stop", channel);
ret = wl1251_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd));
if (ret < 0) {
wl1251_error("tx %s cmd for channel %d failed",
enable ? "start" : "stop", channel);
goto out;
}
wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d",
enable ? "start" : "stop", channel);
out:
kfree(cmd);
return ret;
}
int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel,
u16 beacon_interval, u8 dtim_interval)
{
struct cmd_join *join;
int ret, i;
u8 *bssid;
join = kzalloc(sizeof(*join), GFP_KERNEL);
if (!join) {
ret = -ENOMEM;
goto out;
}
wl1251_debug(DEBUG_CMD, "cmd join%s ch %d %d/%d",
bss_type == BSS_TYPE_IBSS ? " ibss" : "",
channel, beacon_interval, dtim_interval);
/* Reverse order BSSID */
bssid = (u8 *) &join->bssid_lsb;
for (i = 0; i < ETH_ALEN; i++)
bssid[i] = wl->bssid[ETH_ALEN - i - 1];
join->rx_config_options = wl->rx_config;
join->rx_filter_options = wl->rx_filter;
/*
* FIXME: disable temporarily all filters because after commit
* 9cef8737 "mac80211: fix managed mode BSSID handling" broke
* association. The filter logic needs to be implemented properly
* and once that is done, this hack can be removed.
*/
join->rx_config_options = 0;
join->rx_filter_options = WL1251_DEFAULT_RX_FILTER;
join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS |
RATE_MASK_5_5MBPS | RATE_MASK_11MBPS;
join->beacon_interval = beacon_interval;
join->dtim_interval = dtim_interval;
join->bss_type = bss_type;
join->channel = channel;
join->ctrl = JOIN_CMD_CTRL_TX_FLUSH;
ret = wl1251_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join));
if (ret < 0) {
wl1251_error("failed to initiate cmd join");
goto out;
}
out:
kfree(join);
return ret;
}
int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode)
{
struct wl1251_cmd_ps_params *ps_params = NULL;
int ret = 0;
wl1251_debug(DEBUG_CMD, "cmd set ps mode");
ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL);
if (!ps_params) {
ret = -ENOMEM;
goto out;
}
ps_params->ps_mode = ps_mode;
ps_params->send_null_data = 1;
ps_params->retries = 5;
ps_params->hang_over_period = 128;
ps_params->null_data_rate = 1; /* 1 Mbps */
ret = wl1251_cmd_send(wl, CMD_SET_PS_MODE, ps_params,
sizeof(*ps_params));
if (ret < 0) {
wl1251_error("cmd set_ps_mode failed");
goto out;
}
out:
kfree(ps_params);
return ret;
}
int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer,
size_t len)
{
struct cmd_read_write_memory *cmd;
int ret = 0;
wl1251_debug(DEBUG_CMD, "cmd read memory");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
WARN_ON(len > MAX_READ_SIZE);
len = min_t(size_t, len, MAX_READ_SIZE);
cmd->addr = addr;
cmd->size = len;
ret = wl1251_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd));
if (ret < 0) {
wl1251_error("read memory command failed: %d", ret);
goto out;
}
/* the read command got in, we can now read the answer */
wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd));
if (cmd->header.status != CMD_STATUS_SUCCESS)
wl1251_error("error in read command result: %d",
cmd->header.status);
memcpy(answer, cmd->value, len);
out:
kfree(cmd);
return ret;
}
int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id,
void *buf, size_t buf_len)
{
struct wl1251_cmd_packet_template *cmd;
size_t cmd_len;
int ret = 0;
wl1251_debug(DEBUG_CMD, "cmd template %d", cmd_id);
WARN_ON(buf_len > WL1251_MAX_TEMPLATE_SIZE);
buf_len = min_t(size_t, buf_len, WL1251_MAX_TEMPLATE_SIZE);
cmd_len = ALIGN(sizeof(*cmd) + buf_len, 4);
cmd = kzalloc(cmd_len, GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
cmd->size = cpu_to_le16(buf_len);
if (buf)
memcpy(cmd->data, buf, buf_len);
ret = wl1251_cmd_send(wl, cmd_id, cmd, cmd_len);
if (ret < 0) {
wl1251_warning("cmd set_template failed: %d", ret);
goto out;
}
out:
kfree(cmd);
return ret;
}
int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len,
struct ieee80211_channel *channels[],
unsigned int n_channels, unsigned int n_probes)
{
struct wl1251_cmd_scan *cmd;
int i, ret = 0;
wl1251_debug(DEBUG_CMD, "cmd scan");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
cmd->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD);
cmd->params.rx_filter_options = cpu_to_le32(CFG_RX_PRSP_EN |
CFG_RX_MGMT_EN |
CFG_RX_BCN_EN);
cmd->params.scan_options = 0;
cmd->params.num_channels = n_channels;
cmd->params.num_probe_requests = n_probes;
cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */
cmd->params.tid_trigger = 0;
for (i = 0; i < n_channels; i++) {
cmd->channels[i].min_duration =
cpu_to_le32(WL1251_SCAN_MIN_DURATION);
cmd->channels[i].max_duration =
cpu_to_le32(WL1251_SCAN_MAX_DURATION);
memset(&cmd->channels[i].bssid_lsb, 0xff, 4);
memset(&cmd->channels[i].bssid_msb, 0xff, 2);
cmd->channels[i].early_termination = 0;
cmd->channels[i].tx_power_att = 0;
cmd->channels[i].channel = channels[i]->hw_value;
}
cmd->params.ssid_len = ssid_len;
if (ssid)
memcpy(cmd->params.ssid, ssid, ssid_len);
ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd));
if (ret < 0) {
wl1251_error("cmd scan failed: %d", ret);
goto out;
}
wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd));
if (cmd->header.status != CMD_STATUS_SUCCESS) {
wl1251_error("cmd scan status wasn't success: %d",
cmd->header.status);
ret = -EIO;
goto out;
}
out:
kfree(cmd);
return ret;
}
int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout)
{
struct wl1251_cmd_trigger_scan_to *cmd;
int ret;
wl1251_debug(DEBUG_CMD, "cmd trigger scan to");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
cmd->timeout = timeout;
ret = wl1251_cmd_send(wl, CMD_TRIGGER_SCAN_TO, cmd, sizeof(*cmd));
if (ret < 0) {
wl1251_error("cmd trigger scan to failed: %d", ret);
goto out;
}
out:
kfree(cmd);
return ret;
}

View File

@@ -0,0 +1,415 @@
/*
* This file is part of wl1251
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL1251_CMD_H__
#define __WL1251_CMD_H__
#include "wl1251.h"
#include <net/cfg80211.h>
struct acx_header;
int wl1251_cmd_send(struct wl1251 *wl, u16 type, void *buf, size_t buf_len);
int wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer);
int wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len);
int wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len);
int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity,
void *bitmap, u16 bitmap_len, u8 bitmap_control);
int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable);
int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel,
u16 beacon_interval, u8 dtim_interval);
int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode);
int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer,
size_t len);
int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id,
void *buf, size_t buf_len);
int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len,
struct ieee80211_channel *channels[],
unsigned int n_channels, unsigned int n_probes);
int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout);
/* unit ms */
#define WL1251_COMMAND_TIMEOUT 2000
enum wl1251_commands {
CMD_RESET = 0,
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 = 8,
CMD_STOP_SCAN = 9,
CMD_VBM = 10,
CMD_START_JOIN = 11,
CMD_SET_KEYS = 12,
CMD_READ_MEMORY = 13,
CMD_WRITE_MEMORY = 14,
CMD_BEACON = 19,
CMD_PROBE_RESP = 20,
CMD_NULL_DATA = 21,
CMD_PROBE_REQ = 22,
CMD_TEST = 23,
CMD_RADIO_CALIBRATE = 25, /* OBSOLETE */
CMD_ENABLE_RX_PATH = 27, /* OBSOLETE */
CMD_NOISE_HIST = 28,
CMD_RX_RESET = 29,
CMD_PS_POLL = 30,
CMD_QOS_NULL_DATA = 31,
CMD_LNA_CONTROL = 32,
CMD_SET_BCN_MODE = 33,
CMD_MEASUREMENT = 34,
CMD_STOP_MEASUREMENT = 35,
CMD_DISCONNECT = 36,
CMD_SET_PS_MODE = 37,
CMD_CHANNEL_SWITCH = 38,
CMD_STOP_CHANNEL_SWICTH = 39,
CMD_AP_DISCOVERY = 40,
CMD_STOP_AP_DISCOVERY = 41,
CMD_SPS_SCAN = 42,
CMD_STOP_SPS_SCAN = 43,
CMD_HEALTH_CHECK = 45,
CMD_DEBUG = 46,
CMD_TRIGGER_SCAN_TO = 47,
NUM_COMMANDS,
MAX_COMMAND_ID = 0xFFFF,
};
#define MAX_CMD_PARAMS 572
struct wl1251_cmd_header {
u16 id;
u16 status;
/* payload */
u8 data[0];
} __packed;
struct wl1251_command {
struct wl1251_cmd_header header;
u8 parameters[MAX_CMD_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.*/
MAX_COMMAND_STATUS = 0xff
};
/*
* CMD_READ_MEMORY
*
* The host issues this command to read the WiLink device memory/registers.
*
* Note: The Base Band address has special handling (16 bits registers and
* addresses). For more information, see the hardware specification.
*/
/*
* CMD_WRITE_MEMORY
*
* The host issues this command to write the WiLink device memory/registers.
*
* The Base Band address has special handling (16 bits registers and
* addresses). For more information, see the hardware specification.
*/
#define MAX_READ_SIZE 256
struct cmd_read_write_memory {
struct wl1251_cmd_header header;
/* The address of the memory to read from or write to.*/
u32 addr;
/* The amount of data in bytes to read from or write to the WiLink
* device.*/
u32 size;
/* The actual value read from or written to the Wilink. The source
of this field is the Host in WRITE command or the Wilink in READ
command. */
u8 value[MAX_READ_SIZE];
} __packed;
#define CMDMBOX_HEADER_LEN 4
#define CMDMBOX_INFO_ELEM_HEADER_LEN 4
#define WL1251_SCAN_MIN_DURATION 30000
#define WL1251_SCAN_MAX_DURATION 60000
#define WL1251_SCAN_NUM_PROBES 3
struct wl1251_scan_parameters {
__le32 rx_config_options;
__le32 rx_filter_options;
/*
* Scan options:
* bit 0: When this bit is set, passive scan.
* bit 1: Band, when this bit is set we scan
* in the 5Ghz band.
* bit 2: voice mode, 0 for normal scan.
* bit 3: scan priority, 1 for high priority.
*/
__le16 scan_options;
/* Number of channels to scan */
u8 num_channels;
/* Number opf probe requests to send, per channel */
u8 num_probe_requests;
/* Rate and modulation for probe requests */
__le16 tx_rate;
u8 tid_trigger;
u8 ssid_len;
u8 ssid[32];
} __packed;
struct wl1251_scan_ch_parameters {
__le32 min_duration; /* in TU */
__le32 max_duration; /* in TU */
u32 bssid_lsb;
u16 bssid_msb;
/*
* bits 0-3: Early termination count.
* bits 4-5: Early termination condition.
*/
u8 early_termination;
u8 tx_power_att;
u8 channel;
u8 pad[3];
} __packed;
/* SCAN parameters */
#define SCAN_MAX_NUM_OF_CHANNELS 16
struct wl1251_cmd_scan {
struct wl1251_cmd_header header;
struct wl1251_scan_parameters params;
struct wl1251_scan_ch_parameters channels[SCAN_MAX_NUM_OF_CHANNELS];
} __packed;
enum {
BSS_TYPE_IBSS = 0,
BSS_TYPE_STA_BSS = 2,
BSS_TYPE_AP_BSS = 3,
MAX_BSS_TYPE = 0xFF
};
#define JOIN_CMD_CTRL_TX_FLUSH 0x80 /* Firmware flushes all Tx */
#define JOIN_CMD_CTRL_EARLY_WAKEUP_ENABLE 0x01 /* Early wakeup time */
struct cmd_join {
struct wl1251_cmd_header header;
u32 bssid_lsb;
u16 bssid_msb;
u16 beacon_interval; /* in TBTTs */
u32 rx_config_options;
u32 rx_filter_options;
/*
* The target uses this field to determine the rate at
* which to transmit control frame responses (such as
* ACK or CTS frames).
*/
u16 basic_rate_set;
u8 dtim_interval;
u8 tx_ctrl_frame_rate; /* OBSOLETE */
u8 tx_ctrl_frame_mod; /* OBSOLETE */
/*
* bits 0-2: This bitwise field specifies the type
* of BSS to start or join (BSS_TYPE_*).
* bit 4: Band - The radio band in which to join
* or start.
* 0 - 2.4GHz band
* 1 - 5GHz band
* bits 3, 5-7: Reserved
*/
u8 bss_type;
u8 channel;
u8 ssid_len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ctrl; /* JOIN_CMD_CTRL_* */
u8 tx_mgt_frame_rate; /* OBSOLETE */
u8 tx_mgt_frame_mod; /* OBSOLETE */
u8 reserved;
} __packed;
struct cmd_enabledisable_path {
struct wl1251_cmd_header header;
u8 channel;
u8 padding[3];
} __packed;
#define WL1251_MAX_TEMPLATE_SIZE 300
struct wl1251_cmd_packet_template {
struct wl1251_cmd_header header;
__le16 size;
u8 data[0];
} __packed;
#define TIM_ELE_ID 5
#define PARTIAL_VBM_MAX 251
struct wl1251_tim {
u8 identity;
u8 length;
u8 dtim_count;
u8 dtim_period;
u8 bitmap_ctrl;
u8 pvb_field[PARTIAL_VBM_MAX]; /* Partial Virtual Bitmap */
} __packed;
/* Virtual Bit Map update */
struct wl1251_cmd_vbm_update {
struct wl1251_cmd_header header;
__le16 len;
u8 padding[2];
struct wl1251_tim tim;
} __packed;
enum wl1251_cmd_ps_mode {
CHIP_ACTIVE_MODE,
CHIP_POWER_SAVE_MODE
};
struct wl1251_cmd_ps_params {
struct wl1251_cmd_header header;
u8 ps_mode; /* STATION_* */
u8 send_null_data; /* Do we have to send NULL data packet ? */
u8 retries; /* Number of retires for the initial NULL data packet */
/*
* TUs during which the target stays awake after switching
* to power save mode.
*/
u8 hang_over_period;
u16 null_data_rate;
u8 pad[2];
} __packed;
struct wl1251_cmd_trigger_scan_to {
struct wl1251_cmd_header header;
u32 timeout;
} __packed;
/* HW encryption keys */
#define NUM_ACCESS_CATEGORIES_COPY 4
#define MAX_KEY_SIZE 32
/* When set, disable HW encryption */
#define DF_ENCRYPTION_DISABLE 0x01
/* When set, disable HW decryption */
#define DF_SNIFF_MODE_ENABLE 0x80
enum wl1251_cmd_key_action {
KEY_ADD_OR_REPLACE = 1,
KEY_REMOVE = 2,
KEY_SET_ID = 3,
MAX_KEY_ACTION = 0xffff,
};
enum wl1251_cmd_key_type {
KEY_WEP_DEFAULT = 0,
KEY_WEP_ADDR = 1,
KEY_AES_GROUP = 4,
KEY_AES_PAIRWISE = 5,
KEY_WEP_GROUP = 6,
KEY_TKIP_MIC_GROUP = 10,
KEY_TKIP_MIC_PAIRWISE = 11,
};
/*
*
* key_type_e key size key format
* ---------- --------- ----------
* 0x00 5, 13, 29 Key data
* 0x01 5, 13, 29 Key data
* 0x04 16 16 bytes of key data
* 0x05 16 16 bytes of key data
* 0x0a 32 16 bytes of TKIP key data
* 8 bytes of RX MIC key data
* 8 bytes of TX MIC key data
* 0x0b 32 16 bytes of TKIP key data
* 8 bytes of RX MIC key data
* 8 bytes of TX MIC key data
*
*/
struct wl1251_cmd_set_keys {
struct wl1251_cmd_header header;
/* Ignored for default WEP key */
u8 addr[ETH_ALEN];
/* key_action_e */
u16 key_action;
u16 reserved_1;
/* key size in bytes */
u8 key_size;
/* key_type_e */
u8 key_type;
u8 ssid_profile;
/*
* TKIP, AES: frame's key id field.
* For WEP default key: key id;
*/
u8 id;
u8 reserved_2[6];
u8 key[MAX_KEY_SIZE];
u16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY];
u32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
} __packed;
#endif /* __WL1251_CMD_H__ */

View File

@@ -0,0 +1,545 @@
/*
* This file is part of wl1251
*
* Copyright (C) 2009 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "debugfs.h"
#include <linux/skbuff.h>
#include <linux/slab.h>
#include "wl1251.h"
#include "acx.h"
#include "ps.h"
/* ms */
#define WL1251_DEBUGFS_STATS_LIFETIME 1000
/* debugfs macros idea from mac80211 */
#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \
static ssize_t name## _read(struct file *file, char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wl1251 *wl = file->private_data; \
char buf[buflen]; \
int res; \
\
res = scnprintf(buf, buflen, fmt "\n", ##value); \
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
} \
\
static const struct file_operations name## _ops = { \
.read = name## _read, \
.open = wl1251_open_file_generic, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_ADD(name, parent) \
wl->debugfs.name = debugfs_create_file(#name, 0400, parent, \
wl, &name## _ops); \
if (IS_ERR(wl->debugfs.name)) { \
ret = PTR_ERR(wl->debugfs.name); \
wl->debugfs.name = NULL; \
goto out; \
}
#define DEBUGFS_DEL(name) \
do { \
debugfs_remove(wl->debugfs.name); \
wl->debugfs.name = NULL; \
} while (0)
#define DEBUGFS_FWSTATS_FILE(sub, name, buflen, fmt) \
static ssize_t sub## _ ##name## _read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wl1251 *wl = file->private_data; \
char buf[buflen]; \
int res; \
\
wl1251_debugfs_update_stats(wl); \
\
res = scnprintf(buf, buflen, fmt "\n", \
wl->stats.fw_stats->sub.name); \
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
} \
\
static const struct file_operations sub## _ ##name## _ops = { \
.read = sub## _ ##name## _read, \
.open = wl1251_open_file_generic, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_FWSTATS_ADD(sub, name) \
DEBUGFS_ADD(sub## _ ##name, wl->debugfs.fw_statistics)
#define DEBUGFS_FWSTATS_DEL(sub, name) \
DEBUGFS_DEL(sub## _ ##name)
static void wl1251_debugfs_update_stats(struct wl1251 *wl)
{
int ret;
mutex_lock(&wl->mutex);
ret = wl1251_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
if (wl->state == WL1251_STATE_ON &&
time_after(jiffies, wl->stats.fw_stats_update +
msecs_to_jiffies(WL1251_DEBUGFS_STATS_LIFETIME))) {
wl1251_acx_statistics(wl, wl->stats.fw_stats);
wl->stats.fw_stats_update = jiffies;
}
wl1251_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
}
static int wl1251_open_file_generic(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, 20, "%u");
DEBUGFS_FWSTATS_FILE(rx, out_of_mem, 20, "%u");
DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, 20, "%u");
DEBUGFS_FWSTATS_FILE(rx, hw_stuck, 20, "%u");
DEBUGFS_FWSTATS_FILE(rx, dropped, 20, "%u");
DEBUGFS_FWSTATS_FILE(rx, fcs_err, 20, "%u");
DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, 20, "%u");
DEBUGFS_FWSTATS_FILE(rx, path_reset, 20, "%u");
DEBUGFS_FWSTATS_FILE(rx, reset_counter, 20, "%u");
DEBUGFS_FWSTATS_FILE(dma, rx_requested, 20, "%u");
DEBUGFS_FWSTATS_FILE(dma, rx_errors, 20, "%u");
DEBUGFS_FWSTATS_FILE(dma, tx_requested, 20, "%u");
DEBUGFS_FWSTATS_FILE(dma, tx_errors, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, fiqs, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, rx_headers, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, rx_rdys, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, irqs, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, tx_procs, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, decrypt_done, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, dma0_done, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, dma1_done, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, commands, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, rx_procs, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, pci_pm, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, wakeups, 20, "%u");
DEBUGFS_FWSTATS_FILE(isr, low_rssi, 20, "%u");
DEBUGFS_FWSTATS_FILE(wep, addr_key_count, 20, "%u");
DEBUGFS_FWSTATS_FILE(wep, default_key_count, 20, "%u");
/* skipping wep.reserved */
DEBUGFS_FWSTATS_FILE(wep, key_not_found, 20, "%u");
DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, 20, "%u");
DEBUGFS_FWSTATS_FILE(wep, packets, 20, "%u");
DEBUGFS_FWSTATS_FILE(wep, interrupt, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, ps_enter, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, elp_enter, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, power_save_off, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, enable_ps, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, disable_ps, 20, "%u");
DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, 20, "%u");
/* skipping cont_miss_bcns_spread for now */
DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, 20, "%u");
DEBUGFS_FWSTATS_FILE(mic, rx_pkts, 20, "%u");
DEBUGFS_FWSTATS_FILE(mic, calc_failure, 20, "%u");
DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, 20, "%u");
DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, 20, "%u");
DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, 20, "%u");
DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, 20, "%u");
DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, 20, "%u");
DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, 20, "%u");
DEBUGFS_FWSTATS_FILE(event, heart_beat, 20, "%u");
DEBUGFS_FWSTATS_FILE(event, calibration, 20, "%u");
DEBUGFS_FWSTATS_FILE(event, rx_mismatch, 20, "%u");
DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, 20, "%u");
DEBUGFS_FWSTATS_FILE(event, rx_pool, 20, "%u");
DEBUGFS_FWSTATS_FILE(event, oom_late, 20, "%u");
DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, 20, "%u");
DEBUGFS_FWSTATS_FILE(event, tx_stuck, 20, "%u");
DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, 20, "%u");
DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, 20, "%u");
DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, 20, "%u");
DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, 20, "%u");
DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, 20, "%u");
DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, 20, "%u");
DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, 20, "%u");
DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, 20, "%u");
DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, 20, "%u");
DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data,
20, "%u");
DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, 20, "%u");
DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, 20, "%u");
DEBUGFS_READONLY_FILE(retry_count, 20, "%u", wl->stats.retry_count);
DEBUGFS_READONLY_FILE(excessive_retries, 20, "%u",
wl->stats.excessive_retries);
static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct wl1251 *wl = file->private_data;
u32 queue_len;
char buf[20];
int res;
queue_len = skb_queue_len(&wl->tx_queue);
res = scnprintf(buf, sizeof(buf), "%u\n", queue_len);
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
static const struct file_operations tx_queue_len_ops = {
.read = tx_queue_len_read,
.open = wl1251_open_file_generic,
.llseek = generic_file_llseek,
};
static ssize_t tx_queue_status_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct wl1251 *wl = file->private_data;
char buf[3], status;
int len;
if (wl->tx_queue_stopped)
status = 's';
else
status = 'r';
len = scnprintf(buf, sizeof(buf), "%c\n", status);
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
}
static const struct file_operations tx_queue_status_ops = {
.read = tx_queue_status_read,
.open = wl1251_open_file_generic,
.llseek = generic_file_llseek,
};
static void wl1251_debugfs_delete_files(struct wl1251 *wl)
{
DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow);
DEBUGFS_FWSTATS_DEL(rx, out_of_mem);
DEBUGFS_FWSTATS_DEL(rx, hdr_overflow);
DEBUGFS_FWSTATS_DEL(rx, hw_stuck);
DEBUGFS_FWSTATS_DEL(rx, dropped);
DEBUGFS_FWSTATS_DEL(rx, fcs_err);
DEBUGFS_FWSTATS_DEL(rx, xfr_hint_trig);
DEBUGFS_FWSTATS_DEL(rx, path_reset);
DEBUGFS_FWSTATS_DEL(rx, reset_counter);
DEBUGFS_FWSTATS_DEL(dma, rx_requested);
DEBUGFS_FWSTATS_DEL(dma, rx_errors);
DEBUGFS_FWSTATS_DEL(dma, tx_requested);
DEBUGFS_FWSTATS_DEL(dma, tx_errors);
DEBUGFS_FWSTATS_DEL(isr, cmd_cmplt);
DEBUGFS_FWSTATS_DEL(isr, fiqs);
DEBUGFS_FWSTATS_DEL(isr, rx_headers);
DEBUGFS_FWSTATS_DEL(isr, rx_mem_overflow);
DEBUGFS_FWSTATS_DEL(isr, rx_rdys);
DEBUGFS_FWSTATS_DEL(isr, irqs);
DEBUGFS_FWSTATS_DEL(isr, tx_procs);
DEBUGFS_FWSTATS_DEL(isr, decrypt_done);
DEBUGFS_FWSTATS_DEL(isr, dma0_done);
DEBUGFS_FWSTATS_DEL(isr, dma1_done);
DEBUGFS_FWSTATS_DEL(isr, tx_exch_complete);
DEBUGFS_FWSTATS_DEL(isr, commands);
DEBUGFS_FWSTATS_DEL(isr, rx_procs);
DEBUGFS_FWSTATS_DEL(isr, hw_pm_mode_changes);
DEBUGFS_FWSTATS_DEL(isr, host_acknowledges);
DEBUGFS_FWSTATS_DEL(isr, pci_pm);
DEBUGFS_FWSTATS_DEL(isr, wakeups);
DEBUGFS_FWSTATS_DEL(isr, low_rssi);
DEBUGFS_FWSTATS_DEL(wep, addr_key_count);
DEBUGFS_FWSTATS_DEL(wep, default_key_count);
/* skipping wep.reserved */
DEBUGFS_FWSTATS_DEL(wep, key_not_found);
DEBUGFS_FWSTATS_DEL(wep, decrypt_fail);
DEBUGFS_FWSTATS_DEL(wep, packets);
DEBUGFS_FWSTATS_DEL(wep, interrupt);
DEBUGFS_FWSTATS_DEL(pwr, ps_enter);
DEBUGFS_FWSTATS_DEL(pwr, elp_enter);
DEBUGFS_FWSTATS_DEL(pwr, missing_bcns);
DEBUGFS_FWSTATS_DEL(pwr, wake_on_host);
DEBUGFS_FWSTATS_DEL(pwr, wake_on_timer_exp);
DEBUGFS_FWSTATS_DEL(pwr, tx_with_ps);
DEBUGFS_FWSTATS_DEL(pwr, tx_without_ps);
DEBUGFS_FWSTATS_DEL(pwr, rcvd_beacons);
DEBUGFS_FWSTATS_DEL(pwr, power_save_off);
DEBUGFS_FWSTATS_DEL(pwr, enable_ps);
DEBUGFS_FWSTATS_DEL(pwr, disable_ps);
DEBUGFS_FWSTATS_DEL(pwr, fix_tsf_ps);
/* skipping cont_miss_bcns_spread for now */
DEBUGFS_FWSTATS_DEL(pwr, rcvd_awake_beacons);
DEBUGFS_FWSTATS_DEL(mic, rx_pkts);
DEBUGFS_FWSTATS_DEL(mic, calc_failure);
DEBUGFS_FWSTATS_DEL(aes, encrypt_fail);
DEBUGFS_FWSTATS_DEL(aes, decrypt_fail);
DEBUGFS_FWSTATS_DEL(aes, encrypt_packets);
DEBUGFS_FWSTATS_DEL(aes, decrypt_packets);
DEBUGFS_FWSTATS_DEL(aes, encrypt_interrupt);
DEBUGFS_FWSTATS_DEL(aes, decrypt_interrupt);
DEBUGFS_FWSTATS_DEL(event, heart_beat);
DEBUGFS_FWSTATS_DEL(event, calibration);
DEBUGFS_FWSTATS_DEL(event, rx_mismatch);
DEBUGFS_FWSTATS_DEL(event, rx_mem_empty);
DEBUGFS_FWSTATS_DEL(event, rx_pool);
DEBUGFS_FWSTATS_DEL(event, oom_late);
DEBUGFS_FWSTATS_DEL(event, phy_transmit_error);
DEBUGFS_FWSTATS_DEL(event, tx_stuck);
DEBUGFS_FWSTATS_DEL(ps, pspoll_timeouts);
DEBUGFS_FWSTATS_DEL(ps, upsd_timeouts);
DEBUGFS_FWSTATS_DEL(ps, upsd_max_sptime);
DEBUGFS_FWSTATS_DEL(ps, upsd_max_apturn);
DEBUGFS_FWSTATS_DEL(ps, pspoll_max_apturn);
DEBUGFS_FWSTATS_DEL(ps, pspoll_utilization);
DEBUGFS_FWSTATS_DEL(ps, upsd_utilization);
DEBUGFS_FWSTATS_DEL(rxpipe, rx_prep_beacon_drop);
DEBUGFS_FWSTATS_DEL(rxpipe, descr_host_int_trig_rx_data);
DEBUGFS_FWSTATS_DEL(rxpipe, beacon_buffer_thres_host_int_trig_rx_data);
DEBUGFS_FWSTATS_DEL(rxpipe, missed_beacon_host_int_trig_rx_data);
DEBUGFS_FWSTATS_DEL(rxpipe, tx_xfr_host_int_trig_rx_data);
DEBUGFS_DEL(tx_queue_len);
DEBUGFS_DEL(tx_queue_status);
DEBUGFS_DEL(retry_count);
DEBUGFS_DEL(excessive_retries);
}
static int wl1251_debugfs_add_files(struct wl1251 *wl)
{
int ret = 0;
DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow);
DEBUGFS_FWSTATS_ADD(rx, out_of_mem);
DEBUGFS_FWSTATS_ADD(rx, hdr_overflow);
DEBUGFS_FWSTATS_ADD(rx, hw_stuck);
DEBUGFS_FWSTATS_ADD(rx, dropped);
DEBUGFS_FWSTATS_ADD(rx, fcs_err);
DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig);
DEBUGFS_FWSTATS_ADD(rx, path_reset);
DEBUGFS_FWSTATS_ADD(rx, reset_counter);
DEBUGFS_FWSTATS_ADD(dma, rx_requested);
DEBUGFS_FWSTATS_ADD(dma, rx_errors);
DEBUGFS_FWSTATS_ADD(dma, tx_requested);
DEBUGFS_FWSTATS_ADD(dma, tx_errors);
DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt);
DEBUGFS_FWSTATS_ADD(isr, fiqs);
DEBUGFS_FWSTATS_ADD(isr, rx_headers);
DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow);
DEBUGFS_FWSTATS_ADD(isr, rx_rdys);
DEBUGFS_FWSTATS_ADD(isr, irqs);
DEBUGFS_FWSTATS_ADD(isr, tx_procs);
DEBUGFS_FWSTATS_ADD(isr, decrypt_done);
DEBUGFS_FWSTATS_ADD(isr, dma0_done);
DEBUGFS_FWSTATS_ADD(isr, dma1_done);
DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete);
DEBUGFS_FWSTATS_ADD(isr, commands);
DEBUGFS_FWSTATS_ADD(isr, rx_procs);
DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes);
DEBUGFS_FWSTATS_ADD(isr, host_acknowledges);
DEBUGFS_FWSTATS_ADD(isr, pci_pm);
DEBUGFS_FWSTATS_ADD(isr, wakeups);
DEBUGFS_FWSTATS_ADD(isr, low_rssi);
DEBUGFS_FWSTATS_ADD(wep, addr_key_count);
DEBUGFS_FWSTATS_ADD(wep, default_key_count);
/* skipping wep.reserved */
DEBUGFS_FWSTATS_ADD(wep, key_not_found);
DEBUGFS_FWSTATS_ADD(wep, decrypt_fail);
DEBUGFS_FWSTATS_ADD(wep, packets);
DEBUGFS_FWSTATS_ADD(wep, interrupt);
DEBUGFS_FWSTATS_ADD(pwr, ps_enter);
DEBUGFS_FWSTATS_ADD(pwr, elp_enter);
DEBUGFS_FWSTATS_ADD(pwr, missing_bcns);
DEBUGFS_FWSTATS_ADD(pwr, wake_on_host);
DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp);
DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps);
DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps);
DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons);
DEBUGFS_FWSTATS_ADD(pwr, power_save_off);
DEBUGFS_FWSTATS_ADD(pwr, enable_ps);
DEBUGFS_FWSTATS_ADD(pwr, disable_ps);
DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps);
/* skipping cont_miss_bcns_spread for now */
DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons);
DEBUGFS_FWSTATS_ADD(mic, rx_pkts);
DEBUGFS_FWSTATS_ADD(mic, calc_failure);
DEBUGFS_FWSTATS_ADD(aes, encrypt_fail);
DEBUGFS_FWSTATS_ADD(aes, decrypt_fail);
DEBUGFS_FWSTATS_ADD(aes, encrypt_packets);
DEBUGFS_FWSTATS_ADD(aes, decrypt_packets);
DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt);
DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt);
DEBUGFS_FWSTATS_ADD(event, heart_beat);
DEBUGFS_FWSTATS_ADD(event, calibration);
DEBUGFS_FWSTATS_ADD(event, rx_mismatch);
DEBUGFS_FWSTATS_ADD(event, rx_mem_empty);
DEBUGFS_FWSTATS_ADD(event, rx_pool);
DEBUGFS_FWSTATS_ADD(event, oom_late);
DEBUGFS_FWSTATS_ADD(event, phy_transmit_error);
DEBUGFS_FWSTATS_ADD(event, tx_stuck);
DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts);
DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts);
DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime);
DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn);
DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn);
DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization);
DEBUGFS_FWSTATS_ADD(ps, upsd_utilization);
DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop);
DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data);
DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data);
DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data);
DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
DEBUGFS_ADD(tx_queue_len, wl->debugfs.rootdir);
DEBUGFS_ADD(tx_queue_status, wl->debugfs.rootdir);
DEBUGFS_ADD(retry_count, wl->debugfs.rootdir);
DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir);
out:
if (ret < 0)
wl1251_debugfs_delete_files(wl);
return ret;
}
void wl1251_debugfs_reset(struct wl1251 *wl)
{
if (wl->stats.fw_stats != NULL)
memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats));
wl->stats.retry_count = 0;
wl->stats.excessive_retries = 0;
}
int wl1251_debugfs_init(struct wl1251 *wl)
{
int ret;
wl->debugfs.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
if (IS_ERR(wl->debugfs.rootdir)) {
ret = PTR_ERR(wl->debugfs.rootdir);
wl->debugfs.rootdir = NULL;
goto err;
}
wl->debugfs.fw_statistics = debugfs_create_dir("fw-statistics",
wl->debugfs.rootdir);
if (IS_ERR(wl->debugfs.fw_statistics)) {
ret = PTR_ERR(wl->debugfs.fw_statistics);
wl->debugfs.fw_statistics = NULL;
goto err_root;
}
wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats),
GFP_KERNEL);
if (!wl->stats.fw_stats) {
ret = -ENOMEM;
goto err_fw;
}
wl->stats.fw_stats_update = jiffies;
ret = wl1251_debugfs_add_files(wl);
if (ret < 0)
goto err_file;
return 0;
err_file:
kfree(wl->stats.fw_stats);
wl->stats.fw_stats = NULL;
err_fw:
debugfs_remove(wl->debugfs.fw_statistics);
wl->debugfs.fw_statistics = NULL;
err_root:
debugfs_remove(wl->debugfs.rootdir);
wl->debugfs.rootdir = NULL;
err:
return ret;
}
void wl1251_debugfs_exit(struct wl1251 *wl)
{
wl1251_debugfs_delete_files(wl);
kfree(wl->stats.fw_stats);
wl->stats.fw_stats = NULL;
debugfs_remove(wl->debugfs.fw_statistics);
wl->debugfs.fw_statistics = NULL;
debugfs_remove(wl->debugfs.rootdir);
wl->debugfs.rootdir = NULL;
}

View File

@@ -0,0 +1,31 @@
/*
* This file is part of wl1251
*
* Copyright (C) 2009 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef WL1251_DEBUGFS_H
#define WL1251_DEBUGFS_H
#include "wl1251.h"
int wl1251_debugfs_init(struct wl1251 *wl);
void wl1251_debugfs_exit(struct wl1251 *wl);
void wl1251_debugfs_reset(struct wl1251 *wl);
#endif /* WL1251_DEBUGFS_H */

View File

@@ -0,0 +1,188 @@
/*
* This file is part of wl1251
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "wl1251.h"
#include "reg.h"
#include "io.h"
#include "event.h"
#include "ps.h"
static int wl1251_event_scan_complete(struct wl1251 *wl,
struct event_mailbox *mbox)
{
wl1251_debug(DEBUG_EVENT, "status: 0x%x, channels: %d",
mbox->scheduled_scan_status,
mbox->scheduled_scan_channels);
if (wl->scanning) {
ieee80211_scan_completed(wl->hw, false);
wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan completed");
wl->scanning = false;
}
return 0;
}
static void wl1251_event_mbox_dump(struct event_mailbox *mbox)
{
wl1251_debug(DEBUG_EVENT, "MBOX DUMP:");
wl1251_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector);
wl1251_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
}
static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
{
int ret;
u32 vector;
wl1251_event_mbox_dump(mbox);
vector = mbox->events_vector & ~(mbox->events_mask);
wl1251_debug(DEBUG_EVENT, "vector: 0x%x", vector);
if (vector & SCAN_COMPLETE_EVENT_ID) {
ret = wl1251_event_scan_complete(wl, mbox);
if (ret < 0)
return ret;
}
if (vector & BSS_LOSE_EVENT_ID) {
wl1251_debug(DEBUG_EVENT, "BSS_LOSE_EVENT");
if (wl->psm_requested &&
wl->station_mode != STATION_ACTIVE_MODE) {
ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE);
if (ret < 0)
return ret;
}
}
if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID &&
wl->station_mode != STATION_ACTIVE_MODE) {
wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT");
/* indicate to the stack, that beacons have been lost */
ieee80211_beacon_loss(wl->vif);
}
if (vector & REGAINED_BSS_EVENT_ID) {
if (wl->psm_requested) {
ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
if (ret < 0)
return ret;
}
}
if (wl->vif && wl->rssi_thold) {
if (vector & ROAMING_TRIGGER_LOW_RSSI_EVENT_ID) {
wl1251_debug(DEBUG_EVENT,
"ROAMING_TRIGGER_LOW_RSSI_EVENT");
ieee80211_cqm_rssi_notify(wl->vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
GFP_KERNEL);
}
if (vector & ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID) {
wl1251_debug(DEBUG_EVENT,
"ROAMING_TRIGGER_REGAINED_RSSI_EVENT");
ieee80211_cqm_rssi_notify(wl->vif,
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
GFP_KERNEL);
}
}
return 0;
}
/*
* Poll the mailbox event field until any of the bits in the mask is set or a
* timeout occurs (WL1251_EVENT_TIMEOUT in msecs)
*/
int wl1251_event_wait(struct wl1251 *wl, u32 mask, int timeout_ms)
{
u32 events_vector, event;
unsigned long timeout;
timeout = jiffies + msecs_to_jiffies(timeout_ms);
do {
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
msleep(1);
/* read from both event fields */
wl1251_mem_read(wl, wl->mbox_ptr[0], &events_vector,
sizeof(events_vector));
event = events_vector & mask;
wl1251_mem_read(wl, wl->mbox_ptr[1], &events_vector,
sizeof(events_vector));
event |= events_vector & mask;
} while (!event);
return 0;
}
int wl1251_event_unmask(struct wl1251 *wl)
{
int ret;
ret = wl1251_acx_event_mbox_mask(wl, ~(wl->event_mask));
if (ret < 0)
return ret;
return 0;
}
void wl1251_event_mbox_config(struct wl1251 *wl)
{
wl->mbox_ptr[0] = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR);
wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
wl1251_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x",
wl->mbox_ptr[0], wl->mbox_ptr[1]);
}
int wl1251_event_handle(struct wl1251 *wl, u8 mbox_num)
{
struct event_mailbox mbox;
int ret;
wl1251_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num);
if (mbox_num > 1)
return -EINVAL;
/* first we read the mbox descriptor */
wl1251_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox,
sizeof(struct event_mailbox));
/* process the descriptor */
ret = wl1251_event_process(wl, &mbox);
if (ret < 0)
return ret;
/* then we let the firmware know it can go on...*/
wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK);
return 0;
}

View File

@@ -0,0 +1,120 @@
/*
* This file is part of wl1251
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL1251_EVENT_H__
#define __WL1251_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 {
RESERVED1_EVENT_ID = BIT(0),
RESERVED2_EVENT_ID = BIT(1),
MEASUREMENT_START_EVENT_ID = BIT(2),
SCAN_COMPLETE_EVENT_ID = BIT(3),
CALIBRATION_COMPLETE_EVENT_ID = BIT(4),
ROAMING_TRIGGER_LOW_RSSI_EVENT_ID = BIT(5),
PS_REPORT_EVENT_ID = BIT(6),
SYNCHRONIZATION_TIMEOUT_EVENT_ID = BIT(7),
HEALTH_REPORT_EVENT_ID = BIT(8),
ACI_DETECTION_EVENT_ID = BIT(9),
DEBUG_REPORT_EVENT_ID = BIT(10),
MAC_STATUS_EVENT_ID = BIT(11),
DISCONNECT_EVENT_COMPLETE_ID = BIT(12),
JOIN_EVENT_COMPLETE_ID = BIT(13),
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(14),
BSS_LOSE_EVENT_ID = BIT(15),
ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(16),
MEASUREMENT_COMPLETE_EVENT_ID = BIT(17),
AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(18),
SCHEDULED_SCAN_COMPLETE_EVENT_ID = BIT(19),
PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(20),
RESET_BSS_EVENT_ID = BIT(21),
REGAINED_BSS_EVENT_ID = BIT(22),
ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID = BIT(23),
ROAMING_TRIGGER_LOW_SNR_EVENT_ID = BIT(24),
ROAMING_TRIGGER_REGAINED_SNR_EVENT_ID = BIT(25),
DBG_EVENT_ID = BIT(26),
BT_PTA_SENSE_EVENT_ID = BIT(27),
BT_PTA_PREDICTION_EVENT_ID = BIT(28),
BT_PTA_AVALANCHE_EVENT_ID = BIT(29),
PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(30),
EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff,
};
struct event_debug_report {
u8 debug_event_id;
u8 num_params;
u16 pad;
u32 report_1;
u32 report_2;
u32 report_3;
} __packed;
struct event_mailbox {
u32 events_vector;
u32 events_mask;
u32 reserved_1;
u32 reserved_2;
char average_rssi_level;
u8 ps_status;
u8 channel_switch_status;
u8 scheduled_scan_status;
/* Channels scanned by the scheduled scan */
u16 scheduled_scan_channels;
/* If bit 0 is set -> target's fatal error */
u16 health_report;
u16 bad_fft_counter;
u8 bt_pta_sense_info;
u8 bt_pta_protective_info;
u32 reserved;
u32 debug_report[2];
/* Number of FCS errors since last event */
u32 fcs_err_counter;
struct event_debug_report report;
u8 average_snr_level;
u8 padding[19];
} __packed;
int wl1251_event_unmask(struct wl1251 *wl);
void wl1251_event_mbox_config(struct wl1251 *wl);
int wl1251_event_handle(struct wl1251 *wl, u8 mbox);
int wl1251_event_wait(struct wl1251 *wl, u32 mask, int timeout_ms);
#endif

View File

@@ -0,0 +1,423 @@
/*
* This file is part of wl1251
*
* Copyright (C) 2009 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "init.h"
#include "wl12xx_80211.h"
#include "acx.h"
#include "cmd.h"
#include "reg.h"
int wl1251_hw_init_hwenc_config(struct wl1251 *wl)
{
int ret;
ret = wl1251_acx_feature_cfg(wl);
if (ret < 0) {
wl1251_warning("couldn't set feature config");
return ret;
}
ret = wl1251_acx_default_key(wl, wl->default_key);
if (ret < 0) {
wl1251_warning("couldn't set default key");
return ret;
}
return 0;
}
int wl1251_hw_init_templates_config(struct wl1251 *wl)
{
int ret;
u8 partial_vbm[PARTIAL_VBM_MAX];
/* send empty templates for fw memory reservation */
ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, NULL,
sizeof(struct wl12xx_probe_req_template));
if (ret < 0)
return ret;
ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, NULL,
sizeof(struct wl12xx_null_data_template));
if (ret < 0)
return ret;
ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, NULL,
sizeof(struct wl12xx_ps_poll_template));
if (ret < 0)
return ret;
ret = wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, NULL,
sizeof
(struct wl12xx_qos_null_data_template));
if (ret < 0)
return ret;
ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, NULL,
sizeof
(struct wl12xx_probe_resp_template));
if (ret < 0)
return ret;
ret = wl1251_cmd_template_set(wl, CMD_BEACON, NULL,
sizeof
(struct wl12xx_beacon_template));
if (ret < 0)
return ret;
/* tim templates, first reserve space then allocate an empty one */
memset(partial_vbm, 0, PARTIAL_VBM_MAX);
ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, PARTIAL_VBM_MAX, 0);
if (ret < 0)
return ret;
ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, 1, 0);
if (ret < 0)
return ret;
return 0;
}
int wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter)
{
int ret;
ret = wl1251_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF);
if (ret < 0)
return ret;
ret = wl1251_acx_rx_config(wl, config, filter);
if (ret < 0)
return ret;
return 0;
}
int wl1251_hw_init_phy_config(struct wl1251 *wl)
{
int ret;
ret = wl1251_acx_pd_threshold(wl);
if (ret < 0)
return ret;
ret = wl1251_acx_slot(wl, DEFAULT_SLOT_TIME);
if (ret < 0)
return ret;
ret = wl1251_acx_group_address_tbl(wl);
if (ret < 0)
return ret;
ret = wl1251_acx_service_period_timeout(wl);
if (ret < 0)
return ret;
ret = wl1251_acx_rts_threshold(wl, RTS_THRESHOLD_DEF);
if (ret < 0)
return ret;
return 0;
}
int wl1251_hw_init_beacon_filter(struct wl1251 *wl)
{
int ret;
/* disable beacon filtering at this stage */
ret = wl1251_acx_beacon_filter_opt(wl, false);
if (ret < 0)
return ret;
ret = wl1251_acx_beacon_filter_table(wl);
if (ret < 0)
return ret;
return 0;
}
int wl1251_hw_init_pta(struct wl1251 *wl)
{
int ret;
ret = wl1251_acx_sg_enable(wl);
if (ret < 0)
return ret;
ret = wl1251_acx_sg_cfg(wl);
if (ret < 0)
return ret;
return 0;
}
int wl1251_hw_init_energy_detection(struct wl1251 *wl)
{
int ret;
ret = wl1251_acx_cca_threshold(wl);
if (ret < 0)
return ret;
return 0;
}
int wl1251_hw_init_beacon_broadcast(struct wl1251 *wl)
{
int ret;
ret = wl1251_acx_bcn_dtim_options(wl);
if (ret < 0)
return ret;
return 0;
}
int wl1251_hw_init_power_auth(struct wl1251 *wl)
{
return wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM);
}
int wl1251_hw_init_mem_config(struct wl1251 *wl)
{
int ret;
ret = wl1251_acx_mem_cfg(wl);
if (ret < 0)
return ret;
wl->target_mem_map = kzalloc(sizeof(struct wl1251_acx_mem_map),
GFP_KERNEL);
if (!wl->target_mem_map) {
wl1251_error("couldn't allocate target memory map");
return -ENOMEM;
}
/* we now ask for the firmware built memory map */
ret = wl1251_acx_mem_map(wl, wl->target_mem_map,
sizeof(struct wl1251_acx_mem_map));
if (ret < 0) {
wl1251_error("couldn't retrieve firmware memory map");
kfree(wl->target_mem_map);
wl->target_mem_map = NULL;
return ret;
}
return 0;
}
static int wl1251_hw_init_txq_fill(u8 qid,
struct acx_tx_queue_qos_config *config,
u32 num_blocks)
{
config->qid = qid;
switch (qid) {
case QOS_AC_BE:
config->high_threshold =
(QOS_TX_HIGH_BE_DEF * num_blocks) / 100;
config->low_threshold =
(QOS_TX_LOW_BE_DEF * num_blocks) / 100;
break;
case QOS_AC_BK:
config->high_threshold =
(QOS_TX_HIGH_BK_DEF * num_blocks) / 100;
config->low_threshold =
(QOS_TX_LOW_BK_DEF * num_blocks) / 100;
break;
case QOS_AC_VI:
config->high_threshold =
(QOS_TX_HIGH_VI_DEF * num_blocks) / 100;
config->low_threshold =
(QOS_TX_LOW_VI_DEF * num_blocks) / 100;
break;
case QOS_AC_VO:
config->high_threshold =
(QOS_TX_HIGH_VO_DEF * num_blocks) / 100;
config->low_threshold =
(QOS_TX_LOW_VO_DEF * num_blocks) / 100;
break;
default:
wl1251_error("Invalid TX queue id: %d", qid);
return -EINVAL;
}
return 0;
}
static int wl1251_hw_init_tx_queue_config(struct wl1251 *wl)
{
struct acx_tx_queue_qos_config *config;
struct wl1251_acx_mem_map *wl_mem_map = wl->target_mem_map;
int ret, i;
wl1251_debug(DEBUG_ACX, "acx tx queue config");
config = kzalloc(sizeof(*config), GFP_KERNEL);
if (!config) {
ret = -ENOMEM;
goto out;
}
for (i = 0; i < MAX_NUM_OF_AC; i++) {
ret = wl1251_hw_init_txq_fill(i, config,
wl_mem_map->num_tx_mem_blocks);
if (ret < 0)
goto out;
ret = wl1251_cmd_configure(wl, ACX_TX_QUEUE_CFG,
config, sizeof(*config));
if (ret < 0)
goto out;
}
wl1251_acx_ac_cfg(wl, AC_BE, CWMIN_BE, CWMAX_BE, AIFS_DIFS, TXOP_BE);
wl1251_acx_ac_cfg(wl, AC_BK, CWMIN_BK, CWMAX_BK, AIFS_DIFS, TXOP_BK);
wl1251_acx_ac_cfg(wl, AC_VI, CWMIN_VI, CWMAX_VI, AIFS_DIFS, TXOP_VI);
wl1251_acx_ac_cfg(wl, AC_VO, CWMIN_VO, CWMAX_VO, AIFS_DIFS, TXOP_VO);
out:
kfree(config);
return ret;
}
static int wl1251_hw_init_data_path_config(struct wl1251 *wl)
{
int ret;
/* asking for the data path parameters */
wl->data_path = kzalloc(sizeof(struct acx_data_path_params_resp),
GFP_KERNEL);
if (!wl->data_path) {
wl1251_error("Couldnt allocate data path parameters");
return -ENOMEM;
}
ret = wl1251_acx_data_path_params(wl, wl->data_path);
if (ret < 0) {
kfree(wl->data_path);
wl->data_path = NULL;
return ret;
}
return 0;
}
int wl1251_hw_init(struct wl1251 *wl)
{
struct wl1251_acx_mem_map *wl_mem_map;
int ret;
ret = wl1251_hw_init_hwenc_config(wl);
if (ret < 0)
return ret;
/* Template settings */
ret = wl1251_hw_init_templates_config(wl);
if (ret < 0)
return ret;
/* Default memory configuration */
ret = wl1251_hw_init_mem_config(wl);
if (ret < 0)
return ret;
/* Default data path configuration */
ret = wl1251_hw_init_data_path_config(wl);
if (ret < 0)
goto out_free_memmap;
/* RX config */
ret = wl1251_hw_init_rx_config(wl,
RX_CFG_PROMISCUOUS | RX_CFG_TSF,
RX_FILTER_OPTION_DEF);
/* RX_CONFIG_OPTION_ANY_DST_ANY_BSS,
RX_FILTER_OPTION_FILTER_ALL); */
if (ret < 0)
goto out_free_data_path;
/* TX queues config */
ret = wl1251_hw_init_tx_queue_config(wl);
if (ret < 0)
goto out_free_data_path;
/* PHY layer config */
ret = wl1251_hw_init_phy_config(wl);
if (ret < 0)
goto out_free_data_path;
/* Initialize connection monitoring thresholds */
ret = wl1251_acx_conn_monit_params(wl);
if (ret < 0)
goto out_free_data_path;
/* Beacon filtering */
ret = wl1251_hw_init_beacon_filter(wl);
if (ret < 0)
goto out_free_data_path;
/* Bluetooth WLAN coexistence */
ret = wl1251_hw_init_pta(wl);
if (ret < 0)
goto out_free_data_path;
/* Energy detection */
ret = wl1251_hw_init_energy_detection(wl);
if (ret < 0)
goto out_free_data_path;
/* Beacons and boradcast settings */
ret = wl1251_hw_init_beacon_broadcast(wl);
if (ret < 0)
goto out_free_data_path;
/* Enable data path */
ret = wl1251_cmd_data_path(wl, wl->channel, 1);
if (ret < 0)
goto out_free_data_path;
/* Default power state */
ret = wl1251_hw_init_power_auth(wl);
if (ret < 0)
goto out_free_data_path;
wl_mem_map = wl->target_mem_map;
wl1251_info("%d tx blocks at 0x%x, %d rx blocks at 0x%x",
wl_mem_map->num_tx_mem_blocks,
wl->data_path->tx_control_addr,
wl_mem_map->num_rx_mem_blocks,
wl->data_path->rx_control_addr);
return 0;
out_free_data_path:
kfree(wl->data_path);
out_free_memmap:
kfree(wl->target_mem_map);
return ret;
}

View File

@@ -0,0 +1,86 @@
/*
* This file is part of wl1251
*
* Copyright (C) 2009 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL1251_INIT_H__
#define __WL1251_INIT_H__
#include "wl1251.h"
enum {
/* best effort/legacy */
AC_BE = 0,
/* background */
AC_BK = 1,
/* video */
AC_VI = 2,
/* voice */
AC_VO = 3,
/* broadcast dummy access category */
AC_BCAST = 4,
NUM_ACCESS_CATEGORIES = 4
};
/* following are defult values for the IE fields*/
#define CWMIN_BK 15
#define CWMIN_BE 15
#define CWMIN_VI 7
#define CWMIN_VO 3
#define CWMAX_BK 1023
#define CWMAX_BE 63
#define CWMAX_VI 15
#define CWMAX_VO 7
/* slot number setting to start transmission at PIFS interval */
#define AIFS_PIFS 1
/*
* slot number setting to start transmission at DIFS interval - normal DCF
* access
*/
#define AIFS_DIFS 2
#define AIFSN_BK 7
#define AIFSN_BE 3
#define AIFSN_VI AIFS_PIFS
#define AIFSN_VO AIFS_PIFS
#define TXOP_BK 0
#define TXOP_BE 0
#define TXOP_VI 3008
#define TXOP_VO 1504
int wl1251_hw_init_hwenc_config(struct wl1251 *wl);
int wl1251_hw_init_templates_config(struct wl1251 *wl);
int wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter);
int wl1251_hw_init_phy_config(struct wl1251 *wl);
int wl1251_hw_init_beacon_filter(struct wl1251 *wl);
int wl1251_hw_init_pta(struct wl1251 *wl);
int wl1251_hw_init_energy_detection(struct wl1251 *wl);
int wl1251_hw_init_beacon_broadcast(struct wl1251 *wl);
int wl1251_hw_init_power_auth(struct wl1251 *wl);
int wl1251_hw_init_mem_config(struct wl1251 *wl);
int wl1251_hw_init(struct wl1251 *wl);
#endif

View File

@@ -0,0 +1,194 @@
/*
* This file is part of wl12xx
*
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "wl1251.h"
#include "reg.h"
#include "io.h"
/* FIXME: this is static data nowadays and the table can be removed */
static enum wl12xx_acx_int_reg wl1251_io_reg_table[ACX_REG_TABLE_LEN] = {
[ACX_REG_INTERRUPT_TRIG] = (REGISTERS_BASE + 0x0474),
[ACX_REG_INTERRUPT_TRIG_H] = (REGISTERS_BASE + 0x0478),
[ACX_REG_INTERRUPT_MASK] = (REGISTERS_BASE + 0x0494),
[ACX_REG_HINT_MASK_SET] = (REGISTERS_BASE + 0x0498),
[ACX_REG_HINT_MASK_CLR] = (REGISTERS_BASE + 0x049C),
[ACX_REG_INTERRUPT_NO_CLEAR] = (REGISTERS_BASE + 0x04B0),
[ACX_REG_INTERRUPT_CLEAR] = (REGISTERS_BASE + 0x04A4),
[ACX_REG_INTERRUPT_ACK] = (REGISTERS_BASE + 0x04A8),
[ACX_REG_SLV_SOFT_RESET] = (REGISTERS_BASE + 0x0000),
[ACX_REG_EE_START] = (REGISTERS_BASE + 0x080C),
[ACX_REG_ECPU_CONTROL] = (REGISTERS_BASE + 0x0804)
};
static int wl1251_translate_reg_addr(struct wl1251 *wl, int addr)
{
/* If the address is lower than REGISTERS_BASE, it means that this is
* a chip-specific register address, so look it up in the registers
* table */
if (addr < REGISTERS_BASE) {
/* Make sure we don't go over the table */
if (addr >= ACX_REG_TABLE_LEN) {
wl1251_error("address out of range (%d)", addr);
return -EINVAL;
}
addr = wl1251_io_reg_table[addr];
}
return addr - wl->physical_reg_addr + wl->virtual_reg_addr;
}
static int wl1251_translate_mem_addr(struct wl1251 *wl, int addr)
{
return addr - wl->physical_mem_addr + wl->virtual_mem_addr;
}
void wl1251_mem_read(struct wl1251 *wl, int addr, void *buf, size_t len)
{
int physical;
physical = wl1251_translate_mem_addr(wl, addr);
wl->if_ops->read(wl, physical, buf, len);
}
void wl1251_mem_write(struct wl1251 *wl, int addr, void *buf, size_t len)
{
int physical;
physical = wl1251_translate_mem_addr(wl, addr);
wl->if_ops->write(wl, physical, buf, len);
}
u32 wl1251_mem_read32(struct wl1251 *wl, int addr)
{
return wl1251_read32(wl, wl1251_translate_mem_addr(wl, addr));
}
void wl1251_mem_write32(struct wl1251 *wl, int addr, u32 val)
{
wl1251_write32(wl, wl1251_translate_mem_addr(wl, addr), val);
}
u32 wl1251_reg_read32(struct wl1251 *wl, int addr)
{
return wl1251_read32(wl, wl1251_translate_reg_addr(wl, addr));
}
void wl1251_reg_write32(struct wl1251 *wl, int addr, u32 val)
{
wl1251_write32(wl, wl1251_translate_reg_addr(wl, addr), val);
}
/* Set the partitions to access the chip addresses.
*
* There are two VIRTUAL partitions (the memory partition and the
* registers partition), which are mapped to two different areas of the
* PHYSICAL (hardware) memory. This function also makes other checks to
* ensure that the partitions are not overlapping. In the diagram below, the
* memory partition comes before the register partition, but the opposite is
* also supported.
*
* PHYSICAL address
* space
*
* | |
* ...+----+--> mem_start
* VIRTUAL address ... | |
* space ... | | [PART_0]
* ... | |
* 0x00000000 <--+----+... ...+----+--> mem_start + mem_size
* | | ... | |
* |MEM | ... | |
* | | ... | |
* part_size <--+----+... | | {unused area)
* | | ... | |
* |REG | ... | |
* part_size | | ... | |
* + <--+----+... ...+----+--> reg_start
* reg_size ... | |
* ... | | [PART_1]
* ... | |
* ...+----+--> reg_start + reg_size
* | |
*
*/
void wl1251_set_partition(struct wl1251 *wl,
u32 mem_start, u32 mem_size,
u32 reg_start, u32 reg_size)
{
struct wl1251_partition partition[2];
wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size);
wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size);
/* Make sure that the two partitions together don't exceed the
* address range */
if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) {
wl1251_debug(DEBUG_SPI, "Total size exceeds maximum virtual"
" address range. Truncating partition[0].");
mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size;
wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size);
wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size);
}
if ((mem_start < reg_start) &&
((mem_start + mem_size) > reg_start)) {
/* Guarantee that the memory partition doesn't overlap the
* registers partition */
wl1251_debug(DEBUG_SPI, "End of partition[0] is "
"overlapping partition[1]. Adjusted.");
mem_size = reg_start - mem_start;
wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size);
wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size);
} else if ((reg_start < mem_start) &&
((reg_start + reg_size) > mem_start)) {
/* Guarantee that the register partition doesn't overlap the
* memory partition */
wl1251_debug(DEBUG_SPI, "End of partition[1] is"
" overlapping partition[0]. Adjusted.");
reg_size = mem_start - reg_start;
wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
mem_start, mem_size);
wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
reg_start, reg_size);
}
partition[0].start = mem_start;
partition[0].size = mem_size;
partition[1].start = reg_start;
partition[1].size = reg_size;
wl->physical_mem_addr = mem_start;
wl->physical_reg_addr = reg_start;
wl->virtual_mem_addr = 0;
wl->virtual_reg_addr = mem_size;
wl->if_ops->write(wl, HW_ACCESS_PART0_SIZE_ADDR, partition,
sizeof(partition));
}

View File

@@ -0,0 +1,83 @@
/*
* This file is part of wl12xx
*
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL1251_IO_H__
#define __WL1251_IO_H__
#include "wl1251.h"
#define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0
#define HW_ACCESS_PART0_SIZE_ADDR 0x1FFC0
#define HW_ACCESS_PART0_START_ADDR 0x1FFC4
#define HW_ACCESS_PART1_SIZE_ADDR 0x1FFC8
#define HW_ACCESS_PART1_START_ADDR 0x1FFCC
#define HW_ACCESS_REGISTER_SIZE 4
#define HW_ACCESS_PRAM_MAX_RANGE 0x3c000
static inline u32 wl1251_read32(struct wl1251 *wl, int addr)
{
wl->if_ops->read(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32));
return le32_to_cpu(wl->buffer_32);
}
static inline void wl1251_write32(struct wl1251 *wl, int addr, u32 val)
{
wl->buffer_32 = cpu_to_le32(val);
wl->if_ops->write(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32));
}
static inline u32 wl1251_read_elp(struct wl1251 *wl, int addr)
{
u32 response;
if (wl->if_ops->read_elp)
wl->if_ops->read_elp(wl, addr, &response);
else
wl->if_ops->read(wl, addr, &response, sizeof(u32));
return response;
}
static inline void wl1251_write_elp(struct wl1251 *wl, int addr, u32 val)
{
if (wl->if_ops->write_elp)
wl->if_ops->write_elp(wl, addr, val);
else
wl->if_ops->write(wl, addr, &val, sizeof(u32));
}
/* Memory target IO, address is translated to partition 0 */
void wl1251_mem_read(struct wl1251 *wl, int addr, void *buf, size_t len);
void wl1251_mem_write(struct wl1251 *wl, int addr, void *buf, size_t len);
u32 wl1251_mem_read32(struct wl1251 *wl, int addr);
void wl1251_mem_write32(struct wl1251 *wl, int addr, u32 val);
/* Registers IO */
u32 wl1251_reg_read32(struct wl1251 *wl, int addr);
void wl1251_reg_write32(struct wl1251 *wl, int addr, u32 val);
void wl1251_set_partition(struct wl1251 *wl,
u32 part_start, u32 part_size,
u32 reg_start, u32 reg_size);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,185 @@
/*
* This file is part of wl1251
*
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "reg.h"
#include "ps.h"
#include "cmd.h"
#include "io.h"
/* in ms */
#define WL1251_WAKEUP_TIMEOUT 100
void wl1251_elp_work(struct work_struct *work)
{
struct delayed_work *dwork;
struct wl1251 *wl;
dwork = container_of(work, struct delayed_work, work);
wl = container_of(dwork, struct wl1251, elp_work);
wl1251_debug(DEBUG_PSM, "elp work");
mutex_lock(&wl->mutex);
if (wl->elp || wl->station_mode == STATION_ACTIVE_MODE)
goto out;
wl1251_debug(DEBUG_PSM, "chip to elp");
wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
wl->elp = true;
out:
mutex_unlock(&wl->mutex);
}
#define ELP_ENTRY_DELAY 5
/* Routines to toggle sleep mode while in ELP */
void wl1251_ps_elp_sleep(struct wl1251 *wl)
{
unsigned long delay;
if (wl->station_mode != STATION_ACTIVE_MODE) {
delay = msecs_to_jiffies(ELP_ENTRY_DELAY);
ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay);
}
}
int wl1251_ps_elp_wakeup(struct wl1251 *wl)
{
unsigned long timeout, start;
u32 elp_reg;
if (delayed_work_pending(&wl->elp_work))
cancel_delayed_work(&wl->elp_work);
if (!wl->elp)
return 0;
wl1251_debug(DEBUG_PSM, "waking up chip from elp");
start = jiffies;
timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT);
wl1251_write_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
/*
* FIXME: we should wait for irq from chip but, as a temporary
* solution to simplify locking, let's poll instead
*/
while (!(elp_reg & ELPCTRL_WLAN_READY)) {
if (time_after(jiffies, timeout)) {
wl1251_error("elp wakeup timeout");
return -ETIMEDOUT;
}
msleep(1);
elp_reg = wl1251_read_elp(wl, HW_ACCESS_ELP_CTRL_REG_ADDR);
}
wl1251_debug(DEBUG_PSM, "wakeup time: %u ms",
jiffies_to_msecs(jiffies - start));
wl->elp = false;
return 0;
}
int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_station_mode mode)
{
int ret;
switch (mode) {
case STATION_POWER_SAVE_MODE:
wl1251_debug(DEBUG_PSM, "entering psm");
/* enable beacon filtering */
ret = wl1251_acx_beacon_filter_opt(wl, true);
if (ret < 0)
return ret;
ret = wl1251_acx_wake_up_conditions(wl,
WAKE_UP_EVENT_DTIM_BITMAP,
wl->listen_int);
if (ret < 0)
return ret;
ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_ENABLE,
WL1251_DEFAULT_BET_CONSECUTIVE);
if (ret < 0)
return ret;
ret = wl1251_cmd_ps_mode(wl, CHIP_POWER_SAVE_MODE);
if (ret < 0)
return ret;
ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
if (ret < 0)
return ret;
break;
case STATION_IDLE:
wl1251_debug(DEBUG_PSM, "entering idle");
ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP);
if (ret < 0)
return ret;
ret = wl1251_cmd_template_set(wl, CMD_DISCONNECT, NULL, 0);
if (ret < 0)
return ret;
break;
case STATION_ACTIVE_MODE:
default:
wl1251_debug(DEBUG_PSM, "leaving psm");
ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM);
if (ret < 0)
return ret;
/* disable BET */
ret = wl1251_acx_bet_enable(wl, WL1251_ACX_BET_DISABLE,
WL1251_DEFAULT_BET_CONSECUTIVE);
if (ret < 0)
return ret;
/* disable beacon filtering */
ret = wl1251_acx_beacon_filter_opt(wl, false);
if (ret < 0)
return ret;
ret = wl1251_acx_wake_up_conditions(wl,
WAKE_UP_EVENT_DTIM_BITMAP,
wl->listen_int);
if (ret < 0)
return ret;
ret = wl1251_cmd_ps_mode(wl, CHIP_ACTIVE_MODE);
if (ret < 0)
return ret;
break;
}
wl->station_mode = mode;
return ret;
}

View File

@@ -0,0 +1,35 @@
/*
* This file is part of wl1251
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL1251_PS_H__
#define __WL1251_PS_H__
#include "wl1251.h"
#include "acx.h"
int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_station_mode mode);
void wl1251_ps_elp_sleep(struct wl1251 *wl);
int wl1251_ps_elp_wakeup(struct wl1251 *wl);
void wl1251_elp_work(struct work_struct *work);
#endif /* __WL1251_PS_H__ */

View File

@@ -0,0 +1,655 @@
/*
* This file is part of wl12xx
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __REG_H__
#define __REG_H__
#include <linux/bitops.h>
#define REGISTERS_BASE 0x00300000
#define DRPW_BASE 0x00310000
#define REGISTERS_DOWN_SIZE 0x00008800
#define REGISTERS_WORK_SIZE 0x0000b000
#define HW_ACCESS_ELP_CTRL_REG_ADDR 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
/* Device Configuration registers*/
#define SOR_CFG (REGISTERS_BASE + 0x0800)
#define ECPU_CTRL (REGISTERS_BASE + 0x0804)
#define HI_CFG (REGISTERS_BASE + 0x0808)
/* EEPROM registers */
#define EE_START (REGISTERS_BASE + 0x080C)
#define EE_CTL (REGISTERS_BASE + 0x2000)
#define EE_DATA (REGISTERS_BASE + 0x2004)
#define EE_ADDR (REGISTERS_BASE + 0x2008)
#define EE_CTL_READ 2
#define CHIP_ID_B (REGISTERS_BASE + 0x5674)
#define CHIP_ID_1251_PG10 (0x7010101)
#define CHIP_ID_1251_PG11 (0x7020101)
#define CHIP_ID_1251_PG12 (0x7030101)
#define ENABLE (REGISTERS_BASE + 0x5450)
/* Power Management registers */
#define ELP_CFG_MODE (REGISTERS_BASE + 0x5804)
#define ELP_CMD (REGISTERS_BASE + 0x5808)
#define PLL_CAL_TIME (REGISTERS_BASE + 0x5810)
#define CLK_REQ_TIME (REGISTERS_BASE + 0x5814)
#define CLK_BUF_TIME (REGISTERS_BASE + 0x5818)
#define CFG_PLL_SYNC_CNT (REGISTERS_BASE + 0x5820)
/* Scratch Pad registers*/
#define SCR_PAD0 (REGISTERS_BASE + 0x5608)
#define SCR_PAD1 (REGISTERS_BASE + 0x560C)
#define SCR_PAD2 (REGISTERS_BASE + 0x5610)
#define SCR_PAD3 (REGISTERS_BASE + 0x5614)
#define SCR_PAD4 (REGISTERS_BASE + 0x5618)
#define SCR_PAD4_SET (REGISTERS_BASE + 0x561C)
#define SCR_PAD4_CLR (REGISTERS_BASE + 0x5620)
#define SCR_PAD5 (REGISTERS_BASE + 0x5624)
#define SCR_PAD5_SET (REGISTERS_BASE + 0x5628)
#define SCR_PAD5_CLR (REGISTERS_BASE + 0x562C)
#define SCR_PAD6 (REGISTERS_BASE + 0x5630)
#define SCR_PAD7 (REGISTERS_BASE + 0x5634)
#define SCR_PAD8 (REGISTERS_BASE + 0x5638)
#define SCR_PAD9 (REGISTERS_BASE + 0x563C)
/* Spare registers*/
#define SPARE_A1 (REGISTERS_BASE + 0x0994)
#define SPARE_A2 (REGISTERS_BASE + 0x0998)
#define SPARE_A3 (REGISTERS_BASE + 0x099C)
#define SPARE_A4 (REGISTERS_BASE + 0x09A0)
#define SPARE_A5 (REGISTERS_BASE + 0x09A4)
#define SPARE_A6 (REGISTERS_BASE + 0x09A8)
#define SPARE_A7 (REGISTERS_BASE + 0x09AC)
#define SPARE_A8 (REGISTERS_BASE + 0x09B0)
#define SPARE_B1 (REGISTERS_BASE + 0x5420)
#define SPARE_B2 (REGISTERS_BASE + 0x5424)
#define SPARE_B3 (REGISTERS_BASE + 0x5428)
#define SPARE_B4 (REGISTERS_BASE + 0x542C)
#define SPARE_B5 (REGISTERS_BASE + 0x5430)
#define SPARE_B6 (REGISTERS_BASE + 0x5434)
#define SPARE_B7 (REGISTERS_BASE + 0x5438)
#define SPARE_B8 (REGISTERS_BASE + 0x543C)
enum wl12xx_acx_int_reg {
ACX_REG_INTERRUPT_TRIG,
ACX_REG_INTERRUPT_TRIG_H,
/*=============================================
Host Interrupt Mask Register - 32bit (RW)
------------------------------------------
Setting a bit in this register masks the
corresponding interrupt to the host.
0 - RX0 - Rx first dubble buffer Data Interrupt
1 - TXD - Tx Data Interrupt
2 - TXXFR - Tx Transfer Interrupt
3 - RX1 - Rx second dubble buffer Data Interrupt
4 - RXXFR - Rx Transfer Interrupt
5 - EVENT_A - Event Mailbox interrupt
6 - EVENT_B - Event Mailbox interrupt
7 - WNONHST - Wake On Host Interrupt
8 - TRACE_A - Debug Trace interrupt
9 - TRACE_B - Debug Trace interrupt
10 - CDCMP - Command Complete Interrupt
11 -
12 -
13 -
14 - ICOMP - Initialization Complete Interrupt
16 - SG SE - Soft Gemini - Sense enable interrupt
17 - SG SD - Soft Gemini - Sense disable interrupt
18 - -
19 - -
20 - -
21- -
Default: 0x0001
*==============================================*/
ACX_REG_INTERRUPT_MASK,
/*=============================================
Host Interrupt Mask Set 16bit, (Write only)
------------------------------------------
Setting a bit in this register sets
the corresponding bin in ACX_HINT_MASK register
without effecting the mask
state of other bits (0 = no effect).
==============================================*/
ACX_REG_HINT_MASK_SET,
/*=============================================
Host Interrupt Mask Clear 16bit,(Write only)
------------------------------------------
Setting a bit in this register clears
the corresponding bin in ACX_HINT_MASK register
without effecting the mask
state of other bits (0 = no effect).
=============================================*/
ACX_REG_HINT_MASK_CLR,
/*=============================================
Host Interrupt Status Nondestructive Read
16bit,(Read only)
------------------------------------------
The host can read this register to determine
which interrupts are active.
Reading this register doesn't
effect its content.
=============================================*/
ACX_REG_INTERRUPT_NO_CLEAR,
/*=============================================
Host Interrupt Status Clear on Read Register
16bit,(Read only)
------------------------------------------
The host can read this register to determine
which interrupts are active.
Reading this register clears it,
thus making all interrupts inactive.
==============================================*/
ACX_REG_INTERRUPT_CLEAR,
/*=============================================
Host Interrupt Acknowledge Register
16bit,(Write only)
------------------------------------------
The host can set individual bits in this
register to clear (acknowledge) the corresp.
interrupt status bits in the HINT_STS_CLR and
HINT_STS_ND registers, thus making the
assotiated interrupt inactive. (0-no effect)
==============================================*/
ACX_REG_INTERRUPT_ACK,
/*===============================================
Host Software Reset - 32bit RW
------------------------------------------
[31:1] Reserved
0 SOFT_RESET Soft Reset - When this bit is set,
it holds the Wlan hardware in a soft reset state.
This reset disables all MAC and baseband processor
clocks except the CardBus/PCI interface clock.
It also initializes all MAC state machines except
the host interface. It does not reload the
contents of the EEPROM. When this bit is cleared
(not self-clearing), the Wlan hardware
exits the software reset state.
===============================================*/
ACX_REG_SLV_SOFT_RESET,
/*===============================================
EEPROM Burst Read Start - 32bit RW
------------------------------------------
[31:1] Reserved
0 ACX_EE_START - EEPROM Burst Read Start 0
Setting this bit starts a burst read from
the external EEPROM.
If this bit is set (after reset) before an EEPROM read/write,
the burst read starts at EEPROM address 0.
Otherwise, it starts at the address
following the address of the previous access.
TheWlan hardware hardware clears this bit automatically.
Default: 0x00000000
*================================================*/
ACX_REG_EE_START,
/* Embedded ARM CPU Control */
/*===============================================
Halt eCPU - 32bit RW
------------------------------------------
0 HALT_ECPU Halt Embedded CPU - This bit is the
compliment of bit 1 (MDATA2) in the SOR_CFG register.
During a hardware reset, this bit holds
the inverse of MDATA2.
When downloading firmware from the host,
set this bit (pull down MDATA2).
The host clears this bit after downloading the firmware into
zero-wait-state SSRAM.
When loading firmware from Flash, clear this bit (pull up MDATA2)
so that the eCPU can run the bootloader code in Flash
HALT_ECPU eCPU State
--------------------
1 halt eCPU
0 enable eCPU
===============================================*/
ACX_REG_ECPU_CONTROL,
ACX_REG_TABLE_LEN
};
#define ACX_SLV_SOFT_RESET_BIT BIT(0)
#define ACX_REG_EEPROM_START_BIT BIT(0)
/* Command/Information Mailbox Pointers */
/*===============================================
Command Mailbox Pointer - 32bit RW
------------------------------------------
This register holds the start address of
the command mailbox located in the Wlan hardware memory.
The host must read this pointer after a reset to
find the location of the command mailbox.
The Wlan hardware initializes the command mailbox
pointer with the default address of the command mailbox.
The command mailbox pointer is not valid until after
the host receives the Init Complete interrupt from
the Wlan hardware.
===============================================*/
#define REG_COMMAND_MAILBOX_PTR (SCR_PAD0)
/*===============================================
Information Mailbox Pointer - 32bit RW
------------------------------------------
This register holds the start address of
the information mailbox located in the Wlan hardware memory.
The host must read this pointer after a reset to find
the location of the information mailbox.
The Wlan hardware initializes the information mailbox pointer
with the default address of the information mailbox.
The information mailbox pointer is not valid
until after the host receives the Init Complete interrupt from
the Wlan hardware.
===============================================*/
#define REG_EVENT_MAILBOX_PTR (SCR_PAD1)
/* Misc */
#define REG_ENABLE_TX_RX (ENABLE)
/*
* Rx configuration (filter) information element
* ---------------------------------------------
*/
#define REG_RX_CONFIG (RX_CFG)
#define REG_RX_FILTER (RX_FILTER_CFG)
#define RX_CFG_ENABLE_PHY_HEADER_PLCP 0x0002
/* promiscuous - receives all valid frames */
#define RX_CFG_PROMISCUOUS 0x0008
/* receives frames from any BSSID */
#define RX_CFG_BSSID 0x0020
/* receives frames destined to any MAC address */
#define RX_CFG_MAC 0x0010
#define RX_CFG_ENABLE_ONLY_MY_DEST_MAC 0x0010
#define RX_CFG_ENABLE_ANY_DEST_MAC 0x0000
#define RX_CFG_ENABLE_ONLY_MY_BSSID 0x0020
#define RX_CFG_ENABLE_ANY_BSSID 0x0000
/* discards all broadcast frames */
#define RX_CFG_DISABLE_BCAST 0x0200
#define RX_CFG_ENABLE_ONLY_MY_SSID 0x0400
#define RX_CFG_ENABLE_RX_CMPLT_FCS_ERROR 0x0800
#define RX_CFG_COPY_RX_STATUS 0x2000
#define RX_CFG_TSF 0x10000
#define RX_CONFIG_OPTION_ANY_DST_MY_BSS (RX_CFG_ENABLE_ANY_DEST_MAC | \
RX_CFG_ENABLE_ONLY_MY_BSSID)
#define RX_CONFIG_OPTION_MY_DST_ANY_BSS (RX_CFG_ENABLE_ONLY_MY_DEST_MAC\
| RX_CFG_ENABLE_ANY_BSSID)
#define RX_CONFIG_OPTION_ANY_DST_ANY_BSS (RX_CFG_ENABLE_ANY_DEST_MAC | \
RX_CFG_ENABLE_ANY_BSSID)
#define RX_CONFIG_OPTION_MY_DST_MY_BSS (RX_CFG_ENABLE_ONLY_MY_DEST_MAC\
| RX_CFG_ENABLE_ONLY_MY_BSSID)
#define RX_CONFIG_OPTION_FOR_SCAN (RX_CFG_ENABLE_PHY_HEADER_PLCP \
| RX_CFG_ENABLE_RX_CMPLT_FCS_ERROR \
| RX_CFG_COPY_RX_STATUS | RX_CFG_TSF)
#define RX_CONFIG_OPTION_FOR_MEASUREMENT (RX_CFG_ENABLE_ANY_DEST_MAC)
#define RX_CONFIG_OPTION_FOR_JOIN (RX_CFG_ENABLE_ONLY_MY_BSSID | \
RX_CFG_ENABLE_ONLY_MY_DEST_MAC)
#define RX_CONFIG_OPTION_FOR_IBSS_JOIN (RX_CFG_ENABLE_ONLY_MY_SSID | \
RX_CFG_ENABLE_ONLY_MY_DEST_MAC)
#define RX_FILTER_OPTION_DEF (CFG_RX_MGMT_EN | CFG_RX_DATA_EN\
| CFG_RX_CTL_EN | CFG_RX_BCN_EN\
| CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN)
#define RX_FILTER_OPTION_FILTER_ALL 0
#define RX_FILTER_OPTION_DEF_PRSP_BCN (CFG_RX_PRSP_EN | CFG_RX_MGMT_EN\
| CFG_RX_RCTS_ACK | CFG_RX_BCN_EN)
#define RX_FILTER_OPTION_JOIN (CFG_RX_MGMT_EN | CFG_RX_DATA_EN\
| CFG_RX_BCN_EN | CFG_RX_AUTH_EN\
| CFG_RX_ASSOC_EN | CFG_RX_RCTS_ACK\
| CFG_RX_PRSP_EN)
/*===============================================
EEPROM Read/Write Request 32bit RW
------------------------------------------
1 EE_READ - EEPROM Read Request 1 - Setting this bit
loads a single byte of data into the EE_DATA
register from the EEPROM location specified in
the EE_ADDR register.
The Wlan hardware hardware clears this bit automatically.
EE_DATA is valid when this bit is cleared.
0 EE_WRITE - EEPROM Write Request - Setting this bit
writes a single byte of data from the EE_DATA register into the
EEPROM location specified in the EE_ADDR register.
The Wlan hardware hardware clears this bit automatically.
*===============================================*/
#define EE_CTL (REGISTERS_BASE + 0x2000)
#define ACX_EE_CTL_REG EE_CTL
#define EE_WRITE 0x00000001ul
#define EE_READ 0x00000002ul
/*===============================================
EEPROM Address - 32bit RW
------------------------------------------
This register specifies the address
within the EEPROM from/to which to read/write data.
===============================================*/
#define EE_ADDR (REGISTERS_BASE + 0x2008)
#define ACX_EE_ADDR_REG EE_ADDR
/*===============================================
EEPROM Data - 32bit RW
------------------------------------------
This register either holds the read 8 bits of
data from the EEPROM or the write data
to be written to the EEPROM.
===============================================*/
#define EE_DATA (REGISTERS_BASE + 0x2004)
#define ACX_EE_DATA_REG EE_DATA
#define EEPROM_ACCESS_TO 10000 /* timeout counter */
#define START_EEPROM_MGR 0x00000001
/*===============================================
EEPROM Base Address - 32bit RW
------------------------------------------
This register holds the upper nine bits
[23:15] of the 24-bit Wlan hardware memory
address for burst reads from EEPROM accesses.
The EEPROM provides the lower 15 bits of this address.
The MSB of the address from the EEPROM is ignored.
===============================================*/
#define ACX_EE_CFG EE_CFG
/*===============================================
GPIO Output Values -32bit, RW
------------------------------------------
[31:16] Reserved
[15: 0] Specify the output values (at the output driver inputs) for
GPIO[15:0], respectively.
===============================================*/
#define ACX_GPIO_OUT_REG GPIO_OUT
#define ACX_MAX_GPIO_LINES 15
/*===============================================
Contention window -32bit, RW
------------------------------------------
[31:26] Reserved
[25:16] Max (0x3ff)
[15:07] Reserved
[06:00] Current contention window value - default is 0x1F
===============================================*/
#define ACX_CONT_WIND_CFG_REG CONT_WIND_CFG
#define ACX_CONT_WIND_MIN_MASK 0x0000007f
#define ACX_CONT_WIND_MAX 0x03ff0000
/*===============================================
HI_CFG Interface Configuration Register Values
------------------------------------------
===============================================*/
#define HI_CFG_UART_ENABLE 0x00000004
#define HI_CFG_RST232_ENABLE 0x00000008
#define HI_CFG_CLOCK_REQ_SELECT 0x00000010
#define HI_CFG_HOST_INT_ENABLE 0x00000020
#define HI_CFG_VLYNQ_OUTPUT_ENABLE 0x00000040
#define HI_CFG_HOST_INT_ACTIVE_LOW 0x00000080
#define HI_CFG_UART_TX_OUT_GPIO_15 0x00000100
#define HI_CFG_UART_TX_OUT_GPIO_14 0x00000200
#define HI_CFG_UART_TX_OUT_GPIO_7 0x00000400
/*
* NOTE: USE_ACTIVE_HIGH compilation flag should be defined in makefile
* for platforms using active high interrupt level
*/
#ifdef USE_ACTIVE_HIGH
#define HI_CFG_DEF_VAL \
(HI_CFG_UART_ENABLE | \
HI_CFG_RST232_ENABLE | \
HI_CFG_CLOCK_REQ_SELECT | \
HI_CFG_HOST_INT_ENABLE)
#else
#define HI_CFG_DEF_VAL \
(HI_CFG_UART_ENABLE | \
HI_CFG_RST232_ENABLE | \
HI_CFG_CLOCK_REQ_SELECT | \
HI_CFG_HOST_INT_ENABLE)
#endif
#define REF_FREQ_19_2 0
#define REF_FREQ_26_0 1
#define REF_FREQ_38_4 2
#define REF_FREQ_40_0 3
#define REF_FREQ_33_6 4
#define REF_FREQ_NUM 5
#define LUT_PARAM_INTEGER_DIVIDER 0
#define LUT_PARAM_FRACTIONAL_DIVIDER 1
#define LUT_PARAM_ATTN_BB 2
#define LUT_PARAM_ALPHA_BB 3
#define LUT_PARAM_STOP_TIME_BB 4
#define LUT_PARAM_BB_PLL_LOOP_FILTER 5
#define LUT_PARAM_NUM 6
#define ACX_EEPROMLESS_IND_REG (SCR_PAD4)
#define USE_EEPROM 0
#define SOFT_RESET_MAX_TIME 1000000
#define SOFT_RESET_STALL_TIME 1000
#define NVS_DATA_BUNDARY_ALIGNMENT 4
/* Firmware image load chunk size */
#define CHUNK_SIZE 512
/* Firmware image header size */
#define FW_HDR_SIZE 8
#define ECPU_CONTROL_HALT 0x00000101
/******************************************************************************
CHANNELS, BAND & REG DOMAINS definitions
******************************************************************************/
enum {
RADIO_BAND_2_4GHZ = 0, /* 2.4 Ghz band */
RADIO_BAND_5GHZ = 1, /* 5 Ghz band */
RADIO_BAND_JAPAN_4_9_GHZ = 2,
DEFAULT_BAND = RADIO_BAND_2_4GHZ,
INVALID_BAND = 0xFE,
MAX_RADIO_BANDS = 0xFF
};
enum {
NO_RATE = 0,
RATE_1MBPS = 0x0A,
RATE_2MBPS = 0x14,
RATE_5_5MBPS = 0x37,
RATE_6MBPS = 0x0B,
RATE_9MBPS = 0x0F,
RATE_11MBPS = 0x6E,
RATE_12MBPS = 0x0A,
RATE_18MBPS = 0x0E,
RATE_22MBPS = 0xDC,
RATE_24MBPS = 0x09,
RATE_36MBPS = 0x0D,
RATE_48MBPS = 0x08,
RATE_54MBPS = 0x0C
};
enum {
RATE_INDEX_1MBPS = 0,
RATE_INDEX_2MBPS = 1,
RATE_INDEX_5_5MBPS = 2,
RATE_INDEX_6MBPS = 3,
RATE_INDEX_9MBPS = 4,
RATE_INDEX_11MBPS = 5,
RATE_INDEX_12MBPS = 6,
RATE_INDEX_18MBPS = 7,
RATE_INDEX_22MBPS = 8,
RATE_INDEX_24MBPS = 9,
RATE_INDEX_36MBPS = 10,
RATE_INDEX_48MBPS = 11,
RATE_INDEX_54MBPS = 12,
RATE_INDEX_MAX = RATE_INDEX_54MBPS,
MAX_RATE_INDEX,
INVALID_RATE_INDEX = MAX_RATE_INDEX,
RATE_INDEX_ENUM_MAX_SIZE = 0x7FFFFFFF
};
enum {
RATE_MASK_1MBPS = 0x1,
RATE_MASK_2MBPS = 0x2,
RATE_MASK_5_5MBPS = 0x4,
RATE_MASK_11MBPS = 0x20,
};
#define SHORT_PREAMBLE_BIT BIT(0) /* CCK or Barker depending on the rate */
#define OFDM_RATE_BIT BIT(6)
#define PBCC_RATE_BIT BIT(7)
enum {
CCK_LONG = 0,
CCK_SHORT = SHORT_PREAMBLE_BIT,
PBCC_LONG = PBCC_RATE_BIT,
PBCC_SHORT = PBCC_RATE_BIT | SHORT_PREAMBLE_BIT,
OFDM = OFDM_RATE_BIT
};
/******************************************************************************
Transmit-Descriptor RATE-SET field definitions...
Define a new "Rate-Set" for TX path that incorporates the
Rate & Modulation info into a single 16-bit field.
TxdRateSet_t:
b15 - Indicates Preamble type (1=SHORT, 0=LONG).
Notes:
Must be LONG (0) for 1Mbps rate.
Does not apply (set to 0) for RevG-OFDM rates.
b14 - Indicates PBCC encoding (1=PBCC, 0=not).
Notes:
Does not apply (set to 0) for rates 1 and 2 Mbps.
Does not apply (set to 0) for RevG-OFDM rates.
b13 - Unused (set to 0).
b12-b0 - Supported Rate indicator bits as defined below.
******************************************************************************/
/*************************************************************************
Interrupt Trigger Register (Host -> WiLink)
**************************************************************************/
/* Hardware to Embedded CPU Interrupts - first 32-bit register set */
/*
* Host Command Interrupt. Setting this bit masks
* the interrupt that the host issues to inform
* the FW that it has sent a command
* to the Wlan hardware Command Mailbox.
*/
#define INTR_TRIG_CMD BIT(0)
/*
* Host Event Acknowlegde Interrupt. The host
* sets this bit to acknowledge that it received
* the unsolicited information from the event
* mailbox.
*/
#define INTR_TRIG_EVENT_ACK BIT(1)
/*
* 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)
#endif

View File

@@ -0,0 +1,235 @@
/*
* This file is part of wl1251
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/skbuff.h>
#include <linux/gfp.h>
#include <net/mac80211.h>
#include "wl1251.h"
#include "reg.h"
#include "io.h"
#include "rx.h"
#include "cmd.h"
#include "acx.h"
static void wl1251_rx_header(struct wl1251 *wl,
struct wl1251_rx_descriptor *desc)
{
u32 rx_packet_ring_addr;
rx_packet_ring_addr = wl->data_path->rx_packet_ring_addr;
if (wl->rx_current_buffer)
rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size;
wl1251_mem_read(wl, rx_packet_ring_addr, desc, sizeof(*desc));
}
static void wl1251_rx_status(struct wl1251 *wl,
struct wl1251_rx_descriptor *desc,
struct ieee80211_rx_status *status,
u8 beacon)
{
u64 mactime;
int ret;
memset(status, 0, sizeof(struct ieee80211_rx_status));
status->band = IEEE80211_BAND_2GHZ;
status->mactime = desc->timestamp;
/*
* The rx status timestamp is a 32 bits value while the TSF is a
* 64 bits one.
* For IBSS merging, TSF is mandatory, so we have to get it
* somehow, so we ask for ACX_TSF_INFO.
* That could be moved to the get_tsf() hook, but unfortunately,
* this one must be atomic, while our SPI routines can sleep.
*/
if ((wl->bss_type == BSS_TYPE_IBSS) && beacon) {
ret = wl1251_acx_tsf_info(wl, &mactime);
if (ret == 0)
status->mactime = mactime;
}
status->signal = desc->rssi;
/*
* FIXME: guessing that snr needs to be divided by two, otherwise
* the values don't make any sense
*/
wl->noise = desc->rssi - desc->snr / 2;
status->freq = ieee80211_channel_to_frequency(desc->channel,
status->band);
status->flag |= RX_FLAG_MACTIME_MPDU;
if (desc->flags & RX_DESC_ENCRYPTION_MASK) {
status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
if (likely(!(desc->flags & RX_DESC_DECRYPT_FAIL)))
status->flag |= RX_FLAG_DECRYPTED;
if (unlikely(desc->flags & RX_DESC_MIC_FAIL))
status->flag |= RX_FLAG_MMIC_ERROR;
}
if (unlikely(!(desc->flags & RX_DESC_VALID_FCS)))
status->flag |= RX_FLAG_FAILED_FCS_CRC;
switch (desc->rate) {
/* skip 1 and 12 Mbps because they have same value 0x0a */
case RATE_2MBPS:
status->rate_idx = 1;
break;
case RATE_5_5MBPS:
status->rate_idx = 2;
break;
case RATE_11MBPS:
status->rate_idx = 3;
break;
case RATE_6MBPS:
status->rate_idx = 4;
break;
case RATE_9MBPS:
status->rate_idx = 5;
break;
case RATE_18MBPS:
status->rate_idx = 7;
break;
case RATE_24MBPS:
status->rate_idx = 8;
break;
case RATE_36MBPS:
status->rate_idx = 9;
break;
case RATE_48MBPS:
status->rate_idx = 10;
break;
case RATE_54MBPS:
status->rate_idx = 11;
break;
}
/* for 1 and 12 Mbps we have to check the modulation */
if (desc->rate == RATE_1MBPS) {
if (!(desc->mod_pre & OFDM_RATE_BIT))
/* CCK -> RATE_1MBPS */
status->rate_idx = 0;
else
/* OFDM -> RATE_12MBPS */
status->rate_idx = 6;
}
if (desc->mod_pre & SHORT_PREAMBLE_BIT)
status->flag |= RX_FLAG_SHORTPRE;
}
static void wl1251_rx_body(struct wl1251 *wl,
struct wl1251_rx_descriptor *desc)
{
struct sk_buff *skb;
struct ieee80211_rx_status status;
u8 *rx_buffer, beacon = 0;
u16 length, *fc;
u32 curr_id, last_id_inc, rx_packet_ring_addr;
length = WL1251_RX_ALIGN(desc->length - PLCP_HEADER_LENGTH);
curr_id = (desc->flags & RX_DESC_SEQNUM_MASK) >> RX_DESC_PACKETID_SHIFT;
last_id_inc = (wl->rx_last_id + 1) % (RX_MAX_PACKET_ID + 1);
if (last_id_inc != curr_id) {
wl1251_warning("curr ID:%d, last ID inc:%d",
curr_id, last_id_inc);
wl->rx_last_id = curr_id;
} else {
wl->rx_last_id = last_id_inc;
}
rx_packet_ring_addr = wl->data_path->rx_packet_ring_addr +
sizeof(struct wl1251_rx_descriptor) + 20;
if (wl->rx_current_buffer)
rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size;
skb = __dev_alloc_skb(length, GFP_KERNEL);
if (!skb) {
wl1251_error("Couldn't allocate RX frame");
return;
}
rx_buffer = skb_put(skb, length);
wl1251_mem_read(wl, rx_packet_ring_addr, rx_buffer, length);
/* The actual length doesn't include the target's alignment */
skb->len = desc->length - PLCP_HEADER_LENGTH;
fc = (u16 *)skb->data;
if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)
beacon = 1;
wl1251_rx_status(wl, desc, &status, beacon);
wl1251_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len,
beacon ? "beacon" : "");
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
ieee80211_rx_ni(wl->hw, skb);
}
static void wl1251_rx_ack(struct wl1251 *wl)
{
u32 data, addr;
if (wl->rx_current_buffer) {
addr = ACX_REG_INTERRUPT_TRIG_H;
data = INTR_TRIG_RX_PROC1;
} else {
addr = ACX_REG_INTERRUPT_TRIG;
data = INTR_TRIG_RX_PROC0;
}
wl1251_reg_write32(wl, addr, data);
/* Toggle buffer ring */
wl->rx_current_buffer = !wl->rx_current_buffer;
}
void wl1251_rx(struct wl1251 *wl)
{
struct wl1251_rx_descriptor *rx_desc;
if (wl->state != WL1251_STATE_ON)
return;
rx_desc = wl->rx_descriptor;
/* We first read the frame's header */
wl1251_rx_header(wl, rx_desc);
/* Now we can read the body */
wl1251_rx_body(wl, rx_desc);
/* Finally, we need to ACK the RX */
wl1251_rx_ack(wl);
}

View File

@@ -0,0 +1,122 @@
/*
* This file is part of wl1251
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL1251_RX_H__
#define __WL1251_RX_H__
#include <linux/bitops.h>
#include "wl1251.h"
/*
* RX PATH
*
* The Rx path uses a double buffer and an rx_contro structure, each located
* at a fixed address in the device memory. The host keeps track of which
* buffer is available and alternates between them on a per packet basis.
* The size of each of the two buffers is large enough to hold the longest
* 802.3 packet.
* The RX path goes like that:
* 1) The target generates an interrupt each time a new packet is received.
* There are 2 RX interrupts, one for each buffer.
* 2) The host reads the received packet from one of the double buffers.
* 3) The host triggers a target interrupt.
* 4) The target prepares the next RX packet.
*/
#define WL1251_RX_MAX_RSSI -30
#define WL1251_RX_MIN_RSSI -95
#define WL1251_RX_ALIGN_TO 4
#define WL1251_RX_ALIGN(len) (((len) + WL1251_RX_ALIGN_TO - 1) & \
~(WL1251_RX_ALIGN_TO - 1))
#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
struct wl1251_rx_descriptor {
u32 timestamp; /* In microseconds */
u16 length; /* Paylod length, including headers */
u16 flags;
/*
* 0 - 802.11
* 1 - 802.3
* 2 - IP
* 3 - Raw Codec
*/
u8 type;
/*
* Received Rate:
* 0x0A - 1MBPS
* 0x14 - 2MBPS
* 0x37 - 5_5MBPS
* 0x0B - 6MBPS
* 0x0F - 9MBPS
* 0x6E - 11MBPS
* 0x0A - 12MBPS
* 0x0E - 18MBPS
* 0xDC - 22MBPS
* 0x09 - 24MBPS
* 0x0D - 36MBPS
* 0x08 - 48MBPS
* 0x0C - 54MBPS
*/
u8 rate;
u8 mod_pre; /* Modulation and preamble */
u8 channel;
/*
* 0 - 2.4 Ghz
* 1 - 5 Ghz
*/
u8 band;
s8 rssi; /* in dB */
u8 rcpi; /* in dB */
u8 snr; /* in dB */
} __packed;
void wl1251_rx(struct wl1251 *wl);
#endif

View File

@@ -0,0 +1,374 @@
/*
* wl12xx SDIO routines
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Copyright (C) 2005 Texas Instruments Incorporated
* Copyright (C) 2008 Google Inc
* Copyright (C) 2009 Bob Copeland (me@bobcopeland.com)
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/platform_device.h>
#include <linux/wl12xx.h>
#include <linux/irq.h>
#include <linux/pm_runtime.h>
#include "wl1251.h"
#ifndef SDIO_VENDOR_ID_TI
#define SDIO_VENDOR_ID_TI 0x104c
#endif
#ifndef SDIO_DEVICE_ID_TI_WL1251
#define SDIO_DEVICE_ID_TI_WL1251 0x9066
#endif
struct wl1251_sdio {
struct sdio_func *func;
u32 elp_val;
};
static struct sdio_func *wl_to_func(struct wl1251 *wl)
{
struct wl1251_sdio *wl_sdio = wl->if_priv;
return wl_sdio->func;
}
static void wl1251_sdio_interrupt(struct sdio_func *func)
{
struct wl1251 *wl = sdio_get_drvdata(func);
wl1251_debug(DEBUG_IRQ, "IRQ");
/* FIXME should be synchronous for sdio */
ieee80211_queue_work(wl->hw, &wl->irq_work);
}
static const struct sdio_device_id wl1251_devices[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1251) },
{}
};
MODULE_DEVICE_TABLE(sdio, wl1251_devices);
static void wl1251_sdio_read(struct wl1251 *wl, int addr,
void *buf, size_t len)
{
int ret;
struct sdio_func *func = wl_to_func(wl);
sdio_claim_host(func);
ret = sdio_memcpy_fromio(func, buf, addr, len);
if (ret)
wl1251_error("sdio read failed (%d)", ret);
sdio_release_host(func);
}
static void wl1251_sdio_write(struct wl1251 *wl, int addr,
void *buf, size_t len)
{
int ret;
struct sdio_func *func = wl_to_func(wl);
sdio_claim_host(func);
ret = sdio_memcpy_toio(func, addr, buf, len);
if (ret)
wl1251_error("sdio write failed (%d)", ret);
sdio_release_host(func);
}
static void wl1251_sdio_read_elp(struct wl1251 *wl, int addr, u32 *val)
{
int ret = 0;
struct wl1251_sdio *wl_sdio = wl->if_priv;
struct sdio_func *func = wl_sdio->func;
/*
* The hardware only supports RAW (read after write) access for
* reading, regular sdio_readb won't work here (it interprets
* the unused bits of CMD52 as write data even if we send read
* request).
*/
sdio_claim_host(func);
*val = sdio_writeb_readb(func, wl_sdio->elp_val, addr, &ret);
sdio_release_host(func);
if (ret)
wl1251_error("sdio_readb failed (%d)", ret);
}
static void wl1251_sdio_write_elp(struct wl1251 *wl, int addr, u32 val)
{
int ret = 0;
struct wl1251_sdio *wl_sdio = wl->if_priv;
struct sdio_func *func = wl_sdio->func;
sdio_claim_host(func);
sdio_writeb(func, val, addr, &ret);
sdio_release_host(func);
if (ret)
wl1251_error("sdio_writeb failed (%d)", ret);
else
wl_sdio->elp_val = val;
}
static void wl1251_sdio_reset(struct wl1251 *wl)
{
}
static void wl1251_sdio_enable_irq(struct wl1251 *wl)
{
struct sdio_func *func = wl_to_func(wl);
sdio_claim_host(func);
sdio_claim_irq(func, wl1251_sdio_interrupt);
sdio_release_host(func);
}
static void wl1251_sdio_disable_irq(struct wl1251 *wl)
{
struct sdio_func *func = wl_to_func(wl);
sdio_claim_host(func);
sdio_release_irq(func);
sdio_release_host(func);
}
/* Interrupts when using dedicated WLAN_IRQ pin */
static irqreturn_t wl1251_line_irq(int irq, void *cookie)
{
struct wl1251 *wl = cookie;
ieee80211_queue_work(wl->hw, &wl->irq_work);
return IRQ_HANDLED;
}
static void wl1251_enable_line_irq(struct wl1251 *wl)
{
return enable_irq(wl->irq);
}
static void wl1251_disable_line_irq(struct wl1251 *wl)
{
return disable_irq(wl->irq);
}
static int wl1251_sdio_set_power(struct wl1251 *wl, bool enable)
{
struct sdio_func *func = wl_to_func(wl);
int ret;
if (enable) {
/*
* Power is controlled by runtime PM, but we still call board
* callback in case it wants to do any additional setup,
* for example enabling clock buffer for the module.
*/
if (wl->set_power)
wl->set_power(true);
ret = pm_runtime_get_sync(&func->dev);
if (ret < 0)
goto out;
sdio_claim_host(func);
sdio_enable_func(func);
sdio_release_host(func);
} else {
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
ret = pm_runtime_put_sync(&func->dev);
if (ret < 0)
goto out;
if (wl->set_power)
wl->set_power(false);
}
out:
return ret;
}
static struct wl1251_if_operations wl1251_sdio_ops = {
.read = wl1251_sdio_read,
.write = wl1251_sdio_write,
.write_elp = wl1251_sdio_write_elp,
.read_elp = wl1251_sdio_read_elp,
.reset = wl1251_sdio_reset,
.power = wl1251_sdio_set_power,
};
static int wl1251_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
int ret;
struct wl1251 *wl;
struct ieee80211_hw *hw;
struct wl1251_sdio *wl_sdio;
const struct wl12xx_platform_data *wl12xx_board_data;
hw = wl1251_alloc_hw();
if (IS_ERR(hw))
return PTR_ERR(hw);
wl = hw->priv;
wl_sdio = kzalloc(sizeof(*wl_sdio), GFP_KERNEL);
if (wl_sdio == NULL) {
ret = -ENOMEM;
goto out_free_hw;
}
sdio_claim_host(func);
ret = sdio_enable_func(func);
if (ret)
goto release;
sdio_set_block_size(func, 512);
sdio_release_host(func);
SET_IEEE80211_DEV(hw, &func->dev);
wl_sdio->func = func;
wl->if_priv = wl_sdio;
wl->if_ops = &wl1251_sdio_ops;
wl12xx_board_data = wl12xx_get_platform_data();
if (!IS_ERR(wl12xx_board_data)) {
wl->set_power = wl12xx_board_data->set_power;
wl->irq = wl12xx_board_data->irq;
wl->use_eeprom = wl12xx_board_data->use_eeprom;
}
if (wl->irq) {
ret = request_irq(wl->irq, wl1251_line_irq, 0, "wl1251", wl);
if (ret < 0) {
wl1251_error("request_irq() failed: %d", ret);
goto disable;
}
irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
disable_irq(wl->irq);
wl1251_sdio_ops.enable_irq = wl1251_enable_line_irq;
wl1251_sdio_ops.disable_irq = wl1251_disable_line_irq;
wl1251_info("using dedicated interrupt line");
} else {
wl1251_sdio_ops.enable_irq = wl1251_sdio_enable_irq;
wl1251_sdio_ops.disable_irq = wl1251_sdio_disable_irq;
wl1251_info("using SDIO interrupt");
}
ret = wl1251_init_ieee80211(wl);
if (ret)
goto out_free_irq;
sdio_set_drvdata(func, wl);
/* Tell PM core that we don't need the card to be powered now */
pm_runtime_put_noidle(&func->dev);
return ret;
out_free_irq:
if (wl->irq)
free_irq(wl->irq, wl);
disable:
sdio_claim_host(func);
sdio_disable_func(func);
release:
sdio_release_host(func);
kfree(wl_sdio);
out_free_hw:
wl1251_free_hw(wl);
return ret;
}
static void __devexit wl1251_sdio_remove(struct sdio_func *func)
{
struct wl1251 *wl = sdio_get_drvdata(func);
struct wl1251_sdio *wl_sdio = wl->if_priv;
/* Undo decrement done above in wl1251_probe */
pm_runtime_get_noresume(&func->dev);
if (wl->irq)
free_irq(wl->irq, wl);
kfree(wl_sdio);
wl1251_free_hw(wl);
sdio_claim_host(func);
sdio_release_irq(func);
sdio_disable_func(func);
sdio_release_host(func);
}
static int wl1251_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.
*/
return 0;
}
static int wl1251_resume(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops wl1251_sdio_pm_ops = {
.suspend = wl1251_suspend,
.resume = wl1251_resume,
};
static struct sdio_driver wl1251_sdio_driver = {
.name = "wl1251_sdio",
.id_table = wl1251_devices,
.probe = wl1251_sdio_probe,
.remove = __devexit_p(wl1251_sdio_remove),
.drv.pm = &wl1251_sdio_pm_ops,
};
static int __init wl1251_sdio_init(void)
{
int err;
err = sdio_register_driver(&wl1251_sdio_driver);
if (err)
wl1251_error("failed to register sdio driver: %d", err);
return err;
}
static void __exit wl1251_sdio_exit(void)
{
sdio_unregister_driver(&wl1251_sdio_driver);
wl1251_notice("unloaded");
}
module_init(wl1251_sdio_init);
module_exit(wl1251_sdio_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");

View File

@@ -0,0 +1,355 @@
/*
* This file is part of wl1251
*
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/crc7.h>
#include <linux/spi/spi.h>
#include <linux/wl12xx.h>
#include "wl1251.h"
#include "reg.h"
#include "spi.h"
static irqreturn_t wl1251_irq(int irq, void *cookie)
{
struct wl1251 *wl;
wl1251_debug(DEBUG_IRQ, "IRQ");
wl = cookie;
ieee80211_queue_work(wl->hw, &wl->irq_work);
return IRQ_HANDLED;
}
static struct spi_device *wl_to_spi(struct wl1251 *wl)
{
return wl->if_priv;
}
static void wl1251_spi_reset(struct wl1251 *wl)
{
u8 *cmd;
struct spi_transfer t;
struct spi_message m;
cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
if (!cmd) {
wl1251_error("could not allocate cmd for spi reset");
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(wl_to_spi(wl), &m);
wl1251_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN);
}
static void wl1251_spi_wake(struct wl1251 *wl)
{
u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd;
struct spi_transfer t;
struct spi_message m;
cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
if (!cmd) {
wl1251_error("could not allocate cmd for spi init");
return;
}
memset(crc, 0, sizeof(crc));
memset(&t, 0, sizeof(t));
spi_message_init(&m);
/*
* Set WSPI_INIT_COMMAND
* the data is being send from the MSB to LSB
*/
cmd[2] = 0xff;
cmd[3] = 0xff;
cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
cmd[0] = 0;
cmd[7] = 0;
cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0)
cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY;
else
cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
| WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
crc[0] = cmd[1];
crc[1] = cmd[0];
crc[2] = cmd[7];
crc[3] = cmd[6];
crc[4] = cmd[5];
cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1;
cmd[4] |= WSPI_INIT_CMD_END;
t.tx_buf = cmd;
t.len = WSPI_INIT_CMD_LEN;
spi_message_add_tail(&t, &m);
spi_sync(wl_to_spi(wl), &m);
wl1251_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN);
}
static void wl1251_spi_reset_wake(struct wl1251 *wl)
{
wl1251_spi_reset(wl);
wl1251_spi_wake(wl);
}
static void wl1251_spi_read(struct wl1251 *wl, int addr, void *buf,
size_t len)
{
struct spi_transfer t[3];
struct spi_message m;
u8 *busy_buf;
u32 *cmd;
cmd = &wl->buffer_cmd;
busy_buf = wl->buffer_busyword;
*cmd = 0;
*cmd |= WSPI_CMD_READ;
*cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH;
*cmd |= addr & WSPI_CMD_BYTE_ADDR;
spi_message_init(&m);
memset(t, 0, sizeof(t));
t[0].tx_buf = cmd;
t[0].len = 4;
spi_message_add_tail(&t[0], &m);
/* Busy and non busy words read */
t[1].rx_buf = busy_buf;
t[1].len = WL1251_BUSY_WORD_LEN;
spi_message_add_tail(&t[1], &m);
t[2].rx_buf = buf;
t[2].len = len;
spi_message_add_tail(&t[2], &m);
spi_sync(wl_to_spi(wl), &m);
/* FIXME: check busy words */
wl1251_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd));
wl1251_dump(DEBUG_SPI, "spi_read buf <- ", buf, len);
}
static void wl1251_spi_write(struct wl1251 *wl, int addr, void *buf,
size_t len)
{
struct spi_transfer t[2];
struct spi_message m;
u32 *cmd;
cmd = &wl->buffer_cmd;
*cmd = 0;
*cmd |= WSPI_CMD_WRITE;
*cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH;
*cmd |= addr & WSPI_CMD_BYTE_ADDR;
spi_message_init(&m);
memset(t, 0, sizeof(t));
t[0].tx_buf = cmd;
t[0].len = sizeof(*cmd);
spi_message_add_tail(&t[0], &m);
t[1].tx_buf = buf;
t[1].len = len;
spi_message_add_tail(&t[1], &m);
spi_sync(wl_to_spi(wl), &m);
wl1251_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd));
wl1251_dump(DEBUG_SPI, "spi_write buf -> ", buf, len);
}
static void wl1251_spi_enable_irq(struct wl1251 *wl)
{
return enable_irq(wl->irq);
}
static void wl1251_spi_disable_irq(struct wl1251 *wl)
{
return disable_irq(wl->irq);
}
static int wl1251_spi_set_power(struct wl1251 *wl, bool enable)
{
if (wl->set_power)
wl->set_power(enable);
return 0;
}
static const struct wl1251_if_operations wl1251_spi_ops = {
.read = wl1251_spi_read,
.write = wl1251_spi_write,
.reset = wl1251_spi_reset_wake,
.enable_irq = wl1251_spi_enable_irq,
.disable_irq = wl1251_spi_disable_irq,
.power = wl1251_spi_set_power,
};
static int __devinit wl1251_spi_probe(struct spi_device *spi)
{
struct wl12xx_platform_data *pdata;
struct ieee80211_hw *hw;
struct wl1251 *wl;
int ret;
pdata = spi->dev.platform_data;
if (!pdata) {
wl1251_error("no platform data");
return -ENODEV;
}
hw = wl1251_alloc_hw();
if (IS_ERR(hw))
return PTR_ERR(hw);
wl = hw->priv;
SET_IEEE80211_DEV(hw, &spi->dev);
dev_set_drvdata(&spi->dev, wl);
wl->if_priv = spi;
wl->if_ops = &wl1251_spi_ops;
/* This is the only SPI value that we need to set here, the rest
* comes from the board-peripherals file */
spi->bits_per_word = 32;
ret = spi_setup(spi);
if (ret < 0) {
wl1251_error("spi_setup failed");
goto out_free;
}
wl->set_power = pdata->set_power;
if (!wl->set_power) {
wl1251_error("set power function missing in platform data");
return -ENODEV;
}
wl->irq = spi->irq;
if (wl->irq < 0) {
wl1251_error("irq missing in platform data");
return -ENODEV;
}
wl->use_eeprom = pdata->use_eeprom;
ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl);
if (ret < 0) {
wl1251_error("request_irq() failed: %d", ret);
goto out_free;
}
irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING);
disable_irq(wl->irq);
ret = wl1251_init_ieee80211(wl);
if (ret)
goto out_irq;
return 0;
out_irq:
free_irq(wl->irq, wl);
out_free:
ieee80211_free_hw(hw);
return ret;
}
static int __devexit wl1251_spi_remove(struct spi_device *spi)
{
struct wl1251 *wl = dev_get_drvdata(&spi->dev);
free_irq(wl->irq, wl);
wl1251_free_hw(wl);
return 0;
}
static struct spi_driver wl1251_spi_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.probe = wl1251_spi_probe,
.remove = __devexit_p(wl1251_spi_remove),
};
static int __init wl1251_spi_init(void)
{
int ret;
ret = spi_register_driver(&wl1251_spi_driver);
if (ret < 0) {
wl1251_error("failed to register spi driver: %d", ret);
goto out;
}
out:
return ret;
}
static void __exit wl1251_spi_exit(void)
{
spi_unregister_driver(&wl1251_spi_driver);
wl1251_notice("unloaded");
}
module_init(wl1251_spi_init);
module_exit(wl1251_spi_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");
MODULE_ALIAS("spi:wl1251");

View File

@@ -0,0 +1,59 @@
/*
* This file is part of wl1251
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL1251_SPI_H__
#define __WL1251_SPI_H__
#include "cmd.h"
#include "acx.h"
#include "reg.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 \
((WL1251_BUSY_WORD_LEN - 4) / sizeof(u32))
#define HW_ACCESS_WSPI_INIT_CMD_MASK 0
#endif /* __WL1251_SPI_H__ */

View File

@@ -0,0 +1,560 @@
/*
* This file is part of wl1251
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include "wl1251.h"
#include "reg.h"
#include "tx.h"
#include "ps.h"
#include "io.h"
static bool wl1251_tx_double_buffer_busy(struct wl1251 *wl, u32 data_out_count)
{
int used, data_in_count;
data_in_count = wl->data_in_count;
if (data_in_count < data_out_count)
/* data_in_count has wrapped */
data_in_count += TX_STATUS_DATA_OUT_COUNT_MASK + 1;
used = data_in_count - data_out_count;
WARN_ON(used < 0);
WARN_ON(used > DP_TX_PACKET_RING_CHUNK_NUM);
if (used >= DP_TX_PACKET_RING_CHUNK_NUM)
return true;
else
return false;
}
static int wl1251_tx_path_status(struct wl1251 *wl)
{
u32 status, addr, data_out_count;
bool busy;
addr = wl->data_path->tx_control_addr;
status = wl1251_mem_read32(wl, addr);
data_out_count = status & TX_STATUS_DATA_OUT_COUNT_MASK;
busy = wl1251_tx_double_buffer_busy(wl, data_out_count);
if (busy)
return -EBUSY;
return 0;
}
static int wl1251_tx_id(struct wl1251 *wl, struct sk_buff *skb)
{
int i;
for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
if (wl->tx_frames[i] == NULL) {
wl->tx_frames[i] = skb;
return i;
}
return -EBUSY;
}
static void wl1251_tx_control(struct tx_double_buffer_desc *tx_hdr,
struct ieee80211_tx_info *control, u16 fc)
{
*(u16 *)&tx_hdr->control = 0;
tx_hdr->control.rate_policy = 0;
/* 802.11 packets */
tx_hdr->control.packet_type = 0;
if (control->flags & IEEE80211_TX_CTL_NO_ACK)
tx_hdr->control.ack_policy = 1;
tx_hdr->control.tx_complete = 1;
if ((fc & IEEE80211_FTYPE_DATA) &&
((fc & IEEE80211_STYPE_QOS_DATA) ||
(fc & IEEE80211_STYPE_QOS_NULLFUNC)))
tx_hdr->control.qos = 1;
}
/* RSN + MIC = 8 + 8 = 16 bytes (worst case - AES). */
#define MAX_MSDU_SECURITY_LENGTH 16
#define MAX_MPDU_SECURITY_LENGTH 16
#define WLAN_QOS_HDR_LEN 26
#define MAX_MPDU_HEADER_AND_SECURITY (MAX_MPDU_SECURITY_LENGTH + \
WLAN_QOS_HDR_LEN)
#define HW_BLOCK_SIZE 252
static void wl1251_tx_frag_block_num(struct tx_double_buffer_desc *tx_hdr)
{
u16 payload_len, frag_threshold, mem_blocks;
u16 num_mpdus, mem_blocks_per_frag;
frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
tx_hdr->frag_threshold = cpu_to_le16(frag_threshold);
payload_len = le16_to_cpu(tx_hdr->length) + MAX_MSDU_SECURITY_LENGTH;
if (payload_len > frag_threshold) {
mem_blocks_per_frag =
((frag_threshold + MAX_MPDU_HEADER_AND_SECURITY) /
HW_BLOCK_SIZE) + 1;
num_mpdus = payload_len / frag_threshold;
mem_blocks = num_mpdus * mem_blocks_per_frag;
payload_len -= num_mpdus * frag_threshold;
num_mpdus++;
} else {
mem_blocks_per_frag = 0;
mem_blocks = 0;
num_mpdus = 1;
}
mem_blocks += (payload_len / HW_BLOCK_SIZE) + 1;
if (num_mpdus > 1)
mem_blocks += min(num_mpdus, mem_blocks_per_frag);
tx_hdr->num_mem_blocks = mem_blocks;
}
static int wl1251_tx_fill_hdr(struct wl1251 *wl, struct sk_buff *skb,
struct ieee80211_tx_info *control)
{
struct tx_double_buffer_desc *tx_hdr;
struct ieee80211_rate *rate;
int id;
u16 fc;
if (!skb)
return -EINVAL;
id = wl1251_tx_id(wl, skb);
if (id < 0)
return id;
fc = *(u16 *)skb->data;
tx_hdr = (struct tx_double_buffer_desc *) skb_push(skb,
sizeof(*tx_hdr));
tx_hdr->length = cpu_to_le16(skb->len - sizeof(*tx_hdr));
rate = ieee80211_get_tx_rate(wl->hw, control);
tx_hdr->rate = cpu_to_le16(rate->hw_value);
tx_hdr->expiry_time = cpu_to_le32(1 << 16);
tx_hdr->id = id;
tx_hdr->xmit_queue = wl1251_tx_get_queue(skb_get_queue_mapping(skb));
wl1251_tx_control(tx_hdr, control, fc);
wl1251_tx_frag_block_num(tx_hdr);
return 0;
}
/* We copy the packet to the target */
static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb,
struct ieee80211_tx_info *control)
{
struct tx_double_buffer_desc *tx_hdr;
int len;
u32 addr;
if (!skb)
return -EINVAL;
tx_hdr = (struct tx_double_buffer_desc *) skb->data;
if (control->control.hw_key &&
control->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
int hdrlen;
__le16 fc;
u16 length;
u8 *pos;
fc = *(__le16 *)(skb->data + sizeof(*tx_hdr));
length = le16_to_cpu(tx_hdr->length) + WL1251_TKIP_IV_SPACE;
tx_hdr->length = cpu_to_le16(length);
hdrlen = ieee80211_hdrlen(fc);
pos = skb_push(skb, WL1251_TKIP_IV_SPACE);
memmove(pos, pos + WL1251_TKIP_IV_SPACE,
sizeof(*tx_hdr) + hdrlen);
}
/* Revisit. This is a workaround for getting non-aligned packets.
This happens at least with EAPOL packets from the user space.
Our DMA requires packets to be aligned on a 4-byte boundary.
*/
if (unlikely((long)skb->data & 0x03)) {
int offset = (4 - (long)skb->data) & 0x03;
wl1251_debug(DEBUG_TX, "skb offset %d", offset);
/* check whether the current skb can be used */
if (skb_cloned(skb) || (skb_tailroom(skb) < offset)) {
struct sk_buff *newskb = skb_copy_expand(skb, 0, 3,
GFP_KERNEL);
if (unlikely(newskb == NULL)) {
wl1251_error("Can't allocate skb!");
return -EINVAL;
}
tx_hdr = (struct tx_double_buffer_desc *) newskb->data;
dev_kfree_skb_any(skb);
wl->tx_frames[tx_hdr->id] = skb = newskb;
offset = (4 - (long)skb->data) & 0x03;
wl1251_debug(DEBUG_TX, "new skb offset %d", offset);
}
/* align the buffer on a 4-byte boundary */
if (offset) {
unsigned char *src = skb->data;
skb_reserve(skb, offset);
memmove(skb->data, src, skb->len);
tx_hdr = (struct tx_double_buffer_desc *) skb->data;
}
}
/* Our skb->data at this point includes the HW header */
len = WL1251_TX_ALIGN(skb->len);
if (wl->data_in_count & 0x1)
addr = wl->data_path->tx_packet_ring_addr +
wl->data_path->tx_packet_ring_chunk_size;
else
addr = wl->data_path->tx_packet_ring_addr;
wl1251_mem_write(wl, addr, skb->data, len);
wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x "
"queue %d", tx_hdr->id, skb, tx_hdr->length,
tx_hdr->rate, tx_hdr->xmit_queue);
return 0;
}
static void wl1251_tx_trigger(struct wl1251 *wl)
{
u32 data, addr;
if (wl->data_in_count & 0x1) {
addr = ACX_REG_INTERRUPT_TRIG_H;
data = INTR_TRIG_TX_PROC1;
} else {
addr = ACX_REG_INTERRUPT_TRIG;
data = INTR_TRIG_TX_PROC0;
}
wl1251_reg_write32(wl, addr, data);
/* Bumping data in */
wl->data_in_count = (wl->data_in_count + 1) &
TX_STATUS_DATA_OUT_COUNT_MASK;
}
/* caller must hold wl->mutex */
static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb)
{
struct ieee80211_tx_info *info;
int ret = 0;
u8 idx;
info = IEEE80211_SKB_CB(skb);
if (info->control.hw_key) {
idx = info->control.hw_key->hw_key_idx;
if (unlikely(wl->default_key != idx)) {
ret = wl1251_acx_default_key(wl, idx);
if (ret < 0)
return ret;
}
}
ret = wl1251_tx_path_status(wl);
if (ret < 0)
return ret;
ret = wl1251_tx_fill_hdr(wl, skb, info);
if (ret < 0)
return ret;
ret = wl1251_tx_send_packet(wl, skb, info);
if (ret < 0)
return ret;
wl1251_tx_trigger(wl);
return ret;
}
void wl1251_tx_work(struct work_struct *work)
{
struct wl1251 *wl = container_of(work, struct wl1251, tx_work);
struct sk_buff *skb;
bool woken_up = false;
int ret;
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1251_STATE_OFF))
goto out;
while ((skb = skb_dequeue(&wl->tx_queue))) {
if (!woken_up) {
ret = wl1251_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
woken_up = true;
}
ret = wl1251_tx_frame(wl, skb);
if (ret == -EBUSY) {
skb_queue_head(&wl->tx_queue, skb);
goto out;
} else if (ret < 0) {
dev_kfree_skb(skb);
goto out;
}
}
out:
if (woken_up)
wl1251_ps_elp_sleep(wl);
mutex_unlock(&wl->mutex);
}
static const char *wl1251_tx_parse_status(u8 status)
{
/* 8 bit status field, one character per bit plus null */
static char buf[9];
int i = 0;
memset(buf, 0, sizeof(buf));
if (status & TX_DMA_ERROR)
buf[i++] = 'm';
if (status & TX_DISABLED)
buf[i++] = 'd';
if (status & TX_RETRY_EXCEEDED)
buf[i++] = 'r';
if (status & TX_TIMEOUT)
buf[i++] = 't';
if (status & TX_KEY_NOT_FOUND)
buf[i++] = 'k';
if (status & TX_ENCRYPT_FAIL)
buf[i++] = 'e';
if (status & TX_UNAVAILABLE_PRIORITY)
buf[i++] = 'p';
/* bit 0 is unused apparently */
return buf;
}
static void wl1251_tx_packet_cb(struct wl1251 *wl,
struct tx_result *result)
{
struct ieee80211_tx_info *info;
struct sk_buff *skb;
int hdrlen;
u8 *frame;
skb = wl->tx_frames[result->id];
if (skb == NULL) {
wl1251_error("SKB for packet %d is NULL", result->id);
return;
}
info = IEEE80211_SKB_CB(skb);
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
(result->status == TX_SUCCESS))
info->flags |= IEEE80211_TX_STAT_ACK;
info->status.rates[0].count = result->ack_failures + 1;
wl->stats.retry_count += result->ack_failures;
/*
* We have to remove our private TX header before pushing
* the skb back to mac80211.
*/
frame = skb_pull(skb, sizeof(struct tx_double_buffer_desc));
if (info->control.hw_key &&
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
memmove(frame + WL1251_TKIP_IV_SPACE, frame, hdrlen);
skb_pull(skb, WL1251_TKIP_IV_SPACE);
}
wl1251_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x"
" status 0x%x (%s)",
result->id, skb, result->ack_failures, result->rate,
result->status, wl1251_tx_parse_status(result->status));
ieee80211_tx_status(wl->hw, skb);
wl->tx_frames[result->id] = NULL;
}
/* Called upon reception of a TX complete interrupt */
void wl1251_tx_complete(struct wl1251 *wl)
{
int i, result_index, num_complete = 0, queue_len;
struct tx_result result[FW_TX_CMPLT_BLOCK_SIZE], *result_ptr;
unsigned long flags;
if (unlikely(wl->state != WL1251_STATE_ON))
return;
/* First we read the result */
wl1251_mem_read(wl, wl->data_path->tx_complete_addr,
result, sizeof(result));
result_index = wl->next_tx_complete;
for (i = 0; i < ARRAY_SIZE(result); i++) {
result_ptr = &result[result_index];
if (result_ptr->done_1 == 1 &&
result_ptr->done_2 == 1) {
wl1251_tx_packet_cb(wl, result_ptr);
result_ptr->done_1 = 0;
result_ptr->done_2 = 0;
result_index = (result_index + 1) &
(FW_TX_CMPLT_BLOCK_SIZE - 1);
num_complete++;
} else {
break;
}
}
queue_len = skb_queue_len(&wl->tx_queue);
if ((num_complete > 0) && (queue_len > 0)) {
/* firmware buffer has space, reschedule tx_work */
wl1251_debug(DEBUG_TX, "tx_complete: reschedule tx_work");
ieee80211_queue_work(wl->hw, &wl->tx_work);
}
if (wl->tx_queue_stopped &&
queue_len <= WL1251_TX_QUEUE_LOW_WATERMARK) {
/* tx_queue has space, restart queues */
wl1251_debug(DEBUG_TX, "tx_complete: waking queues");
spin_lock_irqsave(&wl->wl_lock, flags);
ieee80211_wake_queues(wl->hw);
wl->tx_queue_stopped = false;
spin_unlock_irqrestore(&wl->wl_lock, flags);
}
/* Every completed frame needs to be acknowledged */
if (num_complete) {
/*
* If we've wrapped, we have to clear
* the results in 2 steps.
*/
if (result_index > wl->next_tx_complete) {
/* Only 1 write is needed */
wl1251_mem_write(wl,
wl->data_path->tx_complete_addr +
(wl->next_tx_complete *
sizeof(struct tx_result)),
&result[wl->next_tx_complete],
num_complete *
sizeof(struct tx_result));
} else if (result_index < wl->next_tx_complete) {
/* 2 writes are needed */
wl1251_mem_write(wl,
wl->data_path->tx_complete_addr +
(wl->next_tx_complete *
sizeof(struct tx_result)),
&result[wl->next_tx_complete],
(FW_TX_CMPLT_BLOCK_SIZE -
wl->next_tx_complete) *
sizeof(struct tx_result));
wl1251_mem_write(wl,
wl->data_path->tx_complete_addr,
result,
(num_complete -
FW_TX_CMPLT_BLOCK_SIZE +
wl->next_tx_complete) *
sizeof(struct tx_result));
} else {
/* We have to write the whole array */
wl1251_mem_write(wl,
wl->data_path->tx_complete_addr,
result,
FW_TX_CMPLT_BLOCK_SIZE *
sizeof(struct tx_result));
}
}
wl->next_tx_complete = result_index;
}
/* caller must hold wl->mutex */
void wl1251_tx_flush(struct wl1251 *wl)
{
int i;
struct sk_buff *skb;
struct ieee80211_tx_info *info;
/* TX failure */
/* control->flags = 0; FIXME */
while ((skb = skb_dequeue(&wl->tx_queue))) {
info = IEEE80211_SKB_CB(skb);
wl1251_debug(DEBUG_TX, "flushing skb 0x%p", skb);
if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS))
continue;
ieee80211_tx_status(wl->hw, skb);
}
for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
if (wl->tx_frames[i] != NULL) {
skb = wl->tx_frames[i];
info = IEEE80211_SKB_CB(skb);
if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS))
continue;
ieee80211_tx_status(wl->hw, skb);
wl->tx_frames[i] = NULL;
}
}

View File

@@ -0,0 +1,231 @@
/*
* This file is part of wl1251
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL1251_TX_H__
#define __WL1251_TX_H__
#include <linux/bitops.h>
#include "acx.h"
/*
*
* TX PATH
*
* The Tx path uses a double buffer and a tx_control structure, each located
* at a fixed address in the device's memory. On startup, the host retrieves
* the pointers to these addresses. A double buffer allows for continuous data
* flow towards the device. The host keeps track of which buffer is available
* and alternates between these two buffers on a per packet basis.
*
* The size of each of the two buffers is large enough to hold the longest
* 802.3 packet - maximum size Ethernet packet + header + descriptor.
* TX complete indication will be received a-synchronously in a TX done cyclic
* buffer which is composed of 16 tx_result descriptors structures and is used
* in a cyclic manner.
*
* The TX (HOST) procedure is as follows:
* 1. Read the Tx path status, that will give the data_out_count.
* 2. goto 1, if not possible.
* i.e. if data_in_count - data_out_count >= HwBuffer size (2 for double
* buffer).
* 3. Copy the packet (preceded by double_buffer_desc), if possible.
* i.e. if data_in_count - data_out_count < HwBuffer size (2 for double
* buffer).
* 4. increment data_in_count.
* 5. Inform the firmware by generating a firmware internal interrupt.
* 6. FW will increment data_out_count after it reads the buffer.
*
* The TX Complete procedure:
* 1. To get a TX complete indication the host enables the tx_complete flag in
* the TX descriptor Structure.
* 2. For each packet with a Tx Complete field set, the firmware adds the
* transmit results to the cyclic buffer (txDoneRing) and sets both done_1
* and done_2 to 1 to indicate driver ownership.
* 3. The firmware sends a Tx Complete interrupt to the host to trigger the
* host to process the new data. Note: interrupt will be send per packet if
* TX complete indication was requested in tx_control or per crossing
* aggregation threshold.
* 4. After receiving the Tx Complete interrupt, the host reads the
* TxDescriptorDone information in a cyclic manner and clears both done_1
* and done_2 fields.
*
*/
#define TX_COMPLETE_REQUIRED_BIT 0x80
#define TX_STATUS_DATA_OUT_COUNT_MASK 0xf
#define WL1251_TX_ALIGN_TO 4
#define WL1251_TX_ALIGN(len) (((len) + WL1251_TX_ALIGN_TO - 1) & \
~(WL1251_TX_ALIGN_TO - 1))
#define WL1251_TKIP_IV_SPACE 4
struct tx_control {
/* Rate Policy (class) index */
unsigned rate_policy:3;
/* When set, no ack policy is expected */
unsigned ack_policy:1;
/*
* Packet type:
* 0 -> 802.11
* 1 -> 802.3
* 2 -> IP
* 3 -> raw codec
*/
unsigned packet_type:2;
/* If set, this is a QoS-Null or QoS-Data frame */
unsigned qos:1;
/*
* If set, the target triggers the tx complete INT
* upon frame sending completion.
*/
unsigned tx_complete:1;
/* 2 bytes padding before packet header */
unsigned xfer_pad:1;
unsigned reserved:7;
} __packed;
struct tx_double_buffer_desc {
/* Length of payload, including headers. */
__le16 length;
/*
* A bit mask that specifies the initial rate to be used
* Possible values are:
* 0x0001 - 1Mbits
* 0x0002 - 2Mbits
* 0x0004 - 5.5Mbits
* 0x0008 - 6Mbits
* 0x0010 - 9Mbits
* 0x0020 - 11Mbits
* 0x0040 - 12Mbits
* 0x0080 - 18Mbits
* 0x0100 - 22Mbits
* 0x0200 - 24Mbits
* 0x0400 - 36Mbits
* 0x0800 - 48Mbits
* 0x1000 - 54Mbits
*/
__le16 rate;
/* Time in us that a packet can spend in the target */
__le32 expiry_time;
/* index of the TX queue used for this packet */
u8 xmit_queue;
/* Used to identify a packet */
u8 id;
struct tx_control control;
/*
* The FW should cut the packet into fragments
* of this size.
*/
__le16 frag_threshold;
/* Numbers of HW queue blocks to be allocated */
u8 num_mem_blocks;
u8 reserved;
} __packed;
enum {
TX_SUCCESS = 0,
TX_DMA_ERROR = BIT(7),
TX_DISABLED = BIT(6),
TX_RETRY_EXCEEDED = BIT(5),
TX_TIMEOUT = BIT(4),
TX_KEY_NOT_FOUND = BIT(3),
TX_ENCRYPT_FAIL = BIT(2),
TX_UNAVAILABLE_PRIORITY = BIT(1),
};
struct tx_result {
/*
* Ownership synchronization between the host and
* the firmware. If done_1 and done_2 are cleared,
* owned by the FW (no info ready).
*/
u8 done_1;
/* same as double_buffer_desc->id */
u8 id;
/*
* Total air access duration consumed by this
* packet, including all retries and overheads.
*/
u16 medium_usage;
/* Total media delay (from 1st EDCA AIFS counter until TX Complete). */
u32 medium_delay;
/* Time between host xfer and tx complete */
u32 fw_hnadling_time;
/* The LS-byte of the last TKIP sequence number. */
u8 lsb_seq_num;
/* Retry count */
u8 ack_failures;
/* At which rate we got a ACK */
u16 rate;
u16 reserved;
/* TX_* */
u8 status;
/* See done_1 */
u8 done_2;
} __packed;
static inline int wl1251_tx_get_queue(int queue)
{
switch (queue) {
case 0:
return QOS_AC_VO;
case 1:
return QOS_AC_VI;
case 2:
return QOS_AC_BE;
case 3:
return QOS_AC_BK;
default:
return QOS_AC_BE;
}
}
void wl1251_tx_work(struct work_struct *work);
void wl1251_tx_complete(struct wl1251 *wl);
void wl1251_tx_flush(struct wl1251 *wl);
#endif

View File

@@ -0,0 +1,446 @@
/*
* This file is part of wl1251
*
* Copyright (c) 1998-2007 Texas Instruments Incorporated
* Copyright (C) 2008-2009 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL1251_H__
#define __WL1251_H__
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/bitops.h>
#include <net/mac80211.h>
#define DRIVER_NAME "wl1251"
#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_NETLINK = 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_ALL = ~0,
};
#define DEBUG_LEVEL (DEBUG_NONE)
#define DEBUG_DUMP_LIMIT 1024
#define wl1251_error(fmt, arg...) \
printk(KERN_ERR DRIVER_PREFIX "ERROR " fmt "\n", ##arg)
#define wl1251_warning(fmt, arg...) \
printk(KERN_WARNING DRIVER_PREFIX "WARNING " fmt "\n", ##arg)
#define wl1251_notice(fmt, arg...) \
printk(KERN_INFO DRIVER_PREFIX fmt "\n", ##arg)
#define wl1251_info(fmt, arg...) \
printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg)
#define wl1251_debug(level, fmt, arg...) \
do { \
if (level & DEBUG_LEVEL) \
printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg); \
} while (0)
#define wl1251_dump(level, prefix, buf, len) \
do { \
if (level & DEBUG_LEVEL) \
print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
DUMP_PREFIX_OFFSET, 16, 1, \
buf, \
min_t(size_t, len, DEBUG_DUMP_LIMIT), \
0); \
} while (0)
#define wl1251_dump_ascii(level, prefix, buf, len) \
do { \
if (level & DEBUG_LEVEL) \
print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
DUMP_PREFIX_OFFSET, 16, 1, \
buf, \
min_t(size_t, len, DEBUG_DUMP_LIMIT), \
true); \
} while (0)
#define WL1251_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \
CFG_BSSID_FILTER_EN)
#define WL1251_DEFAULT_RX_FILTER (CFG_RX_PRSP_EN | \
CFG_RX_MGMT_EN | \
CFG_RX_DATA_EN | \
CFG_RX_CTL_EN | \
CFG_RX_BCN_EN | \
CFG_RX_AUTH_EN | \
CFG_RX_ASSOC_EN)
#define WL1251_BUSY_WORD_LEN 8
struct boot_attr {
u32 radio_type;
u8 mac_clock;
u8 arm_clock;
int firmware_debug;
u32 minor;
u32 major;
u32 bugfix;
};
enum wl1251_state {
WL1251_STATE_OFF,
WL1251_STATE_ON,
WL1251_STATE_PLT,
};
enum wl1251_partition_type {
PART_DOWN,
PART_WORK,
PART_DRPW,
PART_TABLE_LEN
};
enum wl1251_station_mode {
STATION_ACTIVE_MODE,
STATION_POWER_SAVE_MODE,
STATION_IDLE,
};
struct wl1251_partition {
u32 size;
u32 start;
};
struct wl1251_partition_set {
struct wl1251_partition mem;
struct wl1251_partition reg;
};
struct wl1251;
struct wl1251_stats {
struct acx_statistics *fw_stats;
unsigned long fw_stats_update;
unsigned int retry_count;
unsigned int excessive_retries;
};
struct wl1251_debugfs {
struct dentry *rootdir;
struct dentry *fw_statistics;
struct dentry *tx_internal_desc_overflow;
struct dentry *rx_out_of_mem;
struct dentry *rx_hdr_overflow;
struct dentry *rx_hw_stuck;
struct dentry *rx_dropped;
struct dentry *rx_fcs_err;
struct dentry *rx_xfr_hint_trig;
struct dentry *rx_path_reset;
struct dentry *rx_reset_counter;
struct dentry *dma_rx_requested;
struct dentry *dma_rx_errors;
struct dentry *dma_tx_requested;
struct dentry *dma_tx_errors;
struct dentry *isr_cmd_cmplt;
struct dentry *isr_fiqs;
struct dentry *isr_rx_headers;
struct dentry *isr_rx_mem_overflow;
struct dentry *isr_rx_rdys;
struct dentry *isr_irqs;
struct dentry *isr_tx_procs;
struct dentry *isr_decrypt_done;
struct dentry *isr_dma0_done;
struct dentry *isr_dma1_done;
struct dentry *isr_tx_exch_complete;
struct dentry *isr_commands;
struct dentry *isr_rx_procs;
struct dentry *isr_hw_pm_mode_changes;
struct dentry *isr_host_acknowledges;
struct dentry *isr_pci_pm;
struct dentry *isr_wakeups;
struct dentry *isr_low_rssi;
struct dentry *wep_addr_key_count;
struct dentry *wep_default_key_count;
/* skipping wep.reserved */
struct dentry *wep_key_not_found;
struct dentry *wep_decrypt_fail;
struct dentry *wep_packets;
struct dentry *wep_interrupt;
struct dentry *pwr_ps_enter;
struct dentry *pwr_elp_enter;
struct dentry *pwr_missing_bcns;
struct dentry *pwr_wake_on_host;
struct dentry *pwr_wake_on_timer_exp;
struct dentry *pwr_tx_with_ps;
struct dentry *pwr_tx_without_ps;
struct dentry *pwr_rcvd_beacons;
struct dentry *pwr_power_save_off;
struct dentry *pwr_enable_ps;
struct dentry *pwr_disable_ps;
struct dentry *pwr_fix_tsf_ps;
/* skipping cont_miss_bcns_spread for now */
struct dentry *pwr_rcvd_awake_beacons;
struct dentry *mic_rx_pkts;
struct dentry *mic_calc_failure;
struct dentry *aes_encrypt_fail;
struct dentry *aes_decrypt_fail;
struct dentry *aes_encrypt_packets;
struct dentry *aes_decrypt_packets;
struct dentry *aes_encrypt_interrupt;
struct dentry *aes_decrypt_interrupt;
struct dentry *event_heart_beat;
struct dentry *event_calibration;
struct dentry *event_rx_mismatch;
struct dentry *event_rx_mem_empty;
struct dentry *event_rx_pool;
struct dentry *event_oom_late;
struct dentry *event_phy_transmit_error;
struct dentry *event_tx_stuck;
struct dentry *ps_pspoll_timeouts;
struct dentry *ps_upsd_timeouts;
struct dentry *ps_upsd_max_sptime;
struct dentry *ps_upsd_max_apturn;
struct dentry *ps_pspoll_max_apturn;
struct dentry *ps_pspoll_utilization;
struct dentry *ps_upsd_utilization;
struct dentry *rxpipe_rx_prep_beacon_drop;
struct dentry *rxpipe_descr_host_int_trig_rx_data;
struct dentry *rxpipe_beacon_buffer_thres_host_int_trig_rx_data;
struct dentry *rxpipe_missed_beacon_host_int_trig_rx_data;
struct dentry *rxpipe_tx_xfr_host_int_trig_rx_data;
struct dentry *tx_queue_len;
struct dentry *tx_queue_status;
struct dentry *retry_count;
struct dentry *excessive_retries;
};
struct wl1251_if_operations {
void (*read)(struct wl1251 *wl, int addr, void *buf, size_t len);
void (*write)(struct wl1251 *wl, int addr, void *buf, size_t len);
void (*read_elp)(struct wl1251 *wl, int addr, u32 *val);
void (*write_elp)(struct wl1251 *wl, int addr, u32 val);
int (*power)(struct wl1251 *wl, bool enable);
void (*reset)(struct wl1251 *wl);
void (*enable_irq)(struct wl1251 *wl);
void (*disable_irq)(struct wl1251 *wl);
};
struct wl1251 {
struct ieee80211_hw *hw;
bool mac80211_registered;
void *if_priv;
const struct wl1251_if_operations *if_ops;
void (*set_power)(bool enable);
int irq;
bool use_eeprom;
spinlock_t wl_lock;
enum wl1251_state state;
struct mutex mutex;
int physical_mem_addr;
int physical_reg_addr;
int virtual_mem_addr;
int virtual_reg_addr;
int cmd_box_addr;
int event_box_addr;
struct boot_attr boot_attr;
u8 *fw;
size_t fw_len;
u8 *nvs;
size_t nvs_len;
u8 bssid[ETH_ALEN];
u8 mac_addr[ETH_ALEN];
u8 bss_type;
u8 listen_int;
int channel;
void *target_mem_map;
struct acx_data_path_params_resp *data_path;
/* Number of TX packets transferred to the FW, modulo 16 */
u32 data_in_count;
/* Frames scheduled for transmission, not handled yet */
struct sk_buff_head tx_queue;
bool tx_queue_stopped;
struct work_struct tx_work;
struct work_struct filter_work;
/* Pending TX frames */
struct sk_buff *tx_frames[16];
/*
* Index pointing to the next TX complete entry
* in the cyclic XT complete array we get from
* the FW.
*/
u32 next_tx_complete;
/* FW Rx counter */
u32 rx_counter;
/* Rx frames handled */
u32 rx_handled;
/* Current double buffer */
u32 rx_current_buffer;
u32 rx_last_id;
/* The target interrupt mask */
u32 intr_mask;
struct work_struct irq_work;
/* The mbox event mask */
u32 event_mask;
/* Mailbox pointers */
u32 mbox_ptr[2];
/* Are we currently scanning */
bool scanning;
/* Default key (for WEP) */
u32 default_key;
unsigned int tx_mgmt_frm_rate;
unsigned int tx_mgmt_frm_mod;
unsigned int rx_config;
unsigned int rx_filter;
/* is firmware in elp mode */
bool elp;
struct delayed_work elp_work;
enum wl1251_station_mode station_mode;
/* PSM mode requested */
bool psm_requested;
u16 beacon_int;
u8 dtim_period;
/* in dBm */
int power_level;
int rssi_thold;
struct wl1251_stats stats;
struct wl1251_debugfs debugfs;
__le32 buffer_32;
u32 buffer_cmd;
u8 buffer_busyword[WL1251_BUSY_WORD_LEN];
struct wl1251_rx_descriptor *rx_descriptor;
struct ieee80211_vif *vif;
u32 chip_id;
char fw_ver[21];
/* Most recently reported noise in dBm */
s8 noise;
};
int wl1251_plt_start(struct wl1251 *wl);
int wl1251_plt_stop(struct wl1251 *wl);
struct ieee80211_hw *wl1251_alloc_hw(void);
int wl1251_free_hw(struct wl1251 *wl);
int wl1251_init_ieee80211(struct wl1251 *wl);
void wl1251_enable_interrupts(struct wl1251 *wl);
void wl1251_disable_interrupts(struct wl1251 *wl);
#define DEFAULT_HW_GEN_MODULATION_TYPE CCK_LONG /* Long Preamble */
#define DEFAULT_HW_GEN_TX_RATE RATE_2MBPS
#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */
#define WL1251_DEFAULT_POWER_LEVEL 20
#define WL1251_TX_QUEUE_LOW_WATERMARK 10
#define WL1251_TX_QUEUE_HIGH_WATERMARK 25
#define WL1251_DEFAULT_BEACON_INT 100
#define WL1251_DEFAULT_DTIM_PERIOD 1
#define WL1251_DEFAULT_CHANNEL 0
#define WL1251_DEFAULT_BET_CONSECUTIVE 10
#define CHIP_ID_1251_PG10 (0x7010101)
#define CHIP_ID_1251_PG11 (0x7020101)
#define CHIP_ID_1251_PG12 (0x7030101)
#define CHIP_ID_1271_PG10 (0x4030101)
#define CHIP_ID_1271_PG20 (0x4030111)
#define WL1251_FW_NAME "wl1251-fw.bin"
#define WL1251_NVS_NAME "wl1251-nvs.bin"
#define WL1251_POWER_ON_SLEEP 10 /* in milliseconds */
#define WL1251_PART_DOWN_MEM_START 0x0
#define WL1251_PART_DOWN_MEM_SIZE 0x16800
#define WL1251_PART_DOWN_REG_START REGISTERS_BASE
#define WL1251_PART_DOWN_REG_SIZE REGISTERS_DOWN_SIZE
#define WL1251_PART_WORK_MEM_START 0x28000
#define WL1251_PART_WORK_MEM_SIZE 0x14000
#define WL1251_PART_WORK_REG_START REGISTERS_BASE
#define WL1251_PART_WORK_REG_SIZE REGISTERS_WORK_SIZE
#define WL1251_DEFAULT_LOW_RSSI_WEIGHT 10
#define WL1251_DEFAULT_LOW_RSSI_DEPTH 10
#endif

View File

@@ -0,0 +1,155 @@
#ifndef __WL12XX_80211_H__
#define __WL12XX_80211_H__
#include <linux/if_ether.h> /* ETH_ALEN */
/* 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;
u8 payload[0];
} __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_beacon_template {
struct ieee80211_header header;
__le32 time_stamp[2];
__le16 beacon_interval;
__le16 capability;
struct wl12xx_ie_ssid ssid;
struct wl12xx_ie_rates rates;
struct wl12xx_ie_rates ext_rates;
struct wl12xx_ie_ds_params ds_params;
struct wl12xx_ie_country country;
} __packed;
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_qos_null_data_template {
struct ieee80211_header header;
__le16 qos_ctl;
} __packed;
struct wl12xx_probe_req_template {
struct ieee80211_header header;
struct wl12xx_ie_ssid ssid;
struct wl12xx_ie_rates rates;
struct wl12xx_ie_rates ext_rates;
} __packed;
struct wl12xx_probe_resp_template {
struct ieee80211_header header;
__le32 time_stamp[2];
__le16 beacon_interval;
__le16 capability;
struct wl12xx_ie_ssid ssid;
struct wl12xx_ie_rates rates;
struct wl12xx_ie_rates ext_rates;
struct wl12xx_ie_ds_params ds_params;
struct wl12xx_ie_country country;
} __packed;
#endif

View File

@@ -0,0 +1,48 @@
menuconfig WL12XX_MENU
tristate "TI wl12xx driver support"
depends on MAC80211 && EXPERIMENTAL
---help---
This will enable TI wl12xx driver support for the following chips:
wl1271, wl1273, wl1281 and wl1283.
The drivers make use of the mac80211 stack.
config WL12XX
tristate "TI wl12xx support"
depends on WL12XX_MENU && GENERIC_HARDIRQS
depends on INET
select FW_LOADER
---help---
This module adds support for wireless adapters based on TI wl1271 and
TI wl1273 chipsets. This module does *not* include support for wl1251.
For wl1251 support, use the separate homonymous driver instead.
If you choose to build a module, it will be called wl12xx. Say N if
unsure.
config WL12XX_SPI
tristate "TI wl12xx SPI support"
depends on WL12XX && SPI_MASTER
select CRC7
---help---
This module adds support for the SPI interface of adapters using
TI wl12xx chipsets. Select this if your platform is using
the SPI bus.
If you choose to build a module, it'll be called wl12xx_spi.
Say N if unsure.
config WL12XX_SDIO
tristate "TI wl12xx SDIO support"
depends on WL12XX && MMC
---help---
This module adds support for the SDIO interface of adapters using
TI wl12xx chipsets. Select this if your platform is using
the SDIO bus.
If you choose to build a module, it'll be called wl12xx_sdio.
Say N if unsure.
config WL12XX_PLATFORM_DATA
bool
depends on WL12XX_SDIO != n || WL1251_SDIO != n
default y

View File

@@ -0,0 +1,15 @@
wl12xx-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
wl12xx_spi-objs = spi.o
wl12xx_sdio-objs = sdio.o
wl12xx-$(CONFIG_NL80211_TESTMODE) += testmode.o
obj-$(CONFIG_WL12XX) += wl12xx.o
obj-$(CONFIG_WL12XX_SPI) += wl12xx_spi.o
obj-$(CONFIG_WL12XX_SDIO) += wl12xx_sdio.o
# small builtin driver bit
obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o
ccflags-y += -D__CHECK_ENDIAN__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,794 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/slab.h>
#include <linux/wl12xx.h>
#include <linux/export.h>
#include "debug.h"
#include "acx.h"
#include "reg.h"
#include "boot.h"
#include "io.h"
#include "event.h"
#include "rx.h"
static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
{
u32 cpu_ctrl;
/* 10.5.0 run the firmware (I) */
cpu_ctrl = wl1271_read32(wl, ACX_REG_ECPU_CONTROL);
/* 10.5.1 run the firmware (II) */
cpu_ctrl |= flag;
wl1271_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl);
}
static unsigned int wl12xx_get_fw_ver_quirks(struct wl1271 *wl)
{
unsigned int quirks = 0;
unsigned int *fw_ver = wl->chip.fw_ver;
/* Only new station firmwares support routing fw logs to the host */
if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
(fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN))
quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED;
/* This feature is not yet supported for AP mode */
if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP)
quirks |= WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED;
return quirks;
}
static void wl1271_parse_fw_ver(struct wl1271 *wl)
{
int ret;
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));
return;
}
/* Check if any quirks are needed with older fw versions */
wl->quirks |= wl12xx_get_fw_ver_quirks(wl);
}
static void wl1271_boot_fw_version(struct wl1271 *wl)
{
struct wl1271_static_data *static_data;
static_data = kmalloc(sizeof(*static_data), GFP_DMA);
if (!static_data) {
__WARN();
return;
}
wl1271_read(wl, wl->cmd_box_addr, static_data, sizeof(*static_data),
false);
strncpy(wl->chip.fw_ver_str, static_data->fw_version,
sizeof(wl->chip.fw_ver_str));
kfree(static_data);
/* make sure the string is NULL-terminated */
wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';
wl1271_parse_fw_ver(wl);
}
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
size_t fw_data_len, u32 dest)
{
struct wl1271_partition_set partition;
int addr, chunk_num, partition_limit;
u8 *p, *chunk;
/* 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, &wl12xx_part_table[PART_DOWN], sizeof(partition));
partition.mem.start = dest;
wl1271_set_partition(wl, &partition);
/* 10.1 set partition limit and chunk num */
chunk_num = 0;
partition_limit = wl12xx_part_table[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 +
wl12xx_part_table[PART_DOWN].mem.size;
partition.mem.start = addr;
wl1271_set_partition(wl, &partition);
}
/* 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);
wl1271_write(wl, addr, chunk, CHUNK_SIZE, false);
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);
wl1271_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false);
kfree(chunk);
return 0;
}
static int wl1271_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;
}
static int wl1271_boot_upload_nvs(struct wl1271 *wl)
{
size_t nvs_len, burst_len;
int i;
u32 dest_addr, val;
u8 *nvs_ptr, *nvs_aligned;
if (wl->nvs == NULL)
return -ENODEV;
if (wl->chip.id == CHIP_ID_1283_PG20) {
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("nvs size is not as expected: %zu != %zu",
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;
} else {
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("nvs size is not as expected: %zu != %zu",
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;
}
/* 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_BASE to the destination
*/
dest_addr += REGISTERS_BASE;
/* 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);
wl1271_write32(wl, dest_addr, val);
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 */
wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]);
/* Copy the NVS tables to a new block to ensure alignment */
nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL);
if (!nvs_aligned)
return -ENOMEM;
/* And finally we upload the NVS tables */
wl1271_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false);
kfree(nvs_aligned);
return 0;
out_badnvs:
wl1271_error("nvs data is malformed");
return -EILSEQ;
}
static void wl1271_boot_enable_interrupts(struct wl1271 *wl)
{
wl1271_enable_interrupts(wl);
wl1271_write32(wl, ACX_REG_INTERRUPT_MASK,
WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
wl1271_write32(wl, HI_CFG, HI_CFG_DEF_VAL);
}
static int wl1271_boot_soft_reset(struct wl1271 *wl)
{
unsigned long timeout;
u32 boot_data;
/* perform soft reset */
wl1271_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT);
/* SOFT_RESET is self clearing */
timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME);
while (1) {
boot_data = wl1271_read32(wl, ACX_REG_SLV_SOFT_RESET);
wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data);
if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0)
break;
if (time_after(jiffies, timeout)) {
/* 1.2 check pWhalBus->uSelfClearTime if the
* timeout was reached */
wl1271_error("soft reset timeout");
return -1;
}
udelay(SOFT_RESET_STALL_TIME);
}
/* disable Rx/Tx */
wl1271_write32(wl, ENABLE, 0x0);
/* disable auto calibration on start*/
wl1271_write32(wl, SPARE_A2, 0xffff);
return 0;
}
static int wl1271_boot_run_firmware(struct wl1271 *wl)
{
int loop, ret;
u32 chip_id, intr;
wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT);
chip_id = wl1271_read32(wl, CHIP_ID_B);
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);
intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
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) {
wl1271_write32(wl, ACX_REG_INTERRUPT_ACK,
WL1271_ACX_INTR_INIT_COMPLETE);
break;
}
}
if (loop > INIT_LOOP) {
wl1271_error("timeout waiting for the hardware to "
"complete initialization");
return -EIO;
}
/* get hardware config command mail box */
wl->cmd_box_addr = wl1271_read32(wl, REG_COMMAND_MAILBOX_PTR);
/* get hardware config event mail box */
wl->event_box_addr = wl1271_read32(wl, REG_EVENT_MAILBOX_PTR);
/* set the working partition to its "running" mode offset */
wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]);
wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x",
wl->cmd_box_addr, wl->event_box_addr);
wl1271_boot_fw_version(wl);
/*
* in case of full asynchronous mode the firmware event must be
* ready to receive event from the command mailbox
*/
/* unmask required mbox events */
wl->event_mask = BSS_LOSE_EVENT_ID |
SCAN_COMPLETE_EVENT_ID |
ROLE_STOP_COMPLETE_EVENT_ID |
RSSI_SNR_TRIGGER_0_EVENT_ID |
PSPOLL_DELIVERY_FAILURE_EVENT_ID |
SOFT_GEMINI_SENSE_EVENT_ID |
PERIODIC_SCAN_REPORT_EVENT_ID |
PERIODIC_SCAN_COMPLETE_EVENT_ID |
DUMMY_PACKET_EVENT_ID |
PEER_REMOVE_COMPLETE_EVENT_ID |
BA_SESSION_RX_CONSTRAINT_EVENT_ID |
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
INACTIVE_STA_EVENT_ID |
MAX_TX_RETRY_EVENT_ID |
CHANNEL_SWITCH_COMPLETE_EVENT_ID;
ret = wl1271_event_unmask(wl);
if (ret < 0) {
wl1271_error("EVENT mask setting failed");
return ret;
}
wl1271_event_mbox_config(wl);
/* firmware startup completed */
return 0;
}
static int wl1271_boot_write_irq_polarity(struct wl1271 *wl)
{
u32 polarity;
polarity = wl1271_top_reg_read(wl, OCP_REG_POLARITY);
/* We use HIGH polarity, so unset the LOW bit */
polarity &= ~POLARITY_LOW;
wl1271_top_reg_write(wl, OCP_REG_POLARITY, polarity);
return 0;
}
static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl)
{
u16 spare_reg;
/* Mask bits [2] & [8:4] in the sys_clk_cfg register */
spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG);
if (spare_reg == 0xFFFF)
return -EFAULT;
spare_reg |= (BIT(3) | BIT(5) | BIT(6));
wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg);
/* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */
wl1271_top_reg_write(wl, SYS_CLK_CFG_REG,
WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF);
/* Delay execution for 15msec, to let the HW settle */
mdelay(15);
return 0;
}
static bool wl128x_is_tcxo_valid(struct wl1271 *wl)
{
u16 tcxo_detection;
tcxo_detection = wl1271_top_reg_read(wl, TCXO_CLK_DETECT_REG);
if (tcxo_detection & TCXO_DET_FAILED)
return false;
return true;
}
static bool wl128x_is_fref_valid(struct wl1271 *wl)
{
u16 fref_detection;
fref_detection = wl1271_top_reg_read(wl, FREF_CLK_DETECT_REG);
if (fref_detection & FREF_CLK_DETECT_FAIL)
return false;
return true;
}
static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl)
{
wl1271_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL);
wl1271_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL);
wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL);
return 0;
}
static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
{
u16 spare_reg;
u16 pll_config;
u8 input_freq;
/* Mask bits [3:1] in the sys_clk_cfg register */
spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG);
if (spare_reg == 0xFFFF)
return -EFAULT;
spare_reg |= BIT(2);
wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg);
/* Handle special cases of the TCXO clock */
if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
return wl128x_manually_configure_mcs_pll(wl);
/* Set the input frequency according to the selected clock source */
input_freq = (clk & 1) + 1;
pll_config = wl1271_top_reg_read(wl, MCS_PLL_CONFIG_REG);
if (pll_config == 0xFFFF)
return -EFAULT;
pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT);
pll_config |= MCS_PLL_ENABLE_HP;
wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config);
return 0;
}
/*
* WL128x has two clocks input - TCXO and FREF.
* TCXO is the main clock of the device, while FREF is used to sync
* between the GPS and the cellular modem.
* In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used
* as the WLAN/BT main clock.
*/
static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
{
u16 sys_clk_cfg;
/* For XTAL-only modes, FREF will be used after switching from TCXO */
if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
if (!wl128x_switch_tcxo_to_fref(wl))
return -EINVAL;
goto fref_clk;
}
/* Query the HW, to determine which clock source we should use */
sys_clk_cfg = wl1271_top_reg_read(wl, SYS_CLK_CFG_REG);
if (sys_clk_cfg == 0xFFFF)
return -EINVAL;
if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF)
goto fref_clk;
/* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */
if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
if (!wl128x_switch_tcxo_to_fref(wl))
return -EINVAL;
goto fref_clk;
}
/* TCXO clock is selected */
if (!wl128x_is_tcxo_valid(wl))
return -EINVAL;
*selected_clock = wl->tcxo_clock;
goto config_mcs_pll;
fref_clk:
/* FREF clock is selected */
if (!wl128x_is_fref_valid(wl))
return -EINVAL;
*selected_clock = wl->ref_clock;
config_mcs_pll:
return wl128x_configure_mcs_pll(wl, *selected_clock);
}
static int wl127x_boot_clk(struct wl1271 *wl)
{
u32 pause;
u32 clk;
if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION;
if (wl->ref_clock == CONF_REF_CLK_19_2_E ||
wl->ref_clock == CONF_REF_CLK_38_4_E ||
wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
/* ref clk: 19.2/38.4/38.4-XTAL */
clk = 0x3;
else if (wl->ref_clock == CONF_REF_CLK_26_E ||
wl->ref_clock == CONF_REF_CLK_52_E)
/* ref clk: 26/52 */
clk = 0x5;
else
return -EINVAL;
if (wl->ref_clock != CONF_REF_CLK_19_2_E) {
u16 val;
/* Set clock type (open drain) */
val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE);
val &= FREF_CLK_TYPE_BITS;
wl1271_top_reg_write(wl, OCP_REG_CLK_TYPE, val);
/* Set clock pull mode (no pull) */
val = wl1271_top_reg_read(wl, OCP_REG_CLK_PULL);
val |= NO_PULL;
wl1271_top_reg_write(wl, OCP_REG_CLK_PULL, val);
} else {
u16 val;
/* Set clock polarity */
val = wl1271_top_reg_read(wl, OCP_REG_CLK_POLARITY);
val &= FREF_CLK_POLARITY_BITS;
val |= CLK_REQ_OUTN_SEL;
wl1271_top_reg_write(wl, OCP_REG_CLK_POLARITY, val);
}
wl1271_write32(wl, PLL_PARAMETERS, clk);
pause = wl1271_read32(wl, PLL_PARAMETERS);
wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause);
pause &= ~(WU_COUNTER_PAUSE_VAL);
pause |= WU_COUNTER_PAUSE_VAL;
wl1271_write32(wl, WU_COUNTER_PAUSE, pause);
return 0;
}
/* uploads NVS and firmware */
int wl1271_load_firmware(struct wl1271 *wl)
{
int ret = 0;
u32 tmp, clk;
int selected_clock = -1;
if (wl->chip.id == CHIP_ID_1283_PG20) {
ret = wl128x_boot_clk(wl, &selected_clock);
if (ret < 0)
goto out;
} else {
ret = wl127x_boot_clk(wl);
if (ret < 0)
goto out;
}
/* Continue the ELP wake up sequence */
wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
udelay(500);
wl1271_set_partition(wl, &wl12xx_part_table[PART_DRPW]);
/* Read-modify-write DRPW_SCRATCH_START register (see next state)
to be used by DRPw FW. The RTRIM value will be added by the FW
before taking DRPw out of reset */
wl1271_debug(DEBUG_BOOT, "DRPW_SCRATCH_START %08x", DRPW_SCRATCH_START);
clk = wl1271_read32(wl, DRPW_SCRATCH_START);
wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
if (wl->chip.id == CHIP_ID_1283_PG20) {
clk |= ((selected_clock & 0x3) << 1) << 4;
} else {
clk |= (wl->ref_clock << 1) << 4;
}
wl1271_write32(wl, DRPW_SCRATCH_START, clk);
wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]);
/* Disable interrupts */
wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
ret = wl1271_boot_soft_reset(wl);
if (ret < 0)
goto out;
/* 2. start processing NVS file */
ret = wl1271_boot_upload_nvs(wl);
if (ret < 0)
goto out;
/* write firmware's last address (ie. it's length) to
* ACX_EEPROMLESS_IND_REG */
wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG");
wl1271_write32(wl, ACX_EEPROMLESS_IND_REG, ACX_EEPROMLESS_IND_REG);
tmp = wl1271_read32(wl, CHIP_ID_B);
wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
/* 6. read the EEPROM parameters */
tmp = wl1271_read32(wl, SCR_PAD2);
/* WL1271: The reference driver skips steps 7 to 10 (jumps directly
* to upload_fw) */
if (wl->chip.id == CHIP_ID_1283_PG20)
wl1271_top_reg_write(wl, SDIO_IO_DS, wl->conf.hci_io_ds);
ret = wl1271_boot_upload_firmware(wl);
if (ret < 0)
goto out;
out:
return ret;
}
EXPORT_SYMBOL_GPL(wl1271_load_firmware);
int wl1271_boot(struct wl1271 *wl)
{
int ret;
/* upload NVS and firmware */
ret = wl1271_load_firmware(wl);
if (ret)
return ret;
/* 10.5 start firmware */
ret = wl1271_boot_run_firmware(wl);
if (ret < 0)
goto out;
ret = wl1271_boot_write_irq_polarity(wl);
if (ret < 0)
goto out;
wl1271_write32(wl, ACX_REG_INTERRUPT_MASK,
WL1271_ACX_ALL_EVENTS_VECTOR);
/* Enable firmware interrupts now */
wl1271_boot_enable_interrupts(wl);
wl1271_event_mbox_config(wl);
out:
return ret;
}

View File

@@ -0,0 +1,120 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __BOOT_H__
#define __BOOT_H__
#include "wl12xx.h"
int wl1271_boot(struct wl1271 *wl);
int wl1271_load_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];
};
/* 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
#define OCP_REG_POLARITY 0x0064
#define OCP_REG_CLK_TYPE 0x0448
#define OCP_REG_CLK_POLARITY 0x0cb2
#define OCP_REG_CLK_PULL 0x0cb4
#define CMD_MBOX_ADDRESS 0x407B4
#define POLARITY_LOW BIT(1)
#define NO_PULL (BIT(14) | BIT(15))
#define FREF_CLK_TYPE_BITS 0xfffffe7f
#define CLK_REQ_PRCM 0x100
#define FREF_CLK_POLARITY_BITS 0xfffff8ff
#define CLK_REQ_OUTN_SEL 0x700
/* PLL configuration algorithm for wl128x */
#define SYS_CLK_CFG_REG 0x2200
/* Bit[0] - 0-TCXO, 1-FREF */
#define MCS_PLL_CLK_SEL_FREF BIT(0)
/* Bit[3:2] - 01-TCXO, 10-FREF */
#define WL_CLK_REQ_TYPE_FREF BIT(3)
#define WL_CLK_REQ_TYPE_PG2 (BIT(3) | BIT(2))
/* Bit[4] - 0-TCXO, 1-FREF */
#define PRCM_CM_EN_MUX_WLAN_FREF BIT(4)
#define TCXO_ILOAD_INT_REG 0x2264
#define TCXO_CLK_DETECT_REG 0x2266
#define TCXO_DET_FAILED BIT(4)
#define FREF_ILOAD_INT_REG 0x2084
#define FREF_CLK_DETECT_REG 0x2086
#define FREF_CLK_DETECT_FAIL BIT(4)
/* Use this reg for masking during driver access */
#define WL_SPARE_REG 0x2320
#define WL_SPARE_VAL BIT(2)
/* Bit[6:5:3] - mask wl write SYS_CLK_CFG[8:5:2:4] */
#define WL_SPARE_MASK_8526 (BIT(6) | BIT(5) | BIT(3))
#define PLL_LOCK_COUNTERS_REG 0xD8C
#define PLL_LOCK_COUNTERS_COEX 0x0F
#define PLL_LOCK_COUNTERS_MCS 0xF0
#define MCS_PLL_OVERRIDE_REG 0xD90
#define MCS_PLL_CONFIG_REG 0xD92
#define MCS_SEL_IN_FREQ_MASK 0x0070
#define MCS_SEL_IN_FREQ_SHIFT 4
#define MCS_PLL_CONFIG_REG_VAL 0x73
#define MCS_PLL_ENABLE_HP (BIT(0) | BIT(1))
#define MCS_PLL_M_REG 0xD94
#define MCS_PLL_N_REG 0xD96
#define MCS_PLL_M_REG_VAL 0xC8
#define MCS_PLL_N_REG_VAL 0x07
#define SDIO_IO_DS 0xd14
/* SDIO/wSPI DS configuration values */
enum {
HCI_IO_DS_8MA = 0,
HCI_IO_DS_4MA = 1, /* default */
HCI_IO_DS_6MA = 2,
HCI_IO_DS_2MA = 3,
};
/* end PLL configuration algorithm for wl128x */
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,728 @@
/*
* 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>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __CMD_H__
#define __CMD_H__
#include "wl12xx.h"
struct acx_header;
int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
size_t res_len);
int wl1271_cmd_general_parms(struct wl1271 *wl);
int wl128x_cmd_general_parms(struct wl1271 *wl);
int wl1271_cmd_radio_parms(struct wl1271 *wl);
int wl128x_cmd_radio_parms(struct wl1271 *wl);
int wl1271_cmd_ext_radio_parms(struct wl1271 *wl);
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);
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 len);
int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
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);
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);
int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid);
int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id);
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, u8 hlid);
int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
int wl12xx_cmd_start_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);
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);
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_NOP = 48,
CMD_LAST_COMMAND,
MAX_COMMAND_ID = 0xFFFF,
};
#define MAX_CMD_PARAMS 572
enum {
CMD_TEMPL_KLV_IDX_NULL_DATA = 0,
CMD_TEMPL_KLV_IDX_MAX = 4
};
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_PROBE_REQ_2_4, /* for firmware internal use only */
CMD_TEMPL_PROBE_REQ_5, /* for firmware internal use only */
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_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 750
struct wl1271_cmd_header {
__le16 id;
__le16 status;
/* payload */
u8 data[0];
} __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 = 0xff
};
#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 wl12xx_band {
WL12XX_BAND_2_4GHZ = 0,
WL12XX_BAND_5GHZ = 1,
WL12XX_BAND_JAPAN_4_9_GHZ = 2,
WL12XX_BAND_DEFAULT = WL12XX_BAND_2_4GHZ,
WL12XX_BAND_INVALID = 0x7E,
WL12XX_BAND_MAX_RADIO = 0x7F,
};
struct wl12xx_cmd_role_start {
struct wl1271_cmd_header header;
u8 role_id;
u8 band;
u8 channel;
u8 padding;
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;
u8 padding_1[4];
} __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,
};
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
#define TEST_CMD_INI_FILE_RADIO_PARAM 0x19
#define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E
#define TEST_CMD_INI_FILE_RF_EXTENDED_PARAM 0x26
struct wl1271_general_parms_cmd {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
struct wl1271_ini_general_params general_params;
u8 sr_debug_table[WL1271_INI_MAX_SMART_REFLEX_PARAM];
u8 sr_sen_n_p;
u8 sr_sen_n_p_gain;
u8 sr_sen_nrn;
u8 sr_sen_prn;
u8 padding[3];
} __packed;
struct wl128x_general_parms_cmd {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
struct wl128x_ini_general_params general_params;
u8 sr_debug_table[WL1271_INI_MAX_SMART_REFLEX_PARAM];
u8 sr_sen_n_p;
u8 sr_sen_n_p_gain;
u8 sr_sen_nrn;
u8 sr_sen_prn;
u8 padding[3];
} __packed;
struct wl1271_radio_parms_cmd {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
/* Static radio parameters */
struct wl1271_ini_band_params_2 static_params_2;
struct wl1271_ini_band_params_5 static_params_5;
/* Dynamic radio parameters */
struct wl1271_ini_fem_params_2 dyn_params_2;
u8 padding2;
struct wl1271_ini_fem_params_5 dyn_params_5;
u8 padding3[2];
} __packed;
struct wl128x_radio_parms_cmd {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
/* Static radio parameters */
struct wl128x_ini_band_params_2 static_params_2;
struct wl128x_ini_band_params_5 static_params_5;
u8 fem_vendor_and_options;
/* Dynamic radio parameters */
struct wl128x_ini_fem_params_2 dyn_params_2;
u8 padding2;
struct wl128x_ini_fem_params_5 dyn_params_5;
} __packed;
struct wl1271_ext_radio_parms_cmd {
struct wl1271_cmd_header header;
struct wl1271_cmd_test_header test;
u8 tx_per_channel_power_compensation_2[CONF_TX_PWR_COMPENSATION_LEN_2];
u8 tx_per_channel_power_compensation_5[CONF_TX_PWR_COMPENSATION_LEN_5];
u8 padding[3];
} __packed;
/*
* 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;
u8 padding[2];
} __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 padding1;
} __packed;
struct wl12xx_cmd_remove_peer {
struct wl1271_cmd_header header;
u8 hlid;
u8 reason_opcode;
u8 send_deauth_flag;
u8 padding1;
} __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,
WL12XX_FWLOG_ON_DEMAND
};
/* 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_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_channel_switch {
struct wl1271_cmd_header header;
u8 role_id;
/* The new serving channel */
u8 channel;
/* Relative time of the serving channel switch in TBTT units */
u8 switch_time;
/* Stop the role TX, should expect it after radar detection */
u8 stop_tx;
/* The target channel tx status 1-stopped 0-open*/
u8 post_switch_tx_disable;
u8 padding[3];
} __packed;
struct wl12xx_cmd_stop_channel_switch {
struct wl1271_cmd_header header;
} __packed;
#endif /* __WL1271_CMD_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,102 @@
/*
* 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>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include <linux/bitops.h>
#include <linux/printk.h>
#define DRIVER_NAME "wl12xx"
#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_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_warning(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 wl1271_debug(level, fmt, arg...) \
do { \
if (level & wl12xx_debug_level) \
pr_debug(DRIVER_PREFIX fmt "\n", ##arg); \
} while (0)
/* TODO: use pr_debug_hex_dump when it becomes available */
#define wl1271_dump(level, prefix, buf, len) \
do { \
if (level & wl12xx_debug_level) \
print_hex_dump(KERN_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(KERN_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

View File

@@ -0,0 +1,33 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __DEBUGFS_H__
#define __DEBUGFS_H__
#include "wl12xx.h"
int wl1271_debugfs_init(struct wl1271 *wl);
void wl1271_debugfs_exit(struct wl1271 *wl);
void wl1271_debugfs_reset(struct wl1271 *wl);
#endif /* WL1271_DEBUGFS_H */

View File

@@ -0,0 +1,313 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "wl12xx.h"
#include "debug.h"
#include "reg.h"
#include "io.h"
#include "event.h"
#include "ps.h"
#include "scan.h"
#include "wl12xx_80211.h"
static void wl1271_event_rssi_trigger(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct event_mailbox *mbox)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
enum nl80211_cqm_rssi_threshold_event event;
s8 metric = mbox->rssi_snr_trigger_metric[0];
wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
if (metric <= wlvif->rssi_thold)
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
else
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
if (event != wlvif->last_rssi_event)
ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
wlvif->last_rssi_event = event;
}
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) {
if (!wlvif->sta.ba_rx_bitmap)
return;
ieee80211_stop_rx_ba_session(vif, wlvif->sta.ba_rx_bitmap,
vif->bss_conf.bssid);
} else {
u8 hlid;
struct wl1271_link *lnk;
for_each_set_bit(hlid, wlvif->ap.sta_hlid_map,
WL12XX_MAX_LINKS) {
lnk = &wl->links[hlid];
if (!lnk->ba_bitmap)
continue;
ieee80211_stop_rx_ba_session(vif,
lnk->ba_bitmap,
lnk->addr);
}
}
}
static void wl12xx_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);
}
}
}
static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
{
wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector);
wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
}
static int wl1271_event_process(struct wl1271 *wl)
{
struct event_mailbox *mbox = wl->mbox;
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
u32 vector;
bool beacon_loss = false;
bool disconnect_sta = false;
unsigned long sta_bitmap = 0;
wl1271_event_mbox_dump(mbox);
vector = le32_to_cpu(mbox->events_vector);
vector &= ~(le32_to_cpu(mbox->events_mask));
wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
if (vector & SCAN_COMPLETE_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "status: 0x%x",
mbox->scheduled_scan_status);
wl1271_scan_stm(wl, wl->scan_vif);
}
if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT "
"(status 0x%0x)", mbox->scheduled_scan_status);
wl1271_scan_sched_scan_results(wl);
}
if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT "
"(status 0x%0x)", mbox->scheduled_scan_status);
if (wl->sched_scanning) {
ieee80211_sched_scan_stopped(wl->hw);
wl->sched_scanning = false;
}
}
if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
wl12xx_event_soft_gemini_sense(wl,
mbox->soft_gemini_sense_info);
/*
* The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon
* filtering) is enabled. Without PSM, the stack will receive all
* beacons and can detect beacon loss by itself.
*
* As there's possibility that the driver disables PSM before receiving
* BSS_LOSE_EVENT, beacon loss has to be reported to the stack.
*
*/
if (vector & BSS_LOSE_EVENT_ID) {
/* TODO: check for multi-role */
wl1271_info("Beacon loss detected.");
/* indicate to the stack, that beacons have been lost */
beacon_loss = true;
}
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
/* TODO: check actual multi-role support */
wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
wl12xx_for_each_wlvif_sta(wl, wlvif) {
wl1271_event_rssi_trigger(wl, wlvif, mbox);
}
}
if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) {
u8 role_id = mbox->role_id;
wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. "
"ba_allowed = 0x%x, role_id=%d",
mbox->rx_ba_allowed, role_id);
wl12xx_for_each_wlvif(wl, wlvif) {
if (role_id != 0xff && role_id != wlvif->role_id)
continue;
wlvif->ba_allowed = !!mbox->rx_ba_allowed;
if (!wlvif->ba_allowed)
wl1271_stop_ba_event(wl, wlvif);
}
}
if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. "
"status = 0x%x",
mbox->channel_switch_status);
/*
* That event uses for two cases:
* 1) channel switch complete with status=0
* 2) channel switch failed status=1
*/
/* TODO: configure only the relevant vif */
wl12xx_for_each_wlvif_sta(wl, wlvif) {
bool success;
if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
&wlvif->flags))
continue;
success = mbox->channel_switch_status ? false : true;
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_chswitch_done(vif, success);
}
}
if ((vector & DUMMY_PACKET_EVENT_ID)) {
wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
wl1271_tx_dummy_packet(wl);
}
/*
* "TX retries exceeded" has a different meaning according to mode.
* In AP mode the offending station is disconnected.
*/
if (vector & MAX_TX_RETRY_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID");
sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded);
disconnect_sta = true;
}
if (vector & INACTIVE_STA_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
sta_bitmap |= le16_to_cpu(mbox->sta_aging_status);
disconnect_sta = true;
}
if (disconnect_sta) {
u32 num_packets = wl->conf.tx.max_tx_retries;
struct ieee80211_sta *sta;
const u8 *addr;
int h;
for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_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();
}
}
if (beacon_loss)
wl12xx_for_each_wlvif_sta(wl, wlvif) {
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_connection_loss(vif);
}
return 0;
}
int wl1271_event_unmask(struct wl1271 *wl)
{
int ret;
ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask));
if (ret < 0)
return ret;
return 0;
}
void wl1271_event_mbox_config(struct wl1271 *wl)
{
wl->mbox_ptr[0] = wl1271_read32(wl, REG_EVENT_MAILBOX_PTR);
wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
wl1271_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x",
wl->mbox_ptr[0], wl->mbox_ptr[1]);
}
int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
{
int ret;
wl1271_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num);
if (mbox_num > 1)
return -EINVAL;
/* first we read the mbox descriptor */
wl1271_read(wl, wl->mbox_ptr[mbox_num], wl->mbox,
sizeof(*wl->mbox), false);
/* process the descriptor */
ret = wl1271_event_process(wl);
if (ret < 0)
return ret;
/* then we let the firmware know it can go on...*/
wl1271_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK);
return 0;
}

View File

@@ -0,0 +1,141 @@
/*
* 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>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __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),
MEASUREMENT_START_EVENT_ID = BIT(8),
MEASUREMENT_COMPLETE_EVENT_ID = BIT(9),
SCAN_COMPLETE_EVENT_ID = BIT(10),
WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11),
AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12),
RESERVED1 = BIT(13),
PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14),
ROLE_STOP_COMPLETE_EVENT_ID = BIT(15),
RADAR_DETECTED_EVENT_ID = BIT(16),
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17),
BSS_LOSE_EVENT_ID = BIT(18),
REGAINED_BSS_EVENT_ID = BIT(19),
MAX_TX_RETRY_EVENT_ID = BIT(20),
DUMMY_PACKET_EVENT_ID = BIT(21),
SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID = BIT(23),
SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25),
INACTIVE_STA_EVENT_ID = BIT(26),
PEER_REMOVE_COMPLETE_EVENT_ID = BIT(27),
PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28),
PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29),
BA_SESSION_RX_CONSTRAINT_EVENT_ID = BIT(30),
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(31),
EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff,
};
enum {
EVENT_ENTER_POWER_SAVE_FAIL = 0,
EVENT_ENTER_POWER_SAVE_SUCCESS,
};
#define NUM_OF_RSSI_SNR_TRIGGERS 8
struct event_mailbox {
__le32 events_vector;
__le32 events_mask;
__le32 reserved_1;
__le32 reserved_2;
u8 number_of_scan_results;
u8 scan_tag;
u8 completed_scan_status;
u8 reserved_3;
u8 soft_gemini_sense_info;
u8 soft_gemini_protective_info;
s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
u8 change_auto_mode_timeout;
u8 scheduled_scan_status;
u8 reserved4;
/* tuned channel (roc) */
u8 roc_channel;
__le16 hlid_removed_bitmap;
/* bitmap of aged stations (by HLID) */
__le16 sta_aging_status;
/* bitmap of stations (by HLID) which exceeded max tx retries */
__le16 sta_tx_retry_exceeded;
/* discovery completed results */
u8 discovery_tag;
u8 number_of_preq_results;
u8 number_of_prsp_results;
u8 reserved_5;
/* rx ba constraint */
u8 role_id; /* 0xFF means any role. */
u8 rx_ba_allowed;
u8 reserved_6[2];
/* Channel switch results */
u8 channel_switch_role_id;
u8 channel_switch_status;
u8 reserved_7[2];
u8 ps_poll_delivery_failure_role_ids;
u8 stopped_role_ids;
u8 started_role_ids;
u8 reserved_8[9];
} __packed;
struct wl1271;
int wl1271_event_unmask(struct wl1271 *wl);
void wl1271_event_mbox_config(struct wl1271 *wl);
int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
#endif

View File

@@ -0,0 +1,220 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __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
#define WL1271_INI_FEM_MODULE_COUNT 2
#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[WL1271_INI_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[WL1271_INI_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[WL1271_INI_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[WL1271_INI_FEM_MODULE_COUNT];
} __packed;
#endif

View File

@@ -0,0 +1,765 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/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 "reg.h"
#include "tx.h"
#include "io.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,
CMD_TEMPL_CFG_PROBE_REQ_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,
CMD_TEMPL_CFG_PROBE_REQ_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 < CMD_TEMPL_KLV_IDX_MAX; 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(sizeof(*tmpl), GFP_KERNEL);
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(sizeof(*nullfunc), GFP_KERNEL);
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(sizeof(*qosnull), GFP_KERNEL);
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;
/* enable beacon filtering */
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
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 & WL12XX_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) */
static 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, i;
/* disable all keep-alive templates */
for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
ret = wl1271_acx_keep_alive_config(wl, wlvif, i,
ACX_KEEP_ALIVE_TPL_INVALID);
if (ret < 0)
return 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;
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 ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
supported_rates = CONF_TX_OFDM_RATES;
else
supported_rates = CONF_TX_AP_ENABLED_RATES;
/* unconditionally enable HT rates */
supported_rates |= CONF_TX_MCS_RATES;
/* 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);
}
int wl1271_chip_specific_init(struct wl1271 *wl)
{
int ret = 0;
if (wl->chip.id == CHIP_ID_1283_PG20) {
u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
if (!(wl->quirks & WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT))
/* Enable SDIO padding */
host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
/* Must be before wl1271_acx_init_mem_config() */
ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap);
if (ret < 0)
goto out;
}
out:
return ret;
}
/* 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 intialization */
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;
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.
* TODO: reconfigure on interface removal.
*/
if (!wl->ap_count) {
if (is_ap) {
/* Configure for power always on */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
if (ret < 0)
return ret;
} else if (!wl->sta_count) {
/* Configure for ELP power saving */
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;
return 0;
}
int wl1271_hw_init(struct wl1271 *wl)
{
int ret;
if (wl->chip.id == CHIP_ID_1283_PG20) {
ret = wl128x_cmd_general_parms(wl);
if (ret < 0)
return ret;
ret = wl128x_cmd_radio_parms(wl);
if (ret < 0)
return ret;
} else {
ret = wl1271_cmd_general_parms(wl);
if (ret < 0)
return ret;
ret = wl1271_cmd_radio_parms(wl);
if (ret < 0)
return ret;
ret = wl1271_cmd_ext_radio_parms(wl);
if (ret < 0)
return ret;
}
/* Chip-specific init */
ret = wl1271_chip_specific_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;
/* 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;
}

View File

@@ -0,0 +1,39 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __INIT_H__
#define __INIT_H__
#include "wl12xx.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);
#endif

View File

@@ -0,0 +1,244 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/interrupt.h>
#include "wl12xx.h"
#include "debug.h"
#include "wl12xx_80211.h"
#include "io.h"
#include "tx.h"
#define OCP_CMD_LOOP 32
#define OCP_CMD_WRITE 0x1
#define OCP_CMD_READ 0x2
#define OCP_READY_MASK BIT(18)
#define OCP_STATUS_MASK (BIT(16) | BIT(17))
#define OCP_STATUS_NO_RESP 0x00000
#define OCP_STATUS_OK 0x10000
#define OCP_STATUS_REQ_FAILED 0x20000
#define OCP_STATUS_RESP_ERROR 0x30000
struct wl1271_partition_set wl12xx_part_table[PART_TABLE_LEN] = {
[PART_DOWN] = {
.mem = {
.start = 0x00000000,
.size = 0x000177c0
},
.reg = {
.start = REGISTERS_BASE,
.size = 0x00008800
},
.mem2 = {
.start = 0x00000000,
.size = 0x00000000
},
.mem3 = {
.start = 0x00000000,
.size = 0x00000000
},
},
[PART_WORK] = {
.mem = {
.start = 0x00040000,
.size = 0x00014fc0
},
.reg = {
.start = REGISTERS_BASE,
.size = 0x0000a000
},
.mem2 = {
.start = 0x003004f8,
.size = 0x00000004
},
.mem3 = {
.start = 0x00040404,
.size = 0x00000000
},
},
[PART_DRPW] = {
.mem = {
.start = 0x00040000,
.size = 0x00014fc0
},
.reg = {
.start = DRPW_BASE,
.size = 0x00006000
},
.mem2 = {
.start = 0x00000000,
.size = 0x00000000
},
.mem3 = {
.start = 0x00000000,
.size = 0x00000000
}
}
};
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 wl1271_disable_interrupts(struct wl1271 *wl)
{
disable_irq(wl->irq);
}
void wl1271_enable_interrupts(struct wl1271 *wl)
{
enable_irq(wl->irq);
}
/* Set the SPI partitions to access the chip addresses
*
* To simplify driver code, a fixed (virtual) memory map is defined for
* register and memory addresses. Because in the chipset, in different stages
* of operation, those addresses will move around, an address translation
* mechanism is required.
*
* There are four partitions (three memory and one register partition),
* which are mapped to two different areas of the hardware memory.
*
* Virtual address
* space
*
* | |
* ...+----+--> mem.start
* Physical address ... | |
* space ... | | [PART_0]
* ... | |
* 00000000 <--+----+... ...+----+--> mem.start + mem.size
* | | ... | |
* |MEM | ... | |
* | | ... | |
* mem.size <--+----+... | | {unused area)
* | | ... | |
* |REG | ... | |
* mem.size | | ... | |
* + <--+----+... ...+----+--> reg.start
* reg.size | | ... | |
* |MEM2| ... | | [PART_1]
* | | ... | |
* ...+----+--> reg.start + reg.size
* | |
*
*/
int wl1271_set_partition(struct wl1271 *wl,
struct wl1271_partition_set *p)
{
/* copy partition info */
memcpy(&wl->part, p, sizeof(*p));
wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
p->mem.start, p->mem.size);
wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
p->reg.start, p->reg.size);
wl1271_debug(DEBUG_SPI, "mem2_start %08X mem2_size %08X",
p->mem2.start, p->mem2.size);
wl1271_debug(DEBUG_SPI, "mem3_start %08X mem3_size %08X",
p->mem3.start, p->mem3.size);
/* write partition info to the chipset */
wl1271_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start);
wl1271_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size);
wl1271_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start);
wl1271_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size);
wl1271_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start);
wl1271_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size);
wl1271_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start);
return 0;
}
EXPORT_SYMBOL_GPL(wl1271_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);
}
void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val)
{
/* write address >> 1 + 0x30000 to OCP_POR_CTR */
addr = (addr >> 1) + 0x30000;
wl1271_write32(wl, OCP_POR_CTR, addr);
/* write value to OCP_POR_WDATA */
wl1271_write32(wl, OCP_DATA_WRITE, val);
/* write 1 to OCP_CMD */
wl1271_write32(wl, OCP_CMD, OCP_CMD_WRITE);
}
u16 wl1271_top_reg_read(struct wl1271 *wl, int addr)
{
u32 val;
int timeout = OCP_CMD_LOOP;
/* write address >> 1 + 0x30000 to OCP_POR_CTR */
addr = (addr >> 1) + 0x30000;
wl1271_write32(wl, OCP_POR_CTR, addr);
/* write 2 to OCP_CMD */
wl1271_write32(wl, OCP_CMD, OCP_CMD_READ);
/* poll for data ready */
do {
val = wl1271_read32(wl, OCP_DATA_READ);
} while (!(val & OCP_READY_MASK) && --timeout);
if (!timeout) {
wl1271_warning("Top register access timed out.");
return 0xffff;
}
/* check data status and return if OK */
if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK)
return val & 0xffff;
else {
wl1271_warning("Top register access returned error.");
return 0xffff;
}
}

View File

@@ -0,0 +1,181 @@
/*
* This file is part of wl1271
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2008-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __IO_H__
#define __IO_H__
#include <linux/irqreturn.h>
#include "reg.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_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 24)
#define HW_ACCESS_REGISTER_SIZE 4
#define HW_ACCESS_PRAM_MAX_RANGE 0x3c000
extern struct wl1271_partition_set wl12xx_part_table[PART_TABLE_LEN];
struct wl1271;
void wl1271_disable_interrupts(struct wl1271 *wl);
void wl1271_enable_interrupts(struct wl1271 *wl);
void wl1271_io_reset(struct wl1271 *wl);
void wl1271_io_init(struct wl1271 *wl);
/* Raw target IO, address is not translated */
static inline void wl1271_raw_write(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed)
{
wl->if_ops->write(wl->dev, addr, buf, len, fixed);
}
static inline void wl1271_raw_read(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed)
{
wl->if_ops->read(wl->dev, addr, buf, len, fixed);
}
static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr)
{
wl1271_raw_read(wl, addr, &wl->buffer_32,
sizeof(wl->buffer_32), false);
return le32_to_cpu(wl->buffer_32);
}
static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val)
{
wl->buffer_32 = cpu_to_le32(val);
wl1271_raw_write(wl, addr, &wl->buffer_32,
sizeof(wl->buffer_32), false);
}
/* Translated target IO */
static inline int wl1271_translate_addr(struct wl1271 *wl, int addr)
{
/*
* To translate, first check to which window of addresses the
* particular address belongs. Then subtract the starting address
* of that window from the address. Then, add offset of the
* translated region.
*
* The translated regions occur next to each other in physical device
* memory, so just add the sizes of the preceding address regions to
* get the offset to the new region.
*
* Currently, only the two first regions are addressed, and the
* assumption is that all addresses will fall into either of those
* two.
*/
if ((addr >= wl->part.reg.start) &&
(addr < wl->part.reg.start + wl->part.reg.size))
return addr - wl->part.reg.start + wl->part.mem.size;
else
return addr - wl->part.mem.start;
}
static inline void wl1271_read(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed)
{
int physical;
physical = wl1271_translate_addr(wl, addr);
wl1271_raw_read(wl, physical, buf, len, fixed);
}
static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed)
{
int physical;
physical = wl1271_translate_addr(wl, addr);
wl1271_raw_write(wl, physical, buf, len, fixed);
}
static inline void wl1271_read_hwaddr(struct wl1271 *wl, int hwaddr,
void *buf, size_t len, bool fixed)
{
int physical;
int addr;
/* Addresses are stored internally as addresses to 32 bytes blocks */
addr = hwaddr << 5;
physical = wl1271_translate_addr(wl, addr);
wl1271_raw_read(wl, physical, buf, len, fixed);
}
static inline u32 wl1271_read32(struct wl1271 *wl, int addr)
{
return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr));
}
static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val)
{
wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val);
}
static inline void wl1271_power_off(struct wl1271 *wl)
{
wl->if_ops->power(wl->dev, false);
clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
}
static inline int wl1271_power_on(struct wl1271 *wl)
{
int ret = wl->if_ops->power(wl->dev, true);
if (ret == 0)
set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
return ret;
}
/* Top Register IO */
void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val);
u16 wl1271_top_reg_read(struct wl1271 *wl, int addr);
int wl1271_set_partition(struct wl1271 *wl,
struct wl1271_partition_set *p);
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

View File

@@ -0,0 +1,304 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "reg.h"
#include "ps.h"
#include "io.h"
#include "tx.h"
#include "debug.h"
#define WL1271_WAKEUP_TIMEOUT 500
void wl1271_elp_work(struct work_struct *work)
{
struct delayed_work *dwork;
struct wl1271 *wl;
struct wl12xx_vif *wlvif;
dwork = container_of(work, struct delayed_work, work);
wl = container_of(dwork, struct wl1271, elp_work);
wl1271_debug(DEBUG_PSM, "elp work");
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF))
goto out;
/* our work might have been already cancelled */
if (unlikely(!test_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)))
goto out;
if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags))
goto out;
wl12xx_for_each_wlvif(wl, wlvif) {
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
goto out;
if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) &&
test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
goto out;
}
wl1271_debug(DEBUG_PSM, "chip to elp");
wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
set_bit(WL1271_FLAG_IN_ELP, &wl->flags);
out:
mutex_unlock(&wl->mutex);
}
/* Routines to toggle sleep mode while in ELP */
void wl1271_ps_elp_sleep(struct wl1271 *wl)
{
struct wl12xx_vif *wlvif;
/* we shouldn't get consecutive sleep requests */
if (WARN_ON(test_and_set_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)))
return;
wl12xx_for_each_wlvif(wl, wlvif) {
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
return;
if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) &&
test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
return;
}
ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
msecs_to_jiffies(wl->conf.conn.dynamic_ps_timeout));
}
int wl1271_ps_elp_wakeup(struct wl1271 *wl)
{
DECLARE_COMPLETION_ONSTACK(compl);
unsigned long flags;
int ret;
u32 start_time = jiffies;
bool pending = false;
/*
* we might try to wake up even if we didn't go to sleep
* before (e.g. on boot)
*/
if (!test_and_clear_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags))
return 0;
/* don't cancel_sync as it might contend for a mutex and deadlock */
cancel_delayed_work(&wl->elp_work);
if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags))
return 0;
wl1271_debug(DEBUG_PSM, "waking up chip from elp");
/*
* The spinlock is required here to synchronize both the work and
* the completion variable in one entity.
*/
spin_lock_irqsave(&wl->wl_lock, flags);
if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags))
pending = true;
else
wl->elp_compl = &compl;
spin_unlock_irqrestore(&wl->wl_lock, flags);
wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
if (!pending) {
ret = wait_for_completion_timeout(
&compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT));
if (ret == 0) {
wl1271_error("ELP wakeup timeout!");
wl12xx_queue_recovery_work(wl);
ret = -ETIMEDOUT;
goto err;
} else if (ret < 0) {
wl1271_error("ELP wakeup completion error.");
goto err;
}
}
clear_bit(WL1271_FLAG_IN_ELP, &wl->flags);
wl1271_debug(DEBUG_PSM, "wakeup time: %u ms",
jiffies_to_msecs(jiffies - start_time));
goto out;
err:
spin_lock_irqsave(&wl->wl_lock, flags);
wl->elp_compl = NULL;
spin_unlock_irqrestore(&wl->wl_lock, flags);
return ret;
out:
return 0;
}
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 */
if (wlvif->band == IEEE80211_BAND_2GHZ) {
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 == IEEE80211_BAND_2GHZ) {
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];
/* 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(&wl->links[hlid].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];
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 (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();
}

View File

@@ -0,0 +1,41 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __PS_H__
#define __PS_H__
#include "wl12xx.h"
#include "acx.h"
int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum wl1271_cmd_ps_mode mode);
void wl1271_ps_elp_sleep(struct wl1271 *wl);
int wl1271_ps_elp_wakeup(struct wl1271 *wl);
void wl1271_elp_work(struct work_struct *work);
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__ */

View File

@@ -0,0 +1,555 @@
/*
* This file is part of wl12xx
*
* Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __REG_H__
#define __REG_H__
#include <linux/bitops.h>
#define REGISTERS_BASE 0x00300000
#define DRPW_BASE 0x00310000
#define REGISTERS_DOWN_SIZE 0x00008800
#define REGISTERS_WORK_SIZE 0x0000b000
#define HW_ACCESS_ELP_CTRL_REG_ADDR 0x1FFFC
#define FW_STATUS_ADDR (0x14FC0 + 0xA000)
/* 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
/*===============================================
Host Software Reset - 32bit RW
------------------------------------------
[31:1] Reserved
0 SOFT_RESET Soft Reset - When this bit is set,
it holds the Wlan hardware in a soft reset state.
This reset disables all MAC and baseband processor
clocks except the CardBus/PCI interface clock.
It also initializes all MAC state machines except
the host interface. It does not reload the
contents of the EEPROM. When this bit is cleared
(not self-clearing), the Wlan hardware
exits the software reset state.
===============================================*/
#define ACX_REG_SLV_SOFT_RESET (REGISTERS_BASE + 0x0000)
#define WL1271_SLV_REG_DATA (REGISTERS_BASE + 0x0008)
#define WL1271_SLV_REG_ADATA (REGISTERS_BASE + 0x000c)
#define WL1271_SLV_MEM_DATA (REGISTERS_BASE + 0x0018)
#define ACX_REG_INTERRUPT_TRIG (REGISTERS_BASE + 0x0474)
#define ACX_REG_INTERRUPT_TRIG_H (REGISTERS_BASE + 0x0478)
/*=============================================
Host Interrupt Mask Register - 32bit (RW)
------------------------------------------
Setting a bit in this register masks the
corresponding interrupt to the host.
0 - RX0 - Rx first dubble buffer Data Interrupt
1 - TXD - Tx Data Interrupt
2 - TXXFR - Tx Transfer Interrupt
3 - RX1 - Rx second dubble buffer Data Interrupt
4 - RXXFR - Rx Transfer Interrupt
5 - EVENT_A - Event Mailbox interrupt
6 - EVENT_B - Event Mailbox interrupt
7 - WNONHST - Wake On Host Interrupt
8 - TRACE_A - Debug Trace interrupt
9 - TRACE_B - Debug Trace interrupt
10 - CDCMP - Command Complete Interrupt
11 -
12 -
13 -
14 - ICOMP - Initialization Complete Interrupt
16 - SG SE - Soft Gemini - Sense enable interrupt
17 - SG SD - Soft Gemini - Sense disable interrupt
18 - -
19 - -
20 - -
21- -
Default: 0x0001
*==============================================*/
#define ACX_REG_INTERRUPT_MASK (REGISTERS_BASE + 0x04DC)
/*=============================================
Host Interrupt Mask Set 16bit, (Write only)
------------------------------------------
Setting a bit in this register sets
the corresponding bin in ACX_HINT_MASK register
without effecting the mask
state of other bits (0 = no effect).
==============================================*/
#define ACX_REG_HINT_MASK_SET (REGISTERS_BASE + 0x04E0)
/*=============================================
Host Interrupt Mask Clear 16bit,(Write only)
------------------------------------------
Setting a bit in this register clears
the corresponding bin in ACX_HINT_MASK register
without effecting the mask
state of other bits (0 = no effect).
=============================================*/
#define ACX_REG_HINT_MASK_CLR (REGISTERS_BASE + 0x04E4)
/*=============================================
Host Interrupt Status Nondestructive Read
16bit,(Read only)
------------------------------------------
The host can read this register to determine
which interrupts are active.
Reading this register doesn't
effect its content.
=============================================*/
#define ACX_REG_INTERRUPT_NO_CLEAR (REGISTERS_BASE + 0x04E8)
/*=============================================
Host Interrupt Status Clear on Read Register
16bit,(Read only)
------------------------------------------
The host can read this register to determine
which interrupts are active.
Reading this register clears it,
thus making all interrupts inactive.
==============================================*/
#define ACX_REG_INTERRUPT_CLEAR (REGISTERS_BASE + 0x04F8)
/*=============================================
Host Interrupt Acknowledge Register
16bit,(Write only)
------------------------------------------
The host can set individual bits in this
register to clear (acknowledge) the corresp.
interrupt status bits in the HINT_STS_CLR and
HINT_STS_ND registers, thus making the
assotiated interrupt inactive. (0-no effect)
==============================================*/
#define ACX_REG_INTERRUPT_ACK (REGISTERS_BASE + 0x04F0)
#define RX_DRIVER_COUNTER_ADDRESS (REGISTERS_BASE + 0x0538)
/* Device Configuration registers*/
#define SOR_CFG (REGISTERS_BASE + 0x0800)
/* Embedded ARM CPU Control */
/*===============================================
Halt eCPU - 32bit RW
------------------------------------------
0 HALT_ECPU Halt Embedded CPU - This bit is the
compliment of bit 1 (MDATA2) in the SOR_CFG register.
During a hardware reset, this bit holds
the inverse of MDATA2.
When downloading firmware from the host,
set this bit (pull down MDATA2).
The host clears this bit after downloading the firmware into
zero-wait-state SSRAM.
When loading firmware from Flash, clear this bit (pull up MDATA2)
so that the eCPU can run the bootloader code in Flash
HALT_ECPU eCPU State
--------------------
1 halt eCPU
0 enable eCPU
===============================================*/
#define ACX_REG_ECPU_CONTROL (REGISTERS_BASE + 0x0804)
#define HI_CFG (REGISTERS_BASE + 0x0808)
/*===============================================
EEPROM Burst Read Start - 32bit RW
------------------------------------------
[31:1] Reserved
0 ACX_EE_START - EEPROM Burst Read Start 0
Setting this bit starts a burst read from
the external EEPROM.
If this bit is set (after reset) before an EEPROM read/write,
the burst read starts at EEPROM address 0.
Otherwise, it starts at the address
following the address of the previous access.
TheWlan hardware hardware clears this bit automatically.
Default: 0x00000000
*================================================*/
#define ACX_REG_EE_START (REGISTERS_BASE + 0x080C)
#define OCP_POR_CTR (REGISTERS_BASE + 0x09B4)
#define OCP_DATA_WRITE (REGISTERS_BASE + 0x09B8)
#define OCP_DATA_READ (REGISTERS_BASE + 0x09BC)
#define OCP_CMD (REGISTERS_BASE + 0x09C0)
#define WL1271_HOST_WR_ACCESS (REGISTERS_BASE + 0x09F8)
#define CHIP_ID_B (REGISTERS_BASE + 0x5674)
#define CHIP_ID_1271_PG10 (0x4030101)
#define CHIP_ID_1271_PG20 (0x4030111)
#define CHIP_ID_1283_PG10 (0x05030101)
#define CHIP_ID_1283_PG20 (0x05030111)
#define ENABLE (REGISTERS_BASE + 0x5450)
/* Power Management registers */
#define ELP_CFG_MODE (REGISTERS_BASE + 0x5804)
#define ELP_CMD (REGISTERS_BASE + 0x5808)
#define PLL_CAL_TIME (REGISTERS_BASE + 0x5810)
#define CLK_REQ_TIME (REGISTERS_BASE + 0x5814)
#define CLK_BUF_TIME (REGISTERS_BASE + 0x5818)
#define CFG_PLL_SYNC_CNT (REGISTERS_BASE + 0x5820)
/* Scratch Pad registers*/
#define SCR_PAD0 (REGISTERS_BASE + 0x5608)
#define SCR_PAD1 (REGISTERS_BASE + 0x560C)
#define SCR_PAD2 (REGISTERS_BASE + 0x5610)
#define SCR_PAD3 (REGISTERS_BASE + 0x5614)
#define SCR_PAD4 (REGISTERS_BASE + 0x5618)
#define SCR_PAD4_SET (REGISTERS_BASE + 0x561C)
#define SCR_PAD4_CLR (REGISTERS_BASE + 0x5620)
#define SCR_PAD5 (REGISTERS_BASE + 0x5624)
#define SCR_PAD5_SET (REGISTERS_BASE + 0x5628)
#define SCR_PAD5_CLR (REGISTERS_BASE + 0x562C)
#define SCR_PAD6 (REGISTERS_BASE + 0x5630)
#define SCR_PAD7 (REGISTERS_BASE + 0x5634)
#define SCR_PAD8 (REGISTERS_BASE + 0x5638)
#define SCR_PAD9 (REGISTERS_BASE + 0x563C)
/* Spare registers*/
#define SPARE_A1 (REGISTERS_BASE + 0x0994)
#define SPARE_A2 (REGISTERS_BASE + 0x0998)
#define SPARE_A3 (REGISTERS_BASE + 0x099C)
#define SPARE_A4 (REGISTERS_BASE + 0x09A0)
#define SPARE_A5 (REGISTERS_BASE + 0x09A4)
#define SPARE_A6 (REGISTERS_BASE + 0x09A8)
#define SPARE_A7 (REGISTERS_BASE + 0x09AC)
#define SPARE_A8 (REGISTERS_BASE + 0x09B0)
#define SPARE_B1 (REGISTERS_BASE + 0x5420)
#define SPARE_B2 (REGISTERS_BASE + 0x5424)
#define SPARE_B3 (REGISTERS_BASE + 0x5428)
#define SPARE_B4 (REGISTERS_BASE + 0x542C)
#define SPARE_B5 (REGISTERS_BASE + 0x5430)
#define SPARE_B6 (REGISTERS_BASE + 0x5434)
#define SPARE_B7 (REGISTERS_BASE + 0x5438)
#define SPARE_B8 (REGISTERS_BASE + 0x543C)
#define PLL_PARAMETERS (REGISTERS_BASE + 0x6040)
#define WU_COUNTER_PAUSE (REGISTERS_BASE + 0x6008)
#define WELP_ARM_COMMAND (REGISTERS_BASE + 0x6100)
#define DRPW_SCRATCH_START (DRPW_BASE + 0x002C)
#define ACX_SLV_SOFT_RESET_BIT BIT(1)
#define ACX_REG_EEPROM_START_BIT BIT(1)
/* Command/Information Mailbox Pointers */
/*===============================================
Command Mailbox Pointer - 32bit RW
------------------------------------------
This register holds the start address of
the command mailbox located in the Wlan hardware memory.
The host must read this pointer after a reset to
find the location of the command mailbox.
The Wlan hardware initializes the command mailbox
pointer with the default address of the command mailbox.
The command mailbox pointer is not valid until after
the host receives the Init Complete interrupt from
the Wlan hardware.
===============================================*/
#define REG_COMMAND_MAILBOX_PTR (SCR_PAD0)
/*===============================================
Information Mailbox Pointer - 32bit RW
------------------------------------------
This register holds the start address of
the information mailbox located in the Wlan hardware memory.
The host must read this pointer after a reset to find
the location of the information mailbox.
The Wlan hardware initializes the information mailbox pointer
with the default address of the information mailbox.
The information mailbox pointer is not valid
until after the host receives the Init Complete interrupt from
the Wlan hardware.
===============================================*/
#define REG_EVENT_MAILBOX_PTR (SCR_PAD1)
/*===============================================
EEPROM Read/Write Request 32bit RW
------------------------------------------
1 EE_READ - EEPROM Read Request 1 - Setting this bit
loads a single byte of data into the EE_DATA
register from the EEPROM location specified in
the EE_ADDR register.
The Wlan hardware hardware clears this bit automatically.
EE_DATA is valid when this bit is cleared.
0 EE_WRITE - EEPROM Write Request - Setting this bit
writes a single byte of data from the EE_DATA register into the
EEPROM location specified in the EE_ADDR register.
The Wlan hardware hardware clears this bit automatically.
*===============================================*/
#define ACX_EE_CTL_REG EE_CTL
#define EE_WRITE 0x00000001ul
#define EE_READ 0x00000002ul
/*===============================================
EEPROM Address - 32bit RW
------------------------------------------
This register specifies the address
within the EEPROM from/to which to read/write data.
===============================================*/
#define ACX_EE_ADDR_REG EE_ADDR
/*===============================================
EEPROM Data - 32bit RW
------------------------------------------
This register either holds the read 8 bits of
data from the EEPROM or the write data
to be written to the EEPROM.
===============================================*/
#define ACX_EE_DATA_REG EE_DATA
/*===============================================
EEPROM Base Address - 32bit RW
------------------------------------------
This register holds the upper nine bits
[23:15] of the 24-bit Wlan hardware memory
address for burst reads from EEPROM accesses.
The EEPROM provides the lower 15 bits of this address.
The MSB of the address from the EEPROM is ignored.
===============================================*/
#define ACX_EE_CFG EE_CFG
/*===============================================
GPIO Output Values -32bit, RW
------------------------------------------
[31:16] Reserved
[15: 0] Specify the output values (at the output driver inputs) for
GPIO[15:0], respectively.
===============================================*/
#define ACX_GPIO_OUT_REG GPIO_OUT
#define ACX_MAX_GPIO_LINES 15
/*===============================================
Contention window -32bit, RW
------------------------------------------
[31:26] Reserved
[25:16] Max (0x3ff)
[15:07] Reserved
[06:00] Current contention window value - default is 0x1F
===============================================*/
#define ACX_CONT_WIND_CFG_REG CONT_WIND_CFG
#define ACX_CONT_WIND_MIN_MASK 0x0000007f
#define ACX_CONT_WIND_MAX 0x03ff0000
/*===============================================
HI_CFG Interface Configuration Register Values
------------------------------------------
===============================================*/
#define HI_CFG_UART_ENABLE 0x00000004
#define HI_CFG_RST232_ENABLE 0x00000008
#define HI_CFG_CLOCK_REQ_SELECT 0x00000010
#define HI_CFG_HOST_INT_ENABLE 0x00000020
#define HI_CFG_VLYNQ_OUTPUT_ENABLE 0x00000040
#define HI_CFG_HOST_INT_ACTIVE_LOW 0x00000080
#define HI_CFG_UART_TX_OUT_GPIO_15 0x00000100
#define HI_CFG_UART_TX_OUT_GPIO_14 0x00000200
#define HI_CFG_UART_TX_OUT_GPIO_7 0x00000400
#define HI_CFG_DEF_VAL \
(HI_CFG_UART_ENABLE | \
HI_CFG_RST232_ENABLE | \
HI_CFG_CLOCK_REQ_SELECT | \
HI_CFG_HOST_INT_ENABLE)
#define REF_FREQ_19_2 0
#define REF_FREQ_26_0 1
#define REF_FREQ_38_4 2
#define REF_FREQ_40_0 3
#define REF_FREQ_33_6 4
#define REF_FREQ_NUM 5
#define LUT_PARAM_INTEGER_DIVIDER 0
#define LUT_PARAM_FRACTIONAL_DIVIDER 1
#define LUT_PARAM_ATTN_BB 2
#define LUT_PARAM_ALPHA_BB 3
#define LUT_PARAM_STOP_TIME_BB 4
#define LUT_PARAM_BB_PLL_LOOP_FILTER 5
#define LUT_PARAM_NUM 6
#define ACX_EEPROMLESS_IND_REG (SCR_PAD4)
#define USE_EEPROM 0
#define SOFT_RESET_MAX_TIME 1000000
#define SOFT_RESET_STALL_TIME 1000
#define NVS_DATA_BUNDARY_ALIGNMENT 4
/* Firmware image load chunk size */
#define CHUNK_SIZE 16384
/* Firmware image header size */
#define FW_HDR_SIZE 8
#define ECPU_CONTROL_HALT 0x00000101
/******************************************************************************
CHANNELS, BAND & REG DOMAINS definitions
******************************************************************************/
enum {
RADIO_BAND_2_4GHZ = 0, /* 2.4 Ghz band */
RADIO_BAND_5GHZ = 1, /* 5 Ghz band */
RADIO_BAND_JAPAN_4_9_GHZ = 2,
DEFAULT_BAND = RADIO_BAND_2_4GHZ,
INVALID_BAND = 0xFE,
MAX_RADIO_BANDS = 0xFF
};
#define SHORT_PREAMBLE_BIT BIT(0) /* CCK or Barker depending on the rate */
#define OFDM_RATE_BIT BIT(6)
#define PBCC_RATE_BIT BIT(7)
enum {
CCK_LONG = 0,
CCK_SHORT = SHORT_PREAMBLE_BIT,
PBCC_LONG = PBCC_RATE_BIT,
PBCC_SHORT = PBCC_RATE_BIT | SHORT_PREAMBLE_BIT,
OFDM = OFDM_RATE_BIT
};
/******************************************************************************
Transmit-Descriptor RATE-SET field definitions...
Define a new "Rate-Set" for TX path that incorporates the
Rate & Modulation info into a single 16-bit field.
TxdRateSet_t:
b15 - Indicates Preamble type (1=SHORT, 0=LONG).
Notes:
Must be LONG (0) for 1Mbps rate.
Does not apply (set to 0) for RevG-OFDM rates.
b14 - Indicates PBCC encoding (1=PBCC, 0=not).
Notes:
Does not apply (set to 0) for rates 1 and 2 Mbps.
Does not apply (set to 0) for RevG-OFDM rates.
b13 - Unused (set to 0).
b12-b0 - Supported Rate indicator bits as defined below.
******************************************************************************/
/*************************************************************************
Interrupt Trigger Register (Host -> WiLink)
**************************************************************************/
/* Hardware to Embedded CPU Interrupts - first 32-bit register set */
/*
* Host Command Interrupt. Setting this bit masks
* the interrupt that the host issues to inform
* the FW that it has sent a command
* to the Wlan hardware Command Mailbox.
*/
#define INTR_TRIG_CMD BIT(0)
/*
* Host Event Acknowlegde Interrupt. The host
* sets this bit to acknowledge that it received
* the unsolicited information from the event
* mailbox.
*/
#define INTR_TRIG_EVENT_ACK BIT(1)
/*
* 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 WL127X_REG_FUSE_DATA_2_1 0x050a
#define WL128X_REG_FUSE_DATA_2_1 0x2152
#define PG_VER_MASK 0x3c
#define PG_VER_OFFSET 2
#define WL127X_PG_MAJOR_VER_MASK 0x3
#define WL127X_PG_MAJOR_VER_OFFSET 0x0
#define WL127X_PG_MINOR_VER_MASK 0xc
#define WL127X_PG_MINOR_VER_OFFSET 0x2
#define WL128X_PG_MAJOR_VER_MASK 0xc
#define WL128X_PG_MAJOR_VER_OFFSET 0x2
#define WL128X_PG_MINOR_VER_MASK 0x3
#define WL128X_PG_MINOR_VER_OFFSET 0x0
#define WL127X_PG_GET_MAJOR(pg_ver) ((pg_ver & WL127X_PG_MAJOR_VER_MASK) >> \
WL127X_PG_MAJOR_VER_OFFSET)
#define WL127X_PG_GET_MINOR(pg_ver) ((pg_ver & WL127X_PG_MINOR_VER_MASK) >> \
WL127X_PG_MINOR_VER_OFFSET)
#define WL128X_PG_GET_MAJOR(pg_ver) ((pg_ver & WL128X_PG_MAJOR_VER_MASK) >> \
WL128X_PG_MAJOR_VER_OFFSET)
#define WL128X_PG_GET_MINOR(pg_ver) ((pg_ver & WL128X_PG_MINOR_VER_MASK) >> \
WL128X_PG_MINOR_VER_OFFSET)
#define WL12XX_REG_FUSE_BD_ADDR_1 0x00310eb4
#define WL12XX_REG_FUSE_BD_ADDR_2 0x00310eb8
#endif

View File

@@ -0,0 +1,284 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/gfp.h>
#include <linux/sched.h>
#include "wl12xx.h"
#include "debug.h"
#include "acx.h"
#include "reg.h"
#include "rx.h"
#include "tx.h"
#include "io.h"
static u8 wl12xx_rx_get_mem_block(struct wl12xx_fw_status *status,
u32 drv_rx_counter)
{
return le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) &
RX_MEM_BLOCK_MASK;
}
static u32 wl12xx_rx_get_buf_size(struct wl12xx_fw_status *status,
u32 drv_rx_counter)
{
return (le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) &
RX_BUF_SIZE_MASK) >> RX_BUF_SIZE_SHIFT_DIV;
}
static bool wl12xx_rx_get_unaligned(struct wl12xx_fw_status *status,
u32 drv_rx_counter)
{
/* Convert the value to bool */
return !!(le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) &
RX_BUF_UNALIGNED_PAYLOAD);
}
static void wl1271_rx_status(struct wl1271 *wl,
struct wl1271_rx_descriptor *desc,
struct ieee80211_rx_status *status,
u8 beacon)
{
memset(status, 0, sizeof(struct ieee80211_rx_status));
if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG)
status->band = IEEE80211_BAND_2GHZ;
else
status->band = IEEE80211_BAND_5GHZ;
status->rate_idx = wl1271_rate_to_idx(desc->rate, status->band);
/* 11n support */
if (desc->rate <= CONF_HW_RXTX_RATE_MCS0)
status->flag |= RX_FLAG_HT;
status->signal = desc->rssi;
/*
* 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");
}
}
}
static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
bool unaligned, u8 *hlid)
{
struct wl1271_rx_descriptor *desc;
struct sk_buff *skb;
struct ieee80211_hdr *hdr;
u8 *buf;
u8 beacon = 0;
u8 is_data = 0;
u8 reserved = unaligned ? NET_IP_ALIGN : 0;
u16 seq_num;
/*
* 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;
/* 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);
wake_up_interruptible(&wl->fwlog_waitq);
return 0;
}
switch (desc->status & WL1271_RX_DESC_STATUS_MASK) {
/* discard corrupted packets */
case WL1271_RX_DESC_DRIVER_RX_Q_FAIL:
case WL1271_RX_DESC_DECRYPT_FAIL:
wl1271_warning("corrupted packet in RX with status: 0x%x",
desc->status & WL1271_RX_DESC_STATUS_MASK);
return -EINVAL;
case WL1271_RX_DESC_SUCCESS:
case WL1271_RX_DESC_MIC_FAIL:
break;
default:
wl1271_error("invalid RX descriptor status: 0x%x",
desc->status & WL1271_RX_DESC_STATUS_MASK);
return -EINVAL;
}
/* skb length not included rx descriptor */
skb = __dev_alloc_skb(length + reserved - sizeof(*desc), GFP_KERNEL);
if (!skb) {
wl1271_error("Couldn't allocate RX frame");
return -ENOMEM;
}
/* reserve the unaligned payload(if any) */
skb_reserve(skb, reserved);
buf = skb_put(skb, length - sizeof(*desc));
/*
* 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.
*/
memcpy(buf, data + sizeof(*desc), length - sizeof(*desc));
*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);
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_trim(skb, skb->len - desc->pad_len);
skb_queue_tail(&wl->deferred_rx_queue, skb);
queue_work(wl->freezable_wq, &wl->netstack_work);
return is_data;
}
void wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status)
{
struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
u32 buf_size;
u32 fw_rx_counter = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
u32 rx_counter;
u32 mem_block;
u32 pkt_length;
u32 pkt_offset;
u8 hlid;
bool unaligned = false;
while (drv_rx_counter != fw_rx_counter) {
buf_size = 0;
rx_counter = drv_rx_counter;
while (rx_counter != fw_rx_counter) {
pkt_length = wl12xx_rx_get_buf_size(status, rx_counter);
if (buf_size + pkt_length > WL1271_AGGR_BUFFER_SIZE)
break;
buf_size += pkt_length;
rx_counter++;
rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
}
if (buf_size == 0) {
wl1271_warning("received empty data");
break;
}
if (wl->chip.id != CHIP_ID_1283_PG20) {
/*
* Choose the block we want to read
* For aggregated packets, only the first memory block
* should be retrieved. The FW takes care of the rest.
*/
mem_block = wl12xx_rx_get_mem_block(status,
drv_rx_counter);
wl->rx_mem_pool_addr.addr = (mem_block << 8) +
le32_to_cpu(wl_mem_map->packet_memory_pool_start);
wl->rx_mem_pool_addr.addr_extra =
wl->rx_mem_pool_addr.addr + 4;
wl1271_write(wl, WL1271_SLV_REG_DATA,
&wl->rx_mem_pool_addr,
sizeof(wl->rx_mem_pool_addr), false);
}
/* Read all available packets at once */
wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
buf_size, true);
/* Split data into separate packets */
pkt_offset = 0;
while (pkt_offset < buf_size) {
pkt_length = wl12xx_rx_get_buf_size(status,
drv_rx_counter);
unaligned = wl12xx_rx_get_unaligned(status,
drv_rx_counter);
/*
* 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_length, unaligned,
&hlid) == 1) {
if (hlid < WL12XX_MAX_LINKS)
__set_bit(hlid, active_hlids);
else
WARN(1,
"hlid exceeded WL12XX_MAX_LINKS "
"(%d)\n", hlid);
}
wl->rx_counter++;
drv_rx_counter++;
drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
pkt_offset += pkt_length;
}
}
/*
* Write the driver's packet counter to the FW. This is only required
* for older hardware revisions
*/
if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION)
wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
wl12xx_rearm_rx_streaming(wl, active_hlids);
}

View File

@@ -0,0 +1,132 @@
/*
* 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>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __RX_H__
#define __RX_H__
#include <linux/bitops.h>
#define WL1271_RX_MAX_RSSI -30
#define WL1271_RX_MIN_RSSI -95
#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 NUM_RX_PKT_DESC_MOD_MASK 7
#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 0x03
#define WL1271_RX_DESC_SUCCESS 0x00
#define WL1271_RX_DESC_DECRYPT_FAIL 0x01
#define WL1271_RX_DESC_MIC_FAIL 0x02
#define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03
#define RX_MEM_BLOCK_MASK 0xFF
#define RX_BUF_SIZE_MASK 0xFFF00
#define RX_BUF_SIZE_SHIFT_DIV 6
/* If set, the start of IP payload is not 4 bytes aligned */
#define RX_BUF_UNALIGNED_PAYLOAD BIT(20)
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;
void wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status);
u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
#endif

View File

@@ -0,0 +1,790 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/ieee80211.h>
#include "wl12xx.h"
#include "debug.h"
#include "cmd.h"
#include "scan.h"
#include "acx.h"
#include "ps.h"
#include "tx.h"
void wl1271_scan_complete_work(struct work_struct *work)
{
struct delayed_work *dwork;
struct wl1271 *wl;
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
int ret;
dwork = container_of(work, struct delayed_work, work);
wl = container_of(dwork, struct wl1271, scan_complete_work);
wl1271_debug(DEBUG_SCAN, "Scanning complete");
mutex_lock(&wl->mutex);
if (wl->state == WL1271_STATE_OFF)
goto out;
if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
goto out;
vif = wl->scan_vif;
wlvif = wl12xx_vif_to_data(vif);
/*
* 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_vif = NULL;
ret = wl1271_ps_elp_wakeup(wl);
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);
}
wl1271_ps_elp_sleep(wl);
if (wl->scan.failed) {
wl1271_info("Scan completed due to error.");
wl12xx_queue_recovery_work(wl);
}
ieee80211_scan_completed(wl->hw, false);
out:
mutex_unlock(&wl->mutex);
}
static int wl1271_get_scan_channels(struct wl1271 *wl,
struct cfg80211_scan_request *req,
struct basic_scan_channel_params *channels,
enum ieee80211_band band, bool passive)
{
struct conf_scan_settings *c = &wl->conf.scan;
int i, j;
u32 flags;
for (i = 0, j = 0;
i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
i++) {
flags = req->channels[i]->flags;
if (!test_bit(i, wl->scan.scanned_ch) &&
!(flags & IEEE80211_CHAN_DISABLED) &&
(req->channels[i]->band == band) &&
/*
* In passive scans, we scan all remaining
* channels, even if not marked as such.
* In active scans, we only scan channels not
* marked as passive.
*/
(passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) {
wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
req->channels[i]->band,
req->channels[i]->center_freq);
wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
req->channels[i]->hw_value,
req->channels[i]->flags);
wl1271_debug(DEBUG_SCAN,
"max_antenna_gain %d, max_power %d",
req->channels[i]->max_antenna_gain,
req->channels[i]->max_power);
wl1271_debug(DEBUG_SCAN, "beacon_found %d",
req->channels[i]->beacon_found);
if (!passive) {
channels[j].min_duration =
cpu_to_le32(c->min_dwell_time_active);
channels[j].max_duration =
cpu_to_le32(c->max_dwell_time_active);
} else {
channels[j].min_duration =
cpu_to_le32(c->min_dwell_time_passive);
channels[j].max_duration =
cpu_to_le32(c->max_dwell_time_passive);
}
channels[j].early_termination = 0;
channels[j].tx_power_att = req->channels[i]->max_power;
channels[j].channel = req->channels[i]->hw_value;
memset(&channels[j].bssid_lsb, 0xff, 4);
memset(&channels[j].bssid_msb, 0xff, 2);
/* Mark the channels we already used */
set_bit(i, wl->scan.scanned_ch);
j++;
}
}
return j;
}
#define WL1271_NOTHING_TO_SCAN 1
static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif,
enum ieee80211_band band,
bool passive, u32 basic_rate)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl1271_cmd_scan *cmd;
struct wl1271_cmd_trigger_scan_to *trigger;
int ret;
u16 scan_options = 0;
/* skip active scans if we don't have SSIDs */
if (!passive && wl->scan.req->n_ssids == 0)
return WL1271_NOTHING_TO_SCAN;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
if (!cmd || !trigger) {
ret = -ENOMEM;
goto out;
}
if (wl->conf.scan.split_scan_timeout)
scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
if (passive)
scan_options |= WL1271_SCAN_OPT_PASSIVE;
if (wlvif->bss_type == BSS_TYPE_AP_BSS ||
test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
cmd->params.role_id = wlvif->role_id;
else
cmd->params.role_id = wlvif->dev_role_id;
if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
ret = -EINVAL;
goto out;
}
cmd->params.scan_options = cpu_to_le16(scan_options);
cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
cmd->channels,
band, passive);
if (cmd->params.n_ch == 0) {
ret = WL1271_NOTHING_TO_SCAN;
goto out;
}
cmd->params.tx_rate = cpu_to_le32(basic_rate);
cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
if (band == IEEE80211_BAND_2GHZ)
cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
else
cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
if (wl->scan.ssid_len && wl->scan.ssid) {
cmd->params.ssid_len = wl->scan.ssid_len;
memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
}
memcpy(cmd->addr, vif->addr, ETH_ALEN);
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
cmd->params.role_id, band,
wl->scan.ssid, wl->scan.ssid_len,
wl->scan.req->ie,
wl->scan.req->ie_len);
if (ret < 0) {
wl1271_error("PROBE request template failed");
goto out;
}
trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout);
ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
sizeof(*trigger), 0);
if (ret < 0) {
wl1271_error("trigger scan to failed for hw scan");
goto out;
}
wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("SCAN failed");
goto out;
}
out:
kfree(cmd);
kfree(trigger);
return ret;
}
void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret = 0;
enum ieee80211_band band;
u32 rate, mask;
switch (wl->scan.state) {
case WL1271_SCAN_STATE_IDLE:
break;
case WL1271_SCAN_STATE_2GHZ_ACTIVE:
band = IEEE80211_BAND_2GHZ;
mask = wlvif->bitrate_masks[band];
if (wl->scan.req->no_cck) {
mask &= ~CONF_TX_CCK_RATES;
if (!mask)
mask = CONF_TX_RATE_MASK_BASIC_P2P;
}
rate = wl1271_tx_min_rate_get(wl, mask);
ret = wl1271_scan_send(wl, vif, band, false, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
wl1271_scan_stm(wl, vif);
}
break;
case WL1271_SCAN_STATE_2GHZ_PASSIVE:
band = IEEE80211_BAND_2GHZ;
mask = wlvif->bitrate_masks[band];
if (wl->scan.req->no_cck) {
mask &= ~CONF_TX_CCK_RATES;
if (!mask)
mask = CONF_TX_RATE_MASK_BASIC_P2P;
}
rate = wl1271_tx_min_rate_get(wl, mask);
ret = wl1271_scan_send(wl, vif, band, true, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
if (wl->enable_11a)
wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
else
wl->scan.state = WL1271_SCAN_STATE_DONE;
wl1271_scan_stm(wl, vif);
}
break;
case WL1271_SCAN_STATE_5GHZ_ACTIVE:
band = IEEE80211_BAND_5GHZ;
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
ret = wl1271_scan_send(wl, vif, band, false, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
wl1271_scan_stm(wl, vif);
}
break;
case WL1271_SCAN_STATE_5GHZ_PASSIVE:
band = IEEE80211_BAND_5GHZ;
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
ret = wl1271_scan_send(wl, vif, band, true, rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_DONE;
wl1271_scan_stm(wl, vif);
}
break;
case WL1271_SCAN_STATE_DONE:
wl->scan.failed = false;
cancel_delayed_work(&wl->scan_complete_work);
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
msecs_to_jiffies(0));
break;
default:
wl1271_error("invalid scan state");
break;
}
if (ret < 0) {
cancel_delayed_work(&wl->scan_complete_work);
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
msecs_to_jiffies(0));
}
}
int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len,
struct cfg80211_scan_request *req)
{
/*
* 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_vif = vif;
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));
wl1271_scan_stm(wl, vif);
return 0;
}
int wl1271_scan_stop(struct wl1271 *wl)
{
struct wl1271_cmd_header *cmd = NULL;
int ret = 0;
if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
return -EINVAL;
wl1271_debug(DEBUG_CMD, "cmd scan stop");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("cmd stop_scan failed");
goto out;
}
out:
kfree(cmd);
return ret;
}
static int
wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
struct cfg80211_sched_scan_request *req,
struct conn_scan_ch_params *channels,
u32 band, bool radar, bool passive,
int start, int max_channels)
{
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
int i, j;
u32 flags;
bool force_passive = !req->n_ssids;
u32 min_dwell_time_active, max_dwell_time_active, delta_per_probe;
u32 dwell_time_passive, dwell_time_dfs;
if (band == IEEE80211_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 +
req->n_ssids * c->num_probe_reqs * delta_per_probe;
max_dwell_time_active = min_dwell_time_active + c->max_dwell_time_delta;
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(c->dwell_time_passive, 1000);
dwell_time_dfs = DIV_ROUND_UP(c->dwell_time_dfs, 1000);
for (i = 0, j = start;
i < req->n_channels && j < max_channels;
i++) {
flags = req->channels[i]->flags;
if (force_passive)
flags |= IEEE80211_CHAN_PASSIVE_SCAN;
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_PASSIVE_SCAN) == passive)) {
wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
req->channels[i]->band,
req->channels[i]->center_freq);
wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
req->channels[i]->hw_value,
req->channels[i]->flags);
wl1271_debug(DEBUG_SCAN, "max_power %d",
req->channels[i]->max_power);
wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d",
min_dwell_time_active,
max_dwell_time_active);
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;
j++;
}
}
return j - start;
}
static bool
wl1271_scan_sched_scan_channels(struct wl1271 *wl,
struct cfg80211_sched_scan_request *req,
struct wl1271_cmd_sched_scan_config *cfg)
{
cfg->passive[0] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
IEEE80211_BAND_2GHZ,
false, true, 0,
MAX_CHANNELS_2GHZ);
cfg->active[0] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
IEEE80211_BAND_2GHZ,
false, false,
cfg->passive[0],
MAX_CHANNELS_2GHZ);
cfg->passive[1] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
IEEE80211_BAND_5GHZ,
false, true, 0,
MAX_CHANNELS_5GHZ);
cfg->dfs =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
IEEE80211_BAND_5GHZ,
true, true,
cfg->passive[1],
MAX_CHANNELS_5GHZ);
cfg->active[1] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
IEEE80211_BAND_5GHZ,
false, false,
cfg->passive[1] + cfg->dfs,
MAX_CHANNELS_5GHZ);
/* 802.11j channels are not supported yet */
cfg->passive[2] = 0;
cfg->active[2] = 0;
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];
}
/* Returns the scan type to be used or a negative value on error */
static int
wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
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, "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(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}
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 (!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;
}
}
}
}
wl1271_dump(DEBUG_SCAN, "SSID_LIST: ", cmd, sizeof(*cmd));
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;
}
int wl1271_scan_sched_scan_config(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
{
struct wl1271_cmd_sched_scan_config *cfg = NULL;
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
int i, ret;
bool force_passive = !req->n_ssids;
wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
cfg->rssi_threshold = c->rssi_threshold;
cfg->snr_threshold = c->snr_threshold;
cfg->n_probe_reqs = c->num_probe_reqs;
/* cycles set to 0 it means infinite (until manually stopped) */
cfg->cycles = 0;
/* report APs when at least 1 is found */
cfg->report_after = 1;
/* don't stop scanning automatically when something is found */
cfg->terminate = 0;
cfg->tag = WL1271_SCAN_DEFAULT_TAG;
/* don't filter on BSS type */
cfg->bss_type = SCAN_BSS_TYPE_ANY;
/* currently NL80211 supports only a single interval */
for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
cfg->intervals[i] = cpu_to_le32(req->interval);
cfg->ssid_len = 0;
ret = wl12xx_scan_sched_scan_ssid_list(wl, req);
if (ret < 0)
goto out;
cfg->filter_type = ret;
wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) {
wl1271_error("scan channel list is empty");
ret = -EINVAL;
goto out;
}
if (!force_passive && cfg->active[0]) {
u8 band = IEEE80211_BAND_2GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
wlvif->dev_role_id, band,
req->ssids[0].ssid,
req->ssids[0].ssid_len,
ies->ie[band],
ies->len[band]);
if (ret < 0) {
wl1271_error("2.4GHz PROBE request template failed");
goto out;
}
}
if (!force_passive && cfg->active[1]) {
u8 band = IEEE80211_BAND_5GHZ;
ret = wl12xx_cmd_build_probe_req(wl, wlvif,
wlvif->dev_role_id, band,
req->ssids[0].ssid,
req->ssids[0].ssid_len,
ies->ie[band],
ies->len[band]);
if (ret < 0) {
wl1271_error("5GHz PROBE request template failed");
goto out;
}
}
wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
sizeof(*cfg), 0);
if (ret < 0) {
wl1271_error("SCAN configuration failed");
goto out;
}
out:
kfree(cfg);
return ret;
}
int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl1271_cmd_sched_scan_start *start;
int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
if (wlvif->bss_type != BSS_TYPE_STA_BSS)
return -EOPNOTSUPP;
if (test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
return -EBUSY;
start = kzalloc(sizeof(*start), GFP_KERNEL);
if (!start)
return -ENOMEM;
start->tag = WL1271_SCAN_DEFAULT_TAG;
ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
sizeof(*start), 0);
if (ret < 0) {
wl1271_error("failed to send scan start command");
goto out_free;
}
out_free:
kfree(start);
return ret;
}
void wl1271_scan_sched_scan_results(struct wl1271 *wl)
{
wl1271_debug(DEBUG_SCAN, "got periodic scan results");
ieee80211_sched_scan_results(wl->hw);
}
void wl1271_scan_sched_scan_stop(struct wl1271 *wl)
{
struct wl1271_cmd_sched_scan_stop *stop;
int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
/* FIXME: what to do if alloc'ing to stop fails? */
stop = kzalloc(sizeof(*stop), GFP_KERNEL);
if (!stop) {
wl1271_error("failed to alloc memory to send sched scan stop");
return;
}
stop->tag = WL1271_SCAN_DEFAULT_TAG;
ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
sizeof(*stop), 0);
if (ret < 0) {
wl1271_error("failed to send sched scan stop command");
goto out_free;
}
out_free:
kfree(stop);
}

View File

@@ -0,0 +1,233 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __SCAN_H__
#define __SCAN_H__
#include "wl12xx.h"
int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len,
struct cfg80211_scan_request *req);
int wl1271_scan_stop(struct wl1271 *wl);
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 ieee80211_vif *vif);
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_sched_scan_ies *ies);
int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl1271_scan_sched_scan_stop(struct wl1271 *wl);
void wl1271_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 basic_scan_params {
/* Scan option flags (WL1271_SCAN_OPT_*) */
__le16 scan_options;
u8 role_id;
/* Number of scan channels in the list (maximum 30) */
u8 n_ch;
/* This field indicates the number of probe requests to send
per channel for an active scan */
u8 n_probe_reqs;
u8 tid_trigger;
u8 ssid_len;
u8 use_ssid_list;
/* Rate bit field for sending the probes */
__le32 tx_rate;
u8 ssid[IEEE80211_MAX_SSID_LEN];
/* Band to scan */
u8 band;
u8 scan_tag;
u8 padding2[2];
} __packed;
struct basic_scan_channel_params {
/* Duration in TU to wait for frames on a channel for active scan */
__le32 min_duration;
__le32 max_duration;
__le32 bssid_lsb;
__le16 bssid_msb;
u8 early_termination;
u8 tx_power_att;
u8 channel;
/* FW internal use only! */
u8 dfs_candidate;
u8 activity_detected;
u8 pad;
} __packed;
struct wl1271_cmd_scan {
struct wl1271_cmd_header header;
struct basic_scan_params params;
struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
/* src mac address */
u8 addr[ETH_ALEN];
u8 padding[2];
} __packed;
struct wl1271_cmd_trigger_scan_to {
struct wl1271_cmd_header header;
__le32 timeout;
} __packed;
#define MAX_CHANNELS_2GHZ 14
#define MAX_CHANNELS_5GHZ 23
#define MAX_CHANNELS_4GHZ 4
#define SCAN_MAX_CYCLE_INTERVALS 16
#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)
#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;
struct wl1271_cmd_sched_scan_config {
struct wl1271_cmd_header header;
__le32 intervals[SCAN_MAX_CYCLE_INTERVALS];
s8 rssi_threshold; /* for filtering (in dBm) */
s8 snr_threshold; /* for filtering (in dB) */
u8 cycles; /* maximum number of scan cycles */
u8 report_after; /* report when this number of results are received */
u8 terminate; /* stop scanning after reporting */
u8 tag;
u8 bss_type; /* for filtering */
u8 filter_type;
u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 n_probe_reqs; /* Number of probes requests per channel */
u8 passive[SCAN_MAX_BANDS];
u8 active[SCAN_MAX_BANDS];
u8 dfs;
u8 padding[3];
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];
} __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 padding[3];
} __packed;
struct wl1271_cmd_sched_scan_start {
struct wl1271_cmd_header header;
u8 tag;
u8 padding[3];
} __packed;
struct wl1271_cmd_sched_scan_stop {
struct wl1271_cmd_header header;
u8 tag;
u8 padding[3];
} __packed;
#endif /* __WL1271_SCAN_H__ */

View File

@@ -0,0 +1,378 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/platform_device.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/gpio.h>
#include <linux/wl12xx.h>
#include <linux/pm_runtime.h>
#include "wl12xx.h"
#include "wl12xx_80211.h"
#include "io.h"
#ifndef SDIO_VENDOR_ID_TI
#define SDIO_VENDOR_ID_TI 0x0097
#endif
#ifndef SDIO_DEVICE_ID_TI_WL1271
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
#endif
struct wl12xx_sdio_glue {
struct device *dev;
struct platform_device *core;
};
static const struct sdio_device_id wl1271_devices[] __devinitconst = {
{ 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 void 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_ADDR)) {
((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(child->parent, "sdio read failed (%d)\n", ret);
}
static void 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(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
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(child->parent, "sdio write failed (%d)\n", ret);
}
static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue)
{
int ret;
struct sdio_func *func = dev_to_sdio_func(glue->dev);
/* If enabled, tell runtime PM not to power off the card */
if (pm_runtime_enabled(&func->dev)) {
ret = pm_runtime_get_sync(&func->dev);
if (ret < 0)
goto out;
} else {
/* Runtime PM is disabled: power up the card manually */
ret = mmc_power_restore_host(func->card->host);
if (ret < 0)
goto out;
}
sdio_claim_host(func);
sdio_enable_func(func);
sdio_release_host(func);
out:
return ret;
}
static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
{
int ret;
struct sdio_func *func = dev_to_sdio_func(glue->dev);
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
/* Power off the card manually, even if runtime PM is enabled. */
ret = mmc_power_save_host(func->card->host);
if (ret < 0)
return ret;
/* If enabled, let runtime PM know the card is powered off */
if (pm_runtime_enabled(&func->dev))
ret = pm_runtime_put_sync(&func->dev);
return ret;
}
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,
};
static int __devinit wl1271_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct wl12xx_platform_data *wlan_data;
struct wl12xx_sdio_glue *glue;
struct resource res[1];
mmc_pm_flag_t mmcflags;
int ret = -ENOMEM;
/* We are only able to handle the wlan function */
if (func->num != 0x02)
return -ENODEV;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&func->dev, "can't allocate glue\n");
goto out;
}
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;
wlan_data = wl12xx_get_platform_data();
if (IS_ERR(wlan_data)) {
ret = PTR_ERR(wlan_data);
dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
goto out_free_glue;
}
/* 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)
wlan_data->pwr_in_suspend = true;
wlan_data->ops = &sdio_ops;
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);
glue->core = platform_device_alloc("wl12xx", -1);
if (!glue->core) {
dev_err(glue->dev, "can't allocate platform_device");
ret = -ENOMEM;
goto out_free_glue;
}
glue->core->dev.parent = &func->dev;
memset(res, 0x00, sizeof(res));
res[0].start = wlan_data->irq;
res[0].flags = IORESOURCE_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, wlan_data,
sizeof(*wlan_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_free_glue:
kfree(glue);
out:
return ret;
}
static void __devexit 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_del(glue->core);
platform_device_put(glue->core);
kfree(glue);
}
#ifdef CONFIG_PM
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;
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,
};
#endif
static struct sdio_driver wl1271_sdio_driver = {
.name = "wl1271_sdio",
.id_table = wl1271_devices,
.probe = wl1271_probe,
.remove = __devexit_p(wl1271_remove),
#ifdef CONFIG_PM
.drv = {
.pm = &wl1271_sdio_pm_ops,
},
#endif
};
static int __init wl1271_init(void)
{
return sdio_register_driver(&wl1271_sdio_driver);
}
static void __exit wl1271_exit(void)
{
sdio_unregister_driver(&wl1271_sdio_driver);
}
module_init(wl1271_init);
module_exit(wl1271_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
MODULE_FIRMWARE(WL127X_PLT_FW_NAME);
MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE);
MODULE_FIRMWARE(WL128X_FW_NAME_MULTI);
MODULE_FIRMWARE(WL128X_PLT_FW_NAME);

View File

@@ -0,0 +1,442 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/crc7.h>
#include <linux/spi/spi.h>
#include <linux/wl12xx.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "wl12xx.h"
#include "wl12xx_80211.h"
#include "io.h"
#include "reg.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
#define WSPI_MAX_NUM_OF_CHUNKS (WL1271_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE)
struct wl12xx_spi_glue {
struct device *dev;
struct platform_device *core;
};
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);
u8 crc[WSPI_INIT_CMD_CRC_LEN], *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 init\n");
return;
}
memset(crc, 0, sizeof(crc));
memset(&t, 0, sizeof(t));
spi_message_init(&m);
/*
* Set WSPI_INIT_COMMAND
* the data is being send from the MSB to LSB
*/
cmd[2] = 0xff;
cmd[3] = 0xff;
cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
cmd[0] = 0;
cmd[7] = 0;
cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0)
cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY;
else
cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
| WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
crc[0] = cmd[1];
crc[1] = cmd[0];
crc[2] = cmd[7];
crc[3] = cmd[6];
crc[4] = cmd[5];
cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1;
cmd[4] |= WSPI_INIT_CMD_END;
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);
}
#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 void 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((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;
}
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;
}
}
static void 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[2 * WSPI_MAX_NUM_OF_CHUNKS];
struct spi_message m;
u32 commands[WSPI_MAX_NUM_OF_CHUNKS];
u32 *cmd;
u32 chunk_len;
int i;
WARN_ON(len > WL1271_AGGR_BUFFER_SIZE);
spi_message_init(&m);
memset(t, 0, sizeof(t));
cmd = &commands[0];
i = 0;
while (len > 0) {
chunk_len = min((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);
}
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,
.set_block_size = NULL,
};
static int __devinit wl1271_probe(struct spi_device *spi)
{
struct wl12xx_spi_glue *glue;
struct wl12xx_platform_data *pdata;
struct resource res[1];
int ret = -ENOMEM;
pdata = spi->dev.platform_data;
if (!pdata) {
dev_err(&spi->dev, "no platform data\n");
return -ENODEV;
}
pdata->ops = &spi_ops;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&spi->dev, "can't allocate glue\n");
goto out;
}
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;
ret = spi_setup(spi);
if (ret < 0) {
dev_err(glue->dev, "spi_setup failed\n");
goto out_free_glue;
}
glue->core = platform_device_alloc("wl12xx", -1);
if (!glue->core) {
dev_err(glue->dev, "can't allocate platform_device\n");
ret = -ENOMEM;
goto out_free_glue;
}
glue->core->dev.parent = &spi->dev;
memset(res, 0x00, sizeof(res));
res[0].start = spi->irq;
res[0].flags = IORESOURCE_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, pdata, sizeof(*pdata));
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);
out_free_glue:
kfree(glue);
out:
return ret;
}
static int __devexit wl1271_remove(struct spi_device *spi)
{
struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
platform_device_del(glue->core);
platform_device_put(glue->core);
kfree(glue);
return 0;
}
static struct spi_driver wl1271_spi_driver = {
.driver = {
.name = "wl1271_spi",
.owner = THIS_MODULE,
},
.probe = wl1271_probe,
.remove = __devexit_p(wl1271_remove),
};
static int __init wl1271_init(void)
{
return spi_register_driver(&wl1271_spi_driver);
}
static void __exit wl1271_exit(void)
{
spi_unregister_driver(&wl1271_spi_driver);
}
module_init(wl1271_init);
module_exit(wl1271_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
MODULE_FIRMWARE(WL127X_PLT_FW_NAME);
MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE);
MODULE_FIRMWARE(WL128X_FW_NAME_MULTI);
MODULE_FIRMWARE(WL128X_PLT_FW_NAME);
MODULE_ALIAS("spi:wl1271");

View File

@@ -0,0 +1,344 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "testmode.h"
#include <linux/slab.h>
#include <net/genetlink.h>
#include "wl12xx.h"
#include "debug.h"
#include "acx.h"
#include "reg.h"
#include "ps.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,
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 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 (wl->state == WL1271_STATE_OFF) {
ret = -EINVAL;
goto out;
}
ret = wl1271_ps_elp_wakeup(wl);
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) {
len = nla_total_size(buf_len);
skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
if (!skb) {
ret = -ENOMEM;
goto out_sleep;
}
NLA_PUT(skb, WL1271_TM_ATTR_DATA, buf_len, buf);
ret = cfg80211_testmode_reply(skb);
if (ret < 0)
goto out_sleep;
}
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return ret;
nla_put_failure:
kfree_skb(skb);
ret = -EMSGSIZE;
goto out_sleep;
}
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 (wl->state == WL1271_STATE_OFF) {
ret = -EINVAL;
goto out;
}
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out_sleep;
}
ret = wl1271_cmd_interrogate(wl, ie_id, cmd, 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;
}
NLA_PUT(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd);
ret = cfg80211_testmode_reply(skb);
if (ret < 0)
goto out_free;
out_free:
kfree(cmd);
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return ret;
nla_put_failure:
kfree_skb(skb);
ret = -EMSGSIZE;
goto out_free;
}
static int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[])
{
int buf_len, ret;
void *buf;
u8 ie_id;
wl1271_debug(DEBUG_TESTMODE, "testmode cmd configure");
if (!tb[WL1271_TM_ATTR_DATA])
return -EINVAL;
if (!tb[WL1271_TM_ATTR_IE_ID])
return -EINVAL;
ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]);
buf = nla_data(tb[WL1271_TM_ATTR_DATA]);
buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]);
if (buf_len > sizeof(struct wl1271_command))
return -EMSGSIZE;
mutex_lock(&wl->mutex);
ret = wl1271_cmd_configure(wl, ie_id, buf, buf_len);
mutex_unlock(&wl->mutex);
if (ret < 0) {
wl1271_warning("testmode cmd configure failed: %d", ret);
return ret;
}
return 0;
}
static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[])
{
u32 val;
int ret;
wl1271_debug(DEBUG_TESTMODE, "testmode cmd set plt mode");
if (!tb[WL1271_TM_ATTR_PLT_MODE])
return -EINVAL;
val = nla_get_u32(tb[WL1271_TM_ATTR_PLT_MODE]);
switch (val) {
case 0:
ret = wl1271_plt_stop(wl);
break;
case 1:
ret = wl1271_plt_start(wl);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[])
{
wl1271_debug(DEBUG_TESTMODE, "testmode cmd recover");
wl12xx_queue_recovery_work(wl);
return 0;
}
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;
}
NLA_PUT(skb, WL1271_TM_ATTR_DATA, ETH_ALEN, mac_addr);
ret = cfg80211_testmode_reply(skb);
if (ret < 0)
goto out;
out:
mutex_unlock(&wl->mutex);
return ret;
nla_put_failure:
kfree_skb(skb);
ret = -EMSGSIZE;
goto out;
}
int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len)
{
struct wl1271 *wl = hw->priv;
struct nlattr *tb[WL1271_TM_ATTR_MAX + 1];
int err;
err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy);
if (err)
return err;
if (!tb[WL1271_TM_ATTR_CMD_ID])
return -EINVAL;
switch (nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID])) {
case WL1271_TM_CMD_TEST:
return wl1271_tm_cmd_test(wl, tb);
case WL1271_TM_CMD_INTERROGATE:
return wl1271_tm_cmd_interrogate(wl, tb);
case WL1271_TM_CMD_CONFIGURE:
return wl1271_tm_cmd_configure(wl, tb);
case WL1271_TM_CMD_SET_PLT_MODE:
return wl1271_tm_cmd_set_plt_mode(wl, tb);
case WL1271_TM_CMD_RECOVER:
return wl1271_tm_cmd_recover(wl, tb);
case WL1271_TM_CMD_GET_MAC:
return wl12xx_tm_cmd_get_mac(wl, tb);
default:
return -EOPNOTSUPP;
}
}

View File

@@ -0,0 +1,31 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __TESTMODE_H__
#define __TESTMODE_H__
#include <net/mac80211.h>
int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len);
#endif /* __WL1271_TESTMODE_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
/*
* 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>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __TX_H__
#define __TX_H__
#define TX_HW_BLOCK_SPARE_DEFAULT 1
#define TX_HW_BLOCK_SIZE 252
#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_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
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;
/*
* 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;
} __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;
u8 reserved;
} __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;
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 wl1271_tx_get_mac80211_queue(int queue)
{
switch (queue) {
case CONF_TX_AC_VO:
return 0;
case CONF_TX_AC_VI:
return 1;
case CONF_TX_AC_BE:
return 2;
case CONF_TX_AC_BK:
return 3;
default:
return 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);
void wl1271_tx_work_locked(struct wl1271 *wl);
void wl1271_tx_complete(struct wl1271 *wl);
void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues);
void wl1271_tx_flush(struct wl1271 *wl);
u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
enum ieee80211_band rate_band);
u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set);
u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb);
u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb);
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);
/* 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

View File

@@ -0,0 +1,698 @@
/*
* 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>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL12XX_H__
#define __WL12XX_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"
#include "event.h"
#define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin"
#define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-4-sr.bin"
#define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-4-mr.bin"
#define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin"
#define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin"
#define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin"
/*
* wl127x and wl128x are using the same NVS file name. However, the
* ini parameters between them are different. The driver validates
* the correct NVS size in wl1271_boot_upload_nvs().
*/
#define WL12XX_NVS_NAME "ti-connectivity/wl1271-nvs.bin"
#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
#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_MAX_LINKS 12
#define WL12XX_INVALID_ROLE_ID 0xff
#define WL12XX_INVALID_LINK_ID 0xff
#define WL12XX_MAX_RATE_POLICIES 16
/* 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
#define ACX_TX_DESCRIPTORS 16
#define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
enum wl1271_state {
WL1271_STATE_OFF,
WL1271_STATE_ON,
};
enum wl12xx_fw_type {
WL12XX_FW_TYPE_NONE,
WL12XX_FW_TYPE_NORMAL,
WL12XX_FW_TYPE_MULTI,
WL12XX_FW_TYPE_PLT,
};
enum wl1271_partition_type {
PART_DOWN,
PART_WORK,
PART_DRPW,
PART_TABLE_LEN
};
struct wl1271_partition {
u32 size;
u32 start;
};
struct wl1271_partition_set {
struct wl1271_partition mem;
struct wl1271_partition reg;
struct wl1271_partition mem2;
struct wl1271_partition mem3;
};
struct wl1271;
enum {
FW_VER_CHIP,
FW_VER_IF_TYPE,
FW_VER_MAJOR,
FW_VER_SUBTYPE,
FW_VER_MINOR,
NUM_FW_VER
};
#define FW_VER_CHIP_WL127X 6
#define FW_VER_CHIP_WL128X 7
#define FW_VER_IF_TYPE_STA 1
#define FW_VER_IF_TYPE_AP 2
#define FW_VER_MINOR_1_SPARE_STA_MIN 58
#define FW_VER_MINOR_1_SPARE_AP_MIN 47
#define FW_VER_MINOR_FWLOG_STA_MIN 70
struct wl1271_chip {
u32 id;
char fw_ver_str[ETHTOOL_BUSINFO_LEN];
unsigned int fw_ver[NUM_FW_VER];
};
struct wl1271_stats {
struct acx_statistics *fw_stats;
unsigned long fw_stats_update;
unsigned int retry_count;
unsigned int excessive_retries;
};
#define NUM_TX_QUEUES 4
#define NUM_RX_PKT_DESC 8
#define AP_MAX_STATIONS 8
/* FW status registers */
struct wl12xx_fw_status {
__le32 intr;
u8 fw_rx_counter;
u8 drv_rx_counter;
u8 reserved;
u8 tx_results_counter;
__le32 rx_pkt_descs[NUM_RX_PKT_DESC];
__le32 fw_localtime;
/*
* A bitmap (where each bit represents a single HLID)
* to indicate if the station is in PS mode.
*/
__le32 link_ps_bitmap;
/*
* A bitmap (where each bit represents a single HLID) to indicate
* if the station is in Fast mode
*/
__le32 link_fast_bitmap;
/* Cumulative counter of total released mem blocks since FW-reset */
__le32 total_released_blks;
/* Size (in Memory Blocks) of TX pool */
__le32 tx_total;
/* Cumulative counter of released packets per AC */
u8 tx_released_pkts[NUM_TX_QUEUES];
/* Cumulative counter of freed packets per HLID */
u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS];
/* Cumulative counter of released Voice memory blocks */
u8 tx_voice_released_blks;
u8 padding_1[3];
__le32 log_start_addr;
} __packed;
struct wl1271_rx_mem_pool_addr {
u32 addr;
u32 addr_extra;
};
#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 {
void (*read)(struct device *child, int addr, void *buf, size_t len,
bool fixed);
void (*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);
};
#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;
};
enum wl12xx_flags {
WL1271_FLAG_GPIO_POWER,
WL1271_FLAG_TX_QUEUE_STOPPED,
WL1271_FLAG_TX_PENDING,
WL1271_FLAG_IN_ELP,
WL1271_FLAG_ELP_REQUESTED,
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,
};
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,
};
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;
u8 addr[ETH_ALEN];
/* bitmap of TIDs where RX BA sessions are active for this link */
u8 ba_bitmap;
};
struct wl1271 {
struct ieee80211_hw *hw;
bool mac80211_registered;
struct device *dev;
void *if_priv;
struct wl1271_if_operations *if_ops;
void (*set_power)(bool enable);
int irq;
int ref_clock;
spinlock_t wl_lock;
enum wl1271_state state;
enum wl12xx_fw_type fw_type;
bool plt;
u8 last_vif_count;
struct mutex mutex;
unsigned long flags;
struct wl1271_partition_set part;
struct wl1271_chip chip;
int cmd_box_addr;
int event_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[2];
int channel;
u8 system_hlid;
unsigned long links_map[BITS_TO_LONGS(WL12XX_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)];
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;
/* amount of spare TX blocks to use */
u32 tx_spare_blocks;
/* 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];
long stopped_queues_map;
/* 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(ACX_TX_DESCRIPTORS)];
struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS];
int tx_frames_cnt;
/* FW Rx counter */
u32 rx_counter;
/* Rx memory pool address */
struct wl1271_rx_mem_pool_addr rx_mem_pool_addr;
/* Intermediate buffer, used for packet aggregation */
u8 *aggr_buf;
/* 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;
/* Sysfs FW log entry readers wait queue */
wait_queue_head_t fwlog_waitq;
/* Hardware recovery work */
struct work_struct recovery_work;
struct event_mailbox *mbox;
/* The mbox event mask */
u32 event_mask;
/* Mailbox pointers */
u32 mbox_ptr[2];
/* Are we currently scanning */
struct ieee80211_vif *scan_vif;
struct wl1271_scan scan;
struct delayed_work scan_complete_work;
bool sched_scanning;
/* The current band */
enum ieee80211_band band;
struct completion *elp_compl;
struct delayed_work elp_work;
/* in dBm */
int power_level;
struct wl1271_stats stats;
__le32 buffer_32;
u32 buffer_cmd;
u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
struct wl12xx_fw_status *fw_status;
struct wl1271_tx_hw_res_if *tx_res_if;
/* Current chipset configuration */
struct conf_drv_settings conf;
bool sg_enabled;
bool enable_11a;
/* Most recently reported noise in dBm */
s8 noise;
/* bands supported by this instance of wl12xx */
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
int tcxo_clock;
/*
* 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[WL12XX_MAX_LINKS];
/* AP-mode - a bitmap of links currently in PS mode according to FW */
u32 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;
/* Platform limitations */
unsigned int platform_quirks;
/* number of currently active RX BA sessions */
int ba_rx_session_count;
/* AP-mode - number of currently connected stations */
int active_sta_count;
/* last wlvif we transmitted from */
struct wl12xx_vif *last_wlvif;
/* work to fire when Tx is stuck */
struct delayed_work tx_watchdog_work;
};
struct wl1271_station {
u8 hlid;
};
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 ba_rx_bitmap;
u8 basic_rate_idx;
u8 ap_rate_idx;
u8 p2p_rate_idx;
bool qos;
} sta;
struct {
u8 global_hlid;
u8 bcast_hlid;
/* HLIDs bitmap of associated stations */
unsigned long sta_hlid_map[BITS_TO_LONGS(
WL12XX_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;
unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
u8 ssid_len;
/* The current band */
enum ieee80211_band band;
int channel;
u32 bitrate_masks[IEEE80211_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;
/* Session counter for the chipset */
int session_counter;
/* 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;
/* Rx Streaming */
struct work_struct rx_streaming_enable_work;
struct work_struct rx_streaming_disable_work;
struct timer_list rx_streaming_timer;
/*
* 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];
/*
* Security sequence number
* bits 0-15: lower 16 bits part of sequence number
* bits 16-47: higher 32 bits part of sequence number
* bits 48-63: not in use
*/
u64 tx_security_seq;
/* 8 bits of the last sequence number in use */
u8 tx_security_last_seq_lsb;
};
};
static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *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);
}
#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);
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);
#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
/* Quirks */
/* Each RX/TX transaction requires an end-of-transaction transfer */
#define WL12XX_QUIRK_END_OF_TRANSACTION BIT(0)
/* wl127x and SPI don't support SDIO block size alignment */
#define WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT BIT(2)
/* Older firmwares did not implement the FW logger over bus feature */
#define WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED BIT(4)
#define WL12XX_HW_BLOCK_SIZE 256
#endif

View File

@@ -0,0 +1,137 @@
#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;
u8 payload[0];
} __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

View File

@@ -0,0 +1,49 @@
/*
* This file is part of wl12xx
*
* Copyright (C) 2010-2011 Texas Instruments, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/module.h>
#include <linux/err.h>
#include <linux/wl12xx.h>
static struct wl12xx_platform_data *platform_data;
int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
{
if (platform_data)
return -EBUSY;
if (!data)
return -EINVAL;
platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL);
if (!platform_data)
return -ENOMEM;
return 0;
}
struct wl12xx_platform_data *wl12xx_get_platform_data(void)
{
if (!platform_data)
return ERR_PTR(-ENODEV);
return platform_data;
}
EXPORT_SYMBOL(wl12xx_get_platform_data);