Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem

This commit is contained in:
John W. Linville 2011-12-12 14:19:43 -05:00
commit f2abba4921
98 changed files with 4735 additions and 3569 deletions

View File

@ -263,8 +263,7 @@ Who: Ravikiran Thirumalai <kiran@scalex86.org>
What: Code that is now under CONFIG_WIRELESS_EXT_SYSFS
(in net/core/net-sysfs.c)
When: After the only user (hal) has seen a release with the patches
for enough time, probably some time in 2010.
When: 3.5
Why: Over 1K .text/.data size reduction, data is available in other
ways (ioctls)
Who: Johannes Berg <johannes@sipsolutions.net>

View File

@ -21,48 +21,58 @@ static void bcma_host_pci_switch_core(struct bcma_device *core)
pr_debug("Switched to core: 0x%X\n", core->id.id);
}
static u8 bcma_host_pci_read8(struct bcma_device *core, u16 offset)
/* Provides access to the requested core. Returns base offset that has to be
* used. It makes use of fixed windows when possible. */
static u16 bcma_host_pci_provide_access_to_core(struct bcma_device *core)
{
switch (core->id.id) {
case BCMA_CORE_CHIPCOMMON:
return 3 * BCMA_CORE_SIZE;
case BCMA_CORE_PCIE:
return 2 * BCMA_CORE_SIZE;
}
if (core->bus->mapped_core != core)
bcma_host_pci_switch_core(core);
return 0;
}
static u8 bcma_host_pci_read8(struct bcma_device *core, u16 offset)
{
offset += bcma_host_pci_provide_access_to_core(core);
return ioread8(core->bus->mmio + offset);
}
static u16 bcma_host_pci_read16(struct bcma_device *core, u16 offset)
{
if (core->bus->mapped_core != core)
bcma_host_pci_switch_core(core);
offset += bcma_host_pci_provide_access_to_core(core);
return ioread16(core->bus->mmio + offset);
}
static u32 bcma_host_pci_read32(struct bcma_device *core, u16 offset)
{
if (core->bus->mapped_core != core)
bcma_host_pci_switch_core(core);
offset += bcma_host_pci_provide_access_to_core(core);
return ioread32(core->bus->mmio + offset);
}
static void bcma_host_pci_write8(struct bcma_device *core, u16 offset,
u8 value)
{
if (core->bus->mapped_core != core)
bcma_host_pci_switch_core(core);
offset += bcma_host_pci_provide_access_to_core(core);
iowrite8(value, core->bus->mmio + offset);
}
static void bcma_host_pci_write16(struct bcma_device *core, u16 offset,
u16 value)
{
if (core->bus->mapped_core != core)
bcma_host_pci_switch_core(core);
offset += bcma_host_pci_provide_access_to_core(core);
iowrite16(value, core->bus->mmio + offset);
}
static void bcma_host_pci_write32(struct bcma_device *core, u16 offset,
u32 value)
{
if (core->bus->mapped_core != core)
bcma_host_pci_switch_core(core);
offset += bcma_host_pci_provide_access_to_core(core);
iowrite32(value, core->bus->mmio + offset);
}

View File

@ -188,7 +188,7 @@ config BT_MRVL
The core driver to support Marvell Bluetooth devices.
This driver is required if you want to support
Marvell Bluetooth devices, such as 8688/8787.
Marvell Bluetooth devices, such as 8688/8787/8797.
Say Y here to compile Marvell Bluetooth driver
into the kernel or say M to compile it as module.
@ -201,8 +201,8 @@ config BT_MRVL_SDIO
The driver for Marvell Bluetooth chipsets with SDIO interface.
This driver is required if you want to use Marvell Bluetooth
devices with SDIO interface. Currently SD8688/SD8787 chipsets are
supported.
devices with SDIO interface. Currently SD8688/SD8787/SD8797
chipsets are supported.
Say Y here to compile support for Marvell BT-over-SDIO driver
into the kernel or say M to compile it as module.

View File

@ -65,7 +65,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = {
.io_port_1 = 0x01,
.io_port_2 = 0x02,
};
static const struct btmrvl_sdio_card_reg btmrvl_reg_8787 = {
static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
.cfg = 0x00,
.host_int_mask = 0x02,
.host_intstatus = 0x03,
@ -92,7 +92,14 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
.helper = NULL,
.firmware = "mrvl/sd8787_uapsta.bin",
.reg = &btmrvl_reg_8787,
.reg = &btmrvl_reg_87xx,
.sd_blksz_fw_dl = 256,
};
static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
.helper = NULL,
.firmware = "mrvl/sd8797_uapsta.bin",
.reg = &btmrvl_reg_87xx,
.sd_blksz_fw_dl = 256,
};
@ -103,6 +110,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = {
/* Marvell SD8787 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
/* Marvell SD8797 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
.driver_data = (unsigned long) &btmrvl_sdio_sd8797 },
{ } /* Terminating entry */
};
@ -1076,3 +1086,4 @@ MODULE_LICENSE("GPL v2");
MODULE_FIRMWARE("sd8688_helper.bin");
MODULE_FIRMWARE("sd8688.bin");
MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");

View File

@ -785,9 +785,8 @@ skip_waking:
usb_mark_last_busy(data->udev);
}
usb_free_urb(urb);
done:
usb_free_urb(urb);
return err;
}

View File

@ -41,6 +41,8 @@
#define VERSION "1.3"
static bool amp;
struct vhci_data {
struct hci_dev *hdev;
@ -239,6 +241,9 @@ static int vhci_open(struct inode *inode, struct file *file)
hdev->bus = HCI_VIRTUAL;
hdev->driver_data = data;
if (amp)
hdev->dev_type = HCI_AMP;
hdev->open = vhci_open_dev;
hdev->close = vhci_close_dev;
hdev->flush = vhci_flush;
@ -303,6 +308,9 @@ static void __exit vhci_exit(void)
module_init(vhci_init);
module_exit(vhci_exit);
module_param(amp, bool, 0644);
MODULE_PARM_DESC(amp, "Create AMP controller device");
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
MODULE_VERSION(VERSION);

View File

@ -203,7 +203,7 @@ static void ar9002_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
i);
ath_dbg(common, ATH_DBG_CALIBRATE,
"Orignal: Chn %diq_corr_meas = 0x%08x\n",
"Original: Chn %d iq_corr_meas = 0x%08x\n",
i, ah->totalIqCorrMeas[i]);
iqCorrNeg = 0;

View File

@ -226,7 +226,7 @@ static void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
i);
ath_dbg(common, ATH_DBG_CALIBRATE,
"Orignal: Chn %diq_corr_meas = 0x%08x\n",
"Original: Chn %d iq_corr_meas = 0x%08x\n",
i, ah->totalIqCorrMeas[i]);
iqCorrNeg = 0;

View File

@ -41,24 +41,24 @@ static const u32 ar9462_pciephy_clkreq_enable_L1_2p0[][2] = {
static const u32 ar9462_2p0_baseband_postamble[][5] = {
/* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
{0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011},
{0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e},
{0x00009824, 0x5ac640de, 0x5ac640d0, 0x5ac640d0, 0x5ac640de},
{0x00009828, 0x0796be89, 0x0696b081, 0x0696b881, 0x0796be89},
{0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a800d},
{0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a01ae},
{0x00009824, 0x5ac640de, 0x5ac640d0, 0x5ac640d0, 0x63c640da},
{0x00009828, 0x0796be89, 0x0696b081, 0x0696b881, 0x09143e81},
{0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
{0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
{0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
{0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0},
{0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
{0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
{0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x92c84d2e},
{0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e},
{0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000d8},
{0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e},
{0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3376605e, 0x33795d5e},
{0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c782},
{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282},
{0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
{0x0000a204, 0x013187c0, 0x013187c4, 0x013187c4, 0x013187c0},
@ -81,6 +81,15 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = {
{0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a3a4, 0x00000010, 0x00000010, 0x00000000, 0x00000000},
{0x0000a3a8, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa},
{0x0000a3ac, 0xaaaaaa00, 0xaaaaaa30, 0xaaaaaa00, 0xaaaaaa00},
{0x0000a41c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce},
{0x0000a420, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce},
{0x0000a424, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce},
{0x0000a428, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce},
{0x0000a42c, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce},
{0x0000a430, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce},
{0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
{0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000},
{0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
@ -1107,11 +1116,11 @@ static const u32 ar9462_2p0_baseband_core[][2] = {
{0x00009e30, 0x06336f77},
{0x00009e34, 0x6af6532f},
{0x00009e38, 0x0cc80c00},
{0x00009e40, 0x0d261820},
{0x00009e40, 0x15262820},
{0x00009e4c, 0x00001004},
{0x00009e50, 0x00ff03f1},
{0x00009e54, 0xe4c355c7},
{0x00009e58, 0xfd897735},
{0x00009e54, 0xe4c555c2},
{0x00009e58, 0xfd857722},
{0x00009e5c, 0xe9198724},
{0x00009fc0, 0x803e4788},
{0x00009fc4, 0x0001efb5},
@ -1142,9 +1151,6 @@ static const u32 ar9462_2p0_baseband_core[][2] = {
{0x0000a398, 0x001f0e0f},
{0x0000a39c, 0x0075393f},
{0x0000a3a0, 0xb79f6427},
{0x0000a3a4, 0x00000000},
{0x0000a3a8, 0xaaaaaaaa},
{0x0000a3ac, 0x3c466478},
{0x0000a3c0, 0x20202020},
{0x0000a3c4, 0x22222220},
{0x0000a3c8, 0x20200020},
@ -1167,12 +1173,6 @@ static const u32 ar9462_2p0_baseband_core[][2] = {
{0x0000a40c, 0x00820820},
{0x0000a414, 0x1ce739ce},
{0x0000a418, 0x2d001dce},
{0x0000a41c, 0x1ce739ce},
{0x0000a420, 0x000001ce},
{0x0000a424, 0x1ce739ce},
{0x0000a428, 0x000001ce},
{0x0000a42c, 0x1ce739ce},
{0x0000a430, 0x1ce739ce},
{0x0000a434, 0x00000000},
{0x0000a438, 0x00001801},
{0x0000a43c, 0x00100000},

View File

@ -808,7 +808,8 @@ void ath9k_htc_ani_work(struct work_struct *work)
}
/* Verify whether we must check ANI */
if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
if (ah->config.enable_ani &&
(timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
aniflag = true;
common->ani.checkani_timer = timestamp;
}
@ -838,7 +839,7 @@ set_timer:
* short calibration and long calibration.
*/
cal_interval = ATH_LONG_CALINTERVAL;
if (priv->ah->config.enable_ani)
if (ah->config.enable_ani)
cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
if (!common->ani.caldone)
cal_interval = min(cal_interval, (u32)short_cal_interval);

View File

@ -504,7 +504,7 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
return ecode;
}
if (!AR_SREV_9100(ah) && !AR_SREV_9340(ah)) {
if (ah->config.enable_ani) {
ath9k_hw_ani_setup(ah);
ath9k_hw_ani_init(ah);
}
@ -610,6 +610,10 @@ static int __ath9k_hw_init(struct ath_hw *ah)
if (!AR_SREV_9300_20_OR_LATER(ah))
ah->ani_function &= ~ATH9K_ANI_MRC_CCK;
/* disable ANI for 9340 */
if (AR_SREV_9340(ah))
ah->config.enable_ani = false;
ath9k_hw_init_mode_regs(ah);
if (!ah->is_pciexpress)

View File

@ -1110,7 +1110,6 @@ bool ath9k_hw_disable(struct ath_hw *ah);
void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test);
void ath9k_hw_setopmode(struct ath_hw *ah);
void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1);
void ath9k_hw_setbssidmask(struct ath_hw *ah);
void ath9k_hw_write_associd(struct ath_hw *ah);
u32 ath9k_hw_gettsf32(struct ath_hw *ah);
u64 ath9k_hw_gettsf64(struct ath_hw *ah);

View File

@ -258,6 +258,8 @@ static void setup_ht_cap(struct ath_softc *sc,
if (AR_SREV_9330(ah) || AR_SREV_9485(ah))
max_streams = 1;
else if (AR_SREV_9462(ah))
max_streams = 2;
else if (AR_SREV_9300_20_OR_LATER(ah))
max_streams = 3;
else

View File

@ -118,7 +118,7 @@ void ath9k_ps_restore(struct ath_softc *sc)
if (--sc->ps_usecount != 0)
goto unlock;
if (sc->ps_idle)
if (sc->ps_idle && (sc->ps_flags & PS_WAIT_FOR_TX_ACK))
mode = ATH9K_PM_FULL_SLEEP;
else if (sc->ps_enabled &&
!(sc->ps_flags & (PS_WAIT_FOR_BEACON |
@ -332,7 +332,8 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
hchan = ah->curchan;
}
if (fastcc && !ath9k_hw_check_alive(ah))
if (fastcc && (ah->chip_fullsleep ||
!ath9k_hw_check_alive(ah)))
fastcc = false;
if (!ath_prepare_reset(sc, retry_tx, flush))
@ -561,7 +562,6 @@ void ath_ani_calibrate(unsigned long data)
/* Long calibration runs independently of short calibration. */
if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) {
longcal = true;
ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
common->ani.longcal_timer = timestamp;
}
@ -569,8 +569,6 @@ void ath_ani_calibrate(unsigned long data)
if (!common->ani.caldone) {
if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
shortcal = true;
ath_dbg(common, ATH_DBG_ANI,
"shortcal @%lu\n", jiffies);
common->ani.shortcal_timer = timestamp;
common->ani.resetcal_timer = timestamp;
}
@ -584,8 +582,9 @@ void ath_ani_calibrate(unsigned long data)
}
/* Verify whether we must check ANI */
if ((timestamp - common->ani.checkani_timer) >=
ah->config.ani_poll_interval) {
if (sc->sc_ah->config.enable_ani
&& (timestamp - common->ani.checkani_timer) >=
ah->config.ani_poll_interval) {
aniflag = true;
common->ani.checkani_timer = timestamp;
}
@ -605,6 +604,11 @@ void ath_ani_calibrate(unsigned long data)
ah->rxchainmask, longcal);
}
ath_dbg(common, ATH_DBG_ANI,
"Calibration @%lu finished: %s %s %s, caldone: %s\n", jiffies,
longcal ? "long" : "", shortcal ? "short" : "",
aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
ath9k_ps_restore(sc);
set_timer:
@ -886,82 +890,6 @@ chip_reset:
#undef SCHED_INTR
}
static void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_channel *channel = hw->conf.channel;
int r;
ath9k_ps_wakeup(sc);
spin_lock_bh(&sc->sc_pcu_lock);
atomic_set(&ah->intr_ref_cnt, -1);
ath9k_hw_configpcipowersave(ah, false);
if (!ah->curchan)
ah->curchan = ath9k_cmn_get_curchannel(sc->hw, ah);
r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (r) {
ath_err(common,
"Unable to reset channel (%u MHz), reset status %d\n",
channel->center_freq, r);
}
ath_complete_reset(sc, true);
/* Enable LED */
ath9k_hw_cfg_output(ah, ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_set_gpio(ah, ah->led_pin, 0);
spin_unlock_bh(&sc->sc_pcu_lock);
ath9k_ps_restore(sc);
}
void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
{
struct ath_hw *ah = sc->sc_ah;
struct ieee80211_channel *channel = hw->conf.channel;
int r;
ath9k_ps_wakeup(sc);
ath_cancel_work(sc);
spin_lock_bh(&sc->sc_pcu_lock);
/*
* Keep the LED on when the radio is disabled
* during idle unassociated state.
*/
if (!sc->ps_idle) {
ath9k_hw_set_gpio(ah, ah->led_pin, 1);
ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
}
ath_prepare_reset(sc, false, true);
if (!ah->curchan)
ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (r) {
ath_err(ath9k_hw_common(sc->sc_ah),
"Unable to reset channel (%u MHz), reset status %d\n",
channel->center_freq, r);
}
ath9k_hw_phy_disable(ah);
ath9k_hw_configpcipowersave(ah, true);
spin_unlock_bh(&sc->sc_pcu_lock);
ath9k_ps_restore(sc);
}
static int ath_reset(struct ath_softc *sc, bool retry_tx)
{
int r;
@ -1097,6 +1025,9 @@ static int ath9k_start(struct ieee80211_hw *hw)
* and then setup of the interrupt mask.
*/
spin_lock_bh(&sc->sc_pcu_lock);
atomic_set(&ah->intr_ref_cnt, -1);
r = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
if (r) {
ath_err(common,
@ -1138,6 +1069,18 @@ static int ath9k_start(struct ieee80211_hw *hw)
goto mutex_unlock;
}
if (ah->led_pin >= 0) {
ath9k_hw_cfg_output(ah, ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_set_gpio(ah, ah->led_pin, 0);
}
/*
* Reset key cache to sane defaults (all entries cleared) instead of
* semi-random values after suspend/resume.
*/
ath9k_cmn_init_crypto(sc->sc_ah);
spin_unlock_bh(&sc->sc_pcu_lock);
if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
@ -1183,6 +1126,13 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
}
}
/*
* Cannot tx while the hardware is in full sleep, it first needs a full
* chip reset to recover from that
*/
if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_FULL_SLEEP))
goto exit;
if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) {
/*
* We are using PS-Poll and mac80211 can request TX while in
@ -1229,6 +1179,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
bool prev_idle;
mutex_lock(&sc->mutex);
@ -1259,21 +1210,6 @@ static void ath9k_stop(struct ieee80211_hw *hw)
* before setting the invalid flag. */
ath9k_hw_disable_interrupts(ah);
if (!(sc->sc_flags & SC_OP_INVALID)) {
ath_drain_all_txq(sc, false);
ath_stoprecv(sc);
ath9k_hw_phy_disable(ah);
} else
sc->rx.rxlink = NULL;
if (sc->rx.frag) {
dev_kfree_skb_any(sc->rx.frag);
sc->rx.frag = NULL;
}
/* disable HAL and put h/w to sleep */
ath9k_hw_disable(ah);
spin_unlock_bh(&sc->sc_pcu_lock);
/* we can now sync irq and kill any running tasklets, since we already
@ -1282,12 +1218,37 @@ static void ath9k_stop(struct ieee80211_hw *hw)
tasklet_kill(&sc->intr_tq);
tasklet_kill(&sc->bcon_tasklet);
prev_idle = sc->ps_idle;
sc->ps_idle = true;
spin_lock_bh(&sc->sc_pcu_lock);
if (ah->led_pin >= 0) {
ath9k_hw_set_gpio(ah, ah->led_pin, 1);
ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
}
ath_prepare_reset(sc, false, true);
if (sc->rx.frag) {
dev_kfree_skb_any(sc->rx.frag);
sc->rx.frag = NULL;
}
if (!ah->curchan)
ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
ath9k_hw_phy_disable(ah);
ath9k_hw_configpcipowersave(ah, true);
spin_unlock_bh(&sc->sc_pcu_lock);
ath9k_ps_restore(sc);
sc->ps_idle = true;
ath_radio_disable(sc, hw);
sc->sc_flags |= SC_OP_INVALID;
sc->ps_idle = prev_idle;
mutex_unlock(&sc->mutex);
@ -1627,8 +1588,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &hw->conf;
bool disable_radio = false;
ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
/*
@ -1639,13 +1600,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
*/
if (changed & IEEE80211_CONF_CHANGE_IDLE) {
sc->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE);
if (!sc->ps_idle) {
ath_radio_enable(sc, hw);
ath_dbg(common, ATH_DBG_CONFIG,
"not-idle: enabling radio\n");
} else {
disable_radio = true;
}
if (sc->ps_idle)
ath_cancel_work(sc);
}
/*
@ -1752,18 +1708,12 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
ath_dbg(common, ATH_DBG_CONFIG,
"Set power: %d\n", conf->power_level);
sc->config.txpowlimit = 2 * conf->power_level;
ath9k_ps_wakeup(sc);
ath9k_cmn_update_txpow(ah, sc->curtxpow,
sc->config.txpowlimit, &sc->curtxpow);
ath9k_ps_restore(sc);
}
if (disable_radio) {
ath_dbg(common, ATH_DBG_CONFIG, "idle: disabling radio\n");
ath_radio_disable(sc, hw);
}
mutex_unlock(&sc->mutex);
ath9k_ps_restore(sc);
return 0;
}
@ -2331,9 +2281,6 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
return;
}
if (drop)
timeout = 1;
for (j = 0; j < timeout; j++) {
bool npend = false;
@ -2351,21 +2298,22 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
}
if (!npend)
goto out;
break;
}
ath9k_ps_wakeup(sc);
spin_lock_bh(&sc->sc_pcu_lock);
drain_txq = ath_drain_all_txq(sc, false);
spin_unlock_bh(&sc->sc_pcu_lock);
if (drop) {
ath9k_ps_wakeup(sc);
spin_lock_bh(&sc->sc_pcu_lock);
drain_txq = ath_drain_all_txq(sc, false);
spin_unlock_bh(&sc->sc_pcu_lock);
if (!drain_txq)
ath_reset(sc, false);
if (!drain_txq)
ath_reset(sc, false);
ath9k_ps_restore(sc);
ieee80211_wake_queues(hw);
ath9k_ps_restore(sc);
ieee80211_wake_queues(hw);
}
out:
ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0);
mutex_unlock(&sc->mutex);
}

View File

@ -307,12 +307,11 @@ static int ath_pci_suspend(struct device *device)
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_softc *sc = hw->priv;
ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
/* The device has to be moved to FULLSLEEP forcibly.
* Otherwise the chip never moved to full sleep,
* when no interface is up.
*/
ath9k_hw_disable(sc->sc_ah);
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
return 0;
@ -321,8 +320,6 @@ static int ath_pci_suspend(struct device *device)
static int ath_pci_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_softc *sc = hw->priv;
u32 val;
/*
@ -334,22 +331,6 @@ static int ath_pci_resume(struct device *device)
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
ath9k_ps_wakeup(sc);
/* Enable LED */
ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
/*
* Reset key cache to sane defaults (all entries cleared) instead of
* semi-random values after suspend/resume.
*/
ath9k_cmn_init_crypto(sc->sc_ah);
ath9k_ps_restore(sc);
sc->ps_idle = true;
ath_radio_disable(sc, hw);
return 0;
}

View File

@ -1954,7 +1954,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
skb_pull(skb, padsize);
}
if (sc->ps_flags & PS_WAIT_FOR_TX_ACK) {
if ((sc->ps_flags & PS_WAIT_FOR_TX_ACK) && !txq->axq_depth) {
sc->ps_flags &= ~PS_WAIT_FOR_TX_ACK;
ath_dbg(common, ATH_DBG_PS,
"Going back to sleep after having received TX status (0x%lx)\n",

View File

@ -2786,9 +2786,8 @@ il_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id)
/* Driver ilate data, only for Tx (not command) queues,
* not shared with device. */
if (id != il->cmd_queue) {
txq->txb =
kzalloc(sizeof(txq->txb[0]) * TFD_QUEUE_SIZE_MAX,
GFP_KERNEL);
txq->txb = kcalloc(TFD_QUEUE_SIZE_MAX, sizeof(txq->txb[0]),
GFP_KERNEL);
if (!txq->txb) {
IL_ERR("kmalloc for auxiliary BD "
"structures failed\n");

View File

@ -1,7 +1,7 @@
# WIFI
obj-$(CONFIG_IWLWIFI) += iwlwifi.o
iwlwifi-objs := iwl-agn.o iwl-agn-rs.o iwl-mac80211.o
iwlwifi-objs += iwl-agn-ucode.o iwl-agn-tx.o
iwlwifi-objs += iwl-ucode.o iwl-agn-tx.o
iwlwifi-objs += iwl-agn-lib.o iwl-agn-calib.o iwl-io.o
iwlwifi-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-rx.o
@ -18,7 +18,7 @@ iwlwifi-objs += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o
iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
iwlwifi-$(CONFIG_IWLWIFI_DEVICE_SVTOOL) += iwl-sv-open.o
iwlwifi-$(CONFIG_IWLWIFI_DEVICE_SVTOOL) += iwl-testmode.o
CFLAGS_iwl-devtrace.o := -I$(src)

View File

@ -934,57 +934,6 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid)
return ant;
}
/* notification wait support */
void iwlagn_init_notification_wait(struct iwl_priv *priv,
struct iwl_notification_wait *wait_entry,
u8 cmd,
void (*fn)(struct iwl_priv *priv,
struct iwl_rx_packet *pkt,
void *data),
void *fn_data)
{
wait_entry->fn = fn;
wait_entry->fn_data = fn_data;
wait_entry->cmd = cmd;
wait_entry->triggered = false;
wait_entry->aborted = false;
spin_lock_bh(&priv->notif_wait_lock);
list_add(&wait_entry->list, &priv->notif_waits);
spin_unlock_bh(&priv->notif_wait_lock);
}
int iwlagn_wait_notification(struct iwl_priv *priv,
struct iwl_notification_wait *wait_entry,
unsigned long timeout)
{
int ret;
ret = wait_event_timeout(priv->notif_waitq,
wait_entry->triggered || wait_entry->aborted,
timeout);
spin_lock_bh(&priv->notif_wait_lock);
list_del(&wait_entry->list);
spin_unlock_bh(&priv->notif_wait_lock);
if (wait_entry->aborted)
return -EIO;
/* return value is always >= 0 */
if (ret <= 0)
return -ETIMEDOUT;
return 0;
}
void iwlagn_remove_notification(struct iwl_priv *priv,
struct iwl_notification_wait *wait_entry)
{
spin_lock_bh(&priv->notif_wait_lock);
list_del(&wait_entry->list);
spin_unlock_bh(&priv->notif_wait_lock);
}
#ifdef CONFIG_PM_SLEEP
static void iwlagn_convert_p1k(u16 *p1k, __le16 *out)
{

View File

@ -1131,9 +1131,9 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv)
priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx;
/* set up notification wait support */
spin_lock_init(&priv->notif_wait_lock);
INIT_LIST_HEAD(&priv->notif_waits);
init_waitqueue_head(&priv->notif_waitq);
spin_lock_init(&priv->shrd->notif_wait_lock);
INIT_LIST_HEAD(&priv->shrd->notif_waits);
init_waitqueue_head(&priv->shrd->notif_waitq);
/* Set up BT Rx handlers */
if (priv->cfg->lib->bt_rx_handler_setup)
@ -1152,11 +1152,11 @@ int iwl_rx_dispatch(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
* even if the RX handler consumes the RXB we have
* access to it in the notification wait entry.
*/
if (!list_empty(&priv->notif_waits)) {
if (!list_empty(&priv->shrd->notif_waits)) {
struct iwl_notification_wait *w;
spin_lock(&priv->notif_wait_lock);
list_for_each_entry(w, &priv->notif_waits, list) {
spin_lock(&priv->shrd->notif_wait_lock);
list_for_each_entry(w, &priv->shrd->notif_waits, list) {
if (w->cmd != pkt->hdr.cmd)
continue;
IWL_DEBUG_RX(priv,
@ -1167,9 +1167,9 @@ int iwl_rx_dispatch(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb,
if (w->fn)
w->fn(priv, pkt, w->fn_data);
}
spin_unlock(&priv->notif_wait_lock);
spin_unlock(&priv->shrd->notif_wait_lock);
wake_up_all(&priv->notif_waitq);
wake_up_all(&priv->shrd->notif_waitq);
}
if (priv->pre_rx_handler)

View File

@ -60,7 +60,7 @@ static int iwlagn_disable_pan(struct iwl_priv *priv,
u8 old_dev_type = send->dev_type;
int ret;
iwlagn_init_notification_wait(priv, &disable_wait,
iwl_init_notification_wait(priv->shrd, &disable_wait,
REPLY_WIPAN_DEACTIVATION_COMPLETE,
NULL, NULL);
@ -74,9 +74,9 @@ static int iwlagn_disable_pan(struct iwl_priv *priv,
if (ret) {
IWL_ERR(priv, "Error disabling PAN (%d)\n", ret);
iwlagn_remove_notification(priv, &disable_wait);
iwl_remove_notification(priv->shrd, &disable_wait);
} else {
ret = iwlagn_wait_notification(priv, &disable_wait, HZ);
ret = iwl_wait_notification(priv->shrd, &disable_wait, HZ);
if (ret)
IWL_ERR(priv, "Timed out waiting for PAN disable\n");
}

View File

@ -1232,14 +1232,14 @@ int iwl_alive_start(struct iwl_priv *priv)
priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS;
priv->cur_rssi_ctx = NULL;
iwlagn_send_prio_tbl(priv);
iwl_send_prio_tbl(trans(priv));
/* FIXME: w/a to force change uCode BT state machine */
ret = iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
ret = iwl_send_bt_env(trans(priv), IWL_BT_COEX_ENV_OPEN,
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
if (ret)
return ret;
ret = iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE,
ret = iwl_send_bt_env(trans(priv), IWL_BT_COEX_ENV_CLOSE,
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
if (ret)
return ret;

View File

@ -108,8 +108,8 @@ void iwlagn_config_ht40(struct ieee80211_conf *conf,
int iwlagn_rx_calib_result(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type);
void iwlagn_send_prio_tbl(struct iwl_priv *priv);
int iwl_send_bt_env(struct iwl_trans *trans, u8 action, u8 type);
void iwl_send_prio_tbl(struct iwl_trans *trans);
int iwlagn_run_init_ucode(struct iwl_priv *priv);
int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
enum iwl_ucode_type ucode_type);
@ -356,22 +356,6 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv);
void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac);
/* notification wait support */
void __acquires(wait_entry)
iwlagn_init_notification_wait(struct iwl_priv *priv,
struct iwl_notification_wait *wait_entry,
u8 cmd,
void (*fn)(struct iwl_priv *priv,
struct iwl_rx_packet *pkt,
void *data),
void *fn_data);
int __must_check __releases(wait_entry)
iwlagn_wait_notification(struct iwl_priv *priv,
struct iwl_notification_wait *wait_entry,
unsigned long timeout);
void __releases(wait_entry)
iwlagn_remove_notification(struct iwl_priv *priv,
struct iwl_notification_wait *wait_entry);
extern int iwlagn_init_alive_start(struct iwl_priv *priv);
extern int iwl_alive_start(struct iwl_priv *priv);
/* svtool */

View File

@ -836,19 +836,6 @@ void iwl_print_rx_config_cmd(struct iwl_priv *priv,
}
#endif
static void iwlagn_abort_notification_waits(struct iwl_priv *priv)
{
unsigned long flags;
struct iwl_notification_wait *wait_entry;
spin_lock_irqsave(&priv->notif_wait_lock, flags);
list_for_each_entry(wait_entry, &priv->notif_waits, list)
wait_entry->aborted = true;
spin_unlock_irqrestore(&priv->notif_wait_lock, flags);
wake_up_all(&priv->notif_waitq);
}
void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
{
unsigned int reload_msec;
@ -860,7 +847,7 @@ void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
/* Cancel currently queued command. */
clear_bit(STATUS_HCMD_ACTIVE, &priv->shrd->status);
iwlagn_abort_notification_waits(priv);
iwl_abort_notification_waits(priv->shrd);
/* Keep the restart process from trying to send host
* commands by clearing the ready bit */

View File

@ -134,48 +134,43 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv)
*/
/* 0x0000000F - 0x00000001 */
#define IWL_DL_INFO (1 << 0)
#define IWL_DL_MAC80211 (1 << 1)
#define IWL_DL_HCMD (1 << 2)
#define IWL_DL_STATE (1 << 3)
#define IWL_DL_INFO 0x00000001
#define IWL_DL_MAC80211 0x00000002
#define IWL_DL_HCMD 0x00000004
#define IWL_DL_STATE 0x00000008
/* 0x000000F0 - 0x00000010 */
#define IWL_DL_MACDUMP (1 << 4)
#define IWL_DL_HCMD_DUMP (1 << 5)
#define IWL_DL_EEPROM (1 << 6)
#define IWL_DL_RADIO (1 << 7)
#define IWL_DL_EEPROM 0x00000040
#define IWL_DL_RADIO 0x00000080
/* 0x00000F00 - 0x00000100 */
#define IWL_DL_POWER (1 << 8)
#define IWL_DL_TEMP (1 << 9)
/* reserved (1 << 10) */
#define IWL_DL_SCAN (1 << 11)
#define IWL_DL_POWER 0x00000100
#define IWL_DL_TEMP 0x00000200
#define IWL_DL_SCAN 0x00000800
/* 0x0000F000 - 0x00001000 */
#define IWL_DL_ASSOC (1 << 12)
#define IWL_DL_DROP (1 << 13)
/* reserved (1 << 14) */
#define IWL_DL_COEX (1 << 15)
#define IWL_DL_ASSOC 0x00001000
#define IWL_DL_DROP 0x00002000
#define IWL_DL_COEX 0x00008000
/* 0x000F0000 - 0x00010000 */
#define IWL_DL_FW (1 << 16)
#define IWL_DL_RF_KILL (1 << 17)
#define IWL_DL_FW_ERRORS (1 << 18)
#define IWL_DL_LED (1 << 19)
#define IWL_DL_FW 0x00010000
#define IWL_DL_RF_KILL 0x00020000
#define IWL_DL_FW_ERRORS 0x00040000
#define IWL_DL_LED 0x00080000
/* 0x00F00000 - 0x00100000 */
#define IWL_DL_RATE (1 << 20)
#define IWL_DL_CALIB (1 << 21)
#define IWL_DL_WEP (1 << 22)
#define IWL_DL_TX (1 << 23)
#define IWL_DL_RATE 0x00100000
#define IWL_DL_CALIB 0x00200000
#define IWL_DL_WEP 0x00400000
#define IWL_DL_TX 0x00800000
/* 0x0F000000 - 0x01000000 */
#define IWL_DL_RX (1 << 24)
#define IWL_DL_ISR (1 << 25)
#define IWL_DL_HT (1 << 26)
#define IWL_DL_RX 0x01000000
#define IWL_DL_ISR 0x02000000
#define IWL_DL_HT 0x04000000
/* 0xF0000000 - 0x10000000 */
#define IWL_DL_11H (1 << 28)
#define IWL_DL_STATS (1 << 29)
#define IWL_DL_TX_REPLY (1 << 30)
#define IWL_DL_TX_QUEUES (1 << 31)
#define IWL_DL_11H 0x10000000
#define IWL_DL_STATS 0x20000000
#define IWL_DL_TX_REPLY 0x40000000
#define IWL_DL_TX_QUEUES 0x80000000
#define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a)
#define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a)
#define IWL_DEBUG_MACDUMP(p, f, a...) IWL_DEBUG(p, IWL_DL_MACDUMP, f, ## a)
#define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a)
#define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a)
#define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a)
@ -184,7 +179,6 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv)
#define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a)
#define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a)
#define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a)
#define IWL_DEBUG_HC_DUMP(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD_DUMP, f, ## a)
#define IWL_DEBUG_EEPROM(p, f, a...) IWL_DEBUG(p, IWL_DL_EEPROM, f, ## a)
#define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a)
#define IWL_DEBUG_FW(p, f, a...) IWL_DEBUG(p, IWL_DL_FW, f, ## a)
@ -206,8 +200,6 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv)
#define IWL_DEBUG_STATS_LIMIT(p, f, a...) \
IWL_DEBUG_LIMIT(p, IWL_DL_STATS, f, ## a)
#define IWL_DEBUG_TX_REPLY(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_REPLY, f, ## a)
#define IWL_DEBUG_TX_REPLY_LIMIT(p, f, a...) \
IWL_DEBUG_LIMIT(p, IWL_DL_TX_REPLY, f, ## a)
#define IWL_DEBUG_TX_QUEUES(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_QUEUES, f, ## a)
#define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a)
#define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a)

View File

@ -234,11 +234,12 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
/* default is to dump the entire data segment */
if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) {
struct iwl_trans *trans = trans(priv);
priv->dbgfs_sram_offset = 0x800000;
if (priv->ucode_type == IWL_UCODE_INIT)
priv->dbgfs_sram_len = trans(priv)->ucode_init.data.len;
if (trans->shrd->ucode_type == IWL_UCODE_INIT)
priv->dbgfs_sram_len = trans->ucode_init.data.len;
else
priv->dbgfs_sram_len = trans(priv)->ucode_rt.data.len;
priv->dbgfs_sram_len = trans->ucode_rt.data.len;
}
len = priv->dbgfs_sram_len;

View File

@ -689,35 +689,6 @@ struct iwl_force_reset {
*/
#define IWLAGN_EXT_BEACON_TIME_POS 22
/**
* struct iwl_notification_wait - notification wait entry
* @list: list head for global list
* @fn: function called with the notification
* @cmd: command ID
*
* This structure is not used directly, to wait for a
* notification declare it on the stack, and call
* iwlagn_init_notification_wait() with appropriate
* parameters. Then do whatever will cause the ucode
* to notify the driver, and to wait for that then
* call iwlagn_wait_notification().
*
* Each notification is one-shot. If at some point we
* need to support multi-shot notifications (which
* can't be allocated on the stack) we need to modify
* the code for them.
*/
struct iwl_notification_wait {
struct list_head list;
void (*fn)(struct iwl_priv *priv, struct iwl_rx_packet *pkt,
void *data);
void *fn_data;
u8 cmd;
bool triggered, aborted;
};
struct iwl_rxon_context {
struct ieee80211_vif *vif;
@ -790,6 +761,12 @@ struct iwl_testmode_trace {
dma_addr_t dma_addr;
bool trace_enabled;
};
struct iwl_testmode_sram {
u32 buff_size;
u32 num_chunks;
u8 *buff_addr;
bool sram_readed;
};
#endif
struct iwl_wipan_noa_data {
@ -883,7 +860,6 @@ struct iwl_priv {
u32 ucode_ver; /* version of ucode, copy of
iwl_ucode.ver */
enum iwl_ucode_type ucode_type;
char firmware_name[25];
struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX];
@ -987,10 +963,6 @@ struct iwl_priv {
/* counts reply_tx error */
struct reply_tx_error_statistics reply_tx_stats;
struct reply_agg_tx_error_statistics reply_agg_tx_stats;
/* notification wait support */
struct list_head notif_waits;
spinlock_t notif_wait_lock;
wait_queue_head_t notif_waitq;
/* remain-on-channel offload support */
struct ieee80211_channel *hw_roc_channel;
@ -1070,6 +1042,7 @@ struct iwl_priv {
bool led_registered;
#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL
struct iwl_testmode_trace testmode_trace;
struct iwl_testmode_sram testmode_sram;
u32 tm_fixed_rate;
#endif

View File

@ -481,15 +481,11 @@ static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct iwl_priv *priv = hw->priv;
IWL_DEBUG_MACDUMP(priv, "enter\n");
IWL_DEBUG_TX(priv, "dev->xmit(%d bytes) at rate 0x%02x\n", skb->len,
ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate);
if (iwlagn_tx_skb(priv, skb))
dev_kfree_skb_any(skb);
IWL_DEBUG_MACDUMP(priv, "leave\n");
}
static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw,
@ -521,6 +517,17 @@ static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
return -EOPNOTSUPP;
}
switch (key->cipher) {
case WLAN_CIPHER_SUITE_TKIP:
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
/* fall through */
case WLAN_CIPHER_SUITE_CCMP:
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
break;
default:
break;
}
/*
* We could program these keys into the hardware as well, but we
* don't expect much multicast traffic in IBSS and having keys

View File

@ -256,6 +256,52 @@ struct iwl_tid_data {
struct iwl_ht_agg agg;
};
/**
* enum iwl_ucode_type
*
* The type of ucode currently loaded on the hardware.
*
* @IWL_UCODE_NONE: No ucode loaded
* @IWL_UCODE_REGULAR: Normal runtime ucode
* @IWL_UCODE_INIT: Initial ucode
* @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode
*/
enum iwl_ucode_type {
IWL_UCODE_NONE,
IWL_UCODE_REGULAR,
IWL_UCODE_INIT,
IWL_UCODE_WOWLAN,
};
/**
* struct iwl_notification_wait - notification wait entry
* @list: list head for global list
* @fn: function called with the notification
* @cmd: command ID
*
* This structure is not used directly, to wait for a
* notification declare it on the stack, and call
* iwlagn_init_notification_wait() with appropriate
* parameters. Then do whatever will cause the ucode
* to notify the driver, and to wait for that then
* call iwlagn_wait_notification().
*
* Each notification is one-shot. If at some point we
* need to support multi-shot notifications (which
* can't be allocated on the stack) we need to modify
* the code for them.
*/
struct iwl_notification_wait {
struct list_head list;
void (*fn)(struct iwl_priv *priv, struct iwl_rx_packet *pkt,
void *data);
void *fn_data;
u8 cmd;
bool triggered, aborted;
};
/**
* struct iwl_shared - shared fields for all the layers of the driver
*
@ -273,6 +319,10 @@ struct iwl_tid_data {
* @sta_lock: protects the station table.
* If lock and sta_lock are needed, lock must be acquired first.
* @mutex:
* @ucode_type: indicator of loaded ucode image
* @notif_waits: things waiting for notification
* @notif_wait_lock: lock protecting notification
* @notif_waitq: head of notification wait queue
*/
struct iwl_shared {
#ifdef CONFIG_IWLWIFI_DEBUG
@ -300,6 +350,14 @@ struct iwl_shared {
struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT];
wait_queue_head_t wait_command_queue;
/* ucode related variables */
enum iwl_ucode_type ucode_type;
/* notification wait support */
struct list_head notif_waits;
spinlock_t notif_wait_lock;
wait_queue_head_t notif_waitq;
};
/*Whatever _m is (iwl_trans, iwl_priv, iwl_bus, these macros will work */
@ -443,6 +501,24 @@ bool iwl_check_for_ct_kill(struct iwl_priv *priv);
void iwl_stop_sw_queue(struct iwl_priv *priv, u8 ac);
void iwl_wake_sw_queue(struct iwl_priv *priv, u8 ac);
/* notification wait support */
void iwl_abort_notification_waits(struct iwl_shared *shrd);
void __acquires(wait_entry)
iwl_init_notification_wait(struct iwl_shared *shrd,
struct iwl_notification_wait *wait_entry,
u8 cmd,
void (*fn)(struct iwl_priv *priv,
struct iwl_rx_packet *pkt,
void *data),
void *fn_data);
int __must_check __releases(wait_entry)
iwl_wait_notification(struct iwl_shared *shrd,
struct iwl_notification_wait *wait_entry,
unsigned long timeout);
void __releases(wait_entry)
iwl_remove_notification(struct iwl_shared *shrd,
struct iwl_notification_wait *wait_entry);
#ifdef CONFIG_IWLWIFI_DEBUGFS
void iwl_reset_traffic_log(struct iwl_priv *priv);
#endif /* CONFIG_IWLWIFI_DEBUGFS */

View File

@ -106,6 +106,10 @@ struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = {
[IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, },
[IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, },
[IWL_TM_ATTR_SRAM_ADDR] = { .type = NLA_U32, },
[IWL_TM_ATTR_SRAM_SIZE] = { .type = NLA_U32, },
[IWL_TM_ATTR_SRAM_DUMP] = { .type = NLA_UNSPEC, },
};
/*
@ -177,6 +181,18 @@ void iwl_testmode_init(struct iwl_priv *priv)
{
priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt;
priv->testmode_trace.trace_enabled = false;
priv->testmode_sram.sram_readed = false;
}
static void iwl_sram_cleanup(struct iwl_priv *priv)
{
if (priv->testmode_sram.sram_readed) {
kfree(priv->testmode_sram.buff_addr);
priv->testmode_sram.buff_addr = NULL;
priv->testmode_sram.buff_size = 0;
priv->testmode_sram.num_chunks = 0;
priv->testmode_sram.sram_readed = false;
}
}
static void iwl_trace_cleanup(struct iwl_priv *priv)
@ -201,6 +217,7 @@ static void iwl_trace_cleanup(struct iwl_priv *priv)
void iwl_testmode_cleanup(struct iwl_priv *priv)
{
iwl_trace_cleanup(priv);
iwl_sram_cleanup(priv);
}
/*
@ -356,7 +373,7 @@ static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv)
struct iwl_notification_wait calib_wait;
int ret;
iwlagn_init_notification_wait(priv, &calib_wait,
iwl_init_notification_wait(priv->shrd, &calib_wait,
CALIBRATION_COMPLETE_NOTIFICATION,
NULL, NULL);
ret = iwlagn_init_alive_start(priv);
@ -366,14 +383,14 @@ static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv)
goto cfg_init_calib_error;
}
ret = iwlagn_wait_notification(priv, &calib_wait, 2 * HZ);
ret = iwl_wait_notification(priv->shrd, &calib_wait, 2 * HZ);
if (ret)
IWL_DEBUG_INFO(priv, "Error detecting"
" CALIBRATION_COMPLETE_NOTIFICATION: %d\n", ret);
return ret;
cfg_init_calib_error:
iwlagn_remove_notification(priv, &calib_wait);
iwl_remove_notification(priv->shrd, &calib_wait);
return ret;
}
@ -446,6 +463,21 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
"Error starting the device: %d\n", status);
break;
case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW:
iwl_scan_cancel_timeout(priv, 200);
iwl_trans_stop_device(trans(priv));
status = iwlagn_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN);
if (status) {
IWL_DEBUG_INFO(priv,
"Error loading WOWLAN ucode: %d\n", status);
break;
}
status = iwl_alive_start(priv);
if (status)
IWL_DEBUG_INFO(priv,
"Error starting the device: %d\n", status);
break;
case IWL_TM_CMD_APP2DEV_GET_EEPROM:
if (priv->eeprom) {
skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
@ -558,7 +590,7 @@ static int iwl_testmode_trace(struct ieee80211_hw *hw, struct nlattr **tb)
}
priv->testmode_trace.num_chunks =
DIV_ROUND_UP(priv->testmode_trace.buff_size,
TRACE_CHUNK_SIZE);
DUMP_CHUNK_SIZE);
break;
case IWL_TM_CMD_APP2DEV_END_TRACE:
@ -590,15 +622,15 @@ static int iwl_testmode_trace_dump(struct ieee80211_hw *hw, struct nlattr **tb,
idx = cb->args[4];
if (idx >= priv->testmode_trace.num_chunks)
return -ENOENT;
length = TRACE_CHUNK_SIZE;
length = DUMP_CHUNK_SIZE;
if (((idx + 1) == priv->testmode_trace.num_chunks) &&
(priv->testmode_trace.buff_size % TRACE_CHUNK_SIZE))
(priv->testmode_trace.buff_size % DUMP_CHUNK_SIZE))
length = priv->testmode_trace.buff_size %
TRACE_CHUNK_SIZE;
DUMP_CHUNK_SIZE;
NLA_PUT(skb, IWL_TM_ATTR_TRACE_DUMP, length,
priv->testmode_trace.trace_addr +
(TRACE_CHUNK_SIZE * idx));
(DUMP_CHUNK_SIZE * idx));
idx++;
cb->args[4] = idx;
return 0;
@ -644,6 +676,110 @@ static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb)
return 0;
}
/*
* This function handles the user application commands for SRAM data dump
*
* It retrieves the mandatory fields IWL_TM_ATTR_SRAM_ADDR and
* IWL_TM_ATTR_SRAM_SIZE to decide the memory area for SRAM data reading
*
* Several error will be retured, -EBUSY if the SRAM data retrieved by
* previous command has not been delivered to userspace, or -ENOMSG if
* the mandatory fields (IWL_TM_ATTR_SRAM_ADDR,IWL_TM_ATTR_SRAM_SIZE)
* are missing, or -ENOMEM if the buffer allocation fails.
*
* Otherwise 0 is replied indicating the success of the SRAM reading.
*
* @hw: ieee80211_hw object that represents the device
* @tb: gnl message fields from the user space
*/
static int iwl_testmode_sram(struct ieee80211_hw *hw, struct nlattr **tb)
{
struct iwl_priv *priv = hw->priv;
u32 base, ofs, size, maxsize;
if (priv->testmode_sram.sram_readed)
return -EBUSY;
if (!tb[IWL_TM_ATTR_SRAM_ADDR]) {
IWL_DEBUG_INFO(priv, "Error finding SRAM offset address\n");
return -ENOMSG;
}
ofs = nla_get_u32(tb[IWL_TM_ATTR_SRAM_ADDR]);
if (!tb[IWL_TM_ATTR_SRAM_SIZE]) {
IWL_DEBUG_INFO(priv, "Error finding size for SRAM reading\n");
return -ENOMSG;
}
size = nla_get_u32(tb[IWL_TM_ATTR_SRAM_SIZE]);
switch (priv->shrd->ucode_type) {
case IWL_UCODE_REGULAR:
maxsize = trans(priv)->ucode_rt.data.len;
break;
case IWL_UCODE_INIT:
maxsize = trans(priv)->ucode_init.data.len;
break;
case IWL_UCODE_WOWLAN:
maxsize = trans(priv)->ucode_wowlan.data.len;
break;
case IWL_UCODE_NONE:
IWL_DEBUG_INFO(priv, "Error, uCode does not been loaded\n");
return -ENOSYS;
default:
IWL_DEBUG_INFO(priv, "Error, unsupported uCode type\n");
return -ENOSYS;
}
if ((ofs + size) > maxsize) {
IWL_DEBUG_INFO(priv, "Invalid offset/size: out of range\n");
return -EINVAL;
}
priv->testmode_sram.buff_size = (size / 4) * 4;
priv->testmode_sram.buff_addr =
kmalloc(priv->testmode_sram.buff_size, GFP_KERNEL);
if (priv->testmode_sram.buff_addr == NULL) {
IWL_DEBUG_INFO(priv, "Error allocating memory\n");
return -ENOMEM;
}
base = 0x800000;
_iwl_read_targ_mem_words(bus(priv), base + ofs,
priv->testmode_sram.buff_addr,
priv->testmode_sram.buff_size / 4);
priv->testmode_sram.num_chunks =
DIV_ROUND_UP(priv->testmode_sram.buff_size, DUMP_CHUNK_SIZE);
priv->testmode_sram.sram_readed = true;
return 0;
}
static int iwl_testmode_sram_dump(struct ieee80211_hw *hw, struct nlattr **tb,
struct sk_buff *skb,
struct netlink_callback *cb)
{
struct iwl_priv *priv = hw->priv;
int idx, length;
if (priv->testmode_sram.sram_readed) {
idx = cb->args[4];
if (idx >= priv->testmode_sram.num_chunks) {
iwl_sram_cleanup(priv);
return -ENOENT;
}
length = DUMP_CHUNK_SIZE;
if (((idx + 1) == priv->testmode_sram.num_chunks) &&
(priv->testmode_sram.buff_size % DUMP_CHUNK_SIZE))
length = priv->testmode_sram.buff_size %
DUMP_CHUNK_SIZE;
NLA_PUT(skb, IWL_TM_ATTR_SRAM_DUMP, length,
priv->testmode_sram.buff_addr +
(DUMP_CHUNK_SIZE * idx));
idx++;
cb->args[4] = idx;
return 0;
} else
return -EFAULT;
nla_put_failure:
return -ENOBUFS;
}
/* The testmode gnl message handler that takes the gnl message from the
* user space and parses it per the policy iwl_testmode_gnl_msg_policy, then
@ -705,6 +841,7 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW:
case IWL_TM_CMD_APP2DEV_GET_EEPROM:
case IWL_TM_CMD_APP2DEV_FIXRATE_REQ:
case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW:
IWL_DEBUG_INFO(priv, "testmode cmd to driver\n");
result = iwl_testmode_driver(hw, tb);
break;
@ -721,6 +858,11 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
result = iwl_testmode_ownership(hw, tb);
break;
case IWL_TM_CMD_APP2DEV_READ_SRAM:
IWL_DEBUG_INFO(priv, "testmode sram read cmd to driver\n");
result = iwl_testmode_sram(hw, tb);
break;
default:
IWL_DEBUG_INFO(priv, "Unknown testmode command\n");
result = -ENOSYS;
@ -769,6 +911,10 @@ int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
IWL_DEBUG_INFO(priv, "uCode trace cmd to driver\n");
result = iwl_testmode_trace_dump(hw, tb, skb, cb);
break;
case IWL_TM_CMD_APP2DEV_DUMP_SRAM:
IWL_DEBUG_INFO(priv, "testmode sram dump cmd to driver\n");
result = iwl_testmode_sram_dump(hw, tb, skb, cb);
break;
default:
result = -EINVAL;
break;

View File

@ -103,14 +103,22 @@
* @IWL_TM_CMD_DEV2APP_EEPROM_RSP:
* commands from kernel space to carry the eeprom response
* to user application
*
* @IWL_TM_CMD_APP2DEV_OWNERSHIP:
* commands from user application to own change the ownership of the uCode
* if application has the ownership, the only host command from
* testmode will deliver to uCode. Default owner is driver
*
* @IWL_TM_CMD_APP2DEV_INDIRECT_REG_READ32:
* @IWL_TM_CMD_APP2DEV_INDIRECT_REG_WRITE32:
* commands from user applicaiton to indirectly access peripheral register
*
* @IWL_TM_CMD_APP2DEV_READ_SRAM:
* @IWL_TM_CMD_APP2DEV_DUMP_SRAM:
* commands from user applicaiton to read data in sram
*
* @IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: load Weak On Wireless LAN uCode image
*
*/
enum iwl_tm_cmd_t {
IWL_TM_CMD_APP2DEV_UCODE = 1,
@ -132,7 +140,10 @@ enum iwl_tm_cmd_t {
IWL_TM_CMD_APP2DEV_OWNERSHIP = 17,
IWL_TM_CMD_APP2DEV_INDIRECT_REG_READ32 = 18,
IWL_TM_CMD_APP2DEV_INDIRECT_REG_WRITE32 = 19,
IWL_TM_CMD_MAX = 20,
IWL_TM_CMD_APP2DEV_READ_SRAM = 20,
IWL_TM_CMD_APP2DEV_DUMP_SRAM = 21,
IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW = 22,
IWL_TM_CMD_MAX = 23,
};
/*
@ -202,6 +213,18 @@ enum iwl_tm_cmd_t {
* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_OWNERSHIP,
* The mandatory fields are:
* IWL_TM_ATTR_UCODE_OWNER for the new owner
*
* @IWL_TM_ATTR_SRAM_ADDR:
* @IWL_TM_ATTR_SRAM_SIZE:
* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_READ_SRAM,
* The mandatory fields are:
* IWL_TM_ATTR_SRAM_ADDR for the address in sram
* IWL_TM_ATTR_SRAM_SIZE for the buffer size of data reading
*
* @IWL_TM_ATTR_SRAM_DUMP:
* When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_DUMP_SRAM,
* IWL_TM_ATTR_SRAM_DUMP for the data in sram
*
*/
enum iwl_tm_attr_t {
IWL_TM_ATTR_NOT_APPLICABLE = 0,
@ -219,7 +242,10 @@ enum iwl_tm_attr_t {
IWL_TM_ATTR_TRACE_DUMP = 12,
IWL_TM_ATTR_FIXRATE = 13,
IWL_TM_ATTR_UCODE_OWNER = 14,
IWL_TM_ATTR_MAX = 15,
IWL_TM_ATTR_SRAM_ADDR = 15,
IWL_TM_ATTR_SRAM_SIZE = 16,
IWL_TM_ATTR_SRAM_DUMP = 17,
IWL_TM_ATTR_MAX = 18,
};
/* uCode trace buffer */
@ -227,6 +253,8 @@ enum iwl_tm_attr_t {
#define TRACE_BUFF_SIZE_MIN 0x20000
#define TRACE_BUFF_SIZE_DEF TRACE_BUFF_SIZE_MIN
#define TRACE_BUFF_PADD 0x2000
#define TRACE_CHUNK_SIZE (PAGE_SIZE - 1024)
/* Maximum data size of each dump it packet */
#define DUMP_CHUNK_SIZE (PAGE_SIZE - 1024)
#endif

View File

@ -595,7 +595,7 @@ static void iwl_dump_nic_error_log(struct iwl_trans *trans)
IWL_TRANS_GET_PCIE_TRANS(trans);
base = priv->device_pointers.error_event_table;
if (priv->ucode_type == IWL_UCODE_INIT) {
if (trans->shrd->ucode_type == IWL_UCODE_INIT) {
if (!base)
base = priv->init_errlog_ptr;
} else {
@ -607,7 +607,7 @@ static void iwl_dump_nic_error_log(struct iwl_trans *trans)
IWL_ERR(trans,
"Not valid error log pointer 0x%08X for %s uCode\n",
base,
(priv->ucode_type == IWL_UCODE_INIT)
(trans->shrd->ucode_type == IWL_UCODE_INIT)
? "Init" : "RT");
return;
}
@ -710,7 +710,7 @@ static int iwl_print_event_log(struct iwl_trans *trans, u32 start_idx,
return pos;
base = priv->device_pointers.log_event_table;
if (priv->ucode_type == IWL_UCODE_INIT) {
if (trans->shrd->ucode_type == IWL_UCODE_INIT) {
if (!base)
base = priv->init_evtlog_ptr;
} else {
@ -824,7 +824,7 @@ int iwl_dump_nic_event_log(struct iwl_trans *trans, bool full_log,
struct iwl_priv *priv = priv(trans);
base = priv->device_pointers.log_event_table;
if (priv->ucode_type == IWL_UCODE_INIT) {
if (trans->shrd->ucode_type == IWL_UCODE_INIT) {
logsize = priv->init_evtlog_size;
if (!base)
base = priv->init_evtlog_ptr;
@ -838,7 +838,7 @@ int iwl_dump_nic_event_log(struct iwl_trans *trans, bool full_log,
IWL_ERR(trans,
"Invalid event log pointer 0x%08X for %s uCode\n",
base,
(priv->ucode_type == IWL_UCODE_INIT)
(trans->shrd->ucode_type == IWL_UCODE_INIT)
? "Init" : "RT");
return -EINVAL;
}

View File

@ -220,13 +220,6 @@ struct fw_img {
struct fw_desc data; /* firmware data image */
};
enum iwl_ucode_type {
IWL_UCODE_NONE,
IWL_UCODE_REGULAR,
IWL_UCODE_INIT,
IWL_UCODE_WOWLAN,
};
/**
* struct iwl_trans - transport common data
* @ops - pointer to iwl_trans_ops

View File

@ -122,7 +122,7 @@ int iwl_alloc_fw_desc(struct iwl_bus *bus, struct fw_desc *desc,
/*
* ucode
*/
static int iwlagn_load_section(struct iwl_trans *trans, const char *name,
static int iwl_load_section(struct iwl_trans *trans, const char *name,
struct fw_desc *image, u32 dst_addr)
{
struct iwl_bus *bus = bus(trans);
@ -188,7 +188,7 @@ static inline struct fw_img *iwl_get_ucode_image(struct iwl_trans *trans,
return NULL;
}
static int iwlagn_load_given_ucode(struct iwl_trans *trans,
static int iwl_load_given_ucode(struct iwl_trans *trans,
enum iwl_ucode_type ucode_type)
{
int ret = 0;
@ -201,19 +201,19 @@ static int iwlagn_load_given_ucode(struct iwl_trans *trans,
return -EINVAL;
}
ret = iwlagn_load_section(trans, "INST", &image->code,
ret = iwl_load_section(trans, "INST", &image->code,
IWLAGN_RTC_INST_LOWER_BOUND);
if (ret)
return ret;
return iwlagn_load_section(trans, "DATA", &image->data,
return iwl_load_section(trans, "DATA", &image->data,
IWLAGN_RTC_DATA_LOWER_BOUND);
}
/*
* Calibration
*/
static int iwlagn_set_Xtal_calib(struct iwl_priv *priv)
static int iwl_set_Xtal_calib(struct iwl_priv *priv)
{
struct iwl_calib_xtal_freq_cmd cmd;
__le16 *xtal_calib =
@ -225,7 +225,7 @@ static int iwlagn_set_Xtal_calib(struct iwl_priv *priv)
return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
}
static int iwlagn_set_temperature_offset_calib(struct iwl_priv *priv)
static int iwl_set_temperature_offset_calib(struct iwl_priv *priv)
{
struct iwl_calib_temperature_offset_cmd cmd;
__le16 *offset_calib =
@ -242,7 +242,7 @@ static int iwlagn_set_temperature_offset_calib(struct iwl_priv *priv)
return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
}
static int iwlagn_set_temperature_offset_calib_v2(struct iwl_priv *priv)
static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv)
{
struct iwl_calib_temperature_offset_v2_cmd cmd;
__le16 *offset_calib_high = (__le16 *)iwl_eeprom_query_addr(priv,
@ -277,7 +277,7 @@ static int iwlagn_set_temperature_offset_calib_v2(struct iwl_priv *priv)
return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
}
static int iwlagn_send_calib_cfg(struct iwl_priv *priv)
static int iwl_send_calib_cfg(struct iwl_trans *trans)
{
struct iwl_calib_cfg_cmd calib_cfg_cmd;
struct iwl_host_cmd cmd = {
@ -293,7 +293,7 @@ static int iwlagn_send_calib_cfg(struct iwl_priv *priv)
calib_cfg_cmd.ucd_calib_cfg.flags =
IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK;
return iwl_trans_send_cmd(trans(priv), &cmd);
return iwl_trans_send_cmd(trans, &cmd);
}
int iwlagn_rx_calib_result(struct iwl_priv *priv,
@ -326,14 +326,14 @@ int iwlagn_init_alive_start(struct iwl_priv *priv)
* no need to close the envlope since we are going
* to load the runtime uCode later.
*/
ret = iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
ret = iwl_send_bt_env(trans(priv), IWL_BT_COEX_ENV_OPEN,
BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
if (ret)
return ret;
}
ret = iwlagn_send_calib_cfg(priv);
ret = iwl_send_calib_cfg(trans(priv));
if (ret)
return ret;
@ -343,15 +343,15 @@ int iwlagn_init_alive_start(struct iwl_priv *priv)
*/
if (priv->cfg->need_temp_offset_calib) {
if (priv->cfg->temp_offset_v2)
return iwlagn_set_temperature_offset_calib_v2(priv);
return iwl_set_temperature_offset_calib_v2(priv);
else
return iwlagn_set_temperature_offset_calib(priv);
return iwl_set_temperature_offset_calib(priv);
}
return 0;
}
static int iwlagn_send_wimax_coex(struct iwl_priv *priv)
static int iwl_send_wimax_coex(struct iwl_priv *priv)
{
struct iwl_wimax_coex_cmd coex_cmd;
@ -379,7 +379,7 @@ static int iwlagn_send_wimax_coex(struct iwl_priv *priv)
sizeof(coex_cmd), &coex_cmd);
}
static const u8 iwlagn_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
@ -401,42 +401,42 @@ static const u8 iwlagn_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
0, 0, 0, 0, 0, 0, 0
};
void iwlagn_send_prio_tbl(struct iwl_priv *priv)
void iwl_send_prio_tbl(struct iwl_trans *trans)
{
struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd;
memcpy(prio_tbl_cmd.prio_tbl, iwlagn_bt_prio_tbl,
sizeof(iwlagn_bt_prio_tbl));
if (iwl_trans_send_cmd_pdu(trans(priv),
memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl,
sizeof(iwl_bt_prio_tbl));
if (iwl_trans_send_cmd_pdu(trans,
REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC,
sizeof(prio_tbl_cmd), &prio_tbl_cmd))
IWL_ERR(priv, "failed to send BT prio tbl command\n");
IWL_ERR(trans, "failed to send BT prio tbl command\n");
}
int iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
int iwl_send_bt_env(struct iwl_trans *trans, u8 action, u8 type)
{
struct iwl_bt_coex_prot_env_cmd env_cmd;
int ret;
env_cmd.action = action;
env_cmd.type = type;
ret = iwl_trans_send_cmd_pdu(trans(priv),
ret = iwl_trans_send_cmd_pdu(trans,
REPLY_BT_COEX_PROT_ENV, CMD_SYNC,
sizeof(env_cmd), &env_cmd);
if (ret)
IWL_ERR(priv, "failed to send BT env command\n");
IWL_ERR(trans, "failed to send BT env command\n");
return ret;
}
static int iwlagn_alive_notify(struct iwl_priv *priv)
static int iwl_alive_notify(struct iwl_priv *priv)
{
struct iwl_rxon_context *ctx;
int ret;
if (!priv->tx_cmd_pool)
priv->tx_cmd_pool =
kmem_cache_create("iwlagn_dev_cmd",
kmem_cache_create("iwl_dev_cmd",
sizeof(struct iwl_device_cmd),
sizeof(void *), 0, NULL);
@ -447,12 +447,12 @@ static int iwlagn_alive_notify(struct iwl_priv *priv)
for_each_context(priv, ctx)
ctx->last_tx_rejected = false;
ret = iwlagn_send_wimax_coex(priv);
ret = iwl_send_wimax_coex(priv);
if (ret)
return ret;
if (!priv->cfg->no_xtal_calib) {
ret = iwlagn_set_Xtal_calib(priv);
ret = iwl_set_Xtal_calib(priv);
if (ret)
return ret;
}
@ -548,7 +548,7 @@ struct iwlagn_alive_data {
u8 subtype;
};
static void iwlagn_alive_fn(struct iwl_priv *priv,
static void iwl_alive_fn(struct iwl_priv *priv,
struct iwl_rx_packet *pkt,
void *data)
{
@ -571,6 +571,70 @@ static void iwlagn_alive_fn(struct iwl_priv *priv,
alive_data->valid = palive->is_valid == UCODE_VALID_OK;
}
/* notification wait support */
void iwl_init_notification_wait(struct iwl_shared *shrd,
struct iwl_notification_wait *wait_entry,
u8 cmd,
void (*fn)(struct iwl_priv *priv,
struct iwl_rx_packet *pkt,
void *data),
void *fn_data)
{
wait_entry->fn = fn;
wait_entry->fn_data = fn_data;
wait_entry->cmd = cmd;
wait_entry->triggered = false;
wait_entry->aborted = false;
spin_lock_bh(&shrd->notif_wait_lock);
list_add(&wait_entry->list, &shrd->notif_waits);
spin_unlock_bh(&shrd->notif_wait_lock);
}
int iwl_wait_notification(struct iwl_shared *shrd,
struct iwl_notification_wait *wait_entry,
unsigned long timeout)
{
int ret;
ret = wait_event_timeout(shrd->notif_waitq,
wait_entry->triggered || wait_entry->aborted,
timeout);
spin_lock_bh(&shrd->notif_wait_lock);
list_del(&wait_entry->list);
spin_unlock_bh(&shrd->notif_wait_lock);
if (wait_entry->aborted)
return -EIO;
/* return value is always >= 0 */
if (ret <= 0)
return -ETIMEDOUT;
return 0;
}
void iwl_remove_notification(struct iwl_shared *shrd,
struct iwl_notification_wait *wait_entry)
{
spin_lock_bh(&shrd->notif_wait_lock);
list_del(&wait_entry->list);
spin_unlock_bh(&shrd->notif_wait_lock);
}
void iwl_abort_notification_waits(struct iwl_shared *shrd)
{
unsigned long flags;
struct iwl_notification_wait *wait_entry;
spin_lock_irqsave(&shrd->notif_wait_lock, flags);
list_for_each_entry(wait_entry, &shrd->notif_waits, list)
wait_entry->aborted = true;
spin_unlock_irqrestore(&shrd->notif_wait_lock, flags);
wake_up_all(&shrd->notif_waitq);
}
#define UCODE_ALIVE_TIMEOUT HZ
#define UCODE_CALIB_TIMEOUT (2*HZ)
@ -579,41 +643,43 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
{
struct iwl_notification_wait alive_wait;
struct iwlagn_alive_data alive_data;
struct iwl_trans *trans = trans(priv);
int ret;
enum iwl_ucode_type old_type;
ret = iwl_trans_start_device(trans(priv));
ret = iwl_trans_start_device(trans);
if (ret)
return ret;
iwlagn_init_notification_wait(priv, &alive_wait, REPLY_ALIVE,
iwlagn_alive_fn, &alive_data);
iwl_init_notification_wait(trans->shrd, &alive_wait, REPLY_ALIVE,
iwl_alive_fn, &alive_data);
old_type = priv->ucode_type;
priv->ucode_type = ucode_type;
old_type = trans->shrd->ucode_type;
trans->shrd->ucode_type = ucode_type;
ret = iwlagn_load_given_ucode(trans(priv), ucode_type);
ret = iwl_load_given_ucode(trans, ucode_type);
if (ret) {
priv->ucode_type = old_type;
iwlagn_remove_notification(priv, &alive_wait);
trans->shrd->ucode_type = old_type;
iwl_remove_notification(trans->shrd, &alive_wait);
return ret;
}
iwl_trans_kick_nic(trans(priv));
iwl_trans_kick_nic(trans);
/*
* Some things may run in the background now, but we
* just wait for the ALIVE notification here.
*/
ret = iwlagn_wait_notification(priv, &alive_wait, UCODE_ALIVE_TIMEOUT);
ret = iwl_wait_notification(trans->shrd, &alive_wait,
UCODE_ALIVE_TIMEOUT);
if (ret) {
priv->ucode_type = old_type;
trans->shrd->ucode_type = old_type;
return ret;
}
if (!alive_data.valid) {
IWL_ERR(priv, "Loaded ucode is not valid!\n");
priv->ucode_type = old_type;
trans->shrd->ucode_type = old_type;
return -EIO;
}
@ -623,9 +689,9 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
* skip it for WoWLAN.
*/
if (ucode_type != IWL_UCODE_WOWLAN) {
ret = iwl_verify_ucode(trans(priv), ucode_type);
ret = iwl_verify_ucode(trans, ucode_type);
if (ret) {
priv->ucode_type = old_type;
trans->shrd->ucode_type = old_type;
return ret;
}
@ -633,11 +699,11 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
msleep(5);
}
ret = iwlagn_alive_notify(priv);
ret = iwl_alive_notify(priv);
if (ret) {
IWL_WARN(priv,
"Could not complete ALIVE transition: %d\n", ret);
priv->ucode_type = old_type;
trans->shrd->ucode_type = old_type;
return ret;
}
@ -655,10 +721,10 @@ int iwlagn_run_init_ucode(struct iwl_priv *priv)
if (!trans(priv)->ucode_init.code.len)
return 0;
if (priv->ucode_type != IWL_UCODE_NONE)
if (priv->shrd->ucode_type != IWL_UCODE_NONE)
return 0;
iwlagn_init_notification_wait(priv, &calib_wait,
iwl_init_notification_wait(priv->shrd, &calib_wait,
CALIBRATION_COMPLETE_NOTIFICATION,
NULL, NULL);
@ -675,12 +741,13 @@ int iwlagn_run_init_ucode(struct iwl_priv *priv)
* Some things may run in the background now, but we
* just wait for the calibration complete notification.
*/
ret = iwlagn_wait_notification(priv, &calib_wait, UCODE_CALIB_TIMEOUT);
ret = iwl_wait_notification(priv->shrd, &calib_wait,
UCODE_CALIB_TIMEOUT);
goto out;
error:
iwlagn_remove_notification(priv, &calib_wait);
iwl_remove_notification(priv->shrd, &calib_wait);
out:
/* Whatever happened, stop the device */
iwl_trans_stop_device(trans(priv));

View File

@ -831,11 +831,11 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev,
if (spec->supported_rates & SUPPORT_RATE_OFDM)
num_rates += 8;
channels = kzalloc(sizeof(*channels) * spec->num_channels, GFP_KERNEL);
channels = kcalloc(spec->num_channels, sizeof(*channels), GFP_KERNEL);
if (!channels)
return -ENOMEM;
rates = kzalloc(sizeof(*rates) * num_rates, GFP_KERNEL);
rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL);
if (!rates)
goto exit_free_channels;

View File

@ -262,10 +262,10 @@ int rtl92c_download_fw(struct ieee80211_hw *hw)
u32 fwsize;
enum version_8192c version = rtlhal->version;
pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name);
if (!rtlhal->pfirmware)
return 1;
pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name);
pfwheader = (struct rtl92c_firmware_header *)rtlhal->pfirmware;
pfwdata = (u8 *) rtlhal->pfirmware;
fwsize = rtlhal->fwsize;

View File

@ -42,16 +42,6 @@ config WL12XX_SDIO
If you choose to build a module, it'll be called wl12xx_sdio.
Say N if unsure.
config WL12XX_SDIO_TEST
tristate "TI wl12xx SDIO testing support"
depends on WL12XX && MMC && WL12XX_SDIO
default n
---help---
This module adds support for the SDIO bus testing with the
TI wl12xx chipsets. You probably don't want this unless you are
testing a new hardware platform. Select this if you want to test the
SDIO bus which is connected to the wl12xx chip.
config WL12XX_PLATFORM_DATA
bool
depends on WL12XX_SDIO != n || WL1251_SDIO != n

View File

@ -3,14 +3,11 @@ wl12xx-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
wl12xx_spi-objs = spi.o
wl12xx_sdio-objs = sdio.o
wl12xx_sdio_test-objs = sdio_test.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
obj-$(CONFIG_WL12XX_SDIO_TEST) += wl12xx_sdio_test.o
# small builtin driver bit
obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o

View File

@ -29,11 +29,12 @@
#include <linux/slab.h>
#include "wl12xx.h"
#include "debug.h"
#include "wl12xx_80211.h"
#include "reg.h"
#include "ps.h"
int wl1271_acx_wake_up_conditions(struct wl1271 *wl)
int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct acx_wake_up_condition *wake_up;
int ret;
@ -46,7 +47,7 @@ int wl1271_acx_wake_up_conditions(struct wl1271 *wl)
goto out;
}
wake_up->role_id = wl->role_id;
wake_up->role_id = wlvif->role_id;
wake_up->wake_up_event = wl->conf.conn.wake_up_event;
wake_up->listen_interval = wl->conf.conn.listen_interval;
@ -84,7 +85,8 @@ out:
return ret;
}
int wl1271_acx_tx_power(struct wl1271 *wl, int power)
int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif,
int power)
{
struct acx_current_tx_power *acx;
int ret;
@ -100,7 +102,7 @@ int wl1271_acx_tx_power(struct wl1271 *wl, int power)
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->current_tx_power = power * 10;
ret = wl1271_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx));
@ -114,7 +116,7 @@ out:
return ret;
}
int wl1271_acx_feature_cfg(struct wl1271 *wl)
int wl1271_acx_feature_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct acx_feature_config *feature;
int ret;
@ -128,7 +130,7 @@ int wl1271_acx_feature_cfg(struct wl1271 *wl)
}
/* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */
feature->role_id = wl->role_id;
feature->role_id = wlvif->role_id;
feature->data_flow_options = 0;
feature->options = 0;
@ -210,7 +212,8 @@ out:
return ret;
}
int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time)
int wl1271_acx_slot(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum acx_slot_type slot_time)
{
struct acx_slot *slot;
int ret;
@ -223,7 +226,7 @@ int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time)
goto out;
}
slot->role_id = wl->role_id;
slot->role_id = wlvif->role_id;
slot->wone_index = STATION_WONE_INDEX;
slot->slot_time = slot_time;
@ -238,8 +241,8 @@ out:
return ret;
}
int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
void *mc_list, u32 mc_list_len)
int wl1271_acx_group_address_tbl(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable, void *mc_list, u32 mc_list_len)
{
struct acx_dot11_grp_addr_tbl *acx;
int ret;
@ -253,7 +256,7 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
}
/* MAC filtering */
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->enabled = enable;
acx->num_groups = mc_list_len;
memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN);
@ -270,7 +273,8 @@ out:
return ret;
}
int wl1271_acx_service_period_timeout(struct wl1271 *wl)
int wl1271_acx_service_period_timeout(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
struct acx_rx_timeout *rx_timeout;
int ret;
@ -283,7 +287,7 @@ int wl1271_acx_service_period_timeout(struct wl1271 *wl)
wl1271_debug(DEBUG_ACX, "acx service period timeout");
rx_timeout->role_id = wl->role_id;
rx_timeout->role_id = wlvif->role_id;
rx_timeout->ps_poll_timeout = cpu_to_le16(wl->conf.rx.ps_poll_timeout);
rx_timeout->upsd_timeout = cpu_to_le16(wl->conf.rx.upsd_timeout);
@ -300,7 +304,8 @@ out:
return ret;
}
int wl1271_acx_rts_threshold(struct wl1271 *wl, u32 rts_threshold)
int wl1271_acx_rts_threshold(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u32 rts_threshold)
{
struct acx_rts_threshold *rts;
int ret;
@ -320,7 +325,7 @@ int wl1271_acx_rts_threshold(struct wl1271 *wl, u32 rts_threshold)
goto out;
}
rts->role_id = wl->role_id;
rts->role_id = wlvif->role_id;
rts->threshold = cpu_to_le16((u16)rts_threshold);
ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts));
@ -363,7 +368,8 @@ out:
return ret;
}
int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter)
int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable_filter)
{
struct acx_beacon_filter_option *beacon_filter = NULL;
int ret = 0;
@ -380,7 +386,7 @@ int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter)
goto out;
}
beacon_filter->role_id = wl->role_id;
beacon_filter->role_id = wlvif->role_id;
beacon_filter->enable = enable_filter;
/*
@ -401,7 +407,8 @@ out:
return ret;
}
int wl1271_acx_beacon_filter_table(struct wl1271 *wl)
int wl1271_acx_beacon_filter_table(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
struct acx_beacon_filter_ie_table *ie_table;
int i, idx = 0;
@ -417,7 +424,7 @@ int wl1271_acx_beacon_filter_table(struct wl1271 *wl)
}
/* configure default beacon pass-through rules */
ie_table->role_id = wl->role_id;
ie_table->role_id = wlvif->role_id;
ie_table->num_ie = 0;
for (i = 0; i < wl->conf.conn.bcn_filt_ie_count; i++) {
struct conf_bcn_filt_rule *r = &(wl->conf.conn.bcn_filt_ie[i]);
@ -458,7 +465,8 @@ out:
#define ACX_CONN_MONIT_DISABLE_VALUE 0xffffffff
int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable)
int wl1271_acx_conn_monit_params(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable)
{
struct acx_conn_monit_params *acx;
u32 threshold = ACX_CONN_MONIT_DISABLE_VALUE;
@ -479,7 +487,7 @@ int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable)
timeout = wl->conf.conn.bss_lose_timeout;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->synch_fail_thold = cpu_to_le32(threshold);
acx->bss_lose_timeout = cpu_to_le32(timeout);
@ -582,7 +590,7 @@ out:
return ret;
}
int wl1271_acx_bcn_dtim_options(struct wl1271 *wl)
int wl1271_acx_bcn_dtim_options(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct acx_beacon_broadcast *bb;
int ret;
@ -595,7 +603,7 @@ int wl1271_acx_bcn_dtim_options(struct wl1271 *wl)
goto out;
}
bb->role_id = wl->role_id;
bb->role_id = wlvif->role_id;
bb->beacon_rx_timeout = cpu_to_le16(wl->conf.conn.beacon_rx_timeout);
bb->broadcast_timeout = cpu_to_le16(wl->conf.conn.broadcast_timeout);
bb->rx_broadcast_in_ps = wl->conf.conn.rx_broadcast_in_ps;
@ -612,7 +620,7 @@ out:
return ret;
}
int wl1271_acx_aid(struct wl1271 *wl, u16 aid)
int wl1271_acx_aid(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 aid)
{
struct acx_aid *acx_aid;
int ret;
@ -625,7 +633,7 @@ int wl1271_acx_aid(struct wl1271 *wl, u16 aid)
goto out;
}
acx_aid->role_id = wl->role_id;
acx_aid->role_id = wlvif->role_id;
acx_aid->aid = cpu_to_le16(aid);
ret = wl1271_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid));
@ -668,7 +676,8 @@ out:
return ret;
}
int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble)
int wl1271_acx_set_preamble(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum acx_preamble_type preamble)
{
struct acx_preamble *acx;
int ret;
@ -681,7 +690,7 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble)
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->preamble = preamble;
ret = wl1271_cmd_configure(wl, ACX_PREAMBLE_TYPE, acx, sizeof(*acx));
@ -695,7 +704,7 @@ out:
return ret;
}
int wl1271_acx_cts_protect(struct wl1271 *wl,
int wl1271_acx_cts_protect(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum acx_ctsprotect_type ctsprotect)
{
struct acx_ctsprotect *acx;
@ -709,7 +718,7 @@ int wl1271_acx_cts_protect(struct wl1271 *wl,
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->ctsprotect = ctsprotect;
ret = wl1271_cmd_configure(wl, ACX_CTS_PROTECTION, acx, sizeof(*acx));
@ -739,7 +748,7 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
return 0;
}
int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
int wl1271_acx_sta_rate_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct acx_rate_policy *acx;
struct conf_tx_rate_class *c = &wl->conf.tx.sta_rc_conf;
@ -755,11 +764,11 @@ int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
}
wl1271_debug(DEBUG_ACX, "basic_rate: 0x%x, full_rate: 0x%x",
wl->basic_rate, wl->rate_set);
wlvif->basic_rate, wlvif->rate_set);
/* configure one basic rate class */
acx->rate_policy_idx = cpu_to_le32(ACX_TX_BASIC_RATE);
acx->rate_policy.enabled_rates = cpu_to_le32(wl->basic_rate);
acx->rate_policy_idx = cpu_to_le32(wlvif->sta.basic_rate_idx);
acx->rate_policy.enabled_rates = cpu_to_le32(wlvif->basic_rate);
acx->rate_policy.short_retry_limit = c->short_retry_limit;
acx->rate_policy.long_retry_limit = c->long_retry_limit;
acx->rate_policy.aflags = c->aflags;
@ -771,8 +780,8 @@ int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
}
/* configure one AP supported rate class */
acx->rate_policy_idx = cpu_to_le32(ACX_TX_AP_FULL_RATE);
acx->rate_policy.enabled_rates = cpu_to_le32(wl->rate_set);
acx->rate_policy_idx = cpu_to_le32(wlvif->sta.ap_rate_idx);
acx->rate_policy.enabled_rates = cpu_to_le32(wlvif->rate_set);
acx->rate_policy.short_retry_limit = c->short_retry_limit;
acx->rate_policy.long_retry_limit = c->long_retry_limit;
acx->rate_policy.aflags = c->aflags;
@ -788,7 +797,7 @@ int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
* (p2p packets should always go out with OFDM rates, even
* if we are currently connected to 11b AP)
*/
acx->rate_policy_idx = cpu_to_le32(ACX_TX_BASIC_RATE_P2P);
acx->rate_policy_idx = cpu_to_le32(wlvif->sta.p2p_rate_idx);
acx->rate_policy.enabled_rates =
cpu_to_le32(CONF_TX_RATE_MASK_BASIC_P2P);
acx->rate_policy.short_retry_limit = c->short_retry_limit;
@ -839,8 +848,8 @@ out:
return ret;
}
int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
u8 aifsn, u16 txop)
int wl1271_acx_ac_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop)
{
struct acx_ac_cfg *acx;
int ret = 0;
@ -855,7 +864,7 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->ac = ac;
acx->cw_min = cw_min;
acx->cw_max = cpu_to_le16(cw_max);
@ -873,7 +882,8 @@ out:
return ret;
}
int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
int wl1271_acx_tid_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 queue_id, u8 channel_type,
u8 tsid, u8 ps_scheme, u8 ack_policy,
u32 apsd_conf0, u32 apsd_conf1)
{
@ -889,7 +899,7 @@ int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->queue_id = queue_id;
acx->channel_type = channel_type;
acx->tsid = tsid;
@ -1098,7 +1108,8 @@ out:
return ret;
}
int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable)
int wl1271_acx_bet_enable(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable)
{
struct wl1271_acx_bet_enable *acx = NULL;
int ret = 0;
@ -1114,7 +1125,7 @@ int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable)
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->enable = enable ? CONF_BET_MODE_ENABLE : CONF_BET_MODE_DISABLE;
acx->max_consecutive = wl->conf.conn.bet_max_consecutive;
@ -1129,7 +1140,8 @@ out:
return ret;
}
int wl1271_acx_arp_ip_filter(struct wl1271 *wl, u8 enable, __be32 address)
int wl1271_acx_arp_ip_filter(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 enable, __be32 address)
{
struct wl1271_acx_arp_filter *acx;
int ret;
@ -1142,7 +1154,7 @@ int wl1271_acx_arp_ip_filter(struct wl1271 *wl, u8 enable, __be32 address)
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->version = ACX_IPV4_VERSION;
acx->enable = enable;
@ -1189,7 +1201,8 @@ out:
return ret;
}
int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable)
int wl1271_acx_keep_alive_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable)
{
struct wl1271_acx_keep_alive_mode *acx = NULL;
int ret = 0;
@ -1202,7 +1215,7 @@ int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable)
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->enabled = enable;
ret = wl1271_cmd_configure(wl, ACX_KEEP_ALIVE_MODE, acx, sizeof(*acx));
@ -1216,7 +1229,8 @@ out:
return ret;
}
int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid)
int wl1271_acx_keep_alive_config(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 index, u8 tpl_valid)
{
struct wl1271_acx_keep_alive_config *acx = NULL;
int ret = 0;
@ -1229,7 +1243,7 @@ int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid)
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->period = cpu_to_le32(wl->conf.conn.keep_alive_interval);
acx->index = index;
acx->tpl_validation = tpl_valid;
@ -1247,8 +1261,8 @@ out:
return ret;
}
int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
s16 thold, u8 hyst)
int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable, s16 thold, u8 hyst)
{
struct wl1271_acx_rssi_snr_trigger *acx = NULL;
int ret = 0;
@ -1261,9 +1275,9 @@ int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
goto out;
}
wl->last_rssi_event = -1;
wlvif->last_rssi_event = -1;
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->pacing = cpu_to_le16(wl->conf.roam_trigger.trigger_pacing);
acx->metric = WL1271_ACX_TRIG_METRIC_RSSI_BEACON;
acx->type = WL1271_ACX_TRIG_TYPE_EDGE;
@ -1288,7 +1302,8 @@ out:
return ret;
}
int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl)
int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
struct wl1271_acx_rssi_snr_avg_weights *acx = NULL;
struct conf_roam_trigger_settings *c = &wl->conf.roam_trigger;
@ -1302,7 +1317,7 @@ int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl)
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->rssi_beacon = c->avg_weight_rssi_beacon;
acx->rssi_data = c->avg_weight_rssi_data;
acx->snr_beacon = c->avg_weight_snr_beacon;
@ -1367,6 +1382,7 @@ out:
}
int wl1271_acx_set_ht_information(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
u16 ht_operation_mode)
{
struct wl1271_acx_ht_information *acx;
@ -1380,7 +1396,7 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl,
goto out;
}
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->ht_protection =
(u8)(ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION);
acx->rifs_mode = 0;
@ -1402,7 +1418,8 @@ out:
}
/* Configure BA session initiator/receiver parameters setting in the FW. */
int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl)
int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
struct wl1271_acx_ba_initiator_policy *acx;
int ret;
@ -1416,7 +1433,7 @@ int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl)
}
/* set for the current role */
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->tid_bitmap = wl->conf.ht.tx_ba_tid_bitmap;
acx->win_size = wl->conf.ht.tx_ba_win_size;
acx->inactivity_timeout = wl->conf.ht.inactivity_timeout;
@ -1494,7 +1511,8 @@ out:
return ret;
}
int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable)
int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable)
{
struct wl1271_acx_ps_rx_streaming *rx_streaming;
u32 conf_queues, enable_queues;
@ -1523,7 +1541,7 @@ int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable)
if (!(conf_queues & BIT(i)))
continue;
rx_streaming->role_id = wl->role_id;
rx_streaming->role_id = wlvif->role_id;
rx_streaming->tid = i;
rx_streaming->enable = enable_queues & BIT(i);
rx_streaming->period = wl->conf.rx_streaming.interval;
@ -1542,7 +1560,7 @@ out:
return ret;
}
int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl)
int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl1271_acx_ap_max_tx_retry *acx = NULL;
int ret;
@ -1553,7 +1571,7 @@ int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl)
if (!acx)
return -ENOMEM;
acx->role_id = wl->role_id;
acx->role_id = wlvif->role_id;
acx->max_tx_retry = cpu_to_le16(wl->conf.tx.max_tx_retries);
ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx));
@ -1567,7 +1585,7 @@ out:
return ret;
}
int wl1271_acx_config_ps(struct wl1271 *wl)
int wl12xx_acx_config_ps(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl1271_acx_config_ps *config_ps;
int ret;
@ -1582,7 +1600,7 @@ int wl1271_acx_config_ps(struct wl1271 *wl)
config_ps->exit_retries = wl->conf.conn.psm_exit_retries;
config_ps->enter_retries = wl->conf.conn.psm_entry_retries;
config_ps->null_data_rate = cpu_to_le32(wl->basic_rate);
config_ps->null_data_rate = cpu_to_le32(wlvif->basic_rate);
ret = wl1271_cmd_configure(wl, ACX_CONFIG_PS, config_ps,
sizeof(*config_ps));

View File

@ -654,11 +654,6 @@ struct acx_rate_class {
u8 reserved;
};
#define ACX_TX_BASIC_RATE 0
#define ACX_TX_AP_FULL_RATE 1
#define ACX_TX_BASIC_RATE_P2P 2
#define ACX_TX_AP_MODE_MGMT_RATE 4
#define ACX_TX_AP_MODE_BCST_RATE 5
struct acx_rate_policy {
struct acx_header header;
@ -1234,39 +1229,49 @@ enum {
};
int wl1271_acx_wake_up_conditions(struct wl1271 *wl);
int wl1271_acx_wake_up_conditions(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth);
int wl1271_acx_tx_power(struct wl1271 *wl, int power);
int wl1271_acx_feature_cfg(struct wl1271 *wl);
int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif,
int power);
int wl1271_acx_feature_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_acx_mem_map(struct wl1271 *wl,
struct acx_header *mem_map, size_t len);
int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl);
int wl1271_acx_pd_threshold(struct wl1271 *wl);
int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time);
int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
void *mc_list, u32 mc_list_len);
int wl1271_acx_service_period_timeout(struct wl1271 *wl);
int wl1271_acx_rts_threshold(struct wl1271 *wl, u32 rts_threshold);
int wl1271_acx_slot(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum acx_slot_type slot_time);
int wl1271_acx_group_address_tbl(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable, void *mc_list, u32 mc_list_len);
int wl1271_acx_service_period_timeout(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl1271_acx_rts_threshold(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u32 rts_threshold);
int wl1271_acx_dco_itrim_params(struct wl1271 *wl);
int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter);
int wl1271_acx_beacon_filter_table(struct wl1271 *wl);
int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable);
int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable_filter);
int wl1271_acx_beacon_filter_table(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl1271_acx_conn_monit_params(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable);
int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable);
int wl12xx_acx_sg_cfg(struct wl1271 *wl);
int wl1271_acx_cca_threshold(struct wl1271 *wl);
int wl1271_acx_bcn_dtim_options(struct wl1271 *wl);
int wl1271_acx_aid(struct wl1271 *wl, u16 aid);
int wl1271_acx_bcn_dtim_options(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_acx_aid(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 aid);
int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask);
int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble);
int wl1271_acx_cts_protect(struct wl1271 *wl,
int wl1271_acx_set_preamble(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum acx_preamble_type preamble);
int wl1271_acx_cts_protect(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum acx_ctsprotect_type ctsprotect);
int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
int wl1271_acx_sta_rate_policies(struct wl1271 *wl);
int wl1271_acx_sta_rate_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
u8 idx);
int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
u8 aifsn, u16 txop);
int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
int wl1271_acx_ac_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop);
int wl1271_acx_tid_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 queue_id, u8 channel_type,
u8 tsid, u8 ps_scheme, u8 ack_policy,
u32 apsd_conf0, u32 apsd_conf1);
int wl1271_acx_frag_threshold(struct wl1271 *wl, u32 frag_threshold);
@ -1276,26 +1281,34 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl);
int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap);
int wl1271_acx_init_rx_interrupt(struct wl1271 *wl);
int wl1271_acx_smart_reflex(struct wl1271 *wl);
int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable);
int wl1271_acx_arp_ip_filter(struct wl1271 *wl, u8 enable, __be32 address);
int wl1271_acx_bet_enable(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable);
int wl1271_acx_arp_ip_filter(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 enable, __be32 address);
int wl1271_acx_pm_config(struct wl1271 *wl);
int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable);
int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid);
int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
s16 thold, u8 hyst);
int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl);
int wl1271_acx_keep_alive_mode(struct wl1271 *wl, struct wl12xx_vif *vif,
bool enable);
int wl1271_acx_keep_alive_config(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 index, u8 tpl_valid);
int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable, s16 thold, u8 hyst);
int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
struct ieee80211_sta_ht_cap *ht_cap,
bool allow_ht_operation, u8 hlid);
int wl1271_acx_set_ht_information(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
u16 ht_operation_mode);
int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl);
int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
u16 ssn, bool enable, u8 peer_hlid);
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable);
int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl);
int wl1271_acx_config_ps(struct wl1271 *wl);
int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable);
int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_acx_config_ps(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
int wl1271_acx_fm_coex(struct wl1271 *wl);
int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);

View File

@ -25,6 +25,7 @@
#include <linux/wl12xx.h>
#include <linux/export.h>
#include "debug.h"
#include "acx.h"
#include "reg.h"
#include "boot.h"
@ -347,6 +348,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
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));
@ -358,6 +362,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
nvs_ptr += 4;
dest_addr += 4;
}
if (nvs_ptr >= (u8 *) wl->nvs + nvs_len)
goto out_badnvs;
}
/*
@ -369,6 +376,10 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
*/
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 */
@ -384,6 +395,10 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
kfree(nvs_aligned);
return 0;
out_badnvs:
wl1271_error("nvs data is malformed");
return -EILSEQ;
}
static void wl1271_boot_enable_interrupts(struct wl1271 *wl)

View File

@ -29,6 +29,7 @@
#include <linux/slab.h>
#include "wl12xx.h"
#include "debug.h"
#include "reg.h"
#include "io.h"
#include "acx.h"
@ -120,6 +121,11 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
if (!wl->nvs)
return -ENODEV;
if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
wl1271_warning("FEM index from INI out of bounds");
return -EINVAL;
}
gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL);
if (!gen_parms)
return -ENOMEM;
@ -143,6 +149,12 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
gp->tx_bip_fem_manufacturer =
gen_parms->general_params.tx_bip_fem_manufacturer;
if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
wl1271_warning("FEM index from FW out of bounds");
ret = -EINVAL;
goto out;
}
wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n",
answer ? "auto" : "manual", gp->tx_bip_fem_manufacturer);
@ -162,6 +174,11 @@ int wl128x_cmd_general_parms(struct wl1271 *wl)
if (!wl->nvs)
return -ENODEV;
if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
wl1271_warning("FEM index from ini out of bounds");
return -EINVAL;
}
gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL);
if (!gen_parms)
return -ENOMEM;
@ -186,6 +203,12 @@ int wl128x_cmd_general_parms(struct wl1271 *wl)
gp->tx_bip_fem_manufacturer =
gen_parms->general_params.tx_bip_fem_manufacturer;
if (gp->tx_bip_fem_manufacturer >= WL1271_INI_FEM_MODULE_COUNT) {
wl1271_warning("FEM index from FW out of bounds");
ret = -EINVAL;
goto out;
}
wl1271_debug(DEBUG_CMD, "FEM autodetect: %s, manufacturer: %d\n",
answer ? "auto" : "manual", gp->tx_bip_fem_manufacturer);
@ -358,7 +381,8 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
return 0;
}
int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 role_type, u8 *role_id)
int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
u8 *role_id)
{
struct wl12xx_cmd_role_enable *cmd;
int ret;
@ -381,7 +405,7 @@ int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 role_type, u8 *role_id)
goto out_free;
}
memcpy(cmd->mac_address, wl->mac_addr, ETH_ALEN);
memcpy(cmd->mac_address, addr, ETH_ALEN);
cmd->role_type = role_type;
ret = wl1271_cmd_send(wl, CMD_ROLE_ENABLE, cmd, sizeof(*cmd), 0);
@ -433,37 +457,41 @@ out:
return ret;
}
static int wl12xx_allocate_link(struct wl1271 *wl, u8 *hlid)
int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
{
u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS);
if (link >= WL12XX_MAX_LINKS)
return -EBUSY;
__set_bit(link, wl->links_map);
__set_bit(link, wlvif->links_map);
*hlid = link;
return 0;
}
static void wl12xx_free_link(struct wl1271 *wl, u8 *hlid)
void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
{
if (*hlid == WL12XX_INVALID_LINK_ID)
return;
__clear_bit(*hlid, wl->links_map);
__clear_bit(*hlid, wlvif->links_map);
*hlid = WL12XX_INVALID_LINK_ID;
}
static int wl12xx_get_new_session_id(struct wl1271 *wl)
static int wl12xx_get_new_session_id(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
if (wl->session_counter >= SESSION_COUNTER_MAX)
wl->session_counter = 0;
if (wlvif->session_counter >= SESSION_COUNTER_MAX)
wlvif->session_counter = 0;
wl->session_counter++;
wlvif->session_counter++;
return wl->session_counter;
return wlvif->session_counter;
}
int wl12xx_cmd_role_start_dev(struct wl1271 *wl)
static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
struct wl12xx_cmd_role_start *cmd;
int ret;
@ -474,20 +502,20 @@ int wl12xx_cmd_role_start_dev(struct wl1271 *wl)
goto out;
}
wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wl->dev_role_id);
wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id);
cmd->role_id = wl->dev_role_id;
if (wl->band == IEEE80211_BAND_5GHZ)
cmd->role_id = wlvif->dev_role_id;
if (wlvif->band == IEEE80211_BAND_5GHZ)
cmd->band = WL12XX_BAND_5GHZ;
cmd->channel = wl->channel;
cmd->channel = wlvif->channel;
if (wl->dev_hlid == WL12XX_INVALID_LINK_ID) {
ret = wl12xx_allocate_link(wl, &wl->dev_hlid);
if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) {
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->dev_hlid);
if (ret)
goto out_free;
}
cmd->device.hlid = wl->dev_hlid;
cmd->device.session = wl->session_counter;
cmd->device.hlid = wlvif->dev_hlid;
cmd->device.session = wlvif->session_counter;
wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d",
cmd->role_id, cmd->device.hlid, cmd->device.session);
@ -502,9 +530,7 @@ int wl12xx_cmd_role_start_dev(struct wl1271 *wl)
err_hlid:
/* clear links on error */
__clear_bit(wl->dev_hlid, wl->links_map);
wl->dev_hlid = WL12XX_INVALID_LINK_ID;
wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid);
out_free:
kfree(cmd);
@ -513,12 +539,13 @@ out:
return ret;
}
int wl12xx_cmd_role_stop_dev(struct wl1271 *wl)
static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
struct wl12xx_cmd_role_stop *cmd;
int ret;
if (WARN_ON(wl->dev_hlid == WL12XX_INVALID_LINK_ID))
if (WARN_ON(wlvif->dev_hlid == WL12XX_INVALID_LINK_ID))
return -EINVAL;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@ -529,7 +556,7 @@ int wl12xx_cmd_role_stop_dev(struct wl1271 *wl)
wl1271_debug(DEBUG_CMD, "cmd role stop dev");
cmd->role_id = wl->dev_role_id;
cmd->role_id = wlvif->dev_role_id;
cmd->disc_type = DISCONNECT_IMMEDIATE;
cmd->reason = cpu_to_le16(WLAN_REASON_UNSPECIFIED);
@ -545,7 +572,7 @@ int wl12xx_cmd_role_stop_dev(struct wl1271 *wl)
goto out_free;
}
wl12xx_free_link(wl, &wl->dev_hlid);
wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid);
out_free:
kfree(cmd);
@ -554,8 +581,9 @@ out:
return ret;
}
int wl12xx_cmd_role_start_sta(struct wl1271 *wl)
int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct wl12xx_cmd_role_start *cmd;
int ret;
@ -565,33 +593,33 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl)
goto out;
}
wl1271_debug(DEBUG_CMD, "cmd role start sta %d", wl->role_id);
wl1271_debug(DEBUG_CMD, "cmd role start sta %d", wlvif->role_id);
cmd->role_id = wl->role_id;
if (wl->band == IEEE80211_BAND_5GHZ)
cmd->role_id = wlvif->role_id;
if (wlvif->band == IEEE80211_BAND_5GHZ)
cmd->band = WL12XX_BAND_5GHZ;
cmd->channel = wl->channel;
cmd->sta.basic_rate_set = cpu_to_le32(wl->basic_rate_set);
cmd->sta.beacon_interval = cpu_to_le16(wl->beacon_int);
cmd->channel = wlvif->channel;
cmd->sta.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
cmd->sta.beacon_interval = cpu_to_le16(wlvif->beacon_int);
cmd->sta.ssid_type = WL12XX_SSID_TYPE_ANY;
cmd->sta.ssid_len = wl->ssid_len;
memcpy(cmd->sta.ssid, wl->ssid, wl->ssid_len);
memcpy(cmd->sta.bssid, wl->bssid, ETH_ALEN);
cmd->sta.local_rates = cpu_to_le32(wl->rate_set);
cmd->sta.ssid_len = wlvif->ssid_len;
memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
if (wl->sta_hlid == WL12XX_INVALID_LINK_ID) {
ret = wl12xx_allocate_link(wl, &wl->sta_hlid);
if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid);
if (ret)
goto out_free;
}
cmd->sta.hlid = wl->sta_hlid;
cmd->sta.session = wl12xx_get_new_session_id(wl);
cmd->sta.remote_rates = cpu_to_le32(wl->rate_set);
cmd->sta.hlid = wlvif->sta.hlid;
cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif);
cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set);
wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
"basic_rate_set: 0x%x, remote_rates: 0x%x",
wl->role_id, cmd->sta.hlid, cmd->sta.session,
wl->basic_rate_set, wl->rate_set);
wlvif->role_id, cmd->sta.hlid, cmd->sta.session,
wlvif->basic_rate_set, wlvif->rate_set);
ret = wl1271_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
if (ret < 0) {
@ -603,7 +631,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl)
err_hlid:
/* clear links on error. */
wl12xx_free_link(wl, &wl->sta_hlid);
wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
out_free:
kfree(cmd);
@ -613,12 +641,12 @@ out:
}
/* use this function to stop ibss as well */
int wl12xx_cmd_role_stop_sta(struct wl1271 *wl)
int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl12xx_cmd_role_stop *cmd;
int ret;
if (WARN_ON(wl->sta_hlid == WL12XX_INVALID_LINK_ID))
if (WARN_ON(wlvif->sta.hlid == WL12XX_INVALID_LINK_ID))
return -EINVAL;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@ -627,9 +655,9 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl)
goto out;
}
wl1271_debug(DEBUG_CMD, "cmd role stop sta %d", wl->role_id);
wl1271_debug(DEBUG_CMD, "cmd role stop sta %d", wlvif->role_id);
cmd->role_id = wl->role_id;
cmd->role_id = wlvif->role_id;
cmd->disc_type = DISCONNECT_IMMEDIATE;
cmd->reason = cpu_to_le16(WLAN_REASON_UNSPECIFIED);
@ -639,7 +667,7 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl)
goto out_free;
}
wl12xx_free_link(wl, &wl->sta_hlid);
wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
out_free:
kfree(cmd);
@ -648,16 +676,17 @@ out:
return ret;
}
int wl12xx_cmd_role_start_ap(struct wl1271 *wl)
int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl12xx_cmd_role_start *cmd;
struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
int ret;
wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wl->role_id);
wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wlvif->role_id);
/* trying to use hidden SSID with an old hostapd version */
if (wl->ssid_len == 0 && !bss_conf->hidden_ssid) {
if (wlvif->ssid_len == 0 && !bss_conf->hidden_ssid) {
wl1271_error("got a null SSID from beacon/bss");
ret = -EINVAL;
goto out;
@ -669,30 +698,30 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl)
goto out;
}
ret = wl12xx_allocate_link(wl, &wl->ap_global_hlid);
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->ap.global_hlid);
if (ret < 0)
goto out_free;
ret = wl12xx_allocate_link(wl, &wl->ap_bcast_hlid);
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->ap.bcast_hlid);
if (ret < 0)
goto out_free_global;
cmd->role_id = wl->role_id;
cmd->role_id = wlvif->role_id;
cmd->ap.aging_period = cpu_to_le16(wl->conf.tx.ap_aging_period);
cmd->ap.bss_index = WL1271_AP_BSS_INDEX;
cmd->ap.global_hlid = wl->ap_global_hlid;
cmd->ap.broadcast_hlid = wl->ap_bcast_hlid;
cmd->ap.basic_rate_set = cpu_to_le32(wl->basic_rate_set);
cmd->ap.beacon_interval = cpu_to_le16(wl->beacon_int);
cmd->ap.global_hlid = wlvif->ap.global_hlid;
cmd->ap.broadcast_hlid = wlvif->ap.bcast_hlid;
cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int);
cmd->ap.dtim_interval = bss_conf->dtim_period;
cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP;
cmd->channel = wl->channel;
cmd->channel = wlvif->channel;
if (!bss_conf->hidden_ssid) {
/* take the SSID from the beacon for backward compatibility */
cmd->ap.ssid_type = WL12XX_SSID_TYPE_PUBLIC;
cmd->ap.ssid_len = wl->ssid_len;
memcpy(cmd->ap.ssid, wl->ssid, wl->ssid_len);
cmd->ap.ssid_len = wlvif->ssid_len;
memcpy(cmd->ap.ssid, wlvif->ssid, wlvif->ssid_len);
} else {
cmd->ap.ssid_type = WL12XX_SSID_TYPE_HIDDEN;
cmd->ap.ssid_len = bss_conf->ssid_len;
@ -701,7 +730,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl)
cmd->ap.local_rates = cpu_to_le32(0xffffffff);
switch (wl->band) {
switch (wlvif->band) {
case IEEE80211_BAND_2GHZ:
cmd->band = RADIO_BAND_2_4GHZ;
break;
@ -709,7 +738,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl)
cmd->band = RADIO_BAND_5GHZ;
break;
default:
wl1271_warning("ap start - unknown band: %d", (int)wl->band);
wl1271_warning("ap start - unknown band: %d", (int)wlvif->band);
cmd->band = RADIO_BAND_2_4GHZ;
break;
}
@ -723,10 +752,10 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl)
goto out_free;
out_free_bcast:
wl12xx_free_link(wl, &wl->ap_bcast_hlid);
wl12xx_free_link(wl, wlvif, &wlvif->ap.bcast_hlid);
out_free_global:
wl12xx_free_link(wl, &wl->ap_global_hlid);
wl12xx_free_link(wl, wlvif, &wlvif->ap.global_hlid);
out_free:
kfree(cmd);
@ -735,7 +764,7 @@ out:
return ret;
}
int wl12xx_cmd_role_stop_ap(struct wl1271 *wl)
int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl12xx_cmd_role_stop *cmd;
int ret;
@ -746,9 +775,9 @@ int wl12xx_cmd_role_stop_ap(struct wl1271 *wl)
goto out;
}
wl1271_debug(DEBUG_CMD, "cmd role stop ap %d", wl->role_id);
wl1271_debug(DEBUG_CMD, "cmd role stop ap %d", wlvif->role_id);
cmd->role_id = wl->role_id;
cmd->role_id = wlvif->role_id;
ret = wl1271_cmd_send(wl, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0);
if (ret < 0) {
@ -756,8 +785,8 @@ int wl12xx_cmd_role_stop_ap(struct wl1271 *wl)
goto out_free;
}
wl12xx_free_link(wl, &wl->ap_bcast_hlid);
wl12xx_free_link(wl, &wl->ap_global_hlid);
wl12xx_free_link(wl, wlvif, &wlvif->ap.bcast_hlid);
wl12xx_free_link(wl, wlvif, &wlvif->ap.global_hlid);
out_free:
kfree(cmd);
@ -766,10 +795,11 @@ out:
return ret;
}
int wl12xx_cmd_role_start_ibss(struct wl1271 *wl)
int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct wl12xx_cmd_role_start *cmd;
struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
int ret;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@ -778,35 +808,36 @@ int wl12xx_cmd_role_start_ibss(struct wl1271 *wl)
goto out;
}
wl1271_debug(DEBUG_CMD, "cmd role start ibss %d", wl->role_id);
wl1271_debug(DEBUG_CMD, "cmd role start ibss %d", wlvif->role_id);
cmd->role_id = wl->role_id;
if (wl->band == IEEE80211_BAND_5GHZ)
cmd->role_id = wlvif->role_id;
if (wlvif->band == IEEE80211_BAND_5GHZ)
cmd->band = WL12XX_BAND_5GHZ;
cmd->channel = wl->channel;
cmd->ibss.basic_rate_set = cpu_to_le32(wl->basic_rate_set);
cmd->ibss.beacon_interval = cpu_to_le16(wl->beacon_int);
cmd->channel = wlvif->channel;
cmd->ibss.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
cmd->ibss.beacon_interval = cpu_to_le16(wlvif->beacon_int);
cmd->ibss.dtim_interval = bss_conf->dtim_period;
cmd->ibss.ssid_type = WL12XX_SSID_TYPE_ANY;
cmd->ibss.ssid_len = wl->ssid_len;
memcpy(cmd->ibss.ssid, wl->ssid, wl->ssid_len);
memcpy(cmd->ibss.bssid, wl->bssid, ETH_ALEN);
cmd->sta.local_rates = cpu_to_le32(wl->rate_set);
cmd->ibss.ssid_len = wlvif->ssid_len;
memcpy(cmd->ibss.ssid, wlvif->ssid, wlvif->ssid_len);
memcpy(cmd->ibss.bssid, vif->bss_conf.bssid, ETH_ALEN);
cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
if (wl->sta_hlid == WL12XX_INVALID_LINK_ID) {
ret = wl12xx_allocate_link(wl, &wl->sta_hlid);
if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid);
if (ret)
goto out_free;
}
cmd->ibss.hlid = wl->sta_hlid;
cmd->ibss.remote_rates = cpu_to_le32(wl->rate_set);
cmd->ibss.hlid = wlvif->sta.hlid;
cmd->ibss.remote_rates = cpu_to_le32(wlvif->rate_set);
wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
"basic_rate_set: 0x%x, remote_rates: 0x%x",
wl->role_id, cmd->sta.hlid, cmd->sta.session,
wl->basic_rate_set, wl->rate_set);
wlvif->role_id, cmd->sta.hlid, cmd->sta.session,
wlvif->basic_rate_set, wlvif->rate_set);
wl1271_debug(DEBUG_CMD, "wl->bssid = %pM", wl->bssid);
wl1271_debug(DEBUG_CMD, "vif->bss_conf.bssid = %pM",
vif->bss_conf.bssid);
ret = wl1271_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
if (ret < 0) {
@ -818,7 +849,7 @@ int wl12xx_cmd_role_start_ibss(struct wl1271 *wl)
err_hlid:
/* clear links on error. */
wl12xx_free_link(wl, &wl->sta_hlid);
wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
out_free:
kfree(cmd);
@ -962,7 +993,8 @@ out:
return ret;
}
int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode)
int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 ps_mode)
{
struct wl1271_cmd_ps_params *ps_params = NULL;
int ret = 0;
@ -975,7 +1007,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode)
goto out;
}
ps_params->role_id = wl->role_id;
ps_params->role_id = wlvif->role_id;
ps_params->ps_mode = ps_mode;
ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params,
@ -1030,7 +1062,7 @@ out:
return ret;
}
int wl1271_cmd_build_null_data(struct wl1271 *wl)
int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct sk_buff *skb = NULL;
int size;
@ -1038,11 +1070,12 @@ int wl1271_cmd_build_null_data(struct wl1271 *wl)
int ret = -ENOMEM;
if (wl->bss_type == BSS_TYPE_IBSS) {
if (wlvif->bss_type == BSS_TYPE_IBSS) {
size = sizeof(struct wl12xx_null_data_template);
ptr = NULL;
} else {
skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
skb = ieee80211_nullfunc_get(wl->hw,
wl12xx_wlvif_to_vif(wlvif));
if (!skb)
goto out;
size = skb->len;
@ -1050,7 +1083,7 @@ int wl1271_cmd_build_null_data(struct wl1271 *wl)
}
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0,
wl->basic_rate);
wlvif->basic_rate);
out:
dev_kfree_skb(skb);
@ -1061,19 +1094,21 @@ out:
}
int wl1271_cmd_build_klv_null_data(struct wl1271 *wl)
int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct sk_buff *skb = NULL;
int ret = -ENOMEM;
skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
skb = ieee80211_nullfunc_get(wl->hw, vif);
if (!skb)
goto out;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV,
skb->data, skb->len,
CMD_TEMPL_KLV_IDX_NULL_DATA,
wl->basic_rate);
wlvif->basic_rate);
out:
dev_kfree_skb(skb);
@ -1084,32 +1119,35 @@ out:
}
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u16 aid)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct sk_buff *skb;
int ret = 0;
skb = ieee80211_pspoll_get(wl->hw, wl->vif);
skb = ieee80211_pspoll_get(wl->hw, vif);
if (!skb)
goto out;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data,
skb->len, 0, wl->basic_rate_set);
skb->len, 0, wlvif->basic_rate_set);
out:
dev_kfree_skb(skb);
return ret;
}
int wl1271_cmd_build_probe_req(struct wl1271 *wl,
int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len, u8 band)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct sk_buff *skb;
int ret;
u32 rate;
skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
ie, ie_len);
if (!skb) {
ret = -ENOMEM;
@ -1118,7 +1156,7 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl,
wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]);
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
if (band == IEEE80211_BAND_2GHZ)
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
skb->data, skb->len, 0, rate);
@ -1132,20 +1170,22 @@ out:
}
struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct sk_buff *skb)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
int ret;
u32 rate;
if (!skb)
skb = ieee80211_ap_probereq_get(wl->hw, wl->vif);
skb = ieee80211_ap_probereq_get(wl->hw, vif);
if (!skb)
goto out;
wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len);
rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[wl->band]);
if (wl->band == IEEE80211_BAND_2GHZ)
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]);
if (wlvif->band == IEEE80211_BAND_2GHZ)
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
skb->data, skb->len, 0, rate);
else
@ -1159,9 +1199,11 @@ out:
return skb;
}
int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr)
int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif,
__be32 ip_addr)
{
int ret;
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct wl12xx_arp_rsp_template tmpl;
struct ieee80211_hdr_3addr *hdr;
struct arphdr *arp_hdr;
@ -1173,8 +1215,8 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr)
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_DATA |
IEEE80211_FCTL_TODS);
memcpy(hdr->addr1, wl->vif->bss_conf.bssid, ETH_ALEN);
memcpy(hdr->addr2, wl->vif->addr, ETH_ALEN);
memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN);
memcpy(hdr->addr2, vif->addr, ETH_ALEN);
memset(hdr->addr3, 0xff, ETH_ALEN);
/* llc layer */
@ -1190,25 +1232,26 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr)
arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);
/* arp payload */
memcpy(tmpl.sender_hw, wl->vif->addr, ETH_ALEN);
memcpy(tmpl.sender_hw, vif->addr, ETH_ALEN);
tmpl.sender_ip = ip_addr;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP,
&tmpl, sizeof(tmpl), 0,
wl->basic_rate);
wlvif->basic_rate);
return ret;
}
int wl1271_build_qos_null_data(struct wl1271 *wl)
int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct ieee80211_qos_hdr template;
memset(&template, 0, sizeof(template));
memcpy(template.addr1, wl->bssid, ETH_ALEN);
memcpy(template.addr2, wl->mac_addr, ETH_ALEN);
memcpy(template.addr3, wl->bssid, ETH_ALEN);
memcpy(template.addr1, vif->bss_conf.bssid, ETH_ALEN);
memcpy(template.addr2, vif->addr, ETH_ALEN);
memcpy(template.addr3, vif->bss_conf.bssid, ETH_ALEN);
template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_QOS_NULLFUNC |
@ -1219,7 +1262,7 @@ int wl1271_build_qos_null_data(struct wl1271 *wl)
return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template,
sizeof(template), 0,
wl->basic_rate);
wlvif->basic_rate);
}
int wl12xx_cmd_set_default_wep_key(struct wl1271 *wl, u8 id, u8 hlid)
@ -1253,7 +1296,8 @@ out:
return ret;
}
int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
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)
{
@ -1261,7 +1305,7 @@ int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
int ret = 0;
/* hlid might have already been deleted */
if (wl->sta_hlid == WL12XX_INVALID_LINK_ID)
if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)
return 0;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@ -1270,7 +1314,7 @@ int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
goto out;
}
cmd->hlid = wl->sta_hlid;
cmd->hlid = wlvif->sta.hlid;
if (key_type == KEY_WEP)
cmd->lid_key_type = WEP_DEFAULT_LID_TYPE;
@ -1321,9 +1365,10 @@ out:
* TODO: merge with sta/ibss into 1 set_key function.
* note there are slight diffs
*/
int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, u8 hlid, 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)
{
struct wl1271_cmd_set_keys *cmd;
int ret = 0;
@ -1333,7 +1378,7 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
if (!cmd)
return -ENOMEM;
if (hlid == wl->ap_bcast_hlid) {
if (hlid == wlvif->ap.bcast_hlid) {
if (key_type == KEY_WEP)
lid_type = WEP_DEFAULT_LID_TYPE;
else
@ -1411,7 +1456,8 @@ out:
return ret;
}
int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta, u8 hlid)
{
struct wl12xx_cmd_add_peer *cmd;
int i, ret;
@ -1438,13 +1484,13 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
else
cmd->psd_type[i] = WL1271_PSD_LEGACY;
sta_rates = sta->supp_rates[wl->band];
sta_rates = sta->supp_rates[wlvif->band];
if (sta->ht_cap.ht_supported)
sta_rates |= sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET;
cmd->supported_rates =
cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates,
wl->band));
wlvif->band));
wl1271_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x",
cmd->supported_rates, sta->uapsd_queues);
@ -1584,12 +1630,13 @@ out:
return ret;
}
static int wl12xx_cmd_roc(struct wl1271 *wl, u8 role_id)
static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 role_id)
{
struct wl12xx_cmd_roc *cmd;
int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wl->channel, role_id);
wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wlvif->channel, role_id);
if (WARN_ON(role_id == WL12XX_INVALID_ROLE_ID))
return -EINVAL;
@ -1601,8 +1648,8 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, u8 role_id)
}
cmd->role_id = role_id;
cmd->channel = wl->channel;
switch (wl->band) {
cmd->channel = wlvif->channel;
switch (wlvif->band) {
case IEEE80211_BAND_2GHZ:
cmd->band = RADIO_BAND_2_4GHZ;
break;
@ -1610,7 +1657,7 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, u8 role_id)
cmd->band = RADIO_BAND_5GHZ;
break;
default:
wl1271_error("roc - unknown band: %d", (int)wl->band);
wl1271_error("roc - unknown band: %d", (int)wlvif->band);
ret = -EINVAL;
goto out_free;
}
@ -1657,14 +1704,14 @@ out:
return ret;
}
int wl12xx_roc(struct wl1271 *wl, u8 role_id)
int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id)
{
int ret = 0;
if (WARN_ON(test_bit(role_id, wl->roc_map)))
return 0;
ret = wl12xx_cmd_roc(wl, role_id);
ret = wl12xx_cmd_roc(wl, wlvif, role_id);
if (ret < 0)
goto out;
@ -1753,3 +1800,50 @@ out_free:
out:
return ret;
}
/* start dev role and roc on its channel */
int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS ||
wlvif->bss_type == BSS_TYPE_IBSS)))
return -EINVAL;
ret = wl12xx_cmd_role_start_dev(wl, wlvif);
if (ret < 0)
goto out;
ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
if (ret < 0)
goto out_stop;
return 0;
out_stop:
wl12xx_cmd_role_stop_dev(wl, wlvif);
out:
return ret;
}
/* croc dev hlid, and stop the role */
int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS ||
wlvif->bss_type == BSS_TYPE_IBSS)))
return -EINVAL;
if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
ret = wl12xx_croc(wl, wlvif->dev_role_id);
if (ret < 0)
goto out;
}
ret = wl12xx_cmd_role_stop_dev(wl, wlvif);
if (ret < 0)
goto out;
out:
return ret;
}

View File

@ -36,45 +36,54 @@ 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 role_type, u8 *role_id);
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_dev(struct wl1271 *wl);
int wl12xx_cmd_role_stop_dev(struct wl1271 *wl);
int wl12xx_cmd_role_start_sta(struct wl1271 *wl);
int wl12xx_cmd_role_stop_sta(struct wl1271 *wl);
int wl12xx_cmd_role_start_ap(struct wl1271 *wl);
int wl12xx_cmd_role_stop_ap(struct wl1271 *wl);
int wl12xx_cmd_role_start_ibss(struct wl1271 *wl);
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, u8 ps_mode);
int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 ps_mode);
int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
size_t len);
int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
void *buf, size_t buf_len, int index, u32 rates);
int wl1271_cmd_build_null_data(struct wl1271 *wl);
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
int wl1271_cmd_build_probe_req(struct wl1271 *wl,
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 wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len, u8 band);
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, __be32 ip_addr);
int wl1271_build_qos_null_data(struct wl1271 *wl);
int wl1271_cmd_build_klv_null_data(struct wl1271 *wl);
int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif,
__be32 ip_addr);
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, u16 action, u8 id, u8 key_type,
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, u16 action, u8 id, u8 key_type,
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, u8 role_id);
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 ieee80211_sta *sta, u8 hlid);
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);
@ -82,6 +91,9 @@ int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
int wl12xx_cmd_channel_switch(struct wl1271 *wl,
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*/

View File

@ -440,6 +440,10 @@ struct conf_rx_settings {
CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \
CONF_HW_BIT_RATE_54MBPS)
#define CONF_TX_CCK_RATES (CONF_HW_BIT_RATE_1MBPS | \
CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \
CONF_HW_BIT_RATE_11MBPS)
#define CONF_TX_OFDM_RATES (CONF_HW_BIT_RATE_6MBPS | \
CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS | \
CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \

View File

@ -0,0 +1,101 @@
/*
* 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_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__ */

View File

@ -27,6 +27,7 @@
#include <linux/slab.h>
#include "wl12xx.h"
#include "debug.h"
#include "acx.h"
#include "ps.h"
#include "io.h"
@ -346,29 +347,14 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
DRIVER_STATE_PRINT_INT(tx_results_count);
DRIVER_STATE_PRINT_LHEX(flags);
DRIVER_STATE_PRINT_INT(tx_blocks_freed);
DRIVER_STATE_PRINT_INT(tx_security_last_seq_lsb);
DRIVER_STATE_PRINT_INT(rx_counter);
DRIVER_STATE_PRINT_INT(session_counter);
DRIVER_STATE_PRINT_INT(state);
DRIVER_STATE_PRINT_INT(bss_type);
DRIVER_STATE_PRINT_INT(channel);
DRIVER_STATE_PRINT_HEX(rate_set);
DRIVER_STATE_PRINT_HEX(basic_rate_set);
DRIVER_STATE_PRINT_HEX(basic_rate);
DRIVER_STATE_PRINT_INT(band);
DRIVER_STATE_PRINT_INT(beacon_int);
DRIVER_STATE_PRINT_INT(psm_entry_retry);
DRIVER_STATE_PRINT_INT(ps_poll_failures);
DRIVER_STATE_PRINT_INT(power_level);
DRIVER_STATE_PRINT_INT(rssi_thold);
DRIVER_STATE_PRINT_INT(last_rssi_event);
DRIVER_STATE_PRINT_INT(sg_enabled);
DRIVER_STATE_PRINT_INT(enable_11a);
DRIVER_STATE_PRINT_INT(noise);
DRIVER_STATE_PRINT_LHEX(ap_hlid_map[0]);
DRIVER_STATE_PRINT_INT(last_tx_hlid);
DRIVER_STATE_PRINT_INT(ba_support);
DRIVER_STATE_PRINT_HEX(ba_rx_bitmap);
DRIVER_STATE_PRINT_HEX(ap_fw_ps_map);
DRIVER_STATE_PRINT_LHEX(ap_ps_map);
DRIVER_STATE_PRINT_HEX(quirks);
@ -399,6 +385,115 @@ static const struct file_operations driver_state_ops = {
.llseek = default_llseek,
};
static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
struct wl12xx_vif *wlvif;
int ret, res = 0;
const int buf_size = 4096;
char *buf;
char tmp_buf[64];
buf = kzalloc(buf_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&wl->mutex);
#define VIF_STATE_PRINT(x, fmt) \
(res += scnprintf(buf + res, buf_size - res, \
#x " = " fmt "\n", wlvif->x))
#define VIF_STATE_PRINT_LONG(x) VIF_STATE_PRINT(x, "%ld")
#define VIF_STATE_PRINT_INT(x) VIF_STATE_PRINT(x, "%d")
#define VIF_STATE_PRINT_STR(x) VIF_STATE_PRINT(x, "%s")
#define VIF_STATE_PRINT_LHEX(x) VIF_STATE_PRINT(x, "0x%lx")
#define VIF_STATE_PRINT_LLHEX(x) VIF_STATE_PRINT(x, "0x%llx")
#define VIF_STATE_PRINT_HEX(x) VIF_STATE_PRINT(x, "0x%x")
#define VIF_STATE_PRINT_NSTR(x, len) \
do { \
memset(tmp_buf, 0, sizeof(tmp_buf)); \
memcpy(tmp_buf, wlvif->x, \
min_t(u8, len, sizeof(tmp_buf) - 1)); \
res += scnprintf(buf + res, buf_size - res, \
#x " = %s\n", tmp_buf); \
} while (0)
wl12xx_for_each_wlvif(wl, wlvif) {
VIF_STATE_PRINT_INT(role_id);
VIF_STATE_PRINT_INT(bss_type);
VIF_STATE_PRINT_LHEX(flags);
VIF_STATE_PRINT_INT(p2p);
VIF_STATE_PRINT_INT(dev_role_id);
VIF_STATE_PRINT_INT(dev_hlid);
if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
wlvif->bss_type == BSS_TYPE_IBSS) {
VIF_STATE_PRINT_INT(sta.hlid);
VIF_STATE_PRINT_INT(sta.ba_rx_bitmap);
VIF_STATE_PRINT_INT(sta.basic_rate_idx);
VIF_STATE_PRINT_INT(sta.ap_rate_idx);
VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
} else {
VIF_STATE_PRINT_INT(ap.global_hlid);
VIF_STATE_PRINT_INT(ap.bcast_hlid);
VIF_STATE_PRINT_LHEX(ap.sta_hlid_map[0]);
VIF_STATE_PRINT_INT(ap.mgmt_rate_idx);
VIF_STATE_PRINT_INT(ap.bcast_rate_idx);
VIF_STATE_PRINT_INT(ap.ucast_rate_idx[0]);
VIF_STATE_PRINT_INT(ap.ucast_rate_idx[1]);
VIF_STATE_PRINT_INT(ap.ucast_rate_idx[2]);
VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
}
VIF_STATE_PRINT_INT(last_tx_hlid);
VIF_STATE_PRINT_LHEX(links_map[0]);
VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len);
VIF_STATE_PRINT_INT(band);
VIF_STATE_PRINT_INT(channel);
VIF_STATE_PRINT_HEX(bitrate_masks[0]);
VIF_STATE_PRINT_HEX(bitrate_masks[1]);
VIF_STATE_PRINT_HEX(basic_rate_set);
VIF_STATE_PRINT_HEX(basic_rate);
VIF_STATE_PRINT_HEX(rate_set);
VIF_STATE_PRINT_INT(beacon_int);
VIF_STATE_PRINT_INT(default_key);
VIF_STATE_PRINT_INT(aid);
VIF_STATE_PRINT_INT(session_counter);
VIF_STATE_PRINT_INT(ps_poll_failures);
VIF_STATE_PRINT_INT(psm_entry_retry);
VIF_STATE_PRINT_INT(power_level);
VIF_STATE_PRINT_INT(rssi_thold);
VIF_STATE_PRINT_INT(last_rssi_event);
VIF_STATE_PRINT_INT(ba_support);
VIF_STATE_PRINT_INT(ba_allowed);
VIF_STATE_PRINT_LLHEX(tx_security_seq);
VIF_STATE_PRINT_INT(tx_security_last_seq_lsb);
}
#undef VIF_STATE_PRINT_INT
#undef VIF_STATE_PRINT_LONG
#undef VIF_STATE_PRINT_HEX
#undef VIF_STATE_PRINT_LHEX
#undef VIF_STATE_PRINT_LLHEX
#undef VIF_STATE_PRINT_STR
#undef VIF_STATE_PRINT_NSTR
#undef VIF_STATE_PRINT
mutex_unlock(&wl->mutex);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, res);
kfree(buf);
return ret;
}
static const struct file_operations vifs_state_ops = {
.read = vifs_state_read,
.open = wl1271_open_file_generic,
.llseek = default_llseek,
};
static ssize_t dtim_interval_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@ -520,6 +615,7 @@ static ssize_t rx_streaming_interval_write(struct file *file,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
struct wl12xx_vif *wlvif;
unsigned long value;
int ret;
@ -543,7 +639,9 @@ static ssize_t rx_streaming_interval_write(struct file *file,
if (ret < 0)
goto out;
wl1271_recalc_rx_streaming(wl);
wl12xx_for_each_wlvif_sta(wl, wlvif) {
wl1271_recalc_rx_streaming(wl, wlvif);
}
wl1271_ps_elp_sleep(wl);
out:
@ -572,6 +670,7 @@ static ssize_t rx_streaming_always_write(struct file *file,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
struct wl12xx_vif *wlvif;
unsigned long value;
int ret;
@ -595,7 +694,9 @@ static ssize_t rx_streaming_always_write(struct file *file,
if (ret < 0)
goto out;
wl1271_recalc_rx_streaming(wl);
wl12xx_for_each_wlvif_sta(wl, wlvif) {
wl1271_recalc_rx_streaming(wl, wlvif);
}
wl1271_ps_elp_sleep(wl);
out:
@ -624,6 +725,7 @@ static ssize_t beacon_filtering_write(struct file *file,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
struct wl12xx_vif *wlvif;
char buf[10];
size_t len;
unsigned long value;
@ -646,7 +748,9 @@ static ssize_t beacon_filtering_write(struct file *file,
if (ret < 0)
goto out;
ret = wl1271_acx_beacon_filter_opt(wl, !!value);
wl12xx_for_each_wlvif(wl, wlvif) {
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, !!value);
}
wl1271_ps_elp_sleep(wl);
out:
@ -770,6 +874,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
DEBUGFS_ADD(gpio_power, rootdir);
DEBUGFS_ADD(start_recovery, rootdir);
DEBUGFS_ADD(driver_state, rootdir);
DEBUGFS_ADD(vifs_state, rootdir);
DEBUGFS_ADD(dtim_interval, rootdir);
DEBUGFS_ADD(beacon_interval, rootdir);
DEBUGFS_ADD(beacon_filtering, rootdir);

View File

@ -22,6 +22,7 @@
*/
#include "wl12xx.h"
#include "debug.h"
#include "reg.h"
#include "io.h"
#include "event.h"
@ -31,12 +32,16 @@
void wl1271_pspoll_work(struct work_struct *work)
{
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
struct delayed_work *dwork;
struct wl1271 *wl;
int ret;
dwork = container_of(work, struct delayed_work, work);
wl = container_of(dwork, struct wl1271, pspoll_work);
wlvif = container_of(dwork, struct wl12xx_vif, pspoll_work);
vif = container_of((void *)wlvif, struct ieee80211_vif, drv_priv);
wl = wlvif->wl;
wl1271_debug(DEBUG_EVENT, "pspoll work");
@ -45,10 +50,10 @@ void wl1271_pspoll_work(struct work_struct *work)
if (unlikely(wl->state == WL1271_STATE_OFF))
goto out;
if (!test_and_clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags))
if (!test_and_clear_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags))
goto out;
if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
goto out;
/*
@ -60,31 +65,33 @@ void wl1271_pspoll_work(struct work_struct *work)
if (ret < 0)
goto out;
wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, wl->basic_rate, true);
wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE,
wlvif->basic_rate, true);
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
};
static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl)
static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
int delay = wl->conf.conn.ps_poll_recovery_period;
int ret;
wl->ps_poll_failures++;
if (wl->ps_poll_failures == 1)
wlvif->ps_poll_failures++;
if (wlvif->ps_poll_failures == 1)
wl1271_info("AP with dysfunctional ps-poll, "
"trying to work around it.");
/* force active mode receive data from the AP */
if (test_bit(WL1271_FLAG_PSM, &wl->flags)) {
ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
wl->basic_rate, true);
if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE,
wlvif->basic_rate, true);
if (ret < 0)
return;
set_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
ieee80211_queue_delayed_work(wl->hw, &wl->pspoll_work,
set_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags);
ieee80211_queue_delayed_work(wl->hw, &wlvif->pspoll_work,
msecs_to_jiffies(delay));
}
@ -97,6 +104,7 @@ static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl)
}
static int wl1271_event_ps_report(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct event_mailbox *mbox,
bool *beacon_loss)
{
@ -109,41 +117,37 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
case EVENT_ENTER_POWER_SAVE_FAIL:
wl1271_debug(DEBUG_PSM, "PSM entry failed");
if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) {
if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
/* remain in active mode */
wl->psm_entry_retry = 0;
wlvif->psm_entry_retry = 0;
break;
}
if (wl->psm_entry_retry < total_retries) {
wl->psm_entry_retry++;
ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
wl->basic_rate, true);
if (wlvif->psm_entry_retry < total_retries) {
wlvif->psm_entry_retry++;
ret = wl1271_ps_set_mode(wl, wlvif,
STATION_POWER_SAVE_MODE,
wlvif->basic_rate, true);
} else {
wl1271_info("No ack to nullfunc from AP.");
wl->psm_entry_retry = 0;
wlvif->psm_entry_retry = 0;
*beacon_loss = true;
}
break;
case EVENT_ENTER_POWER_SAVE_SUCCESS:
wl->psm_entry_retry = 0;
/* enable beacon filtering */
ret = wl1271_acx_beacon_filter_opt(wl, true);
if (ret < 0)
break;
wlvif->psm_entry_retry = 0;
/*
* BET has only a minor effect in 5GHz and masks
* channel switch IEs, so we only enable BET on 2.4GHz
*/
if (wl->band == IEEE80211_BAND_2GHZ)
if (wlvif->band == IEEE80211_BAND_2GHZ)
/* enable beacon early termination */
ret = wl1271_acx_bet_enable(wl, true);
ret = wl1271_acx_bet_enable(wl, wlvif, true);
if (wl->ps_compl) {
complete(wl->ps_compl);
wl->ps_compl = NULL;
if (wlvif->ps_compl) {
complete(wlvif->ps_compl);
wlvif->ps_compl = NULL;
}
break;
default:
@ -154,39 +158,44 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
}
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 <= wl->rssi_thold)
if (metric <= wlvif->rssi_thold)
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
else
event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
if (event != wl->last_rssi_event)
ieee80211_cqm_rssi_notify(wl->vif, event, GFP_KERNEL);
wl->last_rssi_event = event;
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)
static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
if (wl->bss_type != BSS_TYPE_AP_BSS) {
if (!wl->ba_rx_bitmap)
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(wl->vif, wl->ba_rx_bitmap,
wl->bssid);
ieee80211_stop_rx_ba_session(vif, wlvif->sta.ba_rx_bitmap,
vif->bss_conf.bssid);
} else {
int i;
u8 hlid;
struct wl1271_link *lnk;
for (i = WL1271_AP_STA_HLID_START; i < AP_MAX_LINKS; i++) {
lnk = &wl->links[i];
if (!wl1271_is_active_sta(wl, i) || !lnk->ba_bitmap)
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(wl->vif,
ieee80211_stop_rx_ba_session(vif,
lnk->ba_bitmap,
lnk->addr);
}
@ -196,14 +205,23 @@ static void wl1271_stop_ba_event(struct wl1271 *wl)
static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
u8 enable)
{
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
if (enable) {
/* disable dynamic PS when requested by the firmware */
ieee80211_disable_dyn_ps(wl->vif);
wl12xx_for_each_wlvif_sta(wl, wlvif) {
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_disable_dyn_ps(vif);
}
set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
} else {
ieee80211_enable_dyn_ps(wl->vif);
clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
wl1271_recalc_rx_streaming(wl);
wl12xx_for_each_wlvif_sta(wl, wlvif) {
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_enable_dyn_ps(vif);
wl1271_recalc_rx_streaming(wl, wlvif);
}
}
}
@ -217,10 +235,11 @@ static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
{
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
int ret;
u32 vector;
bool beacon_loss = false;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
bool disconnect_sta = false;
unsigned long sta_bitmap = 0;
@ -234,7 +253,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
wl1271_debug(DEBUG_EVENT, "status: 0x%x",
mbox->scheduled_scan_status);
wl1271_scan_stm(wl);
wl1271_scan_stm(wl, wl->scan_vif);
}
if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
@ -253,8 +272,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
}
}
if (vector & SOFT_GEMINI_SENSE_EVENT_ID &&
wl->bss_type == BSS_TYPE_STA_BSS)
if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
wl12xx_event_soft_gemini_sense(wl,
mbox->soft_gemini_sense_info);
@ -267,40 +285,54 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
* BSS_LOSE_EVENT, beacon loss has to be reported to the stack.
*
*/
if ((vector & BSS_LOSE_EVENT_ID) && !is_ap) {
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 & PS_REPORT_EVENT_ID) && !is_ap) {
if (vector & PS_REPORT_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT");
ret = wl1271_event_ps_report(wl, mbox, &beacon_loss);
if (ret < 0)
return ret;
wl12xx_for_each_wlvif_sta(wl, wlvif) {
ret = wl1271_event_ps_report(wl, wlvif,
mbox, &beacon_loss);
if (ret < 0)
return ret;
}
}
if ((vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) && !is_ap)
wl1271_event_pspoll_delivery_fail(wl);
if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID)
wl12xx_for_each_wlvif_sta(wl, wlvif) {
wl1271_event_pspoll_delivery_fail(wl, wlvif);
}
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
/* TODO: check actual multi-role support */
wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
if (wl->vif)
wl1271_event_rssi_trigger(wl, mbox);
wl12xx_for_each_wlvif_sta(wl, wlvif) {
wl1271_event_rssi_trigger(wl, wlvif, mbox);
}
}
if ((vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)) {
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", mbox->rx_ba_allowed);
"ba_allowed = 0x%x, role_id=%d",
mbox->rx_ba_allowed, role_id);
wl->ba_allowed = !!mbox->rx_ba_allowed;
wl12xx_for_each_wlvif(wl, wlvif) {
if (role_id != 0xff && role_id != wlvif->role_id)
continue;
if (wl->vif && !wl->ba_allowed)
wl1271_stop_ba_event(wl);
wlvif->ba_allowed = !!mbox->rx_ba_allowed;
if (!wlvif->ba_allowed)
wl1271_stop_ba_event(wl, wlvif);
}
}
if ((vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) && !is_ap) {
if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. "
"status = 0x%x",
mbox->channel_switch_status);
@ -309,50 +341,65 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
* 1) channel switch complete with status=0
* 2) channel switch failed status=1
*/
if (test_and_clear_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags) &&
(wl->vif))
ieee80211_chswitch_done(wl->vif,
mbox->channel_switch_status ? false : true);
/* TODO: configure only the relevant vif */
wl12xx_for_each_wlvif_sta(wl, wlvif) {
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
bool success;
if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
&wl->flags))
continue;
success = mbox->channel_switch_status ? false : true;
ieee80211_chswitch_done(vif, success);
}
}
if ((vector & DUMMY_PACKET_EVENT_ID)) {
wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
if (wl->vif)
wl1271_tx_dummy_packet(wl);
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) && is_ap) {
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) && is_ap) {
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 (is_ap && disconnect_sta) {
if (disconnect_sta) {
u32 num_packets = wl->conf.tx.max_tx_retries;
struct ieee80211_sta *sta;
const u8 *addr;
int h;
for (h = find_first_bit(&sta_bitmap, AP_MAX_LINKS);
h < AP_MAX_LINKS;
h = find_next_bit(&sta_bitmap, AP_MAX_LINKS, h+1)) {
if (!wl1271_is_active_sta(wl, 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(wl->vif, addr);
sta = ieee80211_find_sta(vif, addr);
if (sta) {
wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
ieee80211_report_low_ack(sta, num_packets);
@ -361,8 +408,11 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
}
}
if (wl->vif && beacon_loss)
ieee80211_connection_loss(wl->vif);
if (beacon_loss)
wl12xx_for_each_wlvif_sta(wl, wlvif) {
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_connection_loss(vif);
}
return 0;
}

View File

@ -132,7 +132,4 @@ void wl1271_event_mbox_config(struct wl1271 *wl);
int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
void wl1271_pspoll_work(struct work_struct *work);
/* Functions from main.c */
bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid);
#endif

View File

@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include "debug.h"
#include "init.h"
#include "wl12xx_80211.h"
#include "acx.h"
@ -33,7 +34,7 @@
#include "tx.h"
#include "io.h"
int wl1271_sta_init_templates_config(struct wl1271 *wl)
int wl1271_init_templates_config(struct wl1271 *wl)
{
int ret, i;
@ -64,7 +65,7 @@ int wl1271_sta_init_templates_config(struct wl1271 *wl)
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL,
sizeof
(struct wl12xx_qos_null_data_template),
(struct ieee80211_qos_hdr),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
@ -88,105 +89,6 @@ int wl1271_sta_init_templates_config(struct wl1271 *wl)
if (ret < 0)
return ret;
for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, NULL,
WL1271_CMD_TEMPL_DFLT_SIZE, i,
WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
}
return 0;
}
static int wl1271_ap_init_deauth_template(struct wl1271 *wl)
{
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, wl->basic_rate_set);
ret = wl1271_cmd_template_set(wl, 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_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, wl->mac_addr, ETH_ALEN);
memcpy(nullfunc->addr3, wl->mac_addr, ETH_ALEN);
rate = wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
ret = wl1271_cmd_template_set(wl, 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_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, wl->mac_addr, ETH_ALEN);
memcpy(qosnull->addr3, wl->mac_addr, ETH_ALEN);
rate = wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, qosnull,
sizeof(*qosnull), 0, rate);
out:
kfree(qosnull);
return ret;
}
static int wl1271_ap_init_templates_config(struct wl1271 *wl)
{
int ret;
/*
* Put very large empty placeholders for all templates. These
* reserve memory for later.
@ -210,22 +112,106 @@ static int wl1271_ap_init_templates_config(struct wl1271 *wl)
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, 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, CMD_TEMPL_QOS_NULL_DATA, NULL,
sizeof
(struct wl12xx_qos_null_data_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, 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, 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, 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, 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;
@ -245,31 +231,40 @@ int wl1271_init_phy_config(struct wl1271 *wl)
if (ret < 0)
return ret;
ret = wl1271_acx_slot(wl, DEFAULT_SLOT_TIME);
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);
ret = wl1271_acx_service_period_timeout(wl, wlvif);
if (ret < 0)
return ret;
ret = wl1271_acx_rts_threshold(wl, wl->hw->wiphy->rts_threshold);
ret = wl1271_acx_rts_threshold(wl, wlvif, wl->hw->wiphy->rts_threshold);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_init_beacon_filter(struct wl1271 *wl)
static int wl1271_init_sta_beacon_filter(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
int ret;
/* disable beacon filtering at this stage */
ret = wl1271_acx_beacon_filter_opt(wl, false);
ret = wl1271_acx_beacon_filter_table(wl, wlvif);
if (ret < 0)
return ret;
ret = wl1271_acx_beacon_filter_table(wl);
/* enable beacon filtering */
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
if (ret < 0)
return ret;
@ -302,11 +297,12 @@ int wl1271_init_energy_detection(struct wl1271 *wl)
return 0;
}
static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
static int wl1271_init_beacon_broadcast(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
int ret;
ret = wl1271_acx_bcn_dtim_options(wl);
ret = wl1271_acx_bcn_dtim_options(wl, wlvif);
if (ret < 0)
return ret;
@ -327,7 +323,8 @@ static int wl12xx_init_fwlog(struct wl1271 *wl)
return 0;
}
static int wl1271_sta_hw_init(struct wl1271 *wl)
/* generic sta initialization (non vif-specific) */
static int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
@ -338,25 +335,7 @@ static int wl1271_sta_hw_init(struct wl1271 *wl)
}
/* PS config */
ret = wl1271_acx_config_ps(wl);
if (ret < 0)
return ret;
ret = wl1271_sta_init_templates_config(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
if (ret < 0)
return ret;
/* Initialize connection monitoring thresholds */
ret = wl1271_acx_conn_monit_params(wl, false);
if (ret < 0)
return ret;
/* Beacon filtering */
ret = wl1271_init_beacon_filter(wl);
ret = wl12xx_acx_config_ps(wl, wlvif);
if (ret < 0)
return ret;
@ -365,103 +344,61 @@ static int wl1271_sta_hw_init(struct wl1271 *wl)
if (ret < 0)
return ret;
/* Beacons and broadcast settings */
ret = wl1271_init_beacon_broadcast(wl);
if (ret < 0)
return ret;
/* Configure for ELP power saving */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
if (ret < 0)
return ret;
/* Configure rssi/snr averaging weights */
ret = wl1271_acx_rssi_snr_avg_weights(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_sta_rate_policies(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);
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)
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, 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, false);
ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
if (ret < 0)
return ret;
return 0;
}
static int wl1271_ap_hw_init(struct wl1271 *wl)
/* generic ap initialization (non vif-specific) */
static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
ret = wl1271_ap_init_templates_config(wl);
if (ret < 0)
return ret;
/* Configure for power always on */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
if (ret < 0)
return ret;
ret = wl1271_init_ap_rates(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_ap_max_tx_retry(wl);
if (ret < 0)
return ret;
ret = wl12xx_acx_mem_cfg(wl);
if (ret < 0)
return ret;
/* initialize Tx power */
ret = wl1271_acx_tx_power(wl, wl->power_level);
ret = wl1271_init_ap_rates(wl, wlvif);
if (ret < 0)
return ret;
return 0;
}
int wl1271_ap_init_templates(struct wl1271 *wl)
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);
ret = wl1271_ap_init_deauth_template(wl, wlvif);
if (ret < 0)
return ret;
ret = wl1271_ap_init_null_template(wl);
ret = wl1271_ap_init_null_template(wl, vif);
if (ret < 0)
return ret;
ret = wl1271_ap_init_qos_null_template(wl);
ret = wl1271_ap_init_qos_null_template(wl, vif);
if (ret < 0)
return ret;
@ -469,43 +406,45 @@ int wl1271_ap_init_templates(struct wl1271 *wl)
* when operating as AP we want to receive external beacons for
* configuring ERP protection.
*/
ret = wl1271_acx_beacon_filter_opt(wl, false);
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)
static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl,
struct ieee80211_vif *vif)
{
return wl1271_ap_init_templates(wl);
return wl1271_ap_init_templates(wl, vif);
}
int wl1271_init_ap_rates(struct wl1271 *wl)
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", wl->basic_rate_set);
wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x",
wlvif->basic_rate_set);
if (wl->basic_rate_set == 0)
if (wlvif->basic_rate_set == 0)
return -EINVAL;
rc.enabled_rates = wl->basic_rate_set;
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, ACX_TX_AP_MODE_MGMT_RATE);
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, wl->basic_rate_set);
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, ACX_TX_AP_MODE_BCST_RATE);
ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.bcast_rate_idx);
if (ret < 0)
return ret;
@ -513,7 +452,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl)
* If the basic rates contain OFDM rates, use OFDM only
* rates for unicast TX as well. Else use all supported rates.
*/
if ((wl->basic_rate_set & CONF_TX_OFDM_RATES))
if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
supported_rates = CONF_TX_OFDM_RATES;
else
supported_rates = CONF_TX_AP_ENABLED_RATES;
@ -527,7 +466,8 @@ int wl1271_init_ap_rates(struct wl1271 *wl)
rc.short_retry_limit = 10;
rc.long_retry_limit = 10;
rc.aflags = 0;
ret = wl1271_acx_ap_rate_policy(wl, &rc, i);
ret = wl1271_acx_ap_rate_policy(wl, &rc,
wlvif->ap.ucast_rate_idx[i]);
if (ret < 0)
return ret;
}
@ -535,24 +475,23 @@ int wl1271_init_ap_rates(struct wl1271 *wl)
return 0;
}
static int wl1271_set_ba_policies(struct wl1271 *wl)
static int wl1271_set_ba_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
/* Reset the BA RX indicators */
wl->ba_rx_bitmap = 0;
wl->ba_allowed = true;
wlvif->ba_allowed = true;
wl->ba_rx_session_count = 0;
/* BA is supported in STA/AP modes */
if (wl->bss_type != BSS_TYPE_AP_BSS &&
wl->bss_type != BSS_TYPE_STA_BSS) {
wl->ba_support = false;
if (wlvif->bss_type != BSS_TYPE_AP_BSS &&
wlvif->bss_type != BSS_TYPE_STA_BSS) {
wlvif->ba_support = false;
return 0;
}
wl->ba_support = true;
wlvif->ba_support = true;
/* 802.11n initiator BA session setting */
return wl12xx_acx_set_ba_initiator_policy(wl);
return wl12xx_acx_set_ba_initiator_policy(wl, wlvif);
}
int wl1271_chip_specific_init(struct wl1271 *wl)
@ -562,7 +501,7 @@ int wl1271_chip_specific_init(struct wl1271 *wl)
if (wl->chip.id == CHIP_ID_1283_PG20) {
u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT)
if (!(wl->quirks & WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT))
/* Enable SDIO padding */
host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
@ -575,13 +514,150 @@ 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)
{
struct conf_tx_ac_category *conf_ac;
struct conf_tx_tid *conf_tid;
int ret, i;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
int ret;
if (wl->chip.id == CHIP_ID_1283_PG20)
ret = wl128x_cmd_general_parms(wl);
@ -602,12 +678,17 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
return ret;
/* Mode specific init */
if (is_ap)
ret = wl1271_ap_hw_init(wl);
else
ret = wl1271_sta_hw_init(wl);
/* 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;
@ -655,61 +736,20 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
goto out_free_memmap;
/* 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, conf_ac->ac, conf_ac->cw_min,
conf_ac->cw_max, conf_ac->aifsn,
conf_ac->tx_op_limit);
if (ret < 0)
goto out_free_memmap;
conf_tid = &wl->conf.tx.tid_conf[i];
ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id,
conf_tid->channel_type,
conf_tid->tsid,
conf_tid->ps_scheme,
conf_tid->ack_policy,
conf_tid->apsd_conf[0],
conf_tid->apsd_conf[1]);
if (ret < 0)
goto out_free_memmap;
}
/* Enable data path */
ret = wl1271_cmd_data_path(wl, 1);
if (ret < 0)
goto out_free_memmap;
/* Configure HW encryption */
ret = wl1271_acx_feature_cfg(wl);
if (ret < 0)
goto out_free_memmap;
/* configure PM */
ret = wl1271_acx_pm_config(wl);
if (ret < 0)
goto out_free_memmap;
/* Mode specific init - post mem init */
if (is_ap)
ret = wl1271_ap_hw_init_post_mem(wl);
else
ret = wl1271_sta_hw_init_post_mem(wl);
if (ret < 0)
goto out_free_memmap;
ret = wl12xx_acx_set_rate_mgmt_params(wl);
if (ret < 0)
goto out_free_memmap;
/* Configure initiator BA sessions policies */
ret = wl1271_set_ba_policies(wl);
if (ret < 0)
goto out_free_memmap;
/* configure hangover */
ret = wl12xx_acx_config_hangover(wl);
if (ret < 0)

View File

@ -27,13 +27,14 @@
#include "wl12xx.h"
int wl1271_hw_init_power_auth(struct wl1271 *wl);
int wl1271_sta_init_templates_config(struct wl1271 *wl);
int wl1271_init_templates_config(struct wl1271 *wl);
int wl1271_init_phy_config(struct wl1271 *wl);
int wl1271_init_pta(struct wl1271 *wl);
int wl1271_init_energy_detection(struct wl1271 *wl);
int wl1271_chip_specific_init(struct wl1271 *wl);
int wl1271_hw_init(struct wl1271 *wl);
int wl1271_init_ap_rates(struct wl1271 *wl);
int wl1271_ap_init_templates(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

@ -24,8 +24,10 @@
#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"
@ -46,7 +48,7 @@
bool wl1271_set_block_size(struct wl1271 *wl)
{
if (wl->if_ops->set_block_size) {
wl->if_ops->set_block_size(wl, WL12XX_BUS_BLOCK_SIZE);
wl->if_ops->set_block_size(wl->dev, WL12XX_BUS_BLOCK_SIZE);
return true;
}
@ -55,12 +57,12 @@ bool wl1271_set_block_size(struct wl1271 *wl)
void wl1271_disable_interrupts(struct wl1271 *wl)
{
wl->if_ops->disable_irq(wl);
disable_irq(wl->irq);
}
void wl1271_enable_interrupts(struct wl1271 *wl)
{
wl->if_ops->enable_irq(wl);
enable_irq(wl->irq);
}
/* Set the SPI partitions to access the chip addresses
@ -128,13 +130,13 @@ EXPORT_SYMBOL_GPL(wl1271_set_partition);
void wl1271_io_reset(struct wl1271 *wl)
{
if (wl->if_ops->reset)
wl->if_ops->reset(wl);
wl->if_ops->reset(wl->dev);
}
void wl1271_io_init(struct wl1271 *wl)
{
if (wl->if_ops->init)
wl->if_ops->init(wl);
wl->if_ops->init(wl->dev);
}
void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val)

View File

@ -51,23 +51,17 @@ void wl1271_enable_interrupts(struct wl1271 *wl);
void wl1271_io_reset(struct wl1271 *wl);
void wl1271_io_init(struct wl1271 *wl);
static inline struct device *wl1271_wl_to_dev(struct wl1271 *wl)
{
return wl->if_ops->dev(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, addr, buf, len, 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, addr, buf, len, fixed);
wl->if_ops->read(wl->dev, addr, buf, len, fixed);
}
static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr)
@ -155,13 +149,13 @@ static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val)
static inline void wl1271_power_off(struct wl1271 *wl)
{
wl->if_ops->power(wl, false);
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, true);
int ret = wl->if_ops->power(wl->dev, true);
if (ret == 0)
set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
@ -176,15 +170,10 @@ 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_register_hw(struct wl1271 *wl);
void wl1271_unregister_hw(struct wl1271 *wl);
int wl1271_init_ieee80211(struct wl1271 *wl);
struct ieee80211_hw *wl1271_alloc_hw(void);
int wl1271_free_hw(struct wl1271 *wl);
irqreturn_t wl1271_irq(int irq, void *data);
bool wl1271_set_block_size(struct wl1271 *wl);
int wl1271_tx_dummy_packet(struct wl1271 *wl);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@
#include "ps.h"
#include "io.h"
#include "tx.h"
#include "debug.h"
#define WL1271_WAKEUP_TIMEOUT 500
@ -32,6 +33,7 @@ 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);
@ -47,11 +49,15 @@ void wl1271_elp_work(struct work_struct *work)
if (unlikely(!test_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)))
goto out;
if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags) ||
(!test_bit(WL1271_FLAG_PSM, &wl->flags) &&
!test_bit(WL1271_FLAG_IDLE, &wl->flags)))
if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags))
goto out;
wl12xx_for_each_wlvif(wl, wlvif) {
if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags) &&
!test_bit(WL1271_FLAG_IDLE, &wl->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);
@ -65,13 +71,17 @@ out:
/* 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;
if (!test_bit(WL1271_FLAG_PSM, &wl->flags) &&
!test_bit(WL1271_FLAG_IDLE, &wl->flags))
return;
wl12xx_for_each_wlvif(wl, wlvif) {
if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags) &&
!test_bit(WL1271_FLAG_IDLE, &wl->flags))
return;
}
ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
msecs_to_jiffies(ELP_ENTRY_DELAY));
@ -143,8 +153,8 @@ out:
return 0;
}
int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
u32 rates, bool send)
int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum wl1271_cmd_ps_mode mode, u32 rates, bool send)
{
int ret;
@ -152,39 +162,34 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
case STATION_POWER_SAVE_MODE:
wl1271_debug(DEBUG_PSM, "entering psm");
ret = wl1271_acx_wake_up_conditions(wl);
ret = wl1271_acx_wake_up_conditions(wl, wlvif);
if (ret < 0) {
wl1271_error("couldn't set wake up conditions");
return ret;
}
ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE);
ret = wl1271_cmd_ps_mode(wl, wlvif, STATION_POWER_SAVE_MODE);
if (ret < 0)
return ret;
set_bit(WL1271_FLAG_PSM, &wl->flags);
set_bit(WLVIF_FLAG_PSM, &wlvif->flags);
break;
case STATION_ACTIVE_MODE:
default:
wl1271_debug(DEBUG_PSM, "leaving psm");
/* disable beacon early termination */
if (wl->band == IEEE80211_BAND_2GHZ) {
ret = wl1271_acx_bet_enable(wl, false);
if (wlvif->band == IEEE80211_BAND_2GHZ) {
ret = wl1271_acx_bet_enable(wl, wlvif, false);
if (ret < 0)
return ret;
}
/* disable beacon filtering */
ret = wl1271_acx_beacon_filter_opt(wl, false);
ret = wl1271_cmd_ps_mode(wl, wlvif, STATION_ACTIVE_MODE);
if (ret < 0)
return ret;
ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE);
if (ret < 0)
return ret;
clear_bit(WL1271_FLAG_PSM, &wl->flags);
clear_bit(WLVIF_FLAG_PSM, &wlvif->flags);
break;
}
@ -223,9 +228,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
wl1271_handle_tx_low_watermark(wl);
}
void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues)
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;
@ -235,7 +242,7 @@ void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues)
clean_queues);
rcu_read_lock();
sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr);
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);
@ -253,9 +260,10 @@ void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues)
__set_bit(hlid, &wl->ap_ps_map);
}
void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid)
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;
@ -265,7 +273,7 @@ void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid)
__clear_bit(hlid, &wl->ap_ps_map);
rcu_read_lock();
sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr);
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);

View File

@ -27,13 +27,14 @@
#include "wl12xx.h"
#include "acx.h"
int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
u32 rates, bool send);
int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum wl1271_cmd_ps_mode mode, u32 rates, bool send);
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 wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues);
void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid);
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

View File

@ -408,7 +408,7 @@
/* Firmware image load chunk size */
#define CHUNK_SIZE 512
#define CHUNK_SIZE 16384
/* Firmware image header size */
#define FW_HDR_SIZE 8

View File

@ -25,9 +25,11 @@
#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,
@ -96,7 +98,7 @@ static void wl1271_rx_status(struct wl1271 *wl,
}
static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
bool unaligned)
bool unaligned, u8 *hlid)
{
struct wl1271_rx_descriptor *desc;
struct sk_buff *skb;
@ -159,6 +161,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
* 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))
@ -169,10 +172,10 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
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", skb,
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);
seq_num, *hlid);
skb_trim(skb, skb->len - desc->pad_len);
@ -185,6 +188,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
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;
@ -192,8 +196,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status)
u32 mem_block;
u32 pkt_length;
u32 pkt_offset;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
bool had_data = false;
u8 hlid;
bool unaligned = false;
while (drv_rx_counter != fw_rx_counter) {
@ -253,8 +256,11 @@ void wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status)
*/
if (wl1271_rx_handle_data(wl,
wl->aggr_buf + pkt_offset,
pkt_length, unaligned) == 1)
had_data = true;
pkt_length, unaligned,
&hlid) == 1) {
WARN_ON(hlid >= WL12XX_MAX_LINKS);
__set_bit(hlid, active_hlids);
}
wl->rx_counter++;
drv_rx_counter++;
@ -270,17 +276,5 @@ void wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status)
if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION)
wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
if (!is_ap && wl->conf.rx_streaming.interval && had_data &&
(wl->conf.rx_streaming.always ||
test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) {
u32 timeout = wl->conf.rx_streaming.duration;
/* restart rx streaming */
if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
ieee80211_queue_work(wl->hw,
&wl->rx_streaming_enable_work);
mod_timer(&wl->rx_streaming_timer,
jiffies + msecs_to_jiffies(timeout));
}
wl12xx_rearm_rx_streaming(wl, active_hlids);
}

View File

@ -24,6 +24,7 @@
#include <linux/ieee80211.h>
#include "wl12xx.h"
#include "debug.h"
#include "cmd.h"
#include "scan.h"
#include "acx.h"
@ -34,6 +35,8 @@ 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;
bool is_sta, is_ibss;
@ -50,28 +53,31 @@ void wl1271_scan_complete_work(struct work_struct *work)
if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
goto out;
vif = wl->scan_vif;
wlvif = wl12xx_vif_to_data(vif);
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(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
/* restore hardware connection monitoring template */
wl1271_cmd_build_ap_probe_req(wl, wl->probereq);
wl1271_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq);
}
/* return to ROC if needed */
is_sta = (wl->bss_type == BSS_TYPE_STA_BSS);
is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
if (((is_sta && !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) ||
(is_ibss && !test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags))) &&
!test_bit(wl->dev_role_id, wl->roc_map)) {
is_sta = (wlvif->bss_type == BSS_TYPE_STA_BSS);
is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
if (((is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) ||
(is_ibss && !test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))) &&
!test_bit(wlvif->dev_role_id, wl->roc_map)) {
/* restore remain on channel */
wl12xx_cmd_role_start_dev(wl);
wl12xx_roc(wl, wl->dev_role_id);
wl12xx_start_dev(wl, wlvif);
}
wl1271_ps_elp_sleep(wl);
@ -155,9 +161,11 @@ static int wl1271_get_scan_channels(struct wl1271 *wl,
#define WL1271_NOTHING_TO_SCAN 1
static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
bool passive, u32 basic_rate)
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;
@ -177,11 +185,11 @@ static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
if (passive)
scan_options |= WL1271_SCAN_OPT_PASSIVE;
if (WARN_ON(wl->role_id == WL12XX_INVALID_ROLE_ID)) {
if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID)) {
ret = -EINVAL;
goto out;
}
cmd->params.role_id = wl->role_id;
cmd->params.role_id = wlvif->role_id;
cmd->params.scan_options = cpu_to_le16(scan_options);
cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
@ -194,7 +202,6 @@ static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
cmd->params.tx_rate = cpu_to_le32(basic_rate);
cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
cmd->params.tx_rate = cpu_to_le32(basic_rate);
cmd->params.tid_trigger = 0;
cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
@ -208,11 +215,11 @@ static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
}
memcpy(cmd->addr, wl->mac_addr, ETH_ALEN);
memcpy(cmd->addr, vif->addr, ETH_ALEN);
ret = wl1271_cmd_build_probe_req(wl, wl->scan.ssid, wl->scan.ssid_len,
wl->scan.req->ie, wl->scan.req->ie_len,
band);
ret = wl1271_cmd_build_probe_req(wl, wlvif, wl->scan.ssid,
wl->scan.ssid_len, wl->scan.req->ie,
wl->scan.req->ie_len, band);
if (ret < 0) {
wl1271_error("PROBE request template failed");
goto out;
@ -241,11 +248,12 @@ out:
return ret;
}
void wl1271_scan_stm(struct wl1271 *wl)
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;
u32 rate, mask;
switch (wl->scan.state) {
case WL1271_SCAN_STATE_IDLE:
@ -253,47 +261,59 @@ void wl1271_scan_stm(struct wl1271 *wl)
case WL1271_SCAN_STATE_2GHZ_ACTIVE:
band = IEEE80211_BAND_2GHZ;
rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]);
ret = wl1271_scan_send(wl, band, false, rate);
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);
wl1271_scan_stm(wl, vif);
}
break;
case WL1271_SCAN_STATE_2GHZ_PASSIVE:
band = IEEE80211_BAND_2GHZ;
rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]);
ret = wl1271_scan_send(wl, band, true, rate);
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);
wl1271_scan_stm(wl, vif);
}
break;
case WL1271_SCAN_STATE_5GHZ_ACTIVE:
band = IEEE80211_BAND_5GHZ;
rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]);
ret = wl1271_scan_send(wl, band, false, rate);
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);
wl1271_scan_stm(wl, vif);
}
break;
case WL1271_SCAN_STATE_5GHZ_PASSIVE:
band = IEEE80211_BAND_5GHZ;
rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]);
ret = wl1271_scan_send(wl, band, true, rate);
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);
wl1271_scan_stm(wl, vif);
}
break;
@ -317,7 +337,8 @@ void wl1271_scan_stm(struct wl1271 *wl)
}
}
int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len,
struct cfg80211_scan_request *req)
{
/*
@ -338,6 +359,7 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
wl->scan.ssid_len = 0;
}
wl->scan_vif = vif;
wl->scan.req = req;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
@ -346,7 +368,7 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
wl1271_scan_stm(wl);
wl1271_scan_stm(wl, vif);
return 0;
}
@ -550,6 +572,9 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
* 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,
@ -585,6 +610,7 @@ out:
}
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)
{
@ -631,7 +657,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
}
if (!force_passive && cfg->active[0]) {
ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid,
ret = wl1271_cmd_build_probe_req(wl, wlvif, req->ssids[0].ssid,
req->ssids[0].ssid_len,
ies->ie[IEEE80211_BAND_2GHZ],
ies->len[IEEE80211_BAND_2GHZ],
@ -643,7 +669,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
}
if (!force_passive && cfg->active[1]) {
ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid,
ret = wl1271_cmd_build_probe_req(wl, wlvif, req->ssids[0].ssid,
req->ssids[0].ssid_len,
ies->ie[IEEE80211_BAND_5GHZ],
ies->len[IEEE80211_BAND_5GHZ],
@ -667,14 +693,14 @@ out:
return ret;
}
int wl1271_scan_sched_scan_start(struct wl1271 *wl)
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 (wl->bss_type != BSS_TYPE_STA_BSS)
if (wlvif->bss_type != BSS_TYPE_STA_BSS)
return -EOPNOTSUPP;
if (!test_bit(WL1271_FLAG_IDLE, &wl->flags))

View File

@ -26,18 +26,20 @@
#include "wl12xx.h"
int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
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);
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);
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);

View File

@ -24,6 +24,7 @@
#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>
@ -44,107 +45,67 @@
#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 wl1271 *wl, unsigned int blksz)
static void wl1271_sdio_set_block_size(struct device *child,
unsigned int blksz)
{
sdio_claim_host(wl->if_priv);
sdio_set_block_size(wl->if_priv, blksz);
sdio_release_host(wl->if_priv);
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 inline struct sdio_func *wl_to_func(struct wl1271 *wl)
{
return wl->if_priv;
}
static struct device *wl1271_sdio_wl_to_dev(struct wl1271 *wl)
{
return &(wl_to_func(wl)->dev);
}
static irqreturn_t wl1271_hardirq(int irq, void *cookie)
{
struct wl1271 *wl = cookie;
unsigned long flags;
wl1271_debug(DEBUG_IRQ, "IRQ");
/* complete the ELP completion */
spin_lock_irqsave(&wl->wl_lock, flags);
set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
if (wl->elp_compl) {
complete(wl->elp_compl);
wl->elp_compl = NULL;
}
if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
/* don't enqueue a work right now. mark it as pending */
set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
wl1271_debug(DEBUG_IRQ, "should not enqueue work");
disable_irq_nosync(wl->irq);
pm_wakeup_event(wl1271_sdio_wl_to_dev(wl), 0);
spin_unlock_irqrestore(&wl->wl_lock, flags);
return IRQ_HANDLED;
}
spin_unlock_irqrestore(&wl->wl_lock, flags);
return IRQ_WAKE_THREAD;
}
static void wl1271_sdio_disable_interrupts(struct wl1271 *wl)
{
disable_irq(wl->irq);
}
static void wl1271_sdio_enable_interrupts(struct wl1271 *wl)
{
enable_irq(wl->irq);
}
static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf,
size_t len, bool fixed)
{
int ret;
struct sdio_func *func = wl_to_func(wl);
struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
struct sdio_func *func = dev_to_sdio_func(glue->dev);
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x",
addr, ((u8 *)buf)[0]);
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);
wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %zu bytes",
addr, len);
wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
dev_dbg(child->parent, "sdio read 53 addr 0x%x, %zu bytes\n",
addr, len);
}
if (ret)
wl1271_error("sdio read failed (%d)", ret);
dev_err(child->parent, "sdio read failed (%d)\n", ret);
}
static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf,
size_t len, bool fixed)
{
int ret;
struct sdio_func *func = wl_to_func(wl);
struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
struct sdio_func *func = dev_to_sdio_func(glue->dev);
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x",
addr, ((u8 *)buf)[0]);
dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n",
addr, ((u8 *)buf)[0]);
} else {
wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %zu bytes",
addr, len);
wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
dev_dbg(child->parent, "sdio write 53 addr 0x%x, %zu bytes\n",
addr, len);
if (fixed)
ret = sdio_writesb(func, addr, buf, len);
@ -153,13 +114,13 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
}
if (ret)
wl1271_error("sdio write failed (%d)", ret);
dev_err(child->parent, "sdio write failed (%d)\n", ret);
}
static int wl1271_sdio_power_on(struct wl1271 *wl)
static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue)
{
struct sdio_func *func = wl_to_func(wl);
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)) {
@ -180,10 +141,10 @@ out:
return ret;
}
static int wl1271_sdio_power_off(struct wl1271 *wl)
static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
{
struct sdio_func *func = wl_to_func(wl);
int ret;
struct sdio_func *func = dev_to_sdio_func(glue->dev);
sdio_disable_func(func);
sdio_release_host(func);
@ -200,46 +161,43 @@ static int wl1271_sdio_power_off(struct wl1271 *wl)
return ret;
}
static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
static int wl12xx_sdio_set_power(struct device *child, bool enable)
{
struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
if (enable)
return wl1271_sdio_power_on(wl);
return wl12xx_sdio_power_on(glue);
else
return wl1271_sdio_power_off(wl);
return wl12xx_sdio_power_off(glue);
}
static struct wl1271_if_operations sdio_ops = {
.read = wl1271_sdio_raw_read,
.write = wl1271_sdio_raw_write,
.power = wl1271_sdio_set_power,
.dev = wl1271_sdio_wl_to_dev,
.enable_irq = wl1271_sdio_enable_interrupts,
.disable_irq = wl1271_sdio_disable_interrupts,
.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 ieee80211_hw *hw;
const struct wl12xx_platform_data *wlan_data;
struct wl1271 *wl;
unsigned long irqflags;
struct wl12xx_platform_data *wlan_data;
struct wl12xx_sdio_glue *glue;
struct resource res[1];
mmc_pm_flag_t mmcflags;
int ret;
int ret = -ENOMEM;
/* We are only able to handle the wlan function */
if (func->num != 0x02)
return -ENODEV;
hw = wl1271_alloc_hw();
if (IS_ERR(hw))
return PTR_ERR(hw);
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&func->dev, "can't allocate glue\n");
goto out;
}
wl = hw->priv;
wl->if_priv = func;
wl->if_ops = &sdio_ops;
glue->dev = &func->dev;
/* Grab access to FN0 for ELP reg. */
func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
@ -250,80 +208,79 @@ static int __devinit wl1271_probe(struct sdio_func *func,
wlan_data = wl12xx_get_platform_data();
if (IS_ERR(wlan_data)) {
ret = PTR_ERR(wlan_data);
wl1271_error("missing wlan platform data: %d", ret);
goto out_free;
dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
goto out_free_glue;
}
wl->irq = wlan_data->irq;
wl->ref_clock = wlan_data->board_ref_clock;
wl->tcxo_clock = wlan_data->board_tcxo_clock;
wl->platform_quirks = wlan_data->platform_quirks;
/* 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 (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
irqflags = IRQF_TRIGGER_RISING;
else
irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
if (mmcflags & MMC_PM_KEEP_POWER)
wlan_data->pwr_in_suspend = true;
ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq,
irqflags,
DRIVER_NAME, wl);
if (ret < 0) {
wl1271_error("request_irq() failed: %d", ret);
goto out_free;
}
wlan_data->ops = &sdio_ops;
ret = enable_irq_wake(wl->irq);
if (!ret) {
wl->irq_wake_enabled = true;
device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1);
/* if sdio can keep power while host is suspended, enable wow */
mmcflags = sdio_get_host_pm_caps(func);
wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags);
if (mmcflags & MMC_PM_KEEP_POWER)
hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
}
disable_irq(wl->irq);
ret = wl1271_init_ieee80211(wl);
if (ret)
goto out_irq;
ret = wl1271_register_hw(wl);
if (ret)
goto out_irq;
sdio_set_drvdata(func, wl);
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_irq:
free_irq(wl->irq, wl);
out_dev_put:
platform_device_put(glue->core);
out_free:
wl1271_free_hw(wl);
out_free_glue:
kfree(glue);
out:
return ret;
}
static void __devexit wl1271_remove(struct sdio_func *func)
{
struct wl1271 *wl = sdio_get_drvdata(func);
struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
/* Undo decrement done above in wl1271_probe */
pm_runtime_get_noresume(&func->dev);
wl1271_unregister_hw(wl);
if (wl->irq_wake_enabled) {
device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0);
disable_irq_wake(wl->irq);
}
free_irq(wl->irq, wl);
wl1271_free_hw(wl);
platform_device_del(glue->core);
platform_device_put(glue->core);
kfree(glue);
}
#ifdef CONFIG_PM
@ -332,20 +289,21 @@ 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 wl1271 *wl = sdio_get_drvdata(func);
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;
wl1271_debug(DEBUG_MAC80211, "wl1271 suspend. wow_enabled: %d",
wl->wow_enabled);
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)) {
wl1271_error("can't keep power while host "
"is suspended");
dev_err(dev, "can't keep power while host "
"is suspended\n");
ret = -EINVAL;
goto out;
}
@ -353,7 +311,7 @@ static int wl1271_suspend(struct device *dev)
/* keep power while host suspended */
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
if (ret) {
wl1271_error("error while trying to keep power");
dev_err(dev, "error while trying to keep power\n");
goto out;
}
@ -367,9 +325,10 @@ out:
static int wl1271_resume(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
struct wl1271 *wl = sdio_get_drvdata(func);
struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
struct wl1271 *wl = platform_get_drvdata(glue->core);
wl1271_debug(DEBUG_MAC80211, "wl1271 resume");
dev_dbg(dev, "wl1271 resume\n");
if (wl->wow_enabled) {
/* claim back host */
sdio_claim_host(func);

View File

@ -1,543 +0,0 @@
/*
* SDIO testing driver for wl12xx
*
* Copyright (C) 2010 Nokia Corporation
*
* Contact: Roger Quadros <roger.quadros@nokia.com>
*
* wl12xx read/write routines taken from the main module
*
* 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/crc7.h>
#include <linux/vmalloc.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/kthread.h>
#include <linux/firmware.h>
#include <linux/pm_runtime.h>
#include "wl12xx.h"
#include "io.h"
#include "boot.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
static bool rx, tx;
module_param(rx, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(rx, "Perform rx test. Default (0). "
"This test continuously reads data from the SDIO device.\n");
module_param(tx, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(tx, "Perform tx test. Default (0). "
"This test continuously writes data to the SDIO device.\n");
struct wl1271_test {
struct wl1271 wl;
struct task_struct *test_task;
};
static const struct sdio_device_id wl1271_devices[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) },
{}
};
static inline struct sdio_func *wl_to_func(struct wl1271 *wl)
{
return wl->if_priv;
}
static struct device *wl1271_sdio_wl_to_dev(struct wl1271 *wl)
{
return &(wl_to_func(wl)->dev);
}
static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed)
{
int ret = 0;
struct sdio_func *func = wl_to_func(wl);
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x",
addr, ((u8 *)buf)[0]);
} else {
if (fixed)
ret = sdio_readsb(func, buf, addr, len);
else
ret = sdio_memcpy_fromio(func, buf, addr, len);
wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %zu bytes",
addr, len);
wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
}
if (ret)
wl1271_error("sdio read failed (%d)", ret);
}
static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed)
{
int ret = 0;
struct sdio_func *func = wl_to_func(wl);
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x",
addr, ((u8 *)buf)[0]);
} else {
wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %zu bytes",
addr, len);
wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
if (fixed)
ret = sdio_writesb(func, addr, buf, len);
else
ret = sdio_memcpy_toio(func, addr, buf, len);
}
if (ret)
wl1271_error("sdio write failed (%d)", ret);
}
static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
{
struct sdio_func *func = wl_to_func(wl);
int ret;
/* Let the SDIO stack handle wlan_enable control, so we
* keep host claimed while wlan is in use to keep wl1271
* alive.
*/
if (enable) {
/* Power up the card */
ret = pm_runtime_get_sync(&func->dev);
if (ret < 0)
goto out;
/* Runtime PM might be 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);
} else {
sdio_disable_func(func);
sdio_release_host(func);
/* Runtime PM might be disabled, power off the card manually */
ret = mmc_power_save_host(func->card->host);
if (ret < 0)
goto out;
/* Power down the card */
ret = pm_runtime_put_sync(&func->dev);
}
out:
return ret;
}
static void wl1271_sdio_disable_interrupts(struct wl1271 *wl)
{
}
static void wl1271_sdio_enable_interrupts(struct wl1271 *wl)
{
}
static struct wl1271_if_operations sdio_ops = {
.read = wl1271_sdio_raw_read,
.write = wl1271_sdio_raw_write,
.power = wl1271_sdio_set_power,
.dev = wl1271_sdio_wl_to_dev,
.enable_irq = wl1271_sdio_enable_interrupts,
.disable_irq = wl1271_sdio_disable_interrupts,
};
static void wl1271_fw_wakeup(struct wl1271 *wl)
{
u32 elp_reg;
elp_reg = ELPCTRL_WAKE_UP;
wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
}
static int wl1271_fetch_firmware(struct wl1271 *wl)
{
const struct firmware *fw;
int ret;
if (wl->chip.id == CHIP_ID_1283_PG20)
ret = request_firmware(&fw, WL128X_FW_NAME,
wl1271_wl_to_dev(wl));
else
ret = request_firmware(&fw, WL127X_FW_NAME,
wl1271_wl_to_dev(wl));
if (ret < 0) {
wl1271_error("could not get firmware: %d", ret);
return ret;
}
if (fw->size % 4) {
wl1271_error("firmware size is not multiple of 32 bits: %zu",
fw->size);
ret = -EILSEQ;
goto out;
}
wl->fw_len = fw->size;
wl->fw = vmalloc(wl->fw_len);
if (!wl->fw) {
wl1271_error("could not allocate memory for the firmware");
ret = -ENOMEM;
goto out;
}
memcpy(wl->fw, fw->data, wl->fw_len);
ret = 0;
out:
release_firmware(fw);
return ret;
}
static int wl1271_fetch_nvs(struct wl1271 *wl)
{
const struct firmware *fw;
int ret;
ret = request_firmware(&fw, WL12XX_NVS_NAME, wl1271_wl_to_dev(wl));
if (ret < 0) {
wl1271_error("could not get nvs file: %d", ret);
return ret;
}
wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (!wl->nvs) {
wl1271_error("could not allocate memory for the nvs file");
ret = -ENOMEM;
goto out;
}
wl->nvs_len = fw->size;
out:
release_firmware(fw);
return ret;
}
static int wl1271_chip_wakeup(struct wl1271 *wl)
{
struct wl1271_partition_set partition;
int ret;
msleep(WL1271_PRE_POWER_ON_SLEEP);
ret = wl1271_power_on(wl);
if (ret)
return ret;
msleep(WL1271_POWER_ON_SLEEP);
/* We don't need a real memory partition here, because we only want
* to use the registers at this point. */
memset(&partition, 0, sizeof(partition));
partition.reg.start = REGISTERS_BASE;
partition.reg.size = REGISTERS_DOWN_SIZE;
wl1271_set_partition(wl, &partition);
/* ELP module wake up */
wl1271_fw_wakeup(wl);
/* whal_FwCtrl_BootSm() */
/* 0. read chip id from CHIP_ID */
wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
/* 1. check if chip id is valid */
switch (wl->chip.id) {
case CHIP_ID_1271_PG10:
wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
wl->chip.id);
break;
case CHIP_ID_1271_PG20:
wl1271_notice("chip id 0x%x (1271 PG20)",
wl->chip.id);
break;
case CHIP_ID_1283_PG20:
wl1271_notice("chip id 0x%x (1283 PG20)",
wl->chip.id);
break;
case CHIP_ID_1283_PG10:
default:
wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
return -ENODEV;
}
return ret;
}
static struct wl1271_partition_set part_down = {
.mem = {
.start = 0x00000000,
.size = 0x000177c0
},
.reg = {
.start = REGISTERS_BASE,
.size = 0x00008800
},
.mem2 = {
.start = 0x00000000,
.size = 0x00000000
},
.mem3 = {
.start = 0x00000000,
.size = 0x00000000
},
};
static int tester(void *data)
{
struct wl1271 *wl = data;
struct sdio_func *func = wl_to_func(wl);
struct device *pdev = &func->dev;
int ret = 0;
bool rx_started = 0;
bool tx_started = 0;
uint8_t *tx_buf, *rx_buf;
int test_size = PAGE_SIZE;
u32 addr = 0;
struct wl1271_partition_set partition;
/* We assume chip is powered up and firmware fetched */
memcpy(&partition, &part_down, sizeof(partition));
partition.mem.start = addr;
wl1271_set_partition(wl, &partition);
tx_buf = kmalloc(test_size, GFP_KERNEL);
rx_buf = kmalloc(test_size, GFP_KERNEL);
if (!tx_buf || !rx_buf) {
dev_err(pdev,
"Could not allocate memory. Test will not run.\n");
ret = -ENOMEM;
goto free;
}
memset(tx_buf, 0x5a, test_size);
/* write something in data area so we can read it back */
wl1271_write(wl, addr, tx_buf, test_size, false);
while (!kthread_should_stop()) {
if (rx && !rx_started) {
dev_info(pdev, "starting rx test\n");
rx_started = 1;
} else if (!rx && rx_started) {
dev_info(pdev, "stopping rx test\n");
rx_started = 0;
}
if (tx && !tx_started) {
dev_info(pdev, "starting tx test\n");
tx_started = 1;
} else if (!tx && tx_started) {
dev_info(pdev, "stopping tx test\n");
tx_started = 0;
}
if (rx_started)
wl1271_read(wl, addr, rx_buf, test_size, false);
if (tx_started)
wl1271_write(wl, addr, tx_buf, test_size, false);
if (!rx_started && !tx_started)
msleep(100);
}
free:
kfree(tx_buf);
kfree(rx_buf);
return ret;
}
static int __devinit wl1271_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
const struct wl12xx_platform_data *wlan_data;
struct wl1271 *wl;
struct wl1271_test *wl_test;
int ret = 0;
/* wl1271 has 2 sdio functions we handle just the wlan part */
if (func->num != 0x02)
return -ENODEV;
wl_test = kzalloc(sizeof(struct wl1271_test), GFP_KERNEL);
if (!wl_test) {
dev_err(&func->dev, "Could not allocate memory\n");
return -ENOMEM;
}
wl = &wl_test->wl;
wl->if_priv = func;
wl->if_ops = &sdio_ops;
/* 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(&func->dev, "missing wlan platform data: %d\n", ret);
goto out_free;
}
wl->irq = wlan_data->irq;
wl->ref_clock = wlan_data->board_ref_clock;
wl->tcxo_clock = wlan_data->board_tcxo_clock;
sdio_set_drvdata(func, wl_test);
/* power up the device */
ret = wl1271_chip_wakeup(wl);
if (ret) {
dev_err(&func->dev, "could not wake up chip\n");
goto out_free;
}
if (wl->fw == NULL) {
ret = wl1271_fetch_firmware(wl);
if (ret < 0) {
dev_err(&func->dev, "firmware fetch error\n");
goto out_off;
}
}
/* fetch NVS */
if (wl->nvs == NULL) {
ret = wl1271_fetch_nvs(wl);
if (ret < 0) {
dev_err(&func->dev, "NVS fetch error\n");
goto out_off;
}
}
ret = wl1271_load_firmware(wl);
if (ret < 0) {
dev_err(&func->dev, "firmware load error: %d\n", ret);
goto out_free;
}
dev_info(&func->dev, "initialized\n");
/* I/O testing will be done in the tester thread */
wl_test->test_task = kthread_run(tester, wl, "sdio_tester");
if (IS_ERR(wl_test->test_task)) {
dev_err(&func->dev, "unable to create kernel thread\n");
ret = PTR_ERR(wl_test->test_task);
goto out_free;
}
return 0;
out_off:
/* power off the chip */
wl1271_power_off(wl);
out_free:
kfree(wl_test);
return ret;
}
static void __devexit wl1271_remove(struct sdio_func *func)
{
struct wl1271_test *wl_test = sdio_get_drvdata(func);
/* stop the I/O test thread */
kthread_stop(wl_test->test_task);
/* power off the chip */
wl1271_power_off(&wl_test->wl);
vfree(wl_test->wl.fw);
wl_test->wl.fw = NULL;
kfree(wl_test->wl.nvs);
wl_test->wl.nvs = NULL;
kfree(wl_test);
}
static struct sdio_driver wl1271_sdio_driver = {
.name = "wl12xx_sdio_test",
.id_table = wl1271_devices,
.probe = wl1271_probe,
.remove = __devexit_p(wl1271_remove),
};
static int __init wl1271_init(void)
{
int ret;
ret = sdio_register_driver(&wl1271_sdio_driver);
if (ret < 0)
pr_err("failed to register sdio driver: %d\n", ret);
return ret;
}
module_init(wl1271_init);
static void __exit wl1271_exit(void)
{
sdio_unregister_driver(&wl1271_sdio_driver);
}
module_exit(wl1271_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Roger Quadros <roger.quadros@nokia.com>");

View File

@ -27,6 +27,7 @@
#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"
@ -69,35 +70,22 @@
#define WSPI_MAX_NUM_OF_CHUNKS (WL1271_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE)
static inline struct spi_device *wl_to_spi(struct wl1271 *wl)
{
return wl->if_priv;
}
struct wl12xx_spi_glue {
struct device *dev;
struct platform_device *core;
};
static struct device *wl1271_spi_wl_to_dev(struct wl1271 *wl)
{
return &(wl_to_spi(wl)->dev);
}
static void wl1271_spi_disable_interrupts(struct wl1271 *wl)
{
disable_irq(wl->irq);
}
static void wl1271_spi_enable_interrupts(struct wl1271 *wl)
{
enable_irq(wl->irq);
}
static void wl1271_spi_reset(struct wl1271 *wl)
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) {
wl1271_error("could not allocate cmd for spi reset");
dev_err(child->parent,
"could not allocate cmd for spi reset\n");
return;
}
@ -110,21 +98,22 @@ static void wl1271_spi_reset(struct wl1271 *wl)
t.len = WSPI_INIT_CMD_LEN;
spi_message_add_tail(&t, &m);
spi_sync(wl_to_spi(wl), &m);
spi_sync(to_spi_device(glue->dev), &m);
wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN);
kfree(cmd);
}
static void wl1271_spi_init(struct wl1271 *wl)
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) {
wl1271_error("could not allocate cmd for spi init");
dev_err(child->parent,
"could not allocate cmd for spi init\n");
return;
}
@ -165,15 +154,16 @@ static void wl1271_spi_init(struct wl1271 *wl)
t.len = WSPI_INIT_CMD_LEN;
spi_message_add_tail(&t, &m);
spi_sync(wl_to_spi(wl), &m);
wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN);
spi_sync(to_spi_device(glue->dev), &m);
kfree(cmd);
}
#define WL1271_BUSY_WORD_TIMEOUT 1000
static int wl1271_spi_read_busy(struct wl1271 *wl)
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;
@ -194,20 +184,22 @@ static int wl1271_spi_read_busy(struct wl1271 *wl)
t[0].len = sizeof(u32);
t[0].cs_change = true;
spi_message_add_tail(&t[0], &m);
spi_sync(wl_to_spi(wl), &m);
spi_sync(to_spi_device(glue->dev), &m);
if (*busy_buf & 0x1)
return 0;
}
/* The SPI bus is unresponsive, the read failed. */
wl1271_error("SPI read busy-word timeout!\n");
dev_err(child->parent, "SPI read busy-word timeout!\n");
return -ETIMEDOUT;
}
static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
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;
@ -243,10 +235,10 @@ static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
t[1].cs_change = true;
spi_message_add_tail(&t[1], &m);
spi_sync(wl_to_spi(wl), &m);
spi_sync(to_spi_device(glue->dev), &m);
if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1) &&
wl1271_spi_read_busy(wl)) {
wl12xx_spi_read_busy(child)) {
memset(buf, 0, chunk_len);
return;
}
@ -259,10 +251,7 @@ static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
t[0].cs_change = true;
spi_message_add_tail(&t[0], &m);
spi_sync(wl_to_spi(wl), &m);
wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd));
wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, chunk_len);
spi_sync(to_spi_device(glue->dev), &m);
if (!fixed)
addr += chunk_len;
@ -271,9 +260,10 @@ static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
}
}
static void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed)
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];
@ -308,9 +298,6 @@ static void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf,
t[i].len = chunk_len;
spi_message_add_tail(&t[i++], &m);
wl1271_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd));
wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, chunk_len);
if (!fixed)
addr += chunk_len;
buf += chunk_len;
@ -318,72 +305,41 @@ static void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf,
cmd++;
}
spi_sync(wl_to_spi(wl), &m);
}
static irqreturn_t wl1271_hardirq(int irq, void *cookie)
{
struct wl1271 *wl = cookie;
unsigned long flags;
wl1271_debug(DEBUG_IRQ, "IRQ");
/* complete the ELP completion */
spin_lock_irqsave(&wl->wl_lock, flags);
set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
if (wl->elp_compl) {
complete(wl->elp_compl);
wl->elp_compl = NULL;
}
spin_unlock_irqrestore(&wl->wl_lock, flags);
return IRQ_WAKE_THREAD;
}
static int wl1271_spi_set_power(struct wl1271 *wl, bool enable)
{
if (wl->set_power)
wl->set_power(enable);
return 0;
spi_sync(to_spi_device(glue->dev), &m);
}
static struct wl1271_if_operations spi_ops = {
.read = wl1271_spi_raw_read,
.write = wl1271_spi_raw_write,
.reset = wl1271_spi_reset,
.init = wl1271_spi_init,
.power = wl1271_spi_set_power,
.dev = wl1271_spi_wl_to_dev,
.enable_irq = wl1271_spi_enable_interrupts,
.disable_irq = wl1271_spi_disable_interrupts,
.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 ieee80211_hw *hw;
struct wl1271 *wl;
unsigned long irqflags;
int ret;
struct resource res[1];
int ret = -ENOMEM;
pdata = spi->dev.platform_data;
if (!pdata) {
wl1271_error("no platform data");
dev_err(&spi->dev, "no platform data\n");
return -ENODEV;
}
hw = wl1271_alloc_hw();
if (IS_ERR(hw))
return PTR_ERR(hw);
pdata->ops = &spi_ops;
wl = hw->priv;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&spi->dev, "can't allocate glue\n");
goto out;
}
dev_set_drvdata(&spi->dev, wl);
wl->if_priv = spi;
glue->dev = &spi->dev;
wl->if_ops = &spi_ops;
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 */
@ -391,69 +347,61 @@ static int __devinit wl1271_probe(struct spi_device *spi)
ret = spi_setup(spi);
if (ret < 0) {
wl1271_error("spi_setup failed");
goto out_free;
dev_err(glue->dev, "spi_setup failed\n");
goto out_free_glue;
}
wl->set_power = pdata->set_power;
if (!wl->set_power) {
wl1271_error("set power function missing in platform data");
ret = -ENODEV;
goto out_free;
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;
}
wl->ref_clock = pdata->board_ref_clock;
wl->tcxo_clock = pdata->board_tcxo_clock;
wl->platform_quirks = pdata->platform_quirks;
glue->core->dev.parent = &spi->dev;
if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
irqflags = IRQF_TRIGGER_RISING;
else
irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
memset(res, 0x00, sizeof(res));
wl->irq = spi->irq;
if (wl->irq < 0) {
wl1271_error("irq missing in platform data");
ret = -ENODEV;
goto out_free;
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 = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq,
irqflags,
DRIVER_NAME, wl);
if (ret < 0) {
wl1271_error("request_irq() failed: %d", ret);
goto out_free;
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;
}
disable_irq(wl->irq);
ret = wl1271_init_ieee80211(wl);
if (ret)
goto out_irq;
ret = wl1271_register_hw(wl);
if (ret)
goto out_irq;
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_irq:
free_irq(wl->irq, wl);
out_free:
wl1271_free_hw(wl);
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 wl1271 *wl = dev_get_drvdata(&spi->dev);
struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
wl1271_unregister_hw(wl);
free_irq(wl->irq, wl);
wl1271_free_hw(wl);
platform_device_del(glue->core);
platform_device_put(glue->core);
kfree(glue);
return 0;
}

View File

@ -26,8 +26,10 @@
#include <net/genetlink.h>
#include "wl12xx.h"
#include "debug.h"
#include "acx.h"
#include "reg.h"
#include "ps.h"
#define WL1271_TM_MAX_DATA_LENGTH 1024
@ -87,31 +89,47 @@ static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[])
return -EMSGSIZE;
mutex_lock(&wl->mutex);
ret = wl1271_cmd_test(wl, buf, buf_len, answer);
mutex_unlock(&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);
return ret;
goto out_sleep;
}
if (answer) {
len = nla_total_size(buf_len);
skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
if (!skb)
return -ENOMEM;
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)
return ret;
goto out_sleep;
}
return 0;
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return ret;
nla_put_failure:
kfree_skb(skb);
return -EMSGSIZE;
ret = -EMSGSIZE;
goto out_sleep;
}
static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
@ -128,33 +146,53 @@ static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]);
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
mutex_lock(&wl->mutex);
ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd));
mutex_unlock(&wl->mutex);
if (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);
kfree(cmd);
return ret;
goto out_free;
}
skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd));
if (!skb) {
kfree(cmd);
return -ENOMEM;
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;
return 0;
out_free:
kfree(cmd);
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return ret;
nla_put_failure:
kfree_skb(skb);
return -EMSGSIZE;
ret = -EMSGSIZE;
goto out_free;
}
static int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[])

View File

@ -26,22 +26,24 @@
#include <linux/etherdevice.h>
#include "wl12xx.h"
#include "debug.h"
#include "io.h"
#include "reg.h"
#include "ps.h"
#include "tx.h"
#include "event.h"
static int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id)
static int wl1271_set_default_wep_key(struct wl1271 *wl,
struct wl12xx_vif *wlvif, u8 id)
{
int ret;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
if (is_ap)
ret = wl12xx_cmd_set_default_wep_key(wl, id,
wl->ap_bcast_hlid);
wlvif->ap.bcast_hlid);
else
ret = wl12xx_cmd_set_default_wep_key(wl, id, wl->sta_hlid);
ret = wl12xx_cmd_set_default_wep_key(wl, id, wlvif->sta.hlid);
if (ret < 0)
return ret;
@ -76,7 +78,8 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id)
}
static int wl1271_tx_update_filters(struct wl1271 *wl,
struct sk_buff *skb)
struct wl12xx_vif *wlvif,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr;
int ret;
@ -92,15 +95,11 @@ static int wl1271_tx_update_filters(struct wl1271 *wl,
if (!ieee80211_is_auth(hdr->frame_control))
return 0;
if (wl->dev_hlid != WL12XX_INVALID_LINK_ID)
if (wlvif->dev_hlid != WL12XX_INVALID_LINK_ID)
goto out;
wl1271_debug(DEBUG_CMD, "starting device role for roaming");
ret = wl12xx_cmd_role_start_dev(wl);
if (ret < 0)
goto out;
ret = wl12xx_roc(wl, wl->dev_role_id);
ret = wl12xx_start_dev(wl, wlvif);
if (ret < 0)
goto out;
out:
@ -123,18 +122,16 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
}
static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid)
static void wl1271_tx_regulate_link(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
u8 hlid)
{
bool fw_ps, single_sta;
u8 tx_pkts;
/* only regulate station links */
if (hlid < WL1271_AP_STA_HLID_START)
if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
return;
if (WARN_ON(!wl1271_is_active_sta(wl, hlid)))
return;
fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
tx_pkts = wl->links[hlid].allocated_pkts;
single_sta = (wl->active_sta_count == 1);
@ -146,7 +143,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid)
* case FW-memory congestion is not a problem.
*/
if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
wl1271_ps_link_start(wl, hlid, true);
wl12xx_ps_link_start(wl, wlvif, hlid, true);
}
bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
@ -154,7 +151,8 @@ bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
return wl->dummy_packet == skb;
}
u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct sk_buff *skb)
u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb)
{
struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb);
@ -167,49 +165,51 @@ u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct sk_buff *skb)
} else {
struct ieee80211_hdr *hdr;
if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags))
if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
return wl->system_hlid;
hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_mgmt(hdr->frame_control))
return wl->ap_global_hlid;
return wlvif->ap.global_hlid;
else
return wl->ap_bcast_hlid;
return wlvif->ap.bcast_hlid;
}
}
static u8 wl1271_tx_get_hlid(struct wl1271 *wl, struct sk_buff *skb)
u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (wl12xx_is_dummy_packet(wl, skb))
if (!wlvif || wl12xx_is_dummy_packet(wl, skb))
return wl->system_hlid;
if (wl->bss_type == BSS_TYPE_AP_BSS)
return wl12xx_tx_get_hlid_ap(wl, skb);
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
return wl12xx_tx_get_hlid_ap(wl, wlvif, skb);
wl1271_tx_update_filters(wl, skb);
wl1271_tx_update_filters(wl, wlvif, skb);
if ((test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) ||
test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags)) &&
if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) &&
!ieee80211_is_auth(hdr->frame_control) &&
!ieee80211_is_assoc_req(hdr->frame_control))
return wl->sta_hlid;
return wlvif->sta.hlid;
else
return wl->dev_hlid;
return wlvif->dev_hlid;
}
static unsigned int wl12xx_calc_packet_alignment(struct wl1271 *wl,
unsigned int packet_length)
{
if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT)
return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE);
else
if (wl->quirks & WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT)
return ALIGN(packet_length, WL1271_TX_ALIGN_TO);
else
return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE);
}
static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
u32 buf_offset, u8 hlid)
static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb, u32 extra, u32 buf_offset,
u8 hlid)
{
struct wl1271_tx_hw_descr *desc;
u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
@ -217,6 +217,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
u32 total_blocks;
int id, ret = -EBUSY, ac;
u32 spare_blocks = wl->tx_spare_blocks;
bool is_dummy = false;
if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE)
return -EAGAIN;
@ -231,8 +232,10 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
len = wl12xx_calc_packet_alignment(wl, total_len);
/* in case of a dummy packet, use default amount of spare mem blocks */
if (unlikely(wl12xx_is_dummy_packet(wl, skb)))
if (unlikely(wl12xx_is_dummy_packet(wl, skb))) {
is_dummy = true;
spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
}
total_blocks = (len + TX_HW_BLOCK_SIZE - 1) / TX_HW_BLOCK_SIZE +
spare_blocks;
@ -257,8 +260,9 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
wl->tx_allocated_pkts[ac]++;
if (wl->bss_type == BSS_TYPE_AP_BSS &&
hlid >= WL1271_AP_STA_HLID_START)
if (!is_dummy && wlvif &&
wlvif->bss_type == BSS_TYPE_AP_BSS &&
test_bit(hlid, wlvif->ap.sta_hlid_map))
wl->links[hlid].allocated_pkts++;
ret = 0;
@ -273,15 +277,16 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
return ret;
}
static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
u32 extra, struct ieee80211_tx_info *control,
u8 hlid)
static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb, u32 extra,
struct ieee80211_tx_info *control, u8 hlid)
{
struct timespec ts;
struct wl1271_tx_hw_descr *desc;
int aligned_len, ac, rate_idx;
s64 hosttime;
u16 tx_attr;
u16 tx_attr = 0;
bool is_dummy;
desc = (struct wl1271_tx_hw_descr *) skb->data;
@ -298,7 +303,8 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
hosttime = (timespec_to_ns(&ts) >> 10);
desc->start_time = cpu_to_le32(hosttime - wl->time_offset);
if (wl->bss_type != BSS_TYPE_AP_BSS)
is_dummy = wl12xx_is_dummy_packet(wl, skb);
if (is_dummy || !wlvif || wlvif->bss_type != BSS_TYPE_AP_BSS)
desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU);
else
desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU);
@ -307,39 +313,42 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
desc->tid = skb->priority;
if (wl12xx_is_dummy_packet(wl, skb)) {
if (is_dummy) {
/*
* FW expects the dummy packet to have an invalid session id -
* any session id that is different than the one set in the join
*/
tx_attr = ((~wl->session_counter) <<
tx_attr = (SESSION_COUNTER_INVALID <<
TX_HW_ATTR_OFST_SESSION_COUNTER) &
TX_HW_ATTR_SESSION_COUNTER;
tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ;
} else {
} else if (wlvif) {
/* configure the tx attributes */
tx_attr =
wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER;
tx_attr = wlvif->session_counter <<
TX_HW_ATTR_OFST_SESSION_COUNTER;
}
desc->hlid = hlid;
if (wl->bss_type != BSS_TYPE_AP_BSS) {
if (is_dummy || !wlvif)
rate_idx = 0;
else if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
/* if the packets are destined for AP (have a STA entry)
send them with AP rate policies, otherwise use default
basic rates */
if (control->control.sta)
rate_idx = ACX_TX_AP_FULL_RATE;
if (control->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
rate_idx = wlvif->sta.p2p_rate_idx;
else if (control->control.sta)
rate_idx = wlvif->sta.ap_rate_idx;
else
rate_idx = ACX_TX_BASIC_RATE;
rate_idx = wlvif->sta.basic_rate_idx;
} else {
if (hlid == wl->ap_global_hlid)
rate_idx = ACX_TX_AP_MODE_MGMT_RATE;
else if (hlid == wl->ap_bcast_hlid)
rate_idx = ACX_TX_AP_MODE_BCST_RATE;
if (hlid == wlvif->ap.global_hlid)
rate_idx = wlvif->ap.mgmt_rate_idx;
else if (hlid == wlvif->ap.bcast_hlid)
rate_idx = wlvif->ap.bcast_rate_idx;
else
rate_idx = ac;
rate_idx = wlvif->ap.ucast_rate_idx[ac];
}
tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY;
@ -379,20 +388,24 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
}
/* caller must hold wl->mutex */
static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
u32 buf_offset)
static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb, u32 buf_offset)
{
struct ieee80211_tx_info *info;
u32 extra = 0;
int ret = 0;
u32 total_len;
u8 hlid;
bool is_dummy;
if (!skb)
return -EINVAL;
info = IEEE80211_SKB_CB(skb);
/* TODO: handle dummy packets on multi-vifs */
is_dummy = wl12xx_is_dummy_packet(wl, skb);
if (info->control.hw_key &&
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
extra = WL1271_TKIP_IV_SPACE;
@ -405,29 +418,28 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
(cipher == WLAN_CIPHER_SUITE_WEP104);
if (unlikely(is_wep && wl->default_key != idx)) {
ret = wl1271_set_default_wep_key(wl, idx);
if (unlikely(is_wep && wlvif->default_key != idx)) {
ret = wl1271_set_default_wep_key(wl, wlvif, idx);
if (ret < 0)
return ret;
wl->default_key = idx;
wlvif->default_key = idx;
}
}
hlid = wl1271_tx_get_hlid(wl, skb);
hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
if (hlid == WL12XX_INVALID_LINK_ID) {
wl1271_error("invalid hlid. dropping skb 0x%p", skb);
return -EINVAL;
}
ret = wl1271_tx_allocate(wl, skb, extra, buf_offset, hlid);
ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid);
if (ret < 0)
return ret;
wl1271_tx_fill_hdr(wl, skb, extra, info, hlid);
wl1271_tx_fill_hdr(wl, wlvif, skb, extra, info, hlid);
if (wl->bss_type == BSS_TYPE_AP_BSS) {
if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) {
wl1271_tx_ap_update_inconnection_sta(wl, skb);
wl1271_tx_regulate_link(wl, hlid);
wl1271_tx_regulate_link(wl, wlvif, hlid);
}
/*
@ -444,7 +456,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len);
/* Revert side effects in the dummy packet skb, so it can be reused */
if (wl12xx_is_dummy_packet(wl, skb))
if (is_dummy)
skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
return total_len;
@ -522,19 +534,18 @@ static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
return &queues[q];
}
static struct sk_buff *wl1271_sta_skb_dequeue(struct wl1271 *wl)
static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
struct wl1271_link *lnk)
{
struct sk_buff *skb = NULL;
struct sk_buff *skb;
unsigned long flags;
struct sk_buff_head *queue;
queue = wl1271_select_queue(wl, wl->tx_queue);
queue = wl1271_select_queue(wl, lnk->tx_queue);
if (!queue)
goto out;
return NULL;
skb = skb_dequeue(queue);
out:
if (skb) {
int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
spin_lock_irqsave(&wl->wl_lock, flags);
@ -545,43 +556,33 @@ out:
return skb;
}
static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl)
static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
struct sk_buff *skb = NULL;
unsigned long flags;
int i, h, start_hlid;
struct sk_buff_head *queue;
/* start from the link after the last one */
start_hlid = (wl->last_tx_hlid + 1) % AP_MAX_LINKS;
start_hlid = (wlvif->last_tx_hlid + 1) % WL12XX_MAX_LINKS;
/* dequeue according to AC, round robin on each link */
for (i = 0; i < AP_MAX_LINKS; i++) {
h = (start_hlid + i) % AP_MAX_LINKS;
for (i = 0; i < WL12XX_MAX_LINKS; i++) {
h = (start_hlid + i) % WL12XX_MAX_LINKS;
/* only consider connected stations */
if (h >= WL1271_AP_STA_HLID_START &&
!test_bit(h - WL1271_AP_STA_HLID_START, wl->ap_hlid_map))
if (!test_bit(h, wlvif->links_map))
continue;
queue = wl1271_select_queue(wl, wl->links[h].tx_queue);
if (!queue)
skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[h]);
if (!skb)
continue;
skb = skb_dequeue(queue);
if (skb)
break;
wlvif->last_tx_hlid = h;
break;
}
if (skb) {
int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
wl->last_tx_hlid = h;
spin_lock_irqsave(&wl->wl_lock, flags);
wl->tx_queue_count[q]--;
spin_unlock_irqrestore(&wl->wl_lock, flags);
} else {
wl->last_tx_hlid = 0;
}
if (!skb)
wlvif->last_tx_hlid = 0;
return skb;
}
@ -589,12 +590,32 @@ static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl)
static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
{
unsigned long flags;
struct wl12xx_vif *wlvif = wl->last_wlvif;
struct sk_buff *skb = NULL;
if (wl->bss_type == BSS_TYPE_AP_BSS)
skb = wl1271_ap_skb_dequeue(wl);
else
skb = wl1271_sta_skb_dequeue(wl);
if (wlvif) {
wl12xx_for_each_wlvif_continue(wl, wlvif) {
skb = wl12xx_vif_skb_dequeue(wl, wlvif);
if (skb) {
wl->last_wlvif = wlvif;
break;
}
}
}
/* do another pass */
if (!skb) {
wl12xx_for_each_wlvif(wl, wlvif) {
skb = wl12xx_vif_skb_dequeue(wl, wlvif);
if (skb) {
wl->last_wlvif = wlvif;
break;
}
}
}
if (!skb)
skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
if (!skb &&
test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
@ -610,21 +631,21 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
return skb;
}
static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb)
{
unsigned long flags;
int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
if (wl12xx_is_dummy_packet(wl, skb)) {
set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
} else if (wl->bss_type == BSS_TYPE_AP_BSS) {
u8 hlid = wl1271_tx_get_hlid(wl, skb);
} else {
u8 hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
/* make sure we dequeue the same packet next time */
wl->last_tx_hlid = (hlid + AP_MAX_LINKS - 1) % AP_MAX_LINKS;
} else {
skb_queue_head(&wl->tx_queue[q], skb);
wlvif->last_tx_hlid = (hlid + WL12XX_MAX_LINKS - 1) %
WL12XX_MAX_LINKS;
}
spin_lock_irqsave(&wl->wl_lock, flags);
@ -639,29 +660,71 @@ static bool wl1271_tx_is_data_present(struct sk_buff *skb)
return ieee80211_is_data_present(hdr->frame_control);
}
void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids)
{
struct wl12xx_vif *wlvif;
u32 timeout;
u8 hlid;
if (!wl->conf.rx_streaming.interval)
return;
if (!wl->conf.rx_streaming.always &&
!test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))
return;
timeout = wl->conf.rx_streaming.duration;
wl12xx_for_each_wlvif_sta(wl, wlvif) {
bool found = false;
for_each_set_bit(hlid, active_hlids, WL12XX_MAX_LINKS) {
if (test_bit(hlid, wlvif->links_map)) {
found = true;
break;
}
}
if (!found)
continue;
/* enable rx streaming */
if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
ieee80211_queue_work(wl->hw,
&wlvif->rx_streaming_enable_work);
mod_timer(&wlvif->rx_streaming_timer,
jiffies + msecs_to_jiffies(timeout));
}
}
void wl1271_tx_work_locked(struct wl1271 *wl)
{
struct wl12xx_vif *wlvif;
struct sk_buff *skb;
struct wl1271_tx_hw_descr *desc;
u32 buf_offset = 0;
bool sent_packets = false;
bool had_data = false;
bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
int ret;
if (unlikely(wl->state == WL1271_STATE_OFF))
return;
while ((skb = wl1271_skb_dequeue(wl))) {
if (wl1271_tx_is_data_present(skb))
had_data = true;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
bool has_data = false;
ret = wl1271_prepare_tx_frame(wl, skb, buf_offset);
wlvif = NULL;
if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif)
wlvif = wl12xx_vif_to_data(info->control.vif);
has_data = wlvif && wl1271_tx_is_data_present(skb);
ret = wl1271_prepare_tx_frame(wl, wlvif, skb, buf_offset);
if (ret == -EAGAIN) {
/*
* Aggregation buffer is full.
* Flush buffer and try again.
*/
wl1271_skb_queue_head(wl, skb);
wl1271_skb_queue_head(wl, wlvif, skb);
wl1271_write(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
buf_offset, true);
sent_packets = true;
@ -672,7 +735,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
* Firmware buffer is full.
* Queue back last skb, and stop aggregating.
*/
wl1271_skb_queue_head(wl, skb);
wl1271_skb_queue_head(wl, wlvif, skb);
/* No work left, avoid scheduling redundant tx work */
set_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
goto out_ack;
@ -682,6 +745,10 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
}
buf_offset += ret;
wl->tx_packets_count++;
if (has_data) {
desc = (struct wl1271_tx_hw_descr *) skb->data;
__set_bit(desc->hlid, active_hlids);
}
}
out_ack:
@ -701,19 +768,7 @@ out_ack:
wl1271_handle_tx_low_watermark(wl);
}
if (!is_ap && wl->conf.rx_streaming.interval && had_data &&
(wl->conf.rx_streaming.always ||
test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) {
u32 timeout = wl->conf.rx_streaming.duration;
/* enable rx streaming */
if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
ieee80211_queue_work(wl->hw,
&wl->rx_streaming_enable_work);
mod_timer(&wl->rx_streaming_timer,
jiffies + msecs_to_jiffies(timeout));
}
wl12xx_rearm_rx_streaming(wl, active_hlids);
}
void wl1271_tx_work(struct work_struct *work)
@ -737,6 +792,8 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
struct wl1271_tx_hw_res_descr *result)
{
struct ieee80211_tx_info *info;
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
struct sk_buff *skb;
int id = result->id;
int rate = -1;
@ -756,11 +813,16 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
return;
}
/* info->control is valid as long as we don't update info->status */
vif = info->control.vif;
wlvif = wl12xx_vif_to_data(vif);
/* update the TX status info */
if (result->status == TX_SUCCESS) {
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_ACK;
rate = wl1271_rate_to_idx(result->rate_class_index, wl->band);
rate = wl1271_rate_to_idx(result->rate_class_index,
wlvif->band);
retries = result->ack_failures;
} else if (result->status == TX_RETRY_EXCEEDED) {
wl->stats.excessive_retries++;
@ -783,14 +845,14 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP ||
info->control.hw_key->cipher == WL1271_CIPHER_SUITE_GEM)) {
u8 fw_lsb = result->tx_security_sequence_number_lsb;
u8 cur_lsb = wl->tx_security_last_seq_lsb;
u8 cur_lsb = wlvif->tx_security_last_seq_lsb;
/*
* update security sequence number, taking care of potential
* wrap-around
*/
wl->tx_security_seq += (fw_lsb - cur_lsb + 256) % 256;
wl->tx_security_last_seq_lsb = fw_lsb;
wlvif->tx_security_seq += (fw_lsb - cur_lsb) & 0xff;
wlvif->tx_security_last_seq_lsb = fw_lsb;
}
/* remove private header from packet */
@ -886,40 +948,31 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
}
/* caller must hold wl->mutex and TX must be stopped */
void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int i;
/* TX failure */
for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
wl1271_free_sta(wl, wlvif, i);
else
wlvif->sta.ba_rx_bitmap = 0;
wl1271_tx_reset_link_queues(wl, i);
wl->links[i].allocated_pkts = 0;
wl->links[i].prev_freed_pkts = 0;
}
wlvif->last_tx_hlid = 0;
}
/* caller must hold wl->mutex and TX must be stopped */
void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
{
int i;
struct sk_buff *skb;
struct ieee80211_tx_info *info;
/* TX failure */
if (wl->bss_type == BSS_TYPE_AP_BSS) {
for (i = 0; i < AP_MAX_LINKS; i++) {
wl1271_free_sta(wl, i);
wl1271_tx_reset_link_queues(wl, i);
wl->links[i].allocated_pkts = 0;
wl->links[i].prev_freed_pkts = 0;
}
wl->last_tx_hlid = 0;
} else {
for (i = 0; i < NUM_TX_QUEUES; i++) {
while ((skb = skb_dequeue(&wl->tx_queue[i]))) {
wl1271_debug(DEBUG_TX, "freeing skb 0x%p",
skb);
if (!wl12xx_is_dummy_packet(wl, skb)) {
info = IEEE80211_SKB_CB(skb);
info->status.rates[0].idx = -1;
info->status.rates[0].count = 0;
ieee80211_tx_status_ni(wl->hw, skb);
}
}
}
wl->ba_rx_bitmap = 0;
}
for (i = 0; i < NUM_TX_QUEUES; i++)
wl->tx_queue_count[i] = 0;

View File

@ -206,18 +206,23 @@ static inline int wl1271_tx_total_queue_count(struct wl1271 *wl)
void wl1271_tx_work(struct work_struct *work);
void wl1271_tx_work_locked(struct wl1271 *wl);
void wl1271_tx_complete(struct wl1271 *wl);
void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues);
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 sk_buff *skb);
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, u8 hlid);
void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
#endif

View File

@ -35,9 +35,6 @@
#include "conf.h"
#include "ini.h"
#define DRIVER_NAME "wl1271"
#define DRIVER_PREFIX DRIVER_NAME ": "
/*
* FW versions support BA 11n
* versions marks x.x.x.50-60.x
@ -45,73 +42,6 @@
#define WL12XX_BA_SUPPORT_FW_COST_VER2_START 50
#define WL12XX_BA_SUPPORT_FW_COST_VER2_END 60
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_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 will be 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)
#define WL127X_FW_NAME "ti-connectivity/wl127x-fw-3.bin"
#define WL128X_FW_NAME "ti-connectivity/wl128x-fw-3.bin"
@ -142,15 +72,11 @@ extern u32 wl12xx_debug_level;
#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
/*
* TODO: we currently don't support multirole. remove
* this constant from the code when we do.
*/
#define WL1271_AP_STA_HLID_START 3
/*
* 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
@ -236,13 +162,6 @@ struct wl1271_stats {
#define AP_MAX_STATIONS 8
/* Broadcast and Global links + system link + links to stations */
/*
* TODO: when WL1271_AP_STA_HLID_START is no longer constant, change all
* the places that use this.
*/
#define AP_MAX_LINKS (AP_MAX_STATIONS + WL1271_AP_STA_HLID_START)
/* FW status registers */
struct wl12xx_fw_status {
__le32 intr;
@ -299,17 +218,14 @@ struct wl1271_scan {
};
struct wl1271_if_operations {
void (*read)(struct wl1271 *wl, int addr, void *buf, size_t len,
void (*read)(struct device *child, int addr, void *buf, size_t len,
bool fixed);
void (*write)(struct wl1271 *wl, int addr, void *buf, size_t len,
void (*write)(struct device *child, int addr, void *buf, size_t len,
bool fixed);
void (*reset)(struct wl1271 *wl);
void (*init)(struct wl1271 *wl);
int (*power)(struct wl1271 *wl, bool enable);
struct device* (*dev)(struct wl1271 *wl);
void (*enable_irq)(struct wl1271 *wl);
void (*disable_irq)(struct wl1271 *wl);
void (*set_block_size) (struct wl1271 *wl, unsigned int blksz);
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
@ -326,29 +242,33 @@ struct wl1271_ap_key {
};
enum wl12xx_flags {
WL1271_FLAG_STA_ASSOCIATED,
WL1271_FLAG_IBSS_JOINED,
WL1271_FLAG_GPIO_POWER,
WL1271_FLAG_TX_QUEUE_STOPPED,
WL1271_FLAG_TX_PENDING,
WL1271_FLAG_IN_ELP,
WL1271_FLAG_ELP_REQUESTED,
WL1271_FLAG_PSM,
WL1271_FLAG_PSM_REQUESTED,
WL1271_FLAG_IRQ_RUNNING,
WL1271_FLAG_IDLE,
WL1271_FLAG_PSPOLL_FAILURE,
WL1271_FLAG_STA_STATE_SENT,
WL1271_FLAG_FW_TX_BUSY,
WL1271_FLAG_AP_STARTED,
WL1271_FLAG_IF_INITIALIZED,
WL1271_FLAG_DUMMY_PACKET_PENDING,
WL1271_FLAG_SUSPENDED,
WL1271_FLAG_PENDING_WORK,
WL1271_FLAG_SOFT_GEMINI,
WL1271_FLAG_RX_STREAMING_STARTED,
WL1271_FLAG_RECOVERY_IN_PROGRESS,
WL1271_FLAG_CS_PROGRESS,
};
enum wl12xx_vif_flags {
WLVIF_FLAG_INITIALIZED,
WLVIF_FLAG_STA_ASSOCIATED,
WLVIF_FLAG_IBSS_JOINED,
WLVIF_FLAG_AP_STARTED,
WLVIF_FLAG_PSM,
WLVIF_FLAG_PSM_REQUESTED,
WLVIF_FLAG_STA_STATE_SENT,
WLVIF_FLAG_RX_STREAMING_STARTED,
WLVIF_FLAG_PSPOLL_FAILURE,
WLVIF_FLAG_CS_PROGRESS,
WLVIF_FLAG_AP_PROBE_RESP_SET,
};
struct wl1271_link {
@ -366,10 +286,11 @@ struct wl1271_link {
};
struct wl1271 {
struct platform_device *plat_dev;
struct ieee80211_hw *hw;
bool mac80211_registered;
struct device *dev;
void *if_priv;
struct wl1271_if_operations *if_ops;
@ -399,25 +320,20 @@ struct wl1271 {
s8 hw_pg_ver;
u8 bssid[ETH_ALEN];
u8 mac_addr[ETH_ALEN];
u8 bss_type;
u8 set_bss_type;
u8 p2p; /* we are using p2p role */
u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
u8 ssid_len;
int channel;
u8 role_id;
u8 dev_role_id;
u8 system_hlid;
u8 sta_hlid;
u8 dev_hlid;
u8 ap_global_hlid;
u8 ap_bcast_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;
@ -440,11 +356,7 @@ struct wl1271 {
/* Time-offset between host and chipset clocks */
s64 time_offset;
/* Session counter for the chipset */
int session_counter;
/* Frames scheduled for transmission, not handled yet */
struct sk_buff_head tx_queue[NUM_TX_QUEUES];
int tx_queue_count[NUM_TX_QUEUES];
long stopped_queues_map;
@ -462,17 +374,6 @@ struct wl1271 {
struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS];
int tx_frames_cnt;
/*
* 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;
/* FW Rx counter */
u32 rx_counter;
@ -507,59 +408,21 @@ struct wl1271 {
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;
/* probe-req template for the current AP */
struct sk_buff *probereq;
/* Our association ID */
u16 aid;
/*
* 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_set;
u32 basic_rate;
u32 rate_set;
u32 bitrate_masks[IEEE80211_NUM_BANDS];
/* The current band */
enum ieee80211_band band;
/* Beaconing interval (needed for ad-hoc) */
u32 beacon_int;
/* Default key (for WEP) */
u32 default_key;
/* Rx Streaming */
struct work_struct rx_streaming_enable_work;
struct work_struct rx_streaming_disable_work;
struct timer_list rx_streaming_timer;
struct completion *elp_compl;
struct completion *ps_compl;
struct delayed_work elp_work;
struct delayed_work pspoll_work;
/* counter for ps-poll delivery failures */
int ps_poll_failures;
/* retry counter for PSM entries */
u8 psm_entry_retry;
/* in dBm */
int power_level;
int rssi_thold;
int last_rssi_event;
struct wl1271_stats stats;
__le32 buffer_32;
@ -583,20 +446,9 @@ struct wl1271 {
/* Most recently reported noise in dBm */
s8 noise;
/* map for HLIDs of associated stations - when operating in AP mode */
unsigned long ap_hlid_map[BITS_TO_LONGS(AP_MAX_STATIONS)];
/* recoreded keys for AP-mode - set here before AP startup */
struct wl1271_ap_key *recorded_ap_keys[MAX_NUM_KEYS];
/* bands supported by this instance of wl12xx */
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
/* RX BA constraint value */
bool ba_support;
u8 ba_rx_bitmap;
bool ba_allowed;
int tcxo_clock;
/*
@ -610,10 +462,7 @@ struct wl1271 {
* AP-mode - links indexed by HLID. The global and broadcast links
* are always active.
*/
struct wl1271_link links[AP_MAX_LINKS];
/* the hlid of the link where the last transmitted skb came from */
int last_tx_hlid;
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;
@ -632,21 +481,173 @@ struct wl1271 {
/* AP-mode - number of currently connected stations */
int active_sta_count;
/* last wlvif we transmitted from */
struct wl12xx_vif *last_wlvif;
};
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;
} 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;
struct completion *ps_compl;
struct delayed_work pspoll_work;
/* counter for ps-poll delivery failures */
int ps_poll_failures;
/* retry counter for PSM entries */
u8 psm_entry_retry;
/* in dBm */
int power_level;
int rssi_thold;
int last_rssi_event;
/* 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);
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 7 /* maximum value for the session counter */
#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
@ -669,8 +670,8 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
/* Each RX/TX transaction requires an end-of-transaction transfer */
#define WL12XX_QUIRK_END_OF_TRANSACTION BIT(0)
/* WL128X requires aggregated packets to be aligned to the SDIO block size */
#define WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT BIT(2)
/* 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)

View File

@ -116,11 +116,6 @@ struct wl12xx_ps_poll_template {
u8 ta[ETH_ALEN];
} __packed;
struct wl12xx_qos_null_data_template {
struct ieee80211_header header;
__le16 qos_ctl;
} __packed;
struct wl12xx_arp_rsp_template {
struct ieee80211_hdr_3addr hdr;

View File

@ -2,7 +2,7 @@
#include <linux/err.h>
#include <linux/wl12xx.h>
static const struct wl12xx_platform_data *platform_data;
static struct wl12xx_platform_data *platform_data;
int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
{
@ -18,7 +18,7 @@ int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
return 0;
}
const struct wl12xx_platform_data *wl12xx_get_platform_data(void)
struct wl12xx_platform_data *wl12xx_get_platform_data(void)
{
if (!platform_data)
return ERR_PTR(-ENODEV);

View File

@ -1694,6 +1694,23 @@ static inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr)
return false;
}
/**
* ieee80211_is_public_action - check if frame is a public action frame
* @hdr: the frame
* @len: length of the frame
*/
static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr,
size_t len)
{
struct ieee80211_mgmt *mgmt = (void *)hdr;
if (len < IEEE80211_MIN_ACTION_SIZE)
return false;
if (!ieee80211_is_action(hdr->frame_control))
return false;
return mgmt->u.action.category == WLAN_CATEGORY_PUBLIC;
}
/**
* ieee80211_fhss_chan_to_freq - get channel frequency
* @channel: the FHSS channel

View File

@ -2785,9 +2785,11 @@ enum nl80211_ap_sme_features {
* @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back
* TX status to the socket error queue when requested with the
* socket option.
* @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
*/
enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
NL80211_FEATURE_HT_IBSS = 1 << 1,
};
/**

View File

@ -54,6 +54,9 @@ struct wl12xx_platform_data {
int board_ref_clock;
int board_tcxo_clock;
unsigned long platform_quirks;
bool pwr_in_suspend;
struct wl1271_if_operations *ops;
};
/* Platform does not support level trigger interrupts */
@ -73,6 +76,6 @@ int wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
#endif
const struct wl12xx_platform_data *wl12xx_get_platform_data(void);
struct wl12xx_platform_data *wl12xx_get_platform_data(void);
#endif

View File

@ -36,6 +36,11 @@
#define PF_BLUETOOTH AF_BLUETOOTH
#endif
/* Bluetooth versions */
#define BLUETOOTH_VER_1_1 1
#define BLUETOOTH_VER_1_2 2
#define BLUETOOTH_VER_2_0 3
/* Reserv for core and drivers use */
#define BT_SKB_RESERVE 8

View File

@ -88,6 +88,14 @@ enum {
HCI_RESET,
};
/*
* BR/EDR and/or LE controller flags: the flags defined here should represent
* states from the controller.
*/
enum {
HCI_LE_SCAN,
};
/* HCI ioctl defines */
#define HCIDEVUP _IOW('H', 201, int)
#define HCIDEVDOWN _IOW('H', 202, int)
@ -453,6 +461,14 @@ struct hci_rp_user_confirm_reply {
#define HCI_OP_USER_CONFIRM_NEG_REPLY 0x042d
#define HCI_OP_USER_PASSKEY_REPLY 0x042e
struct hci_cp_user_passkey_reply {
bdaddr_t bdaddr;
__le32 passkey;
} __packed;
#define HCI_OP_USER_PASSKEY_NEG_REPLY 0x042f
#define HCI_OP_REMOTE_OOB_DATA_REPLY 0x0430
struct hci_cp_remote_oob_data_reply {
bdaddr_t bdaddr;
@ -669,6 +685,12 @@ struct hci_rp_read_local_oob_data {
#define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58
#define HCI_OP_READ_FLOW_CONTROL_MODE 0x0c66
struct hci_rp_read_flow_control_mode {
__u8 status;
__u8 mode;
} __packed;
#define HCI_OP_WRITE_LE_HOST_SUPPORTED 0x0c6d
struct hci_cp_write_le_host_supported {
__u8 le;
@ -760,6 +782,15 @@ struct hci_rp_le_read_buffer_size {
__u8 le_max_pkt;
} __packed;
#define HCI_OP_LE_SET_SCAN_PARAM 0x200b
struct hci_cp_le_set_scan_param {
__u8 type;
__le16 interval;
__le16 window;
__u8 own_address_type;
__u8 filter_policy;
} __packed;
#define HCI_OP_LE_SET_SCAN_ENABLE 0x200c
struct hci_cp_le_set_scan_enable {
__u8 enable;
@ -1076,6 +1107,11 @@ struct hci_ev_user_confirm_req {
__le32 passkey;
} __packed;
#define HCI_EV_USER_PASSKEY_REQUEST 0x34
struct hci_ev_user_passkey_req {
bdaddr_t bdaddr;
} __packed;
#define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35
struct hci_ev_remote_oob_data_request {
bdaddr_t bdaddr;
@ -1331,4 +1367,6 @@ struct hci_inquiry_req {
};
#define IREQ_CACHE_FLUSH 0x0001
extern int enable_hs;
#endif /* __HCI_H */

View File

@ -170,6 +170,8 @@ struct hci_dev {
__u32 amp_max_flush_to;
__u32 amp_be_flush_to;
__u8 flow_ctl_mode;
unsigned int auto_accept_delay;
unsigned long quirks;
@ -250,6 +252,8 @@ struct hci_dev {
struct module *owner;
unsigned long dev_flags;
int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev);
@ -917,11 +921,13 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable);
int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
u8 persistent);
int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
int mgmt_disconnect_failed(struct hci_dev *hdev);
int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
u8 status);
int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type);
int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type);
int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status);
int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 status);
int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status);
@ -933,14 +939,20 @@ int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status);
int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 status);
int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr);
int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status);
int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 status);
int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status);
int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
u8 *randomizer, u8 status);
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
u8 *dev_class, s8 rssi, u8 *eir);
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u8 *eir);
int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name);
int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status);
int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status);
int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status);
int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr);

View File

@ -792,7 +792,6 @@ static inline __u8 __ctrl_size(struct l2cap_chan *chan)
}
extern int disable_ertm;
extern int enable_hs;
int l2cap_init_sockets(void);
void l2cap_cleanup_sockets(void);
@ -810,5 +809,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan);
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
u32 priority);
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
int l2cap_chan_check_security(struct l2cap_chan *chan);
#endif /* __L2CAP_H */

View File

@ -23,6 +23,23 @@
#define MGMT_INDEX_NONE 0xFFFF
#define MGMT_STATUS_SUCCESS 0x00
#define MGMT_STATUS_UNKNOWN_COMMAND 0x01
#define MGMT_STATUS_NOT_CONNECTED 0x02
#define MGMT_STATUS_FAILED 0x03
#define MGMT_STATUS_CONNECT_FAILED 0x04
#define MGMT_STATUS_AUTH_FAILED 0x05
#define MGMT_STATUS_NOT_PAIRED 0x06
#define MGMT_STATUS_NO_RESOURCES 0x07
#define MGMT_STATUS_TIMEOUT 0x08
#define MGMT_STATUS_ALREADY_CONNECTED 0x09
#define MGMT_STATUS_BUSY 0x0a
#define MGMT_STATUS_REJECTED 0x0b
#define MGMT_STATUS_NOT_SUPPORTED 0x0c
#define MGMT_STATUS_INVALID_PARAMS 0x0d
#define MGMT_STATUS_DISCONNECTED 0x0e
#define MGMT_STATUS_NOT_POWERED 0x0f
struct mgmt_hdr {
__le16 opcode;
__le16 index;
@ -119,6 +136,10 @@ struct mgmt_cp_remove_keys {
bdaddr_t bdaddr;
__u8 disconnect;
} __packed;
struct mgmt_rp_remove_keys {
bdaddr_t bdaddr;
__u8 status;
};
#define MGMT_OP_DISCONNECT 0x000F
struct mgmt_cp_disconnect {
@ -126,11 +147,12 @@ struct mgmt_cp_disconnect {
} __packed;
struct mgmt_rp_disconnect {
bdaddr_t bdaddr;
__u8 status;
} __packed;
#define MGMT_ADDR_BREDR 0x00
#define MGMT_ADDR_LE 0x01
#define MGMT_ADDR_BREDR_LE 0x02
#define MGMT_ADDR_LE_PUBLIC 0x01
#define MGMT_ADDR_LE_RANDOM 0x02
#define MGMT_ADDR_INVALID 0xff
struct mgmt_addr_info {
@ -167,11 +189,11 @@ struct mgmt_cp_set_io_capability {
#define MGMT_OP_PAIR_DEVICE 0x0014
struct mgmt_cp_pair_device {
bdaddr_t bdaddr;
struct mgmt_addr_info addr;
__u8 io_cap;
} __packed;
struct mgmt_rp_pair_device {
bdaddr_t bdaddr;
struct mgmt_addr_info addr;
__u8 status;
} __packed;
@ -210,6 +232,9 @@ struct mgmt_cp_remove_remote_oob_data {
} __packed;
#define MGMT_OP_START_DISCOVERY 0x001B
struct mgmt_cp_start_discovery {
__u8 type;
} __packed;
#define MGMT_OP_STOP_DISCOVERY 0x001C
@ -228,6 +253,17 @@ struct mgmt_cp_set_fast_connectable {
__u8 enable;
} __packed;
#define MGMT_OP_USER_PASSKEY_REPLY 0x0020
struct mgmt_cp_user_passkey_reply {
bdaddr_t bdaddr;
__le32 passkey;
} __packed;
#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x0021
struct mgmt_cp_user_passkey_neg_reply {
bdaddr_t bdaddr;
} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
@ -322,3 +358,8 @@ struct mgmt_ev_device_blocked {
struct mgmt_ev_device_unblocked {
bdaddr_t bdaddr;
} __packed;
#define MGMT_EV_USER_PASSKEY_REQUEST 0x0017
struct mgmt_ev_user_passkey_request {
bdaddr_t bdaddr;
} __packed;

View File

@ -1149,6 +1149,7 @@ struct cfg80211_ibss_params {
u8 *ssid;
u8 *bssid;
struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type;
u8 *ie;
u8 ssid_len, ie_len;
u16 beacon_interval;
@ -3267,6 +3268,16 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
const u8 *frame, size_t len,
int freq, gfp_t gfp);
/*
* cfg80211_can_beacon_sec_chan - test if ht40 on extension channel can be used
* @wiphy: the wiphy
* @chan: main channel
* @channel_type: HT mode
*/
int cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type);
/* Logging, debugging and troubleshooting/diagnostic helpers. */
/* wiphy_printk helpers, similar to dev_printk */

View File

@ -77,17 +77,12 @@ static struct bnep_session *__bnep_get_session(u8 *dst)
static void __bnep_link_session(struct bnep_session *s)
{
/* It's safe to call __module_get() here because sessions are added
by the socket layer which has to hold the reference to this module.
*/
__module_get(THIS_MODULE);
list_add(&s->list, &bnep_session_list);
}
static void __bnep_unlink_session(struct bnep_session *s)
{
list_del(&s->list);
module_put(THIS_MODULE);
}
static int bnep_send(struct bnep_session *s, void *data, size_t len)
@ -528,6 +523,7 @@ static int bnep_session(void *arg)
up_write(&bnep_session_sem);
free_netdev(dev);
module_put_and_exit(0);
return 0;
}
@ -614,9 +610,11 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
__bnep_link_session(s);
__module_get(THIS_MODULE);
s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
if (IS_ERR(s->task)) {
/* Session thread start failed, gotta cleanup. */
module_put(THIS_MODULE);
unregister_netdev(dev);
__bnep_unlink_session(s);
err = PTR_ERR(s->task);

View File

@ -65,14 +65,12 @@ static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
static void __cmtp_link_session(struct cmtp_session *session)
{
__module_get(THIS_MODULE);
list_add(&session->list, &cmtp_session_list);
}
static void __cmtp_unlink_session(struct cmtp_session *session)
{
list_del(&session->list);
module_put(THIS_MODULE);
}
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
@ -325,6 +323,7 @@ static int cmtp_session(void *arg)
up_write(&cmtp_session_sem);
kfree(session);
module_put_and_exit(0);
return 0;
}
@ -374,9 +373,11 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
__cmtp_link_session(session);
__module_get(THIS_MODULE);
session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
session->num);
if (IS_ERR(session->task)) {
module_put(THIS_MODULE);
err = PTR_ERR(session->task);
goto unlink;
}

View File

@ -123,7 +123,7 @@ static void hci_acl_connect_cancel(struct hci_conn *conn)
BT_DBG("%p", conn);
if (conn->hdev->hci_ver < 2)
if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2)
return;
bacpy(&cp.bdaddr, &conn->dst);

View File

@ -54,6 +54,8 @@
#define AUTO_OFF_TIMEOUT 2000
int enable_hs;
static void hci_cmd_task(unsigned long arg);
static void hci_rx_task(unsigned long arg);
static void hci_tx_task(unsigned long arg);
@ -228,18 +230,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
/* Read Buffer Size (ACL mtu, max pkt, etc.) */
hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
#if 0
/* Host buffer size */
{
struct hci_cp_host_buffer_size cp;
cp.acl_mtu = cpu_to_le16(HCI_MAX_ACL_SIZE);
cp.sco_mtu = HCI_MAX_SCO_SIZE;
cp.acl_max_pkt = cpu_to_le16(0xffff);
cp.sco_max_pkt = cpu_to_le16(0xffff);
hci_send_cmd(hdev, HCI_OP_HOST_BUFFER_SIZE, sizeof(cp), &cp);
}
#endif
/* Read BD Address */
hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
@ -521,8 +511,9 @@ int hci_dev_open(__u16 dev)
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
set_bit(HCI_RAW, &hdev->flags);
/* Treat all non BR/EDR controllers as raw devices for now */
if (hdev->dev_type != HCI_BREDR)
/* Treat all non BR/EDR controllers as raw devices if
enable_hs is not set */
if (hdev->dev_type != HCI_BREDR && !enable_hs)
set_bit(HCI_RAW, &hdev->flags);
if (hdev->open(hdev)) {
@ -1336,14 +1327,12 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct bdaddr_list *entry;
if (bacmp(bdaddr, BDADDR_ANY) == 0) {
if (bacmp(bdaddr, BDADDR_ANY) == 0)
return hci_blacklist_clear(hdev);
}
entry = hci_blacklist_lookup(hdev, bdaddr);
if (!entry) {
if (!entry)
return -ENOENT;
}
list_del(&entry->list);
kfree(entry);
@ -1451,12 +1440,13 @@ int hci_register_dev(struct hci_dev *hdev)
sprintf(hdev->name, "hci%d", id);
hdev->id = id;
list_add(&hdev->list, head);
list_add_tail(&hdev->list, head);
atomic_set(&hdev->refcnt, 1);
spin_lock_init(&hdev->lock);
hdev->flags = 0;
hdev->dev_flags = 0;
hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
hdev->esco_type = (ESCO_HV1);
hdev->link_mode = (HCI_LM_ACCEPT);
@ -2614,3 +2604,6 @@ int hci_cancel_inquiry(struct hci_dev *hdev)
return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
}
module_param(enable_hs, bool, 0644);
MODULE_PARM_DESC(enable_hs, "Enable High Speed");

View File

@ -55,8 +55,12 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
if (status)
if (status) {
hci_dev_lock(hdev);
mgmt_stop_discovery_failed(hdev, status);
hci_dev_unlock(hdev);
return;
}
clear_bit(HCI_INQUIRY, &hdev->flags);
@ -190,6 +194,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
clear_bit(HCI_RESET, &hdev->flags);
hci_req_complete(hdev, HCI_OP_RESET, status);
hdev->dev_flags = 0;
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@ -494,7 +500,7 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
/* CSR 1.1 dongles does not accept any bitfield so don't try to set
* any event mask for pre 1.2 devices */
if (hdev->lmp_ver <= 1)
if (hdev->hci_ver < BLUETOOTH_VER_1_2)
return;
events[4] |= 0x01; /* Flow Specification Complete */
@ -558,7 +564,7 @@ static void hci_setup(struct hci_dev *hdev)
{
hci_setup_event_mask(hdev);
if (hdev->lmp_ver > 1)
if (hdev->hci_ver > BLUETOOTH_VER_1_1)
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
@ -713,6 +719,21 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status);
}
static void hci_cc_read_flow_control_mode(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_rp_read_flow_control_mode *rp = (void *) skb->data;
BT_DBG("%s status 0x%x", hdev->name, rp->status);
if (rp->status)
return;
hdev->flow_ctl_mode = rp->mode;
hci_req_complete(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, rp->status);
}
static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_read_buffer_size *rp = (void *) skb->data;
@ -927,6 +948,37 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
hci_dev_unlock(hdev);
}
static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
BT_DBG("%s status 0x%x", hdev->name, rp->status);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr,
rp->status);
hci_dev_unlock(hdev);
}
static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
BT_DBG("%s status 0x%x", hdev->name, rp->status);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr,
rp->status);
hci_dev_unlock(hdev);
}
static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
struct sk_buff *skb)
{
@ -940,6 +992,13 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
hci_dev_unlock(hdev);
}
static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%x", hdev->name, status);
}
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
struct sk_buff *skb)
{
@ -956,12 +1015,16 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
return;
if (cp->enable == 0x01) {
set_bit(HCI_LE_SCAN, &hdev->dev_flags);
del_timer(&hdev->adv_timer);
hci_dev_lock(hdev);
hci_adv_entries_clear(hdev);
hci_dev_unlock(hdev);
} else if (cp->enable == 0x00) {
clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT);
}
}
@ -1014,7 +1077,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
hci_conn_check_pending(hdev);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_inquiry_failed(hdev, status);
mgmt_start_discovery_failed(hdev, status);
hci_dev_unlock(hdev);
return;
}
@ -1437,7 +1500,7 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
data.rssi = 0x00;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, 0, NULL);
}
@ -1472,7 +1535,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CONFIG;
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
mgmt_connected(hdev, &ev->bdaddr, conn->type);
mgmt_connected(hdev, &ev->bdaddr, conn->type,
conn->dst_type);
} else
conn->state = BT_CONNECTED;
@ -1494,7 +1558,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
}
/* Set packet type for incoming connection */
if (!conn->out && hdev->hci_ver < 3) {
if (!conn->out && hdev->hci_ver < BLUETOOTH_VER_2_0) {
struct hci_cp_change_conn_ptype cp;
cp.handle = ev->handle;
cp.pkt_type = cpu_to_le16(conn->pkt_type);
@ -1505,7 +1569,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK)
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
ev->status);
conn->dst_type, ev->status);
}
if (conn->type == ACL_LINK)
@ -1604,26 +1668,27 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, ev->status);
if (ev->status) {
hci_dev_lock(hdev);
mgmt_disconnect_failed(hdev);
hci_dev_unlock(hdev);
return;
}
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
if (!conn)
goto unlock;
conn->state = BT_CLOSED;
if (ev->status == 0)
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK || conn->type == LE_LINK)
mgmt_disconnected(hdev, &conn->dst, conn->type);
if (conn->type == ACL_LINK || conn->type == LE_LINK) {
if (ev->status != 0)
mgmt_disconnect_failed(hdev, &conn->dst, ev->status);
else
mgmt_disconnected(hdev, &conn->dst, conn->type,
conn->dst_type);
}
hci_proto_disconn_cfm(conn, ev->reason);
hci_conn_del(conn);
if (ev->status == 0) {
hci_proto_disconn_cfm(conn, ev->reason);
hci_conn_del(conn);
}
unlock:
hci_dev_unlock(hdev);
@ -1961,6 +2026,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_write_ca_timeout(hdev, skb);
break;
case HCI_OP_READ_FLOW_CONTROL_MODE:
hci_cc_read_flow_control_mode(hdev, skb);
break;
case HCI_OP_READ_LOCAL_AMP_INFO:
hci_cc_read_local_amp_info(hdev, skb);
break;
@ -2009,6 +2078,17 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_user_confirm_neg_reply(hdev, skb);
break;
case HCI_OP_USER_PASSKEY_REPLY:
hci_cc_user_passkey_reply(hdev, skb);
break;
case HCI_OP_USER_PASSKEY_NEG_REPLY:
hci_cc_user_passkey_neg_reply(hdev, skb);
case HCI_OP_LE_SET_SCAN_PARAM:
hci_cc_le_set_scan_param(hdev, skb);
break;
case HCI_OP_LE_SET_SCAN_ENABLE:
hci_cc_le_set_scan_enable(hdev, skb);
break;
@ -2096,7 +2176,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
case HCI_OP_DISCONNECT:
if (ev->status != 0)
mgmt_disconnect_failed(hdev);
mgmt_disconnect_failed(hdev, NULL, ev->status);
break;
case HCI_OP_LE_CREATE_CONN:
@ -2444,7 +2524,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, info->rssi,
NULL);
}
@ -2461,7 +2541,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, info->rssi,
NULL);
}
@ -2604,7 +2684,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi;
data.ssp_mode = 0x01;
hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, info->rssi, info->data);
}
@ -2768,6 +2848,21 @@ unlock:
hci_dev_unlock(hdev);
}
static inline void hci_user_passkey_request_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_ev_user_passkey_req *ev = (void *) skb->data;
BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_user_passkey_request(hdev, &ev->bdaddr);
hci_dev_unlock(hdev);
}
static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_simple_pair_complete *ev = (void *) skb->data;
@ -2868,14 +2963,15 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
}
if (ev->status) {
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, ev->status);
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
conn->dst_type, ev->status);
hci_proto_connect_cfm(conn, ev->status);
conn->state = BT_CLOSED;
hci_conn_del(conn);
goto unlock;
}
mgmt_connected(hdev, &ev->bdaddr, conn->type);
mgmt_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type);
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
@ -3106,6 +3202,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_user_confirm_request_evt(hdev, skb);
break;
case HCI_EV_USER_PASSKEY_REQUEST:
hci_user_passkey_request_evt(hdev, skb);
break;
case HCI_EV_SIMPLE_PAIR_COMPLETE:
hci_simple_pair_complete_evt(hdev, skb);
break;

View File

@ -57,7 +57,6 @@
#include <net/bluetooth/smp.h>
int disable_ertm;
int enable_hs;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
@ -97,7 +96,6 @@ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16
return c;
}
return NULL;
}
static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
@ -154,12 +152,9 @@ static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
list_for_each_entry(c, &chan_list, global_l) {
if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
goto found;
return c;
}
c = NULL;
found:
return c;
return NULL;
}
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
@ -234,8 +229,37 @@ static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer)
chan_put(chan);
}
static char *state_to_string(int state)
{
switch(state) {
case BT_CONNECTED:
return "BT_CONNECTED";
case BT_OPEN:
return "BT_OPEN";
case BT_BOUND:
return "BT_BOUND";
case BT_LISTEN:
return "BT_LISTEN";
case BT_CONNECT:
return "BT_CONNECT";
case BT_CONNECT2:
return "BT_CONNECT2";
case BT_CONFIG:
return "BT_CONFIG";
case BT_DISCONN:
return "BT_DISCONN";
case BT_CLOSED:
return "BT_CLOSED";
}
return "invalid state";
}
static void l2cap_state_change(struct l2cap_chan *chan, int state)
{
BT_DBG("%p %s -> %s", chan, state_to_string(chan->state),
state_to_string(state));
chan->state = state;
chan->ops->state_change(chan->data, state);
}
@ -518,7 +542,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
}
/* Service level security */
static inline int l2cap_check_security(struct l2cap_chan *chan)
int l2cap_chan_check_security(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
__u8 auth_type;
@ -664,7 +688,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
return;
if (l2cap_check_security(chan) &&
if (l2cap_chan_check_security(chan) &&
__l2cap_no_conn_pending(chan)) {
struct l2cap_conn_req req;
req.scid = cpu_to_le16(chan->scid);
@ -754,7 +778,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
if (chan->state == BT_CONNECT) {
struct l2cap_conn_req req;
if (!l2cap_check_security(chan) ||
if (!l2cap_chan_check_security(chan) ||
!__l2cap_no_conn_pending(chan)) {
bh_unlock_sock(sk);
continue;
@ -787,7 +811,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
if (l2cap_check_security(chan)) {
if (l2cap_chan_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
struct sock *parent = bt_sk(sk)->parent;
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
@ -1181,7 +1205,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan)
if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
__clear_chan_timer(chan);
if (l2cap_check_security(chan))
if (l2cap_chan_check_security(chan))
l2cap_state_change(chan, BT_CONNECTED);
} else
l2cap_do_start(chan);
@ -1318,14 +1342,12 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq)
if (!skb)
return;
do {
if (bt_cb(skb)->tx_seq == tx_seq)
break;
while (bt_cb(skb)->tx_seq != tx_seq) {
if (skb_queue_is_last(&chan->tx_q, skb))
return;
} while ((skb = skb_queue_next(&chan->tx_q, skb)));
skb = skb_queue_next(&chan->tx_q, skb);
}
if (chan->remote_max_tx &&
bt_cb(skb)->retries == chan->remote_max_tx) {
@ -1906,7 +1928,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
{
struct l2cap_conf_efs efs;
switch(chan->mode) {
switch (chan->mode) {
case L2CAP_MODE_ERTM:
efs.id = chan->local_id;
efs.stype = chan->local_stype;
@ -2606,7 +2628,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
chan->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_check_security(chan)) {
if (l2cap_chan_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
@ -3019,7 +3041,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
l2cap_state_change(chan,BT_DISCONN);
l2cap_state_change(chan, BT_DISCONN);
__clear_chan_timer(chan);
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
bh_unlock_sock(sk);
@ -3562,14 +3584,10 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
bt_cb(skb)->sar = sar;
next_skb = skb_peek(&chan->srej_q);
if (!next_skb) {
__skb_queue_tail(&chan->srej_q, skb);
return 0;
}
tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq);
do {
while (next_skb) {
if (bt_cb(next_skb)->tx_seq == tx_seq)
return -EINVAL;
@ -3582,9 +3600,10 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb,
}
if (skb_queue_is_last(&chan->srej_q, next_skb))
break;
} while ((next_skb = skb_queue_next(&chan->srej_q, next_skb)));
next_skb = NULL;
else
next_skb = skb_queue_next(&chan->srej_q, next_skb);
}
__skb_queue_tail(&chan->srej_q, skb);
@ -3788,7 +3807,7 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq)
}
}
static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
static int l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
{
struct srej_list *new;
u32 control;
@ -3799,6 +3818,9 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
l2cap_send_sframe(chan, control);
new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
if (!new)
return -ENOMEM;
new->tx_seq = chan->expected_tx_seq;
chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
@ -3807,6 +3829,8 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq)
}
chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq);
return 0;
}
static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb)
@ -3877,7 +3901,12 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
return 0;
}
}
l2cap_send_srejframe(chan, tx_seq);
err = l2cap_send_srejframe(chan, tx_seq);
if (err < 0) {
l2cap_send_disconn_req(chan->conn, chan, -err);
return err;
}
}
} else {
expected_tx_seq_offset = __seq_offset(chan,
@ -3899,7 +3928,11 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
set_bit(CONN_SEND_PBIT, &chan->conn_state);
l2cap_send_srejframe(chan, tx_seq);
err = l2cap_send_srejframe(chan, tx_seq);
if (err < 0) {
l2cap_send_disconn_req(chan->conn, chan, -err);
return err;
}
__clear_ack_timer(chan);
}
@ -3928,11 +3961,12 @@ expected:
l2cap_retransmit_frames(chan);
}
__set_ack_timer(chan);
chan->num_acked = (chan->num_acked + 1) % num_to_ack;
if (chan->num_acked == num_to_ack - 1)
l2cap_send_ack(chan);
else
__set_ack_timer(chan);
return 0;
@ -4768,6 +4802,3 @@ void l2cap_exit(void)
module_param(disable_ertm, bool, 0644);
MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode");
module_param(enable_hs, bool, 0644);
MODULE_PARM_DESC(enable_hs, "Enable High Speed");

View File

@ -626,8 +626,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
chan->sec_level = sec.level;
if (!chan->conn)
break;
conn = chan->conn;
if (conn && chan->scid == L2CAP_CID_LE_DATA) {
/*change security for LE channels */
if (chan->scid == L2CAP_CID_LE_DATA) {
if (!conn->hcon->out) {
err = -EINVAL;
break;
@ -635,9 +640,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
if (smp_conn_security(conn, sec.level))
break;
err = 0;
sk->sk_state = BT_CONFIG;
/* or for ACL link, under defer_setup time */
} else if (sk->sk_state == BT_CONNECT2 &&
bt_sk(sk)->defer_setup) {
err = l2cap_chan_check_security(chan);
} else {
err = -EINVAL;
}
break;

File diff suppressed because it is too large Load Diff

View File

@ -232,6 +232,18 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
return 0;
}
static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
{
if (send)
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
&reason);
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason);
del_timer(&conn->security_timer);
smp_chan_destroy(conn);
}
static void confirm_work(struct work_struct *work)
{
struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
@ -270,8 +282,7 @@ static void confirm_work(struct work_struct *work)
return;
error:
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
smp_chan_destroy(conn);
smp_failure(conn, reason, 1);
}
static void random_work(struct work_struct *work)
@ -354,8 +365,7 @@ static void random_work(struct work_struct *work)
return;
error:
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason);
smp_chan_destroy(conn);
smp_failure(conn, reason, 1);
}
static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
@ -379,7 +389,15 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
void smp_chan_destroy(struct l2cap_conn *conn)
{
kfree(conn->smp_chan);
struct smp_chan *smp = conn->smp_chan;
clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
if (smp->tfm)
crypto_free_blkcipher(smp->tfm);
kfree(smp);
conn->smp_chan = NULL;
hci_conn_put(conn->hcon);
}
@ -647,6 +665,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
break;
case SMP_CMD_PAIRING_FAIL:
smp_failure(conn, skb->data[0], 0);
reason = 0;
err = -EPERM;
break;
@ -692,8 +711,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
done:
if (reason)
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
&reason);
smp_failure(conn, reason, 1);
kfree_skb(skb);
return err;

View File

@ -185,6 +185,8 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);

View File

@ -83,6 +83,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
@ -419,7 +421,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_AP)
sdata->vif.type != NL80211_IFTYPE_AP &&
sdata->vif.type != NL80211_IFTYPE_ADHOC)
return -EINVAL;
if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
@ -430,6 +433,27 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
return -EINVAL;
}
/*
* 802.11n-2009 11.5.1.1: If the initiating STA is an HT STA, is a
* member of an IBSS, and has no other existing Block Ack agreement
* with the recipient STA, then the initiating STA shall transmit a
* Probe Request frame to the recipient STA and shall not transmit an
* ADDBA Request frame unless it receives a Probe Response frame
* from the recipient within dot11ADDBAFailureTimeout.
*
* The probe request mechanism for ADDBA is currently not implemented,
* but we only build up Block Ack session with HT STAs. This information
* is set when we receive a bss info from a probe response or a beacon.
*/
if (sta->sdata->vif.type == NL80211_IFTYPE_ADHOC &&
!sta->sta.ht_cap.ht_supported) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - IBSS STA %pM"
"does not advertise HT support\n", pubsta->addr);
#endif /* CONFIG_MAC80211_HT_DEBUG */
return -EINVAL;
}
spin_lock_bh(&sta->lock);
/* we have tried too many times, receiver does not want A-MPDU */

View File

@ -47,7 +47,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
int i;
if (sdata->vif.type != NL80211_IFTYPE_STATION) {
WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
/* AP interfaces call this code when adding new stations,
* so just silently ignore non station interfaces.
*/
return;
}
@ -282,6 +284,8 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);

View File

@ -77,6 +77,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *bss;
u32 bss_change;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
enum nl80211_channel_type channel_type;
lockdep_assert_held(&ifibss->mtx);
@ -105,8 +106,16 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
local->oper_channel = chan;
WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
channel_type = ifibss->channel_type;
if (channel_type > NL80211_CHAN_HT20 &&
!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type))
channel_type = NL80211_CHAN_HT20;
if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
/* can only fail due to HT40+/- mismatch */
channel_type = NL80211_CHAN_HT20;
WARN_ON(!ieee80211_set_channel_type(local, sdata,
NL80211_CHAN_HT20));
}
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
sband = local->hw.wiphy->bands[chan->band];
@ -172,6 +181,19 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
memcpy(skb_put(skb, ifibss->ie_len),
ifibss->ie, ifibss->ie_len);
/* add HT capability and information IEs */
if (channel_type && sband->ht_cap.ht_supported) {
pos = skb_put(skb, 4 +
sizeof(struct ieee80211_ht_cap) +
sizeof(struct ieee80211_ht_info));
pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
sband->ht_cap.cap);
pos = ieee80211_ie_build_ht_info(pos,
&sband->ht_cap,
chan,
channel_type);
}
if (local->hw.queues >= 4) {
pos = skb_put(skb, 9);
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
@ -195,6 +217,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
bss_change |= BSS_CHANGED_BEACON;
bss_change |= BSS_CHANGED_BEACON_ENABLED;
bss_change |= BSS_CHANGED_BASIC_RATES;
bss_change |= BSS_CHANGED_HT;
bss_change |= BSS_CHANGED_IBSS;
sdata->vif.bss_conf.ibss_joined = true;
ieee80211_bss_info_change_notify(sdata, bss_change);
@ -268,6 +291,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
u64 beacon_timestamp, rx_timestamp;
u32 supp_rates = 0;
enum ieee80211_band band = rx_status->band;
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
bool rates_updated = false;
if (elems->ds_params && elems->ds_params_len == 1)
freq = ieee80211_channel_to_frequency(elems->ds_params[0],
@ -307,7 +332,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
prev_rates,
sta->sta.supp_rates[band]);
#endif
rate_control_rate_init(sta);
rates_updated = true;
}
} else
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
@ -318,6 +343,39 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (sta && elems->wmm_info)
set_sta_flag(sta, WLAN_STA_WME);
if (sta && elems->ht_info_elem && elems->ht_cap_elem &&
sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
/* we both use HT */
struct ieee80211_sta_ht_cap sta_ht_cap_new;
enum nl80211_channel_type channel_type =
ieee80211_ht_info_to_channel_type(
elems->ht_info_elem);
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem,
&sta_ht_cap_new);
/*
* fall back to HT20 if we don't use or use
* the other extension channel
*/
if ((channel_type == NL80211_CHAN_HT40MINUS ||
channel_type == NL80211_CHAN_HT40PLUS) &&
channel_type != sdata->u.ibss.channel_type)
sta_ht_cap_new.cap &=
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
if (memcmp(&sta->sta.ht_cap, &sta_ht_cap_new,
sizeof(sta_ht_cap_new))) {
memcpy(&sta->sta.ht_cap, &sta_ht_cap_new,
sizeof(sta_ht_cap_new));
rates_updated = true;
}
}
if (sta && rates_updated)
rate_control_rate_init(sta);
rcu_read_unlock();
}
@ -896,12 +954,18 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
struct cfg80211_ibss_params *params)
{
struct sk_buff *skb;
u32 changed = 0;
skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom +
36 /* bitrates */ +
34 /* SSID */ +
3 /* DS params */ +
4 /* IBSS params */ +
sizeof(struct ieee80211_hdr_3addr) +
12 /* struct ieee80211_mgmt.u.beacon */ +
2 + IEEE80211_MAX_SSID_LEN /* max SSID */ +
2 + 8 /* max Supported Rates */ +
3 /* max DS params */ +
4 /* IBSS params */ +
2 + (IEEE80211_MAX_SUPP_RATES - 8) +
2 + sizeof(struct ieee80211_ht_cap) +
2 + sizeof(struct ieee80211_ht_info) +
params->ie_len);
if (!skb)
return -ENOMEM;
@ -922,13 +986,15 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
sdata->u.ibss.channel = params->channel;
sdata->u.ibss.channel_type = params->channel_type;
sdata->u.ibss.fixed_channel = params->channel_fixed;
/* fix ourselves to that channel now already */
if (params->channel_fixed) {
sdata->local->oper_channel = params->channel;
WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata,
NL80211_CHAN_NO_HT));
if (!ieee80211_set_channel_type(sdata->local, sdata,
params->channel_type))
return -EINVAL;
}
if (params->ie) {
@ -951,6 +1017,23 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
/*
* 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
* reserved, but an HT STA shall protect HT transmissions as though
* the HT Protection field were set to non-HT mixed mode.
*
* In an IBSS, the RIFS Mode field of the HT Operation element is
* also reserved, but an HT STA shall operate as though this field
* were set to 1.
*/
sdata->vif.bss_conf.ht_operation_mode |=
IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED
| IEEE80211_HT_PARAM_RIFS_MODE;
changed |= BSS_CHANGED_HT;
ieee80211_bss_info_change_notify(sdata, changed);
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
return 0;

View File

@ -474,6 +474,7 @@ struct ieee80211_if_ibss {
u8 ssid_len, ie_len;
u8 *ie;
struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type;
unsigned long ibss_join_req;
/* probe response/beacon for IBSS */

View File

@ -570,7 +570,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
WIPHY_FLAG_OFFCHAN_TX |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
wiphy->features = NL80211_FEATURE_SK_TX_STATUS;
wiphy->features = NL80211_FEATURE_SK_TX_STATUS |
NL80211_FEATURE_HT_IBSS;
if (!ops->set_key)
wiphy->flags |= WIPHY_FLAG_IBSS_RSN;

View File

@ -2237,7 +2237,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_AP)
sdata->vif.type != NL80211_IFTYPE_AP &&
sdata->vif.type != NL80211_IFTYPE_ADHOC)
break;
/* verify action_code is present */
@ -2796,10 +2797,17 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
return 0;
} else if (!ieee80211_bssid_match(bssid,
sdata->vif.addr)) {
/*
* Accept public action frames even when the
* BSSID doesn't match, this is used for P2P
* and location updates. Note that mac80211
* itself never looks at these frames.
*/
if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) &&
!ieee80211_is_beacon(hdr->frame_control) &&
!(ieee80211_is_action(hdr->frame_control) &&
sdata->vif.p2p))
ieee80211_is_public_action(hdr, skb->len))
return 1;
if (!(status->rx_flags & IEEE80211_RX_IN_SCAN) &&
!ieee80211_is_beacon(hdr->frame_control))
return 0;
status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
}

View File

@ -1332,8 +1332,11 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL))
CALL_TXH(ieee80211_tx_h_rate_ctrl);
if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION))
if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) {
__skb_queue_tail(&tx->skbs, tx->skb);
tx->skb = NULL;
goto txh_done;
}
CALL_TXH(ieee80211_tx_h_michael_mic_add);
CALL_TXH(ieee80211_tx_h_sequence);

View File

@ -1587,6 +1587,11 @@ u8 *ieee80211_ie_build_ht_info(u8 *pos,
}
if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
ht_info->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
/*
* Note: According to 802.11n-2009 9.13.3.1, HT Protection field and
* RIFS Mode are reserved in IBSS mode, therefore keep them at 0
*/
ht_info->operation_mode = 0x0000;
ht_info->stbc_param = 0x0000;

View File

@ -121,15 +121,16 @@ config CFG80211_WEXT
config WIRELESS_EXT_SYSFS
bool "Wireless extensions sysfs files"
default y
depends on WEXT_CORE && SYSFS
help
This option enables the deprecated wireless statistics
files in /sys/class/net/*/wireless/. The same information
is available via the ioctls as well.
Say Y if you have programs using it, like old versions of
hal.
Say N. If you know you have ancient tools requiring it,
like very old versions of hal (prior to 0.5.12 release),
say Y and update the tools as soon as possible as this
option will be removed soon.
config LIB80211
tristate "Common routines for IEEE802.11 drivers"

View File

@ -6,6 +6,7 @@
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/export.h>
#include <net/cfg80211.h>
#include "core.h"
@ -44,9 +45,9 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
return chan;
}
static bool can_beacon_sec_chan(struct wiphy *wiphy,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type)
int cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type)
{
struct ieee80211_channel *sec_chan;
int diff;
@ -75,6 +76,7 @@ static bool can_beacon_sec_chan(struct wiphy *wiphy,
return true;
}
EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan);
int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, int freq,
@ -109,8 +111,8 @@ int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
switch (channel_type) {
case NL80211_CHAN_HT40PLUS:
case NL80211_CHAN_HT40MINUS:
if (!can_beacon_sec_chan(&rdev->wiphy, chan,
channel_type)) {
if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, chan,
channel_type)) {
printk(KERN_DEBUG
"cfg80211: Secondary channel not "
"allowed to initiate communication\n");

View File

@ -4682,13 +4682,41 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
ibss.channel = ieee80211_get_channel(wiphy,
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
enum nl80211_channel_type channel_type;
channel_type = nla_get_u32(
info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
if (channel_type != NL80211_CHAN_NO_HT &&
channel_type != NL80211_CHAN_HT20 &&
channel_type != NL80211_CHAN_HT40MINUS &&
channel_type != NL80211_CHAN_HT40PLUS)
return -EINVAL;
if (channel_type != NL80211_CHAN_NO_HT &&
!(wiphy->features & NL80211_FEATURE_HT_IBSS))
return -EINVAL;
ibss.channel_type = channel_type;
} else {
ibss.channel_type = NL80211_CHAN_NO_HT;
}
ibss.channel = rdev_freq_to_chan(rdev,
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
ibss.channel_type);
if (!ibss.channel ||
ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
ibss.channel->flags & IEEE80211_CHAN_DISABLED)
return -EINVAL;
/* Both channels should be able to initiate communication */
if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
!cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
ibss.channel_type))
return -EINVAL;
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];