Merge remote-tracking branch 'wireless-next/master' into ath-next
This commit is contained in:
commit
30d88ce331
|
@ -27,6 +27,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/usb.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
||||
#define VERSION "1.0"
|
||||
|
@ -50,12 +51,12 @@
|
|||
#define ATH3K_NAME_LEN 0xFF
|
||||
|
||||
struct ath3k_version {
|
||||
unsigned int rom_version;
|
||||
unsigned int build_version;
|
||||
unsigned int ram_version;
|
||||
unsigned char ref_clock;
|
||||
unsigned char reserved[0x07];
|
||||
};
|
||||
__le32 rom_version;
|
||||
__le32 build_version;
|
||||
__le32 ram_version;
|
||||
__u8 ref_clock;
|
||||
__u8 reserved[7];
|
||||
} __packed;
|
||||
|
||||
static const struct usb_device_id ath3k_table[] = {
|
||||
/* Atheros AR3011 */
|
||||
|
@ -349,7 +350,8 @@ static int ath3k_load_patch(struct usb_device *udev)
|
|||
unsigned char fw_state;
|
||||
char filename[ATH3K_NAME_LEN] = {0};
|
||||
const struct firmware *firmware;
|
||||
struct ath3k_version fw_version, pt_version;
|
||||
struct ath3k_version fw_version;
|
||||
__u32 pt_rom_version, pt_build_version;
|
||||
int ret;
|
||||
|
||||
ret = ath3k_get_state(udev, &fw_state);
|
||||
|
@ -370,7 +372,7 @@ static int ath3k_load_patch(struct usb_device *udev)
|
|||
}
|
||||
|
||||
snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
|
||||
le32_to_cpu(fw_version.rom_version));
|
||||
le32_to_cpu(fw_version.rom_version));
|
||||
|
||||
ret = request_firmware(&firmware, filename, &udev->dev);
|
||||
if (ret < 0) {
|
||||
|
@ -378,12 +380,13 @@ static int ath3k_load_patch(struct usb_device *udev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8);
|
||||
pt_version.build_version = *(int *)
|
||||
(firmware->data + firmware->size - 4);
|
||||
pt_rom_version = get_unaligned_le32(firmware->data +
|
||||
firmware->size - 8);
|
||||
pt_build_version = get_unaligned_le32(firmware->data +
|
||||
firmware->size - 4);
|
||||
|
||||
if ((pt_version.rom_version != fw_version.rom_version) ||
|
||||
(pt_version.build_version <= fw_version.build_version)) {
|
||||
if (pt_rom_version != le32_to_cpu(fw_version.rom_version) ||
|
||||
pt_build_version <= le32_to_cpu(fw_version.build_version)) {
|
||||
BT_ERR("Patch file version did not match with firmware");
|
||||
release_firmware(firmware);
|
||||
return -EINVAL;
|
||||
|
|
|
@ -91,6 +91,7 @@ struct btmrvl_private {
|
|||
|
||||
/* Vendor specific Bluetooth commands */
|
||||
#define BT_CMD_PSCAN_WIN_REPORT_ENABLE 0xFC03
|
||||
#define BT_CMD_SET_BDADDR 0xFC22
|
||||
#define BT_CMD_AUTO_SLEEP_MODE 0xFC23
|
||||
#define BT_CMD_HOST_SLEEP_CONFIG 0xFC59
|
||||
#define BT_CMD_HOST_SLEEP_ENABLE 0xFC5A
|
||||
|
|
|
@ -539,6 +539,29 @@ static int btmrvl_setup(struct hci_dev *hdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int btmrvl_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
long ret;
|
||||
u8 buf[8];
|
||||
|
||||
buf[0] = MRVL_VENDOR_PKT;
|
||||
buf[1] = sizeof(bdaddr_t);
|
||||
memcpy(buf + 2, bdaddr, sizeof(bdaddr_t));
|
||||
|
||||
skb = __hci_cmd_sync(hdev, BT_CMD_SET_BDADDR, sizeof(buf), buf,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: changing btmrvl device address failed (%ld)",
|
||||
hdev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function handles the event generated by firmware, rx data
|
||||
* received from firmware, and tx data sent from kernel.
|
||||
|
@ -632,6 +655,7 @@ int btmrvl_register_hdev(struct btmrvl_private *priv)
|
|||
hdev->flush = btmrvl_flush;
|
||||
hdev->send = btmrvl_send_frame;
|
||||
hdev->setup = btmrvl_setup;
|
||||
hdev->set_bdaddr = btmrvl_set_bdaddr;
|
||||
|
||||
hdev->dev_type = priv->btmrvl_dev.dev_type;
|
||||
|
||||
|
|
|
@ -1169,6 +1169,10 @@ static int btmrvl_sdio_suspend(struct device *dev)
|
|||
}
|
||||
|
||||
priv = card->priv;
|
||||
hcidev = priv->btmrvl_dev.hcidev;
|
||||
BT_DBG("%s: SDIO suspend", hcidev->name);
|
||||
hci_suspend_dev(hcidev);
|
||||
skb_queue_purge(&priv->adapter->tx_queue);
|
||||
|
||||
if (priv->adapter->hs_state != HS_ACTIVATED) {
|
||||
if (btmrvl_enable_hs(priv)) {
|
||||
|
@ -1176,10 +1180,6 @@ static int btmrvl_sdio_suspend(struct device *dev)
|
|||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
hcidev = priv->btmrvl_dev.hcidev;
|
||||
BT_DBG("%s: SDIO suspend", hcidev->name);
|
||||
hci_suspend_dev(hcidev);
|
||||
skb_queue_purge(&priv->adapter->tx_queue);
|
||||
|
||||
priv->adapter->is_suspended = true;
|
||||
|
||||
|
@ -1221,13 +1221,13 @@ static int btmrvl_sdio_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
priv->adapter->is_suspended = false;
|
||||
hcidev = priv->btmrvl_dev.hcidev;
|
||||
BT_DBG("%s: SDIO resume", hcidev->name);
|
||||
hci_resume_dev(hcidev);
|
||||
priv->hw_wakeup_firmware(priv);
|
||||
priv->adapter->hs_state = HS_DEACTIVATED;
|
||||
hcidev = priv->btmrvl_dev.hcidev;
|
||||
BT_DBG("%s: HS DEACTIVATED in resume!", hcidev->name);
|
||||
priv->adapter->is_suspended = false;
|
||||
BT_DBG("%s: SDIO resume", hcidev->name);
|
||||
hci_resume_dev(hcidev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ static struct usb_driver btusb_driver;
|
|||
#define BTUSB_INTEL 0x100
|
||||
#define BTUSB_INTEL_BOOT 0x200
|
||||
#define BTUSB_BCM_PATCHRAM 0x400
|
||||
#define BTUSB_MARVELL 0x800
|
||||
|
||||
static const struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth USB device */
|
||||
|
@ -113,6 +114,9 @@ static const struct usb_device_id btusb_table[] = {
|
|||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
|
||||
.driver_info = BTUSB_BCM_PATCHRAM },
|
||||
|
||||
/* ASUSTek Computer - Broadcom based */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01) },
|
||||
|
||||
/* Belkin F8065bf - Broadcom based */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
|
||||
|
||||
|
@ -242,6 +246,10 @@ static const struct usb_device_id blacklist_table[] = {
|
|||
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
|
||||
{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
|
||||
|
||||
/* Marvell device */
|
||||
{ USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL },
|
||||
{ USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL },
|
||||
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
|
@ -1455,6 +1463,29 @@ static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
|
||||
const bdaddr_t *bdaddr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 buf[8];
|
||||
long ret;
|
||||
|
||||
buf[0] = 0xfe;
|
||||
buf[1] = sizeof(bdaddr_t);
|
||||
memcpy(buf + 2, bdaddr, sizeof(bdaddr_t));
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc22, sizeof(buf), buf, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: changing Marvell device address failed (%ld)",
|
||||
hdev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
|
||||
|
||||
static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
|
||||
|
@ -1766,6 +1797,9 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
hdev->set_bdaddr = btusb_set_bdaddr_intel;
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_MARVELL)
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_marvell;
|
||||
|
||||
if (id->driver_info & BTUSB_INTEL_BOOT)
|
||||
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
||||
|
||||
|
|
|
@ -290,6 +290,14 @@ static struct ieee80211_channel b43_5ghz_nphy_chantable[] = {
|
|||
CHAN5G(182, 0),
|
||||
};
|
||||
|
||||
static struct ieee80211_channel b43_5ghz_nphy_chantable_limited[] = {
|
||||
CHAN5G(36, 0), CHAN5G(40, 0),
|
||||
CHAN5G(44, 0), CHAN5G(48, 0),
|
||||
CHAN5G(149, 0), CHAN5G(153, 0),
|
||||
CHAN5G(157, 0), CHAN5G(161, 0),
|
||||
CHAN5G(165, 0),
|
||||
};
|
||||
|
||||
static struct ieee80211_channel b43_5ghz_aphy_chantable[] = {
|
||||
CHAN5G(34, 0), CHAN5G(36, 0),
|
||||
CHAN5G(38, 0), CHAN5G(40, 0),
|
||||
|
@ -322,6 +330,14 @@ static struct ieee80211_supported_band b43_band_5GHz_nphy = {
|
|||
.n_bitrates = b43_a_ratetable_size,
|
||||
};
|
||||
|
||||
static struct ieee80211_supported_band b43_band_5GHz_nphy_limited = {
|
||||
.band = IEEE80211_BAND_5GHZ,
|
||||
.channels = b43_5ghz_nphy_chantable_limited,
|
||||
.n_channels = ARRAY_SIZE(b43_5ghz_nphy_chantable_limited),
|
||||
.bitrates = b43_a_ratetable,
|
||||
.n_bitrates = b43_a_ratetable_size,
|
||||
};
|
||||
|
||||
static struct ieee80211_supported_band b43_band_5GHz_aphy = {
|
||||
.band = IEEE80211_BAND_5GHZ,
|
||||
.channels = b43_5ghz_aphy_chantable,
|
||||
|
@ -4385,8 +4401,9 @@ static int b43_phy_versioning(struct b43_wldev *dev)
|
|||
u8 phy_type;
|
||||
u8 phy_rev;
|
||||
u16 radio_manuf;
|
||||
u16 radio_ver;
|
||||
u16 radio_id;
|
||||
u16 radio_rev;
|
||||
u8 radio_ver;
|
||||
int unsupported = 0;
|
||||
|
||||
/* Get PHY versioning */
|
||||
|
@ -4452,7 +4469,9 @@ static int b43_phy_versioning(struct b43_wldev *dev)
|
|||
radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA);
|
||||
|
||||
b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 1);
|
||||
radio_ver = b43_read16(dev, B43_MMIO_RADIO24_DATA);
|
||||
radio_id = b43_read16(dev, B43_MMIO_RADIO24_DATA);
|
||||
|
||||
radio_ver = 0; /* Is there version somewhere? */
|
||||
} else if (core_rev >= 24) {
|
||||
u16 radio24[3];
|
||||
|
||||
|
@ -4461,12 +4480,10 @@ static int b43_phy_versioning(struct b43_wldev *dev)
|
|||
radio24[tmp] = b43_read16(dev, B43_MMIO_RADIO24_DATA);
|
||||
}
|
||||
|
||||
/* Broadcom uses "id" for our "ver" and has separated "ver" */
|
||||
/* radio_ver = (radio24[0] & 0xF0) >> 4; */
|
||||
|
||||
radio_manuf = 0x17F;
|
||||
radio_ver = (radio24[2] << 8) | radio24[1];
|
||||
radio_id = (radio24[2] << 8) | radio24[1];
|
||||
radio_rev = (radio24[0] & 0xF);
|
||||
radio_ver = (radio24[0] & 0xF0) >> 4;
|
||||
} else {
|
||||
if (dev->dev->chip_id == 0x4317) {
|
||||
if (dev->dev->chip_rev == 0)
|
||||
|
@ -4485,15 +4502,16 @@ static int b43_phy_versioning(struct b43_wldev *dev)
|
|||
<< 16;
|
||||
}
|
||||
radio_manuf = (tmp & 0x00000FFF);
|
||||
radio_ver = (tmp & 0x0FFFF000) >> 12;
|
||||
radio_id = (tmp & 0x0FFFF000) >> 12;
|
||||
radio_rev = (tmp & 0xF0000000) >> 28;
|
||||
radio_ver = 0; /* Probably not available on old hw */
|
||||
}
|
||||
|
||||
if (radio_manuf != 0x17F /* Broadcom */)
|
||||
unsupported = 1;
|
||||
switch (phy_type) {
|
||||
case B43_PHYTYPE_A:
|
||||
if (radio_ver != 0x2060)
|
||||
if (radio_id != 0x2060)
|
||||
unsupported = 1;
|
||||
if (radio_rev != 1)
|
||||
unsupported = 1;
|
||||
|
@ -4501,30 +4519,31 @@ static int b43_phy_versioning(struct b43_wldev *dev)
|
|||
unsupported = 1;
|
||||
break;
|
||||
case B43_PHYTYPE_B:
|
||||
if ((radio_ver & 0xFFF0) != 0x2050)
|
||||
if ((radio_id & 0xFFF0) != 0x2050)
|
||||
unsupported = 1;
|
||||
break;
|
||||
case B43_PHYTYPE_G:
|
||||
if (radio_ver != 0x2050)
|
||||
if (radio_id != 0x2050)
|
||||
unsupported = 1;
|
||||
break;
|
||||
case B43_PHYTYPE_N:
|
||||
if (radio_ver != 0x2055 && radio_ver != 0x2056 &&
|
||||
radio_ver != 0x2057)
|
||||
if (radio_id != 0x2055 && radio_id != 0x2056 &&
|
||||
radio_id != 0x2057)
|
||||
unsupported = 1;
|
||||
if (radio_ver == 0x2057 && !(radio_rev == 9))
|
||||
if (radio_id == 0x2057 &&
|
||||
!(radio_rev == 9 || radio_rev == 14))
|
||||
unsupported = 1;
|
||||
break;
|
||||
case B43_PHYTYPE_LP:
|
||||
if (radio_ver != 0x2062 && radio_ver != 0x2063)
|
||||
if (radio_id != 0x2062 && radio_id != 0x2063)
|
||||
unsupported = 1;
|
||||
break;
|
||||
case B43_PHYTYPE_HT:
|
||||
if (radio_ver != 0x2059)
|
||||
if (radio_id != 0x2059)
|
||||
unsupported = 1;
|
||||
break;
|
||||
case B43_PHYTYPE_LCN:
|
||||
if (radio_ver != 0x2064)
|
||||
if (radio_id != 0x2064)
|
||||
unsupported = 1;
|
||||
break;
|
||||
default:
|
||||
|
@ -4532,15 +4551,17 @@ static int b43_phy_versioning(struct b43_wldev *dev)
|
|||
}
|
||||
if (unsupported) {
|
||||
b43err(dev->wl,
|
||||
"FOUND UNSUPPORTED RADIO (Manuf 0x%X, ID 0x%X, Revision %u)\n",
|
||||
radio_manuf, radio_ver, radio_rev);
|
||||
"FOUND UNSUPPORTED RADIO (Manuf 0x%X, ID 0x%X, Revision %u, Version %u)\n",
|
||||
radio_manuf, radio_id, radio_rev, radio_ver);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
b43info(dev->wl, "Found Radio: Manuf 0x%X, ID 0x%X, Revision %u\n",
|
||||
radio_manuf, radio_ver, radio_rev);
|
||||
b43info(dev->wl,
|
||||
"Found Radio: Manuf 0x%X, ID 0x%X, Revision %u, Version %u\n",
|
||||
radio_manuf, radio_id, radio_rev, radio_ver);
|
||||
|
||||
/* FIXME: b43 treats "id" as "ver" and ignores the real "ver" */
|
||||
phy->radio_manuf = radio_manuf;
|
||||
phy->radio_ver = radio_ver;
|
||||
phy->radio_ver = radio_id;
|
||||
phy->radio_rev = radio_rev;
|
||||
|
||||
phy->analog = analog_type;
|
||||
|
@ -5150,16 +5171,22 @@ static int b43_setup_bands(struct b43_wldev *dev,
|
|||
struct ieee80211_hw *hw = dev->wl->hw;
|
||||
struct b43_phy *phy = &dev->phy;
|
||||
bool limited_2g;
|
||||
bool limited_5g;
|
||||
|
||||
/* We don't support all 2 GHz channels on some devices */
|
||||
limited_2g = phy->radio_ver == 0x2057 && phy->radio_rev == 9;
|
||||
limited_2g = phy->radio_ver == 0x2057 &&
|
||||
(phy->radio_rev == 9 || phy->radio_rev == 14);
|
||||
limited_5g = phy->radio_ver == 0x2057 &&
|
||||
phy->radio_rev == 9;
|
||||
|
||||
if (have_2ghz_phy)
|
||||
hw->wiphy->bands[IEEE80211_BAND_2GHZ] = limited_2g ?
|
||||
&b43_band_2ghz_limited : &b43_band_2GHz;
|
||||
if (dev->phy.type == B43_PHYTYPE_N) {
|
||||
if (have_5ghz_phy)
|
||||
hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_nphy;
|
||||
hw->wiphy->bands[IEEE80211_BAND_5GHZ] = limited_5g ?
|
||||
&b43_band_5GHz_nphy_limited :
|
||||
&b43_band_5GHz_nphy;
|
||||
} else {
|
||||
if (have_5ghz_phy)
|
||||
hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_aphy;
|
||||
|
@ -5311,7 +5338,6 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
|
|||
switch (dev->phy.type) {
|
||||
case B43_PHYTYPE_A:
|
||||
case B43_PHYTYPE_G:
|
||||
case B43_PHYTYPE_N:
|
||||
case B43_PHYTYPE_LP:
|
||||
case B43_PHYTYPE_HT:
|
||||
b43warn(wl, "5 GHz band is unsupported on this PHY\n");
|
||||
|
|
|
@ -225,13 +225,13 @@ static void b43_nphy_rf_ctl_override_one_to_many(struct b43_wldev *dev,
|
|||
b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 1);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 2);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x0800, value, core, off, 1);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x0800, 0, core, off, 1);
|
||||
break;
|
||||
case N_RF_CTL_OVER_CMD_TX_PU:
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 0);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 2);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x0800, value, core, off, 1);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x0800, 1, core, off, 1);
|
||||
break;
|
||||
case N_RF_CTL_OVER_CMD_RX_GAIN:
|
||||
tmp = value & 0xFF;
|
||||
|
@ -343,6 +343,7 @@ static void b43_nphy_rf_ctl_intc_override_rev7(struct b43_wldev *dev,
|
|||
switch (intc_override) {
|
||||
case N_INTC_OVERRIDE_OFF:
|
||||
b43_phy_write(dev, reg, 0);
|
||||
b43_phy_mask(dev, 0x2ff, ~0x2000);
|
||||
b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
|
||||
break;
|
||||
case N_INTC_OVERRIDE_TRSW:
|
||||
|
@ -1596,7 +1597,7 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
|
|||
bool lpf_bw3, lpf_bw4;
|
||||
|
||||
lpf_bw3 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER3) & 0x80;
|
||||
lpf_bw4 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER3) & 0x80;
|
||||
lpf_bw4 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER4) & 0x80;
|
||||
|
||||
if (lpf_bw3 || lpf_bw4) {
|
||||
/* TODO */
|
||||
|
@ -2117,7 +2118,7 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
|
|||
N_RF_CTL_OVER_CMD_RX_PU,
|
||||
1, 0, false);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x80, 1, 0, false, 0);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x80, 1, 0, false, 0);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x40, 1, 0, false, 0);
|
||||
if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x20, 0, 0, false,
|
||||
0);
|
||||
|
@ -2708,25 +2709,39 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
|
|||
struct ssb_sprom *sprom = dev->dev->bus_sprom;
|
||||
struct b43_phy *phy = &dev->phy;
|
||||
|
||||
/* TX to RX */
|
||||
u8 tx2rx_events[7] = { 4, 3, 5, 2, 1, 8, 31, };
|
||||
u8 tx2rx_delays[7] = { 8, 4, 4, 4, 4, 6, 1, };
|
||||
/* RX to TX */
|
||||
u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3,
|
||||
0x1F };
|
||||
u8 rx2tx_delays_ipa[9] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 };
|
||||
|
||||
u16 ntab7_15e_16e[] = { 0x10f, 0x10f };
|
||||
static const u16 ntab7_15e_16e[] = { 0, 0x10f, 0x10f };
|
||||
u8 ntab7_138_146[] = { 0x11, 0x11 };
|
||||
u8 ntab7_133[] = { 0x77, 0x11, 0x11 };
|
||||
|
||||
u16 lpf_20, lpf_40, lpf_11b;
|
||||
u16 bcap_val, bcap_val_11b, bcap_val_11n_20, bcap_val_11n_40;
|
||||
u16 scap_val, scap_val_11b, scap_val_11n_20, scap_val_11n_40;
|
||||
u16 lpf_ofdm_20mhz[2], lpf_ofdm_40mhz[2], lpf_11b[2];
|
||||
u16 bcap_val;
|
||||
s16 bcap_val_11b[2], bcap_val_11n_20[2], bcap_val_11n_40[2];
|
||||
u16 scap_val;
|
||||
s16 scap_val_11b[2], scap_val_11n_20[2], scap_val_11n_40[2];
|
||||
bool rccal_ovrd = false;
|
||||
|
||||
u16 rx2tx_lut_20_11b, rx2tx_lut_20_11n, rx2tx_lut_40_11n;
|
||||
u16 bias, conv, filt;
|
||||
|
||||
u32 noise_tbl[2];
|
||||
|
||||
u32 tmp32;
|
||||
u8 core;
|
||||
|
||||
b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125);
|
||||
b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01b3);
|
||||
b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105);
|
||||
b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016e);
|
||||
b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00cd);
|
||||
b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020);
|
||||
|
||||
if (phy->rev == 7) {
|
||||
b43_phy_set(dev, B43_NPHY_FINERX2_CGC, 0x10);
|
||||
b43_phy_maskset(dev, B43_NPHY_FREQGAIN0, 0xFF80, 0x0020);
|
||||
|
@ -2746,11 +2761,18 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
|
|||
b43_phy_maskset(dev, B43_NPHY_FREQGAIN7, 0xFF80, 0x0040);
|
||||
b43_phy_maskset(dev, B43_NPHY_FREQGAIN7, 0x80FF, 0x4000);
|
||||
}
|
||||
if (phy->rev <= 8) {
|
||||
|
||||
if (phy->rev >= 16) {
|
||||
b43_phy_write(dev, B43_NPHY_FORCEFRONT0, 0x7ff);
|
||||
b43_phy_write(dev, B43_NPHY_FORCEFRONT1, 0x7ff);
|
||||
} else if (phy->rev <= 8) {
|
||||
b43_phy_write(dev, B43_NPHY_FORCEFRONT0, 0x1B0);
|
||||
b43_phy_write(dev, B43_NPHY_FORCEFRONT1, 0x1B0);
|
||||
}
|
||||
if (phy->rev >= 8)
|
||||
|
||||
if (phy->rev >= 16)
|
||||
b43_phy_maskset(dev, B43_NPHY_TXTAILCNT, ~0xFF, 0xa0);
|
||||
else if (phy->rev >= 8)
|
||||
b43_phy_maskset(dev, B43_NPHY_TXTAILCNT, ~0xFF, 0x72);
|
||||
|
||||
b43_ntab_write(dev, B43_NTAB16(8, 0x00), 2);
|
||||
|
@ -2758,9 +2780,11 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
|
|||
tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0));
|
||||
tmp32 &= 0xffffff;
|
||||
b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32);
|
||||
b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x15e), 2, ntab7_15e_16e);
|
||||
b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x16e), 2, ntab7_15e_16e);
|
||||
b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x15d), 3, ntab7_15e_16e);
|
||||
b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x16d), 3, ntab7_15e_16e);
|
||||
|
||||
b43_nphy_set_rf_sequence(dev, 1, tx2rx_events, tx2rx_delays,
|
||||
ARRAY_SIZE(tx2rx_events));
|
||||
if (b43_nphy_ipa(dev))
|
||||
b43_nphy_set_rf_sequence(dev, 0, rx2tx_events_ipa,
|
||||
rx2tx_delays_ipa, ARRAY_SIZE(rx2tx_events_ipa));
|
||||
|
@ -2768,84 +2792,176 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
|
|||
b43_phy_maskset(dev, B43_NPHY_EPS_OVERRIDEI_0, 0x3FFF, 0x4000);
|
||||
b43_phy_maskset(dev, B43_NPHY_EPS_OVERRIDEI_1, 0x3FFF, 0x4000);
|
||||
|
||||
lpf_20 = b43_nphy_read_lpf_ctl(dev, 0x154);
|
||||
lpf_40 = b43_nphy_read_lpf_ctl(dev, 0x159);
|
||||
lpf_11b = b43_nphy_read_lpf_ctl(dev, 0x152);
|
||||
for (core = 0; core < 2; core++) {
|
||||
lpf_ofdm_20mhz[core] = b43_nphy_read_lpf_ctl(dev, 0x154 + core * 0x10);
|
||||
lpf_ofdm_40mhz[core] = b43_nphy_read_lpf_ctl(dev, 0x159 + core * 0x10);
|
||||
lpf_11b[core] = b43_nphy_read_lpf_ctl(dev, 0x152 + core * 0x10);
|
||||
}
|
||||
|
||||
bcap_val = b43_radio_read(dev, R2057_RCCAL_BCAP_VAL);
|
||||
scap_val = b43_radio_read(dev, R2057_RCCAL_SCAP_VAL);
|
||||
|
||||
if (b43_nphy_ipa(dev)) {
|
||||
if ((phy->radio_rev == 5 && b43_is_40mhz(dev)) ||
|
||||
phy->radio_rev == 7 || phy->radio_rev == 8) {
|
||||
bcap_val = b43_radio_read(dev, 0x16b);
|
||||
scap_val = b43_radio_read(dev, 0x16a);
|
||||
scap_val_11b = scap_val;
|
||||
bcap_val_11b = bcap_val;
|
||||
if (phy->radio_rev == 5 && b43_is_40mhz(dev)) {
|
||||
scap_val_11n_20 = scap_val;
|
||||
bcap_val_11n_20 = bcap_val;
|
||||
scap_val_11n_40 = bcap_val_11n_40 = 0xc;
|
||||
rccal_ovrd = true;
|
||||
} else { /* Rev 7/8 */
|
||||
lpf_20 = 4;
|
||||
lpf_11b = 1;
|
||||
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
|
||||
scap_val_11n_20 = 0xc;
|
||||
bcap_val_11n_20 = 0xc;
|
||||
scap_val_11n_40 = 0xa;
|
||||
bcap_val_11n_40 = 0xa;
|
||||
} else {
|
||||
scap_val_11n_20 = 0x14;
|
||||
bcap_val_11n_20 = 0x14;
|
||||
scap_val_11n_40 = 0xf;
|
||||
bcap_val_11n_40 = 0xf;
|
||||
bool ghz2 = b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ;
|
||||
|
||||
switch (phy->radio_rev) {
|
||||
case 5:
|
||||
/* Check radio version (to be 0) by PHY rev for now */
|
||||
if (phy->rev == 8 && b43_is_40mhz(dev)) {
|
||||
for (core = 0; core < 2; core++) {
|
||||
scap_val_11b[core] = scap_val;
|
||||
bcap_val_11b[core] = bcap_val;
|
||||
scap_val_11n_20[core] = scap_val;
|
||||
bcap_val_11n_20[core] = bcap_val;
|
||||
scap_val_11n_40[core] = 0xc;
|
||||
bcap_val_11n_40[core] = 0xc;
|
||||
}
|
||||
|
||||
rccal_ovrd = true;
|
||||
}
|
||||
if (phy->rev == 9) {
|
||||
/* TODO: Radio version 1 (e.g. BCM5357B0) */
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
for (core = 0; core < 2; core++) {
|
||||
scap_val_11b[core] = scap_val;
|
||||
bcap_val_11b[core] = bcap_val;
|
||||
lpf_ofdm_20mhz[core] = 4;
|
||||
lpf_11b[core] = 1;
|
||||
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
|
||||
scap_val_11n_20[core] = 0xc;
|
||||
bcap_val_11n_20[core] = 0xc;
|
||||
scap_val_11n_40[core] = 0xa;
|
||||
bcap_val_11n_40[core] = 0xa;
|
||||
} else {
|
||||
scap_val_11n_20[core] = 0x14;
|
||||
bcap_val_11n_20[core] = 0x14;
|
||||
scap_val_11n_40[core] = 0xf;
|
||||
bcap_val_11n_40[core] = 0xf;
|
||||
}
|
||||
}
|
||||
|
||||
rccal_ovrd = true;
|
||||
break;
|
||||
case 9:
|
||||
for (core = 0; core < 2; core++) {
|
||||
bcap_val_11b[core] = bcap_val;
|
||||
scap_val_11b[core] = scap_val;
|
||||
lpf_11b[core] = 1;
|
||||
|
||||
if (ghz2) {
|
||||
bcap_val_11n_20[core] = bcap_val + 13;
|
||||
scap_val_11n_20[core] = scap_val + 15;
|
||||
} else {
|
||||
bcap_val_11n_20[core] = bcap_val + 14;
|
||||
scap_val_11n_20[core] = scap_val + 15;
|
||||
}
|
||||
lpf_ofdm_20mhz[core] = 4;
|
||||
|
||||
if (ghz2) {
|
||||
bcap_val_11n_40[core] = bcap_val - 7;
|
||||
scap_val_11n_40[core] = scap_val - 5;
|
||||
} else {
|
||||
bcap_val_11n_40[core] = bcap_val + 2;
|
||||
scap_val_11n_40[core] = scap_val + 4;
|
||||
}
|
||||
lpf_ofdm_40mhz[core] = 4;
|
||||
}
|
||||
|
||||
rccal_ovrd = true;
|
||||
break;
|
||||
case 14:
|
||||
for (core = 0; core < 2; core++) {
|
||||
bcap_val_11b[core] = bcap_val;
|
||||
scap_val_11b[core] = scap_val;
|
||||
lpf_11b[core] = 1;
|
||||
}
|
||||
|
||||
bcap_val_11n_20[0] = bcap_val + 20;
|
||||
scap_val_11n_20[0] = scap_val + 20;
|
||||
lpf_ofdm_20mhz[0] = 3;
|
||||
|
||||
bcap_val_11n_20[1] = bcap_val + 16;
|
||||
scap_val_11n_20[1] = scap_val + 16;
|
||||
lpf_ofdm_20mhz[1] = 3;
|
||||
|
||||
bcap_val_11n_40[0] = bcap_val + 20;
|
||||
scap_val_11n_40[0] = scap_val + 20;
|
||||
lpf_ofdm_40mhz[0] = 4;
|
||||
|
||||
bcap_val_11n_40[1] = bcap_val + 10;
|
||||
scap_val_11n_40[1] = scap_val + 10;
|
||||
lpf_ofdm_40mhz[1] = 4;
|
||||
|
||||
rccal_ovrd = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (phy->radio_rev == 5) {
|
||||
lpf_20 = 1;
|
||||
lpf_40 = 3;
|
||||
bcap_val = b43_radio_read(dev, 0x16b);
|
||||
scap_val = b43_radio_read(dev, 0x16a);
|
||||
scap_val_11b = scap_val;
|
||||
bcap_val_11b = bcap_val;
|
||||
scap_val_11n_20 = 0x11;
|
||||
scap_val_11n_40 = 0x11;
|
||||
bcap_val_11n_20 = 0x13;
|
||||
bcap_val_11n_40 = 0x13;
|
||||
for (core = 0; core < 2; core++) {
|
||||
lpf_ofdm_20mhz[core] = 1;
|
||||
lpf_ofdm_40mhz[core] = 3;
|
||||
scap_val_11b[core] = scap_val;
|
||||
bcap_val_11b[core] = bcap_val;
|
||||
scap_val_11n_20[core] = 0x11;
|
||||
scap_val_11n_40[core] = 0x11;
|
||||
bcap_val_11n_20[core] = 0x13;
|
||||
bcap_val_11n_40[core] = 0x13;
|
||||
}
|
||||
|
||||
rccal_ovrd = true;
|
||||
}
|
||||
}
|
||||
if (rccal_ovrd) {
|
||||
rx2tx_lut_20_11b = (bcap_val_11b << 8) |
|
||||
(scap_val_11b << 3) |
|
||||
lpf_11b;
|
||||
rx2tx_lut_20_11n = (bcap_val_11n_20 << 8) |
|
||||
(scap_val_11n_20 << 3) |
|
||||
lpf_20;
|
||||
rx2tx_lut_40_11n = (bcap_val_11n_40 << 8) |
|
||||
(scap_val_11n_40 << 3) |
|
||||
lpf_40;
|
||||
u16 rx2tx_lut_20_11b[2], rx2tx_lut_20_11n[2], rx2tx_lut_40_11n[2];
|
||||
u8 rx2tx_lut_extra = 1;
|
||||
|
||||
for (core = 0; core < 2; core++) {
|
||||
bcap_val_11b[core] = clamp_val(bcap_val_11b[core], 0, 0x1f);
|
||||
scap_val_11b[core] = clamp_val(scap_val_11b[core], 0, 0x1f);
|
||||
bcap_val_11n_20[core] = clamp_val(bcap_val_11n_20[core], 0, 0x1f);
|
||||
scap_val_11n_20[core] = clamp_val(scap_val_11n_20[core], 0, 0x1f);
|
||||
bcap_val_11n_40[core] = clamp_val(bcap_val_11n_40[core], 0, 0x1f);
|
||||
scap_val_11n_40[core] = clamp_val(scap_val_11n_40[core], 0, 0x1f);
|
||||
|
||||
rx2tx_lut_20_11b[core] = (rx2tx_lut_extra << 13) |
|
||||
(bcap_val_11b[core] << 8) |
|
||||
(scap_val_11b[core] << 3) |
|
||||
lpf_11b[core];
|
||||
rx2tx_lut_20_11n[core] = (rx2tx_lut_extra << 13) |
|
||||
(bcap_val_11n_20[core] << 8) |
|
||||
(scap_val_11n_20[core] << 3) |
|
||||
lpf_ofdm_20mhz[core];
|
||||
rx2tx_lut_40_11n[core] = (rx2tx_lut_extra << 13) |
|
||||
(bcap_val_11n_40[core] << 8) |
|
||||
(scap_val_11n_40[core] << 3) |
|
||||
lpf_ofdm_40mhz[core];
|
||||
}
|
||||
|
||||
for (core = 0; core < 2; core++) {
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x152 + core * 16),
|
||||
rx2tx_lut_20_11b);
|
||||
rx2tx_lut_20_11b[core]);
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x153 + core * 16),
|
||||
rx2tx_lut_20_11n);
|
||||
rx2tx_lut_20_11n[core]);
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x154 + core * 16),
|
||||
rx2tx_lut_20_11n);
|
||||
rx2tx_lut_20_11n[core]);
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x155 + core * 16),
|
||||
rx2tx_lut_40_11n);
|
||||
rx2tx_lut_40_11n[core]);
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x156 + core * 16),
|
||||
rx2tx_lut_40_11n);
|
||||
rx2tx_lut_40_11n[core]);
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x157 + core * 16),
|
||||
rx2tx_lut_40_11n);
|
||||
rx2tx_lut_40_11n[core]);
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x158 + core * 16),
|
||||
rx2tx_lut_40_11n);
|
||||
rx2tx_lut_40_11n[core]);
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x159 + core * 16),
|
||||
rx2tx_lut_40_11n);
|
||||
rx2tx_lut_40_11n[core]);
|
||||
}
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 16, 1, 3, false, 2);
|
||||
}
|
||||
|
||||
b43_phy_write(dev, 0x32F, 0x3);
|
||||
|
||||
if (phy->radio_rev == 4 || phy->radio_rev == 6)
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 4, 1, 3, false, 0);
|
||||
|
||||
|
@ -2893,7 +3009,8 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
|
|||
0x7f);
|
||||
}
|
||||
}
|
||||
if (phy->radio_rev == 3) {
|
||||
switch (phy->radio_rev) {
|
||||
case 3:
|
||||
for (core = 0; core < 2; core++) {
|
||||
if (core == 0) {
|
||||
b43_radio_write(dev, 0x64,
|
||||
|
@ -2919,7 +3036,9 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
|
|||
0x3E);
|
||||
}
|
||||
}
|
||||
} else if (phy->radio_rev == 7 || phy->radio_rev == 8) {
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
if (!b43_is_40mhz(dev)) {
|
||||
b43_radio_write(dev, 0x5F, 0x14);
|
||||
b43_radio_write(dev, 0xE8, 0x12);
|
||||
|
@ -2927,6 +3046,21 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
|
|||
b43_radio_write(dev, 0x5F, 0x16);
|
||||
b43_radio_write(dev, 0xE8, 0x16);
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
for (core = 0; core < 2; core++) {
|
||||
int o = core ? 0x85 : 0;
|
||||
|
||||
b43_radio_write(dev, o + R2057_IPA2G_CASCONV_CORE0, 0x13);
|
||||
b43_radio_write(dev, o + R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, 0x21);
|
||||
b43_radio_write(dev, o + R2057_IPA2G_BIAS_FILTER_CORE0, 0xff);
|
||||
b43_radio_write(dev, o + R2057_PAD2G_IDACS_CORE0, 0x88);
|
||||
b43_radio_write(dev, o + R2057_PAD2G_TUNE_PUS_CORE0, 0x23);
|
||||
b43_radio_write(dev, o + R2057_IPA2G_IMAIN_CORE0, 0x16);
|
||||
b43_radio_write(dev, o + R2057_PAD_BIAS_FILTER_BWS_CORE0, 0x3e);
|
||||
b43_radio_write(dev, o + R2057_BACKUP1_CORE0, 0x10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
u16 freq = phy->chandef->chan->center_freq;
|
||||
|
@ -2974,8 +3108,8 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
|
|||
b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x1);
|
||||
b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x1);
|
||||
b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x1);
|
||||
b43_ntab_write(dev, B43_NTAB16(8, 0x05), 0x20);
|
||||
b43_ntab_write(dev, B43_NTAB16(8, 0x15), 0x20);
|
||||
b43_ntab_write(dev, B43_NTAB16(8, 0x05), 0);
|
||||
b43_ntab_write(dev, B43_NTAB16(8, 0x15), 0);
|
||||
|
||||
b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x4);
|
||||
b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x4);
|
||||
|
@ -2986,20 +3120,20 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
|
|||
b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, 0x2);
|
||||
|
||||
b43_ntab_write(dev, B43_NTAB32(16, 0x100), 20);
|
||||
b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x138), 2, ntab7_138_146);
|
||||
b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x138), 2, ntab7_138_146);
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x141), 0x77);
|
||||
b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x133), 3, ntab7_133);
|
||||
b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x146), 2, ntab7_138_146);
|
||||
b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x133), 3, ntab7_133);
|
||||
b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x146), 2, ntab7_138_146);
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x123), 0x77);
|
||||
b43_ntab_write(dev, B43_NTAB16(7, 0x12A), 0x77);
|
||||
|
||||
if (!b43_is_40mhz(dev)) {
|
||||
b43_ntab_write(dev, B43_NTAB32(16, 0x03), 0x18D);
|
||||
b43_ntab_write(dev, B43_NTAB32(16, 0x7F), 0x18D);
|
||||
} else {
|
||||
b43_ntab_write(dev, B43_NTAB32(16, 0x03), 0x14D);
|
||||
b43_ntab_write(dev, B43_NTAB32(16, 0x7F), 0x14D);
|
||||
}
|
||||
b43_ntab_read_bulk(dev, B43_NTAB32(16, 0x02), 1, noise_tbl);
|
||||
noise_tbl[1] = b43_is_40mhz(dev) ? 0x14D : 0x18D;
|
||||
b43_ntab_write_bulk(dev, B43_NTAB32(16, 0x02), 2, noise_tbl);
|
||||
|
||||
b43_ntab_read_bulk(dev, B43_NTAB32(16, 0x7E), 1, noise_tbl);
|
||||
noise_tbl[1] = b43_is_40mhz(dev) ? 0x14D : 0x18D;
|
||||
b43_ntab_write_bulk(dev, B43_NTAB32(16, 0x7E), 2, noise_tbl);
|
||||
|
||||
b43_nphy_gain_ctl_workarounds(dev);
|
||||
|
||||
|
@ -3410,7 +3544,7 @@ static void b43_nphy_stop_playback(struct b43_wldev *dev)
|
|||
nphy->bb_mult_save = 0;
|
||||
}
|
||||
|
||||
if (phy->rev >= 7) {
|
||||
if (phy->rev >= 7 && nphy->lpf_bw_overrode_for_sample_play) {
|
||||
if (phy->rev >= 19)
|
||||
b43_nphy_rf_ctl_override_rev19(dev, 0x80, 0, 0, true,
|
||||
1);
|
||||
|
@ -3823,15 +3957,16 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev)
|
|||
u32 tmp;
|
||||
s32 rssi[4] = { };
|
||||
|
||||
/* TODO: check if we can transmit */
|
||||
if (phy->chandef->chan->flags & IEEE80211_CHAN_NO_IR)
|
||||
return;
|
||||
|
||||
if (b43_nphy_ipa(dev))
|
||||
b43_nphy_ipa_internal_tssi_setup(dev);
|
||||
|
||||
if (phy->rev >= 19)
|
||||
b43_nphy_rf_ctl_override_rev19(dev, 0x2000, 0, 3, false, 0);
|
||||
b43_nphy_rf_ctl_override_rev19(dev, 0x1000, 0, 3, false, 0);
|
||||
else if (phy->rev >= 7)
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x2000, 0, 3, false, 0);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x1000, 0, 3, false, 0);
|
||||
else if (phy->rev >= 3)
|
||||
b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, false);
|
||||
|
||||
|
@ -3844,9 +3979,9 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev)
|
|||
b43_nphy_rssi_select(dev, 0, N_RSSI_W1);
|
||||
|
||||
if (phy->rev >= 19)
|
||||
b43_nphy_rf_ctl_override_rev19(dev, 0x2000, 0, 3, true, 0);
|
||||
b43_nphy_rf_ctl_override_rev19(dev, 0x1000, 0, 3, true, 0);
|
||||
else if (phy->rev >= 7)
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x2000, 0, 3, true, 0);
|
||||
b43_nphy_rf_ctl_override_rev7(dev, 0x1000, 0, 3, true, 0);
|
||||
else if (phy->rev >= 3)
|
||||
b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, true);
|
||||
|
||||
|
@ -4809,41 +4944,61 @@ static void b43_nphy_update_tx_cal_ladder(struct b43_wldev *dev, u16 core)
|
|||
}
|
||||
}
|
||||
|
||||
static void b43_nphy_pa_set_tx_dig_filter(struct b43_wldev *dev, u16 offset,
|
||||
const s16 *filter)
|
||||
{
|
||||
int i;
|
||||
|
||||
offset = B43_PHY_N(offset);
|
||||
|
||||
for (i = 0; i < 15; i++, offset++)
|
||||
b43_phy_write(dev, offset, filter[i]);
|
||||
}
|
||||
|
||||
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ExtPaSetTxDigiFilts */
|
||||
static void b43_nphy_ext_pa_set_tx_dig_filters(struct b43_wldev *dev)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 15; i++)
|
||||
b43_phy_write(dev, B43_PHY_N(0x2C5 + i),
|
||||
tbl_tx_filter_coef_rev4[2][i]);
|
||||
b43_nphy_pa_set_tx_dig_filter(dev, 0x2C5,
|
||||
tbl_tx_filter_coef_rev4[2]);
|
||||
}
|
||||
|
||||
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IpaSetTxDigiFilts */
|
||||
static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev)
|
||||
{
|
||||
int i, j;
|
||||
/* B43_NPHY_TXF_20CO_S0A1, B43_NPHY_TXF_40CO_S0A1, unknown */
|
||||
static const u16 offset[] = { 0x186, 0x195, 0x2C5 };
|
||||
static const s16 dig_filter_phy_rev16[] = {
|
||||
-375, 136, -407, 208, -1527,
|
||||
956, 93, 186, 93, 230,
|
||||
-44, 230, 201, -191, 201,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
for (j = 0; j < 15; j++)
|
||||
b43_phy_write(dev, B43_PHY_N(offset[i] + j),
|
||||
tbl_tx_filter_coef_rev4[i][j]);
|
||||
b43_nphy_pa_set_tx_dig_filter(dev, offset[i],
|
||||
tbl_tx_filter_coef_rev4[i]);
|
||||
|
||||
if (b43_is_40mhz(dev)) {
|
||||
for (j = 0; j < 15; j++)
|
||||
b43_phy_write(dev, B43_PHY_N(offset[0] + j),
|
||||
tbl_tx_filter_coef_rev4[3][j]);
|
||||
} else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
|
||||
for (j = 0; j < 15; j++)
|
||||
b43_phy_write(dev, B43_PHY_N(offset[0] + j),
|
||||
tbl_tx_filter_coef_rev4[5][j]);
|
||||
/* Verified with BCM43227 and BCM43228 */
|
||||
if (dev->phy.rev == 16)
|
||||
b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16);
|
||||
|
||||
if (dev->dev->chip_id == BCMA_CHIP_ID_BCM43217) {
|
||||
b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16);
|
||||
b43_nphy_pa_set_tx_dig_filter(dev, 0x195,
|
||||
tbl_tx_filter_coef_rev4[1]);
|
||||
}
|
||||
|
||||
if (dev->phy.channel == 14)
|
||||
for (j = 0; j < 15; j++)
|
||||
b43_phy_write(dev, B43_PHY_N(offset[0] + j),
|
||||
tbl_tx_filter_coef_rev4[6][j]);
|
||||
if (b43_is_40mhz(dev)) {
|
||||
b43_nphy_pa_set_tx_dig_filter(dev, 0x186,
|
||||
tbl_tx_filter_coef_rev4[3]);
|
||||
} else {
|
||||
if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
|
||||
b43_nphy_pa_set_tx_dig_filter(dev, 0x186,
|
||||
tbl_tx_filter_coef_rev4[5]);
|
||||
if (dev->phy.channel == 14)
|
||||
b43_nphy_pa_set_tx_dig_filter(dev, 0x186,
|
||||
tbl_tx_filter_coef_rev4[6]);
|
||||
}
|
||||
}
|
||||
|
||||
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetTxGain */
|
||||
|
|
|
@ -3109,11 +3109,11 @@ static const struct nphy_rf_control_override_rev7
|
|||
{ 0x0010, 0x07A, 0x07D, 0x0010, 4 },
|
||||
{ 0x0020, 0x07A, 0x07D, 0x0020, 5 },
|
||||
{ 0x0040, 0x07A, 0x07D, 0x0040, 6 },
|
||||
{ 0x0080, 0x0F8, 0x0FA, 0x0080, 7 },
|
||||
{ 0x0080, 0x07A, 0x07D, 0x0080, 7 },
|
||||
{ 0x0400, 0x0F8, 0x0FA, 0x0070, 4 },
|
||||
{ 0x0800, 0x07B, 0x07E, 0xFFFF, 0 },
|
||||
{ 0x1000, 0x07C, 0x07F, 0xFFFF, 0 },
|
||||
{ 0x6000, 0x348, 0x349, 0xFFFF, 0 },
|
||||
{ 0x6000, 0x348, 0x349, 0x00FF, 0 },
|
||||
{ 0x2000, 0x348, 0x349, 0x000F, 0 },
|
||||
};
|
||||
|
||||
|
|
|
@ -80,9 +80,10 @@ static int b43_plcp_get_bitrate_idx_cck(struct b43_plcp_hdr6 *plcp)
|
|||
}
|
||||
|
||||
/* Extract the bitrate index out of an OFDM PLCP header. */
|
||||
static int b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool aphy)
|
||||
static int b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool ghz5)
|
||||
{
|
||||
int base = aphy ? 0 : 4;
|
||||
/* For 2 GHz band first OFDM rate is at index 4, see main.c */
|
||||
int base = ghz5 ? 0 : 4;
|
||||
|
||||
switch (plcp->raw[0] & 0xF) {
|
||||
case 0xB:
|
||||
|
@ -767,7 +768,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
|
|||
|
||||
if (phystat0 & B43_RX_PHYST0_OFDM)
|
||||
rate_idx = b43_plcp_get_bitrate_idx_ofdm(plcp,
|
||||
phytype == B43_PHYTYPE_A);
|
||||
!!(chanstat & B43_RX_CHAN_5GHZ));
|
||||
else
|
||||
rate_idx = b43_plcp_get_bitrate_idx_cck(plcp);
|
||||
if (unlikely(rate_idx == -1)) {
|
||||
|
|
|
@ -20,16 +20,17 @@ config IWLWIFI
|
|||
Intel 2000 Series Wi-Fi Adapters
|
||||
Intel 7260 Wi-Fi Adapter
|
||||
Intel 3160 Wi-Fi Adapter
|
||||
Intel 7265 Wi-Fi Adapter
|
||||
|
||||
|
||||
This driver uses the kernel's mac80211 subsystem.
|
||||
|
||||
In order to use this driver, you will need a microcode (uCode)
|
||||
In order to use this driver, you will need a firmware
|
||||
image for it. You can obtain the microcode from:
|
||||
|
||||
<http://intellinuxwireless.org/>.
|
||||
<http://wireless.kernel.org/en/users/Drivers/iwlwifi>.
|
||||
|
||||
The microcode is typically installed in /lib/firmware. You can
|
||||
The firmware is typically installed in /lib/firmware. You can
|
||||
look in the hotplug script /etc/hotplug/firmware.agent to
|
||||
determine which directory FIRMWARE_DIR is set to when the script
|
||||
runs.
|
||||
|
@ -39,9 +40,10 @@ config IWLWIFI
|
|||
say M here and read <file:Documentation/kbuild/modules.txt>. The
|
||||
module will be called iwlwifi.
|
||||
|
||||
if IWLWIFI
|
||||
|
||||
config IWLWIFI_LEDS
|
||||
bool
|
||||
depends on IWLWIFI
|
||||
depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI
|
||||
select LEDS_TRIGGERS
|
||||
select MAC80211_LEDS
|
||||
|
@ -49,7 +51,7 @@ config IWLWIFI_LEDS
|
|||
|
||||
config IWLDVM
|
||||
tristate "Intel Wireless WiFi DVM Firmware support"
|
||||
depends on IWLWIFI
|
||||
depends on m
|
||||
default IWLWIFI
|
||||
help
|
||||
This is the driver that supports the DVM firmware which is
|
||||
|
@ -58,7 +60,7 @@ config IWLDVM
|
|||
|
||||
config IWLMVM
|
||||
tristate "Intel Wireless WiFi MVM Firmware support"
|
||||
depends on IWLWIFI
|
||||
depends on m
|
||||
help
|
||||
This is the driver that supports the MVM firmware which is
|
||||
currently only available for 7260 and 3160 devices.
|
||||
|
@ -70,7 +72,7 @@ config IWLWIFI_OPMODE_MODULAR
|
|||
default y if IWLMVM=m
|
||||
|
||||
comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM"
|
||||
depends on IWLWIFI && IWLDVM=n && IWLMVM=n
|
||||
depends on IWLDVM=n && IWLMVM=n
|
||||
|
||||
config IWLWIFI_BCAST_FILTERING
|
||||
bool "Enable broadcast filtering"
|
||||
|
@ -86,11 +88,9 @@ config IWLWIFI_BCAST_FILTERING
|
|||
expect incoming broadcasts for their normal operations.
|
||||
|
||||
menu "Debugging Options"
|
||||
depends on IWLWIFI
|
||||
|
||||
config IWLWIFI_DEBUG
|
||||
bool "Enable full debugging output in the iwlwifi driver"
|
||||
depends on IWLWIFI
|
||||
---help---
|
||||
This option will enable debug tracing output for the iwlwifi drivers
|
||||
|
||||
|
@ -115,7 +115,7 @@ config IWLWIFI_DEBUG
|
|||
|
||||
config IWLWIFI_DEBUGFS
|
||||
bool "iwlwifi debugfs support"
|
||||
depends on IWLWIFI && MAC80211_DEBUGFS
|
||||
depends on MAC80211_DEBUGFS
|
||||
---help---
|
||||
Enable creation of debugfs files for the iwlwifi drivers. This
|
||||
is a low-impact option that allows getting insight into the
|
||||
|
@ -123,13 +123,12 @@ config IWLWIFI_DEBUGFS
|
|||
|
||||
config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
|
||||
bool "Experimental uCode support"
|
||||
depends on IWLWIFI && IWLWIFI_DEBUG
|
||||
depends on IWLWIFI_DEBUG
|
||||
---help---
|
||||
Enable use of experimental ucode for testing and debugging.
|
||||
|
||||
config IWLWIFI_DEVICE_TRACING
|
||||
bool "iwlwifi device access tracing"
|
||||
depends on IWLWIFI
|
||||
depends on EVENT_TRACING
|
||||
help
|
||||
Say Y here to trace all commands, including TX frames and IO
|
||||
|
@ -145,3 +144,5 @@ config IWLWIFI_DEVICE_TRACING
|
|||
If unsure, say Y so we can help you better when problems
|
||||
occur.
|
||||
endmenu
|
||||
|
||||
endif
|
||||
|
|
|
@ -85,6 +85,9 @@
|
|||
#define NVM_HW_SECTION_NUM_FAMILY_8000 10
|
||||
#define DEFAULT_NVM_FILE_FAMILY_8000 "iwl_nvm_8000.bin"
|
||||
|
||||
/* Max SDIO RX aggregation size of the ADDBA request/response */
|
||||
#define MAX_RX_AGG_SIZE_8260_SDIO 28
|
||||
|
||||
static const struct iwl_base_params iwl8000_base_params = {
|
||||
.eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
|
||||
.num_of_queues = IWLAGN_NUM_QUEUES,
|
||||
|
@ -129,6 +132,7 @@ const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
|
|||
.nvm_ver = IWL8000_NVM_VERSION,
|
||||
.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
|
||||
.default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
|
||||
.max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
|
||||
};
|
||||
|
||||
MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
|
||||
|
|
|
@ -240,6 +240,7 @@ struct iwl_pwr_tx_backoff {
|
|||
* @d0i3: device uses d0i3 instead of d3
|
||||
* @nvm_hw_section_num: the ID of the HW NVM section
|
||||
* @pwr_tx_backoffs: translation table between power limits and backoffs
|
||||
* @max_rx_agg_size: max RX aggregation size of the ADDBA request/response
|
||||
*
|
||||
* We enable the driver to be backward compatible wrt. hardware features.
|
||||
* API differences in uCode shouldn't be handled here but through TLVs
|
||||
|
@ -276,6 +277,7 @@ struct iwl_cfg {
|
|||
const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
|
||||
bool no_power_up_nic_in_init;
|
||||
const char *default_nvm_file;
|
||||
unsigned int max_rx_agg_size;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -70,21 +70,24 @@
|
|||
/**
|
||||
* enum iwl_fw_error_dump_type - types of data in the dump file
|
||||
* @IWL_FW_ERROR_DUMP_SRAM:
|
||||
* @IWL_FW_ERROR_DUMP_REG:
|
||||
* @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0
|
||||
* @IWL_FW_ERROR_DUMP_RXF:
|
||||
* @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
|
||||
* &struct iwl_fw_error_dump_txcmd packets
|
||||
* @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info
|
||||
* info on the device / firmware.
|
||||
* @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor
|
||||
* @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several
|
||||
* sections like this in a single file.
|
||||
*/
|
||||
enum iwl_fw_error_dump_type {
|
||||
IWL_FW_ERROR_DUMP_SRAM = 0,
|
||||
IWL_FW_ERROR_DUMP_REG = 1,
|
||||
IWL_FW_ERROR_DUMP_CSR = 1,
|
||||
IWL_FW_ERROR_DUMP_RXF = 2,
|
||||
IWL_FW_ERROR_DUMP_TXCMD = 3,
|
||||
IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4,
|
||||
IWL_FW_ERROR_DUMP_FW_MONITOR = 5,
|
||||
IWL_FW_ERROR_DUMP_PRPH = 6,
|
||||
|
||||
IWL_FW_ERROR_DUMP_MAX,
|
||||
};
|
||||
|
@ -162,6 +165,16 @@ struct iwl_fw_error_dump_fw_mon {
|
|||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_fw_error_dump_prph - periphery registers data
|
||||
* @prph_start: address of the first register in this chunk
|
||||
* @data: the content of the registers
|
||||
*/
|
||||
struct iwl_fw_error_dump_prph {
|
||||
__le32 prph_start;
|
||||
__le32 data[];
|
||||
};
|
||||
|
||||
/**
|
||||
* iwl_fw_error_next_data - advance fw error dump data pointer
|
||||
* @data: previous data block
|
||||
|
|
|
@ -99,7 +99,7 @@ enum iwl_disable_11n {
|
|||
* @wd_disable: disable stuck queue check, default = 1
|
||||
* @bt_coex_active: enable bt coex, default = true
|
||||
* @led_mode: system default, default = 0
|
||||
* @power_save: disable power save, default = false
|
||||
* @power_save: enable power save, default = false
|
||||
* @power_level: power level, default = 1
|
||||
* @debug_level: levels are IWL_DL_*
|
||||
* @ant_coupling: antenna coupling in dB, default = 0
|
||||
|
|
|
@ -394,6 +394,11 @@ struct iwl_trans_config {
|
|||
const char *const *command_names;
|
||||
};
|
||||
|
||||
struct iwl_trans_dump_data {
|
||||
u32 len;
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
struct iwl_trans;
|
||||
|
||||
/**
|
||||
|
@ -461,10 +466,8 @@ struct iwl_trans;
|
|||
* @unref: release a reference previously taken with @ref. Note that
|
||||
* initially the reference count is 1, making an initial @unref
|
||||
* necessary to allow low power states.
|
||||
* @dump_data: fill a data dump with debug data, maybe containing last
|
||||
* TX'ed commands and similar. When called with a NULL buffer and
|
||||
* zero buffer length, provide only the (estimated) required buffer
|
||||
* length. Return the used buffer length.
|
||||
* @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last
|
||||
* TX'ed commands and similar. The buffer will be vfree'd by the caller.
|
||||
* Note that the transport must fill in the proper file headers.
|
||||
*/
|
||||
struct iwl_trans_ops {
|
||||
|
@ -518,7 +521,7 @@ struct iwl_trans_ops {
|
|||
void (*unref)(struct iwl_trans *trans);
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
u32 (*dump_data)(struct iwl_trans *trans, void *buf, u32 buflen);
|
||||
struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -685,12 +688,12 @@ static inline void iwl_trans_unref(struct iwl_trans *trans)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
static inline u32 iwl_trans_dump_data(struct iwl_trans *trans,
|
||||
void *buf, u32 buflen)
|
||||
static inline struct iwl_trans_dump_data *
|
||||
iwl_trans_dump_data(struct iwl_trans *trans)
|
||||
{
|
||||
if (!trans->ops->dump_data)
|
||||
return 0;
|
||||
return trans->ops->dump_data(trans, buf, buflen);
|
||||
return NULL;
|
||||
return trans->ops->dump_data(trans);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -72,16 +72,56 @@
|
|||
|
||||
#define BT_ANTENNA_COUPLING_THRESHOLD (30)
|
||||
|
||||
const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
|
||||
[BT_KILL_MSK_DEFAULT] = 0xffff0000,
|
||||
[BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
|
||||
[BT_KILL_MSK_REDUCED_TXPOW] = 0,
|
||||
const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = {
|
||||
[BT_KILL_MSK_DEFAULT] = 0xfffffc00,
|
||||
[BT_KILL_MSK_NEVER] = 0xffffffff,
|
||||
[BT_KILL_MSK_ALWAYS] = 0,
|
||||
};
|
||||
|
||||
const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
|
||||
[BT_KILL_MSK_DEFAULT] = 0xffff0000,
|
||||
[BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
|
||||
[BT_KILL_MSK_REDUCED_TXPOW] = 0,
|
||||
const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
|
||||
{
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
},
|
||||
{
|
||||
BT_KILL_MSK_NEVER,
|
||||
BT_KILL_MSK_NEVER,
|
||||
BT_KILL_MSK_NEVER,
|
||||
},
|
||||
{
|
||||
BT_KILL_MSK_NEVER,
|
||||
BT_KILL_MSK_NEVER,
|
||||
BT_KILL_MSK_NEVER,
|
||||
},
|
||||
{
|
||||
BT_KILL_MSK_DEFAULT,
|
||||
BT_KILL_MSK_NEVER,
|
||||
BT_KILL_MSK_DEFAULT,
|
||||
},
|
||||
};
|
||||
|
||||
const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
|
||||
{
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
},
|
||||
{
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
},
|
||||
{
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
},
|
||||
{
|
||||
BT_KILL_MSK_DEFAULT,
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
BT_KILL_MSK_DEFAULT,
|
||||
},
|
||||
};
|
||||
|
||||
static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
|
||||
|
@ -611,54 +651,43 @@ send_cmd:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm,
|
||||
bool reduced_tx_power)
|
||||
static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm)
|
||||
{
|
||||
enum iwl_bt_kill_msk bt_kill_msk;
|
||||
struct iwl_bt_coex_sw_boost_update_cmd cmd = {};
|
||||
struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
|
||||
u32 primary_lut = le32_to_cpu(notif->primary_ch_lut);
|
||||
u32 secondary_lut = le32_to_cpu(notif->secondary_ch_lut);
|
||||
u32 ag = le32_to_cpu(notif->bt_activity_grading);
|
||||
struct iwl_bt_coex_sw_boost_update_cmd cmd = {};
|
||||
u8 ack_kill_msk[NUM_PHY_CTX] = {};
|
||||
u8 cts_kill_msk[NUM_PHY_CTX] = {};
|
||||
int i;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (reduced_tx_power) {
|
||||
/* Reduced Tx power has precedence on the type of the profile */
|
||||
bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW;
|
||||
} else {
|
||||
/* Low latency BT profile is active: give higher prio to BT */
|
||||
if (BT_MBOX_MSG(notif, 3, SCO_STATE) ||
|
||||
BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
|
||||
BT_MBOX_MSG(notif, 3, SNIFF_STATE))
|
||||
bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
|
||||
else
|
||||
bt_kill_msk = BT_KILL_MSK_DEFAULT;
|
||||
}
|
||||
ack_kill_msk[0] = iwl_bt_ack_kill_msk[ag][primary_lut];
|
||||
cts_kill_msk[0] = iwl_bt_cts_kill_msk[ag][primary_lut];
|
||||
|
||||
IWL_DEBUG_COEX(mvm,
|
||||
"Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
|
||||
bt_kill_msk,
|
||||
BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
|
||||
BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
|
||||
BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
|
||||
ack_kill_msk[1] = iwl_bt_ack_kill_msk[ag][secondary_lut];
|
||||
cts_kill_msk[1] = iwl_bt_cts_kill_msk[ag][secondary_lut];
|
||||
|
||||
/* Don't send HCMD if there is no update */
|
||||
if (bt_kill_msk == mvm->bt_kill_msk)
|
||||
if (!memcmp(ack_kill_msk, mvm->bt_ack_kill_msk, sizeof(ack_kill_msk)) ||
|
||||
!memcmp(cts_kill_msk, mvm->bt_cts_kill_msk, sizeof(cts_kill_msk)))
|
||||
return 0;
|
||||
|
||||
mvm->bt_kill_msk = bt_kill_msk;
|
||||
memcpy(mvm->bt_ack_kill_msk, ack_kill_msk,
|
||||
sizeof(mvm->bt_ack_kill_msk));
|
||||
memcpy(mvm->bt_cts_kill_msk, cts_kill_msk,
|
||||
sizeof(mvm->bt_cts_kill_msk));
|
||||
|
||||
cmd.boost_values[0].kill_ack_msk =
|
||||
cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
|
||||
cmd.boost_values[0].kill_cts_msk =
|
||||
cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
|
||||
BUILD_BUG_ON(ARRAY_SIZE(ack_kill_msk) < ARRAY_SIZE(cmd.boost_values));
|
||||
|
||||
cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk;
|
||||
cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk;
|
||||
cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk;
|
||||
cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk;
|
||||
|
||||
IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
|
||||
iwl_bt_ack_kill_msk[bt_kill_msk],
|
||||
iwl_bt_cts_kill_msk[bt_kill_msk]);
|
||||
for (i = 0; i < ARRAY_SIZE(cmd.boost_values); i++) {
|
||||
cmd.boost_values[i].kill_ack_msk =
|
||||
cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk[i]]);
|
||||
cmd.boost_values[i].kill_cts_msk =
|
||||
cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk[i]]);
|
||||
}
|
||||
|
||||
return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_SW_BOOST, 0,
|
||||
sizeof(cmd), &cmd);
|
||||
|
@ -700,8 +729,6 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
|
|||
struct iwl_bt_iterator_data {
|
||||
struct iwl_bt_coex_profile_notif *notif;
|
||||
struct iwl_mvm *mvm;
|
||||
u32 num_bss_ifaces;
|
||||
bool reduced_tx_power;
|
||||
struct ieee80211_chanctx_conf *primary;
|
||||
struct ieee80211_chanctx_conf *secondary;
|
||||
bool primary_ll;
|
||||
|
@ -737,22 +764,12 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
/* Count BSSes vifs */
|
||||
data->num_bss_ifaces++;
|
||||
/* default smps_mode for BSS / P2P client is AUTOMATIC */
|
||||
smps_mode = IEEE80211_SMPS_AUTOMATIC;
|
||||
break;
|
||||
case NL80211_IFTYPE_AP:
|
||||
/* default smps_mode for AP / GO is OFF */
|
||||
smps_mode = IEEE80211_SMPS_OFF;
|
||||
if (!mvmvif->ap_ibss_active) {
|
||||
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
|
||||
smps_mode);
|
||||
if (!mvmvif->ap_ibss_active)
|
||||
return;
|
||||
}
|
||||
|
||||
/* the Ack / Cts kill mask must be default if AP / GO */
|
||||
data->reduced_tx_power = false;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
@ -763,11 +780,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
/* If channel context is invalid or not on 2.4GHz .. */
|
||||
if ((!chanctx_conf ||
|
||||
chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
|
||||
/* ... relax constraints and disable rssi events */
|
||||
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
|
||||
smps_mode);
|
||||
data->reduced_tx_power = false;
|
||||
if (vif->type == NL80211_IFTYPE_STATION) {
|
||||
/* ... relax constraints and disable rssi events */
|
||||
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
|
||||
smps_mode);
|
||||
iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
|
||||
false);
|
||||
iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
|
||||
|
@ -779,9 +795,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
if (bt_activity_grading >= BT_HIGH_TRAFFIC)
|
||||
smps_mode = IEEE80211_SMPS_STATIC;
|
||||
else if (bt_activity_grading >= BT_LOW_TRAFFIC)
|
||||
smps_mode = vif->type == NL80211_IFTYPE_AP ?
|
||||
IEEE80211_SMPS_OFF :
|
||||
IEEE80211_SMPS_DYNAMIC;
|
||||
smps_mode = IEEE80211_SMPS_DYNAMIC;
|
||||
|
||||
/* relax SMPS contraints for next association */
|
||||
if (!vif->bss_conf.assoc)
|
||||
|
@ -795,7 +809,9 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
"mac %d: bt_activity_grading %d smps_req %d\n",
|
||||
mvmvif->id, bt_activity_grading, smps_mode);
|
||||
|
||||
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
|
||||
if (vif->type == NL80211_IFTYPE_STATION)
|
||||
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
|
||||
smps_mode);
|
||||
|
||||
/* low latency is always primary */
|
||||
if (iwl_mvm_vif_low_latency(mvmvif)) {
|
||||
|
@ -846,7 +862,6 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
|
||||
mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
|
||||
le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) {
|
||||
data->reduced_tx_power = false;
|
||||
iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
|
||||
iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
|
||||
return;
|
||||
|
@ -861,23 +876,9 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
|
||||
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
|
||||
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
|
||||
|
||||
/*
|
||||
* bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the
|
||||
* BSS / P2P clients have rssi above threshold.
|
||||
* We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before
|
||||
* the iteration, if one interface's rssi isn't good enough,
|
||||
* bt_kill_msk will be set to default values.
|
||||
*/
|
||||
} else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
|
||||
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
|
||||
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
|
||||
|
||||
/*
|
||||
* One interface hasn't rssi above threshold, bt_kill_msk must
|
||||
* be set to default values.
|
||||
*/
|
||||
data->reduced_tx_power = false;
|
||||
}
|
||||
|
||||
/* Begin to monitor the RSSI: it may influence the reduced Tx power */
|
||||
|
@ -889,7 +890,6 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
|
|||
struct iwl_bt_iterator_data data = {
|
||||
.mvm = mvm,
|
||||
.notif = &mvm->last_bt_notif,
|
||||
.reduced_tx_power = true,
|
||||
};
|
||||
struct iwl_bt_coex_ci_cmd cmd = {};
|
||||
u8 ci_bw_idx;
|
||||
|
@ -959,14 +959,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
|
|||
memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are no BSS / P2P client interfaces, reduced Tx Power is
|
||||
* irrelevant since it is based on the RSSI coming from the beacon.
|
||||
* Use BT_KILL_MSK_DEFAULT in that case.
|
||||
*/
|
||||
data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
|
||||
|
||||
if (iwl_mvm_bt_udpate_sw_boost(mvm, data.reduced_tx_power))
|
||||
if (iwl_mvm_bt_udpate_sw_boost(mvm))
|
||||
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
|
||||
}
|
||||
|
||||
|
@ -1035,16 +1028,6 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
|
|||
return;
|
||||
|
||||
mvmsta = iwl_mvm_sta_from_mac80211(sta);
|
||||
|
||||
data->num_bss_ifaces++;
|
||||
|
||||
/*
|
||||
* This interface doesn't support reduced Tx power (because of low
|
||||
* RSSI probably), then set bt_kill_msk to default values.
|
||||
*/
|
||||
if (!mvmsta->bt_reduced_txpower)
|
||||
data->reduced_tx_power = false;
|
||||
/* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */
|
||||
}
|
||||
|
||||
void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
|
@ -1053,7 +1036,6 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
|||
struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
|
||||
struct iwl_bt_iterator_data data = {
|
||||
.mvm = mvm,
|
||||
.reduced_tx_power = true,
|
||||
};
|
||||
int ret;
|
||||
|
||||
|
@ -1100,14 +1082,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
|||
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_bt_rssi_iterator, &data);
|
||||
|
||||
/*
|
||||
* If there are no BSS / P2P client interfaces, reduced Tx Power is
|
||||
* irrelevant since it is based on the RSSI coming from the beacon.
|
||||
* Use BT_KILL_MSK_DEFAULT in that case.
|
||||
*/
|
||||
data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
|
||||
|
||||
if (iwl_mvm_bt_udpate_sw_boost(mvm, data.reduced_tx_power))
|
||||
if (iwl_mvm_bt_udpate_sw_boost(mvm))
|
||||
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
|
||||
}
|
||||
|
||||
|
@ -1150,7 +1125,7 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
|
|||
enum iwl_bt_coex_lut_type lut_type;
|
||||
|
||||
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
|
||||
return iwl_mvm_coex_agg_time_limit_old(mvm, sta);
|
||||
return iwl_mvm_bt_coex_is_mimo_allowed_old(mvm, sta);
|
||||
|
||||
if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
|
||||
return true;
|
||||
|
|
|
@ -649,10 +649,6 @@ int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm)
|
|||
sizeof(iwl_bt_prio_boost));
|
||||
memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
|
||||
sizeof(iwl_bt_mprio_lut));
|
||||
bt_cmd->kill_ack_msk =
|
||||
cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
|
||||
bt_cmd->kill_cts_msk =
|
||||
cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
|
||||
|
||||
send_cmd:
|
||||
memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
|
||||
|
@ -664,12 +660,13 @@ send_cmd:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
|
||||
bool reduced_tx_power)
|
||||
static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm)
|
||||
{
|
||||
enum iwl_bt_kill_msk bt_kill_msk;
|
||||
struct iwl_bt_coex_cmd_old *bt_cmd;
|
||||
struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif_old;
|
||||
u32 primary_lut = le32_to_cpu(notif->primary_ch_lut);
|
||||
u32 ag = le32_to_cpu(notif->bt_activity_grading);
|
||||
struct iwl_bt_coex_cmd_old *bt_cmd;
|
||||
u8 ack_kill_msk, cts_kill_msk;
|
||||
struct iwl_host_cmd cmd = {
|
||||
.id = BT_CONFIG,
|
||||
.data[0] = &bt_cmd,
|
||||
|
@ -680,31 +677,15 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
|
|||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
if (reduced_tx_power) {
|
||||
/* Reduced Tx power has precedence on the type of the profile */
|
||||
bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW;
|
||||
} else {
|
||||
/* Low latency BT profile is active: give higher prio to BT */
|
||||
if (BT_MBOX_MSG(notif, 3, SCO_STATE) ||
|
||||
BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
|
||||
BT_MBOX_MSG(notif, 3, SNIFF_STATE))
|
||||
bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
|
||||
else
|
||||
bt_kill_msk = BT_KILL_MSK_DEFAULT;
|
||||
}
|
||||
ack_kill_msk = iwl_bt_ack_kill_msk[ag][primary_lut];
|
||||
cts_kill_msk = iwl_bt_cts_kill_msk[ag][primary_lut];
|
||||
|
||||
IWL_DEBUG_COEX(mvm,
|
||||
"Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
|
||||
bt_kill_msk,
|
||||
BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
|
||||
BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
|
||||
BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
|
||||
|
||||
/* Don't send HCMD if there is no update */
|
||||
if (bt_kill_msk == mvm->bt_kill_msk)
|
||||
if (mvm->bt_ack_kill_msk[0] == ack_kill_msk &&
|
||||
mvm->bt_cts_kill_msk[0] == cts_kill_msk)
|
||||
return 0;
|
||||
|
||||
mvm->bt_kill_msk = bt_kill_msk;
|
||||
mvm->bt_ack_kill_msk[0] = ack_kill_msk;
|
||||
mvm->bt_cts_kill_msk[0] = cts_kill_msk;
|
||||
|
||||
bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
|
||||
if (!bt_cmd)
|
||||
|
@ -712,16 +693,12 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
|
|||
cmd.data[0] = bt_cmd;
|
||||
bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
|
||||
|
||||
bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
|
||||
bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
|
||||
bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk]);
|
||||
bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk]);
|
||||
bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
|
||||
BT_VALID_KILL_ACK |
|
||||
BT_VALID_KILL_CTS);
|
||||
|
||||
IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
|
||||
iwl_bt_ack_kill_msk[bt_kill_msk],
|
||||
iwl_bt_cts_kill_msk[bt_kill_msk]);
|
||||
|
||||
ret = iwl_mvm_send_cmd(mvm, &cmd);
|
||||
|
||||
kfree(bt_cmd);
|
||||
|
@ -777,8 +754,6 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
|
|||
struct iwl_bt_iterator_data {
|
||||
struct iwl_bt_coex_profile_notif_old *notif;
|
||||
struct iwl_mvm *mvm;
|
||||
u32 num_bss_ifaces;
|
||||
bool reduced_tx_power;
|
||||
struct ieee80211_chanctx_conf *primary;
|
||||
struct ieee80211_chanctx_conf *secondary;
|
||||
bool primary_ll;
|
||||
|
@ -814,22 +789,12 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
/* Count BSSes vifs */
|
||||
data->num_bss_ifaces++;
|
||||
/* default smps_mode for BSS / P2P client is AUTOMATIC */
|
||||
smps_mode = IEEE80211_SMPS_AUTOMATIC;
|
||||
break;
|
||||
case NL80211_IFTYPE_AP:
|
||||
/* default smps_mode for AP / GO is OFF */
|
||||
smps_mode = IEEE80211_SMPS_OFF;
|
||||
if (!mvmvif->ap_ibss_active) {
|
||||
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
|
||||
smps_mode);
|
||||
if (!mvmvif->ap_ibss_active)
|
||||
return;
|
||||
}
|
||||
|
||||
/* the Ack / Cts kill mask must be default if AP / GO */
|
||||
data->reduced_tx_power = false;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
@ -840,11 +805,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
/* If channel context is invalid or not on 2.4GHz .. */
|
||||
if ((!chanctx_conf ||
|
||||
chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
|
||||
/* ... relax constraints and disable rssi events */
|
||||
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
|
||||
smps_mode);
|
||||
data->reduced_tx_power = false;
|
||||
if (vif->type == NL80211_IFTYPE_STATION) {
|
||||
/* ... relax constraints and disable rssi events */
|
||||
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
|
||||
smps_mode);
|
||||
iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
|
||||
false);
|
||||
iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
|
||||
|
@ -869,7 +833,9 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
mvmvif->id, data->notif->bt_status, bt_activity_grading,
|
||||
smps_mode);
|
||||
|
||||
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
|
||||
if (vif->type == NL80211_IFTYPE_STATION)
|
||||
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
|
||||
smps_mode);
|
||||
|
||||
/* low latency is always primary */
|
||||
if (iwl_mvm_vif_low_latency(mvmvif)) {
|
||||
|
@ -920,7 +886,6 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
|
||||
mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
|
||||
!data->notif->bt_status) {
|
||||
data->reduced_tx_power = false;
|
||||
iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
|
||||
iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
|
||||
return;
|
||||
|
@ -935,23 +900,9 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
|
|||
if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
|
||||
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
|
||||
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
|
||||
|
||||
/*
|
||||
* bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the
|
||||
* BSS / P2P clients have rssi above threshold.
|
||||
* We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before
|
||||
* the iteration, if one interface's rssi isn't good enough,
|
||||
* bt_kill_msk will be set to default values.
|
||||
*/
|
||||
} else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
|
||||
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
|
||||
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
|
||||
|
||||
/*
|
||||
* One interface hasn't rssi above threshold, bt_kill_msk must
|
||||
* be set to default values.
|
||||
*/
|
||||
data->reduced_tx_power = false;
|
||||
}
|
||||
|
||||
/* Begin to monitor the RSSI: it may influence the reduced Tx power */
|
||||
|
@ -963,7 +914,6 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
|
|||
struct iwl_bt_iterator_data data = {
|
||||
.mvm = mvm,
|
||||
.notif = &mvm->last_bt_notif_old,
|
||||
.reduced_tx_power = true,
|
||||
};
|
||||
struct iwl_bt_coex_ci_cmd_old cmd = {};
|
||||
u8 ci_bw_idx;
|
||||
|
@ -1037,14 +987,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
|
|||
memcpy(&mvm->last_bt_ci_cmd_old, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are no BSS / P2P client interfaces, reduced Tx Power is
|
||||
* irrelevant since it is based on the RSSI coming from the beacon.
|
||||
* Use BT_KILL_MSK_DEFAULT in that case.
|
||||
*/
|
||||
data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
|
||||
|
||||
if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
|
||||
if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm))
|
||||
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
|
||||
}
|
||||
|
||||
|
@ -1115,16 +1058,6 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
|
|||
return;
|
||||
|
||||
mvmsta = iwl_mvm_sta_from_mac80211(sta);
|
||||
|
||||
data->num_bss_ifaces++;
|
||||
|
||||
/*
|
||||
* This interface doesn't support reduced Tx power (because of low
|
||||
* RSSI probably), then set bt_kill_msk to default values.
|
||||
*/
|
||||
if (!mvmsta->bt_reduced_txpower)
|
||||
data->reduced_tx_power = false;
|
||||
/* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */
|
||||
}
|
||||
|
||||
void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
||||
|
@ -1133,7 +1066,6 @@ void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
|||
struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
|
||||
struct iwl_bt_iterator_data data = {
|
||||
.mvm = mvm,
|
||||
.reduced_tx_power = true,
|
||||
};
|
||||
int ret;
|
||||
|
||||
|
@ -1175,14 +1107,7 @@ void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
|
|||
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
iwl_mvm_bt_rssi_iterator, &data);
|
||||
|
||||
/*
|
||||
* If there are no BSS / P2P client interfaces, reduced Tx Power is
|
||||
* irrelevant since it is based on the RSSI coming from the beacon.
|
||||
* Use BT_KILL_MSK_DEFAULT in that case.
|
||||
*/
|
||||
data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
|
||||
|
||||
if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
|
||||
if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm))
|
||||
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -146,17 +146,47 @@ static ssize_t iwl_dbgfs_fw_error_dump_read(struct file *file,
|
|||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iwl_fw_error_dump_file *dump_file = file->private_data;
|
||||
struct iwl_mvm_dump_ptrs *dump_ptrs = (void *)file->private_data;
|
||||
ssize_t bytes_read = 0;
|
||||
ssize_t bytes_read_trans = 0;
|
||||
|
||||
if (*ppos < dump_ptrs->op_mode_len)
|
||||
bytes_read +=
|
||||
simple_read_from_buffer(user_buf, count, ppos,
|
||||
dump_ptrs->op_mode_ptr,
|
||||
dump_ptrs->op_mode_len);
|
||||
|
||||
if (bytes_read < 0 || *ppos < dump_ptrs->op_mode_len)
|
||||
return bytes_read;
|
||||
|
||||
if (dump_ptrs->trans_ptr) {
|
||||
*ppos -= dump_ptrs->op_mode_len;
|
||||
bytes_read_trans =
|
||||
simple_read_from_buffer(user_buf + bytes_read,
|
||||
count - bytes_read, ppos,
|
||||
dump_ptrs->trans_ptr->data,
|
||||
dump_ptrs->trans_ptr->len);
|
||||
*ppos += dump_ptrs->op_mode_len;
|
||||
|
||||
if (bytes_read_trans >= 0)
|
||||
bytes_read += bytes_read_trans;
|
||||
else if (!bytes_read)
|
||||
/* propagate the failure */
|
||||
return bytes_read_trans;
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos,
|
||||
dump_file,
|
||||
le32_to_cpu(dump_file->file_len));
|
||||
}
|
||||
|
||||
static int iwl_dbgfs_fw_error_dump_release(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
vfree(file->private_data);
|
||||
struct iwl_mvm_dump_ptrs *dump_ptrs = (void *)file->private_data;
|
||||
|
||||
vfree(dump_ptrs->op_mode_ptr);
|
||||
vfree(dump_ptrs->trans_ptr);
|
||||
kfree(dump_ptrs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -514,9 +544,9 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
|
|||
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
|
||||
iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
|
||||
iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[0]]);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
|
||||
iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
|
||||
iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]);
|
||||
|
||||
} else {
|
||||
struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
|
||||
|
@ -531,10 +561,19 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
|
|||
le64_to_cpu(cmd->bt_secondary_ci));
|
||||
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
|
||||
iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
|
||||
pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
|
||||
iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"\tPrimary: ACK Kill Mask 0x%08x\n",
|
||||
iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[0]]);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"\tPrimary: CTS Kill Mask 0x%08x\n",
|
||||
iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"\tSecondary: ACK Kill Mask 0x%08x\n",
|
||||
iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[1]]);
|
||||
pos += scnprintf(buf+pos, bufsz-pos,
|
||||
"\tSecondary: CTS Kill Mask 0x%08x\n",
|
||||
iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[1]]);
|
||||
|
||||
}
|
||||
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
@ -830,8 +869,14 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
|
|||
static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_NMI);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iwl_force_nmi(mvm->trans);
|
||||
|
||||
iwl_mvm_unref(mvm, IWL_MVM_REF_NMI);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -1115,11 +1160,11 @@ static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
|
|||
}
|
||||
#endif
|
||||
|
||||
#define PRINT_MVM_REF(ref) do { \
|
||||
if (test_bit(ref, mvm->ref_bitmap)) \
|
||||
pos += scnprintf(buf + pos, bufsz - pos, \
|
||||
"\t(0x%lx) %s\n", \
|
||||
BIT(ref), #ref); \
|
||||
#define PRINT_MVM_REF(ref) do { \
|
||||
if (mvm->refs[ref]) \
|
||||
pos += scnprintf(buf + pos, bufsz - pos, \
|
||||
"\t(0x%lx): %d %s\n", \
|
||||
BIT(ref), mvm->refs[ref], #ref); \
|
||||
} while (0)
|
||||
|
||||
static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
|
||||
|
@ -1127,12 +1172,17 @@ static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
|
|||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iwl_mvm *mvm = file->private_data;
|
||||
int pos = 0;
|
||||
int i, pos = 0;
|
||||
char buf[256];
|
||||
const size_t bufsz = sizeof(buf);
|
||||
u32 refs = 0;
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%lx\n",
|
||||
mvm->ref_bitmap[0]);
|
||||
for (i = 0; i < IWL_MVM_REF_COUNT; i++)
|
||||
if (mvm->refs[i])
|
||||
refs |= BIT(i);
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%x\n",
|
||||
refs);
|
||||
|
||||
PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN);
|
||||
PRINT_MVM_REF(IWL_MVM_REF_SCAN);
|
||||
|
@ -1158,7 +1208,7 @@ static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf,
|
|||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
taken = test_bit(IWL_MVM_REF_USER, mvm->ref_bitmap);
|
||||
taken = mvm->refs[IWL_MVM_REF_USER];
|
||||
if (value == 1 && !taken)
|
||||
iwl_mvm_ref(mvm, IWL_MVM_REF_USER);
|
||||
else if (value == 0 && taken)
|
||||
|
@ -1194,14 +1244,21 @@ iwl_dbgfs_prph_reg_read(struct file *file,
|
|||
int pos = 0;
|
||||
char buf[32];
|
||||
const size_t bufsz = sizeof(buf);
|
||||
int ret;
|
||||
|
||||
if (!mvm->dbgfs_prph_reg_addr)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_READ);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n",
|
||||
mvm->dbgfs_prph_reg_addr,
|
||||
iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr));
|
||||
|
||||
iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_READ);
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
|
||||
}
|
||||
|
||||
|
@ -1211,6 +1268,7 @@ iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf,
|
|||
{
|
||||
u8 args;
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value);
|
||||
/* if we only want to set the reg address - nothing more to do */
|
||||
|
@ -1221,7 +1279,13 @@ iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf,
|
|||
if (args != 2)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value);
|
||||
|
||||
iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
|
||||
out:
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -385,6 +385,8 @@ enum iwl_bt_activity_grading {
|
|||
BT_ON_NO_CONNECTION = 1,
|
||||
BT_LOW_TRAFFIC = 2,
|
||||
BT_HIGH_TRAFFIC = 3,
|
||||
|
||||
BT_MAX_AG,
|
||||
}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */
|
||||
|
||||
enum iwl_bt_ci_compliance {
|
||||
|
|
|
@ -133,6 +133,7 @@ enum {
|
|||
/* Scan offload */
|
||||
SCAN_OFFLOAD_REQUEST_CMD = 0x51,
|
||||
SCAN_OFFLOAD_ABORT_CMD = 0x52,
|
||||
HOT_SPOT_CMD = 0x53,
|
||||
SCAN_OFFLOAD_COMPLETE = 0x6D,
|
||||
SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
|
||||
SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
|
||||
|
@ -910,6 +911,72 @@ struct iwl_phy_context_cmd {
|
|||
__le32 dsp_cfg_flags;
|
||||
} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
|
||||
|
||||
/*
|
||||
* Aux ROC command
|
||||
*
|
||||
* Command requests the firmware to create a time event for a certain duration
|
||||
* and remain on the given channel. This is done by using the Aux framework in
|
||||
* the FW.
|
||||
* The command was first used for Hot Spot issues - but can be used regardless
|
||||
* to Hot Spot.
|
||||
*
|
||||
* ( HOT_SPOT_CMD 0x53 )
|
||||
*
|
||||
* @id_and_color: ID and color of the MAC
|
||||
* @action: action to perform, one of FW_CTXT_ACTION_*
|
||||
* @event_unique_id: If the action FW_CTXT_ACTION_REMOVE then the
|
||||
* event_unique_id should be the id of the time event assigned by ucode.
|
||||
* Otherwise ignore the event_unique_id.
|
||||
* @sta_id_and_color: station id and color, resumed during "Remain On Channel"
|
||||
* activity.
|
||||
* @channel_info: channel info
|
||||
* @node_addr: Our MAC Address
|
||||
* @reserved: reserved for alignment
|
||||
* @apply_time: GP2 value to start (should always be the current GP2 value)
|
||||
* @apply_time_max_delay: Maximum apply time delay value in TU. Defines max
|
||||
* time by which start of the event is allowed to be postponed.
|
||||
* @duration: event duration in TU To calculate event duration:
|
||||
* timeEventDuration = min(duration, remainingQuota)
|
||||
*/
|
||||
struct iwl_hs20_roc_req {
|
||||
/* COMMON_INDEX_HDR_API_S_VER_1 hdr */
|
||||
__le32 id_and_color;
|
||||
__le32 action;
|
||||
__le32 event_unique_id;
|
||||
__le32 sta_id_and_color;
|
||||
struct iwl_fw_channel_info channel_info;
|
||||
u8 node_addr[ETH_ALEN];
|
||||
__le16 reserved;
|
||||
__le32 apply_time;
|
||||
__le32 apply_time_max_delay;
|
||||
__le32 duration;
|
||||
} __packed; /* HOT_SPOT_CMD_API_S_VER_1 */
|
||||
|
||||
/*
|
||||
* values for AUX ROC result values
|
||||
*/
|
||||
enum iwl_mvm_hot_spot {
|
||||
HOT_SPOT_RSP_STATUS_OK,
|
||||
HOT_SPOT_RSP_STATUS_TOO_MANY_EVENTS,
|
||||
HOT_SPOT_MAX_NUM_OF_SESSIONS,
|
||||
};
|
||||
|
||||
/*
|
||||
* Aux ROC command response
|
||||
*
|
||||
* In response to iwl_hs20_roc_req the FW sends this command to notify the
|
||||
* driver the uid of the timevent.
|
||||
*
|
||||
* ( HOT_SPOT_CMD 0x53 )
|
||||
*
|
||||
* @event_unique_id: Unique ID of time event assigned by ucode
|
||||
* @status: Return status 0 is success, all the rest used for specific errors
|
||||
*/
|
||||
struct iwl_hs20_roc_res {
|
||||
__le32 event_unique_id;
|
||||
__le32 status;
|
||||
} __packed; /* HOT_SPOT_RSP_API_S_VER_1 */
|
||||
|
||||
#define IWL_RX_INFO_PHY_CNT 8
|
||||
#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1
|
||||
#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff
|
||||
|
|
|
@ -1074,8 +1074,12 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
|
|||
/* Fill the common data for all mac context types */
|
||||
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
|
||||
|
||||
/* Also enable probe requests to pass */
|
||||
cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
|
||||
/*
|
||||
* pass probe requests and beacons from other APs (needed
|
||||
* for ht protection)
|
||||
*/
|
||||
cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST |
|
||||
MAC_FILTER_IN_BEACON);
|
||||
|
||||
/* Fill the data specific for ap mode */
|
||||
iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap,
|
||||
|
@ -1096,6 +1100,13 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
|
|||
/* Fill the common data for all mac context types */
|
||||
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
|
||||
|
||||
/*
|
||||
* pass probe requests and beacons from other APs (needed
|
||||
* for ht protection)
|
||||
*/
|
||||
cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST |
|
||||
MAC_FILTER_IN_BEACON);
|
||||
|
||||
/* Fill the data specific for GO mode */
|
||||
iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap,
|
||||
action == FW_CTXT_ACTION_ADD);
|
||||
|
|
|
@ -211,7 +211,9 @@ void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
|
|||
return;
|
||||
|
||||
IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type);
|
||||
WARN_ON(test_and_set_bit(ref_type, mvm->ref_bitmap));
|
||||
spin_lock_bh(&mvm->refs_lock);
|
||||
mvm->refs[ref_type]++;
|
||||
spin_unlock_bh(&mvm->refs_lock);
|
||||
iwl_trans_ref(mvm->trans);
|
||||
}
|
||||
|
||||
|
@ -221,29 +223,35 @@ void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
|
|||
return;
|
||||
|
||||
IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type);
|
||||
WARN_ON(!test_and_clear_bit(ref_type, mvm->ref_bitmap));
|
||||
spin_lock_bh(&mvm->refs_lock);
|
||||
WARN_ON(!mvm->refs[ref_type]--);
|
||||
spin_unlock_bh(&mvm->refs_lock);
|
||||
iwl_trans_unref(mvm->trans);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref)
|
||||
static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm,
|
||||
enum iwl_mvm_ref_type except_ref)
|
||||
{
|
||||
int i;
|
||||
int i, j;
|
||||
|
||||
if (!iwl_mvm_is_d0i3_supported(mvm))
|
||||
return;
|
||||
|
||||
for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) {
|
||||
if (ref == i)
|
||||
spin_lock_bh(&mvm->refs_lock);
|
||||
for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
|
||||
if (except_ref == i || !mvm->refs[i])
|
||||
continue;
|
||||
|
||||
IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d\n", i);
|
||||
clear_bit(i, mvm->ref_bitmap);
|
||||
iwl_trans_unref(mvm->trans);
|
||||
IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d (%d)\n",
|
||||
i, mvm->refs[i]);
|
||||
for (j = 0; j < mvm->refs[i]; j++)
|
||||
iwl_trans_unref(mvm->trans);
|
||||
mvm->refs[i] = 0;
|
||||
}
|
||||
spin_unlock_bh(&mvm->refs_lock);
|
||||
}
|
||||
|
||||
static int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
|
||||
int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
|
||||
{
|
||||
iwl_mvm_ref(mvm, ref_type);
|
||||
|
||||
|
@ -321,13 +329,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
|
|||
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
|
||||
}
|
||||
|
||||
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
|
||||
!iwlwifi_mod_params.uapsd_disable) {
|
||||
hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
|
||||
hw->uapsd_queues = IWL_UAPSD_AC_INFO;
|
||||
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
|
||||
}
|
||||
|
||||
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
|
||||
hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
|
||||
|
||||
|
@ -660,6 +661,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
|
|||
spin_unlock_bh(&mvm->time_event_lock);
|
||||
|
||||
mvmvif->phy_ctxt = NULL;
|
||||
memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
|
@ -668,11 +670,11 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
|
|||
struct iwl_fw_error_dump_file *dump_file;
|
||||
struct iwl_fw_error_dump_data *dump_data;
|
||||
struct iwl_fw_error_dump_info *dump_info;
|
||||
struct iwl_mvm_dump_ptrs *fw_error_dump;
|
||||
const struct fw_img *img;
|
||||
u32 sram_len, sram_ofs;
|
||||
u32 file_len, rxf_len;
|
||||
unsigned long flags;
|
||||
u32 trans_len;
|
||||
int reg_val;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
@ -680,6 +682,10 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
|
|||
if (mvm->fw_error_dump)
|
||||
return;
|
||||
|
||||
fw_error_dump = kzalloc(sizeof(*mvm->fw_error_dump), GFP_KERNEL);
|
||||
if (!fw_error_dump)
|
||||
return;
|
||||
|
||||
img = &mvm->fw->img[mvm->cur_ucode];
|
||||
sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
|
||||
sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
|
||||
|
@ -697,18 +703,15 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
|
|||
rxf_len +
|
||||
sizeof(*dump_info);
|
||||
|
||||
trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
|
||||
if (trans_len)
|
||||
file_len += trans_len;
|
||||
|
||||
dump_file = vzalloc(file_len);
|
||||
if (!dump_file)
|
||||
if (!dump_file) {
|
||||
kfree(fw_error_dump);
|
||||
return;
|
||||
}
|
||||
|
||||
mvm->fw_error_dump = dump_file;
|
||||
fw_error_dump->op_mode_ptr = dump_file;
|
||||
|
||||
dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
|
||||
dump_file->file_len = cpu_to_le32(file_len);
|
||||
dump_data = (void *)dump_file->data;
|
||||
|
||||
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
|
||||
|
@ -749,14 +752,12 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
|
|||
iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data,
|
||||
sram_len);
|
||||
|
||||
if (trans_len) {
|
||||
void *buf = iwl_fw_error_next_data(dump_data);
|
||||
u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
|
||||
trans_len);
|
||||
dump_data = (void *)((u8 *)buf + real_trans_len);
|
||||
dump_file->file_len =
|
||||
cpu_to_le32(file_len - trans_len + real_trans_len);
|
||||
}
|
||||
fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans);
|
||||
fw_error_dump->op_mode_len = file_len;
|
||||
if (fw_error_dump->trans_ptr)
|
||||
file_len += fw_error_dump->trans_ptr->len;
|
||||
dump_file->file_len = cpu_to_le32(file_len);
|
||||
mvm->fw_error_dump = fw_error_dump;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -788,6 +789,12 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
|
|||
iwl_mvm_reset_phy_ctxts(mvm);
|
||||
memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
|
||||
memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
|
||||
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
|
||||
memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
|
||||
memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
|
||||
memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old));
|
||||
memset(&mvm->bt_ack_kill_msk, 0, sizeof(mvm->bt_ack_kill_msk));
|
||||
memset(&mvm->bt_cts_kill_msk, 0, sizeof(mvm->bt_cts_kill_msk));
|
||||
|
||||
ieee80211_wake_queues(mvm->hw);
|
||||
|
||||
|
@ -1399,6 +1406,28 @@ static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
|
|||
}
|
||||
#endif
|
||||
|
||||
static void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
|
||||
{
|
||||
struct ieee80211_sta *sta;
|
||||
struct iwl_mvm_sta *mvmsta;
|
||||
int i;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
|
||||
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
|
||||
lockdep_is_held(&mvm->mutex));
|
||||
if (!sta || IS_ERR(sta) || !sta->tdls)
|
||||
continue;
|
||||
|
||||
mvmsta = iwl_mvm_sta_from_mac80211(sta);
|
||||
ieee80211_tdls_oper_request(mvmsta->vif, sta->addr,
|
||||
NL80211_TDLS_TEARDOWN,
|
||||
WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *bss_conf,
|
||||
|
@ -1494,14 +1523,18 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
|
|||
*/
|
||||
iwl_mvm_remove_time_event(mvm, mvmvif,
|
||||
&mvmvif->time_event_data);
|
||||
iwl_mvm_sf_update(mvm, vif, false);
|
||||
WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
|
||||
} else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
|
||||
BSS_CHANGED_QOS)) {
|
||||
ret = iwl_mvm_power_update_mac(mvm);
|
||||
if (ret)
|
||||
IWL_ERR(mvm, "failed to update power mode\n");
|
||||
}
|
||||
|
||||
if (changes & BSS_CHANGED_BEACON_INFO) {
|
||||
iwl_mvm_sf_update(mvm, vif, false);
|
||||
WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
|
||||
}
|
||||
|
||||
if (changes & BSS_CHANGED_TXPOWER) {
|
||||
IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
|
||||
bss_conf->txpower);
|
||||
|
@ -1533,6 +1566,14 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
|
|||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* iwl_mvm_mac_ctxt_add() might read directly from the device
|
||||
* (the system time), so make sure it is available.
|
||||
*/
|
||||
ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_START_AP);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
/* Send the beacon template */
|
||||
|
@ -1581,6 +1622,10 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
|
|||
|
||||
iwl_mvm_bt_coex_vif_change(mvm);
|
||||
|
||||
/* we don't support TDLS during DCM */
|
||||
if (iwl_mvm_phy_ctx_count(mvm) > 1)
|
||||
iwl_mvm_teardown_tdls_peers(mvm);
|
||||
|
||||
mutex_unlock(&mvm->mutex);
|
||||
return 0;
|
||||
|
||||
|
@ -1594,6 +1639,7 @@ out_remove:
|
|||
iwl_mvm_mac_ctxt_remove(mvm, vif);
|
||||
out_unlock:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
iwl_mvm_unref(mvm, IWL_MVM_REF_START_AP);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1671,6 +1717,14 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
|
|||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
|
||||
/*
|
||||
* iwl_mvm_bss_info_changed_station() might call
|
||||
* iwl_mvm_protect_session(), which reads directly from
|
||||
* the device (the system time), so make sure it is available.
|
||||
*/
|
||||
if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_BSS_CHANGED))
|
||||
return;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
|
||||
|
@ -1690,8 +1744,50 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
|
|||
}
|
||||
|
||||
mutex_unlock(&mvm->mutex);
|
||||
iwl_mvm_unref(mvm, IWL_MVM_REF_BSS_CHANGED);
|
||||
}
|
||||
|
||||
static int iwl_mvm_cancel_scan_wait_notif(struct iwl_mvm *mvm,
|
||||
enum iwl_scan_status scan_type)
|
||||
{
|
||||
int ret;
|
||||
bool wait_for_handlers = false;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
if (mvm->scan_status != scan_type) {
|
||||
ret = 0;
|
||||
/* make sure there are no pending notifications */
|
||||
wait_for_handlers = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (scan_type) {
|
||||
case IWL_MVM_SCAN_SCHED:
|
||||
ret = iwl_mvm_scan_offload_stop(mvm, true);
|
||||
break;
|
||||
case IWL_MVM_SCAN_OS:
|
||||
ret = iwl_mvm_cancel_scan(mvm);
|
||||
break;
|
||||
case IWL_MVM_SCAN_NONE:
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
wait_for_handlers = true;
|
||||
out:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
/* make sure we consume the completion notification */
|
||||
if (wait_for_handlers)
|
||||
iwl_mvm_wait_for_async_handlers(mvm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
|
@ -1704,19 +1800,13 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
|
|||
req->n_channels > mvm->fw->ucode_capa.n_scan_channels)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
switch (mvm->scan_status) {
|
||||
case IWL_MVM_SCAN_SCHED:
|
||||
ret = iwl_mvm_scan_offload_stop(mvm, true);
|
||||
if (ret) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case IWL_MVM_SCAN_NONE:
|
||||
break;
|
||||
default:
|
||||
if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1732,8 +1822,6 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
|
|||
iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
|
||||
out:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
/* make sure to flush the Rx handler before the next scan arrives */
|
||||
iwl_mvm_wait_for_async_handlers(mvm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1885,28 +1973,6 @@ static void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm,
|
|||
iwl_mvm_power_update_mac(mvm);
|
||||
}
|
||||
|
||||
static void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
|
||||
{
|
||||
struct ieee80211_sta *sta;
|
||||
struct iwl_mvm_sta *mvmsta;
|
||||
int i;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
|
||||
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
|
||||
lockdep_is_held(&mvm->mutex));
|
||||
if (!sta || IS_ERR(sta) || !sta->tdls)
|
||||
continue;
|
||||
|
||||
mvmsta = iwl_mvm_sta_from_mac80211(sta);
|
||||
ieee80211_tdls_oper_request(mvmsta->vif, sta->addr,
|
||||
NL80211_TDLS_TEARDOWN,
|
||||
WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
}
|
||||
|
||||
static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
|
@ -2065,10 +2131,19 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
|
|||
if (WARN_ON_ONCE(vif->bss_conf.assoc))
|
||||
return;
|
||||
|
||||
/*
|
||||
* iwl_mvm_protect_session() reads directly from the device
|
||||
* (the system time), so make sure it is available.
|
||||
*/
|
||||
if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX))
|
||||
return;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
/* Try really hard to protect the session and hear a beacon */
|
||||
iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
iwl_mvm_unref(mvm, IWL_MVM_REF_PREPARE_TX);
|
||||
}
|
||||
|
||||
static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
|
||||
|
@ -2077,10 +2152,19 @@ static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
|
|||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int;
|
||||
|
||||
/*
|
||||
* iwl_mvm_protect_session() reads directly from the device
|
||||
* (the system time), so make sure it is available.
|
||||
*/
|
||||
if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_TDLS))
|
||||
return;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
/* Protect the session to hear the TDLS setup response on the channel */
|
||||
iwl_mvm_protect_session(mvm, vif, duration, duration, 100);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS);
|
||||
}
|
||||
|
||||
static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
|
||||
|
@ -2091,6 +2175,10 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
|
|||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
int ret;
|
||||
|
||||
ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
if (!iwl_mvm_is_idle(mvm)) {
|
||||
|
@ -2098,26 +2186,7 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
|
|||
goto out;
|
||||
}
|
||||
|
||||
switch (mvm->scan_status) {
|
||||
case IWL_MVM_SCAN_OS:
|
||||
IWL_DEBUG_SCAN(mvm, "Stopping previous scan for sched_scan\n");
|
||||
ret = iwl_mvm_cancel_scan(mvm);
|
||||
if (ret) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* iwl_mvm_rx_scan_complete() will be called soon but will
|
||||
* not reset the scan status as it won't be IWL_MVM_SCAN_OS
|
||||
* any more since we queue the next scan immediately (below).
|
||||
* We make sure it is called before the next scan starts by
|
||||
* flushing the async-handlers work.
|
||||
*/
|
||||
break;
|
||||
case IWL_MVM_SCAN_NONE:
|
||||
break;
|
||||
default:
|
||||
if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
@ -2145,8 +2214,6 @@ err:
|
|||
mvm->scan_status = IWL_MVM_SCAN_NONE;
|
||||
out:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
/* make sure to flush the Rx handler before the next scan arrives */
|
||||
iwl_mvm_wait_for_async_handlers(mvm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -2266,6 +2333,119 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
|
|||
}
|
||||
|
||||
|
||||
static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait,
|
||||
struct iwl_rx_packet *pkt, void *data)
|
||||
{
|
||||
struct iwl_mvm *mvm =
|
||||
container_of(notif_wait, struct iwl_mvm, notif_wait);
|
||||
struct iwl_hs20_roc_res *resp;
|
||||
int resp_len = iwl_rx_packet_payload_len(pkt);
|
||||
struct iwl_mvm_time_event_data *te_data = data;
|
||||
|
||||
if (WARN_ON(pkt->hdr.cmd != HOT_SPOT_CMD))
|
||||
return true;
|
||||
|
||||
if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
|
||||
IWL_ERR(mvm, "Invalid HOT_SPOT_CMD response\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
resp = (void *)pkt->data;
|
||||
|
||||
IWL_DEBUG_TE(mvm,
|
||||
"Aux ROC: Recieved response from ucode: status=%d uid=%d\n",
|
||||
resp->status, resp->event_unique_id);
|
||||
|
||||
te_data->uid = le32_to_cpu(resp->event_unique_id);
|
||||
IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
|
||||
te_data->uid);
|
||||
|
||||
spin_lock_bh(&mvm->time_event_lock);
|
||||
list_add_tail(&te_data->list, &mvm->aux_roc_te_list);
|
||||
spin_unlock_bh(&mvm->time_event_lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define AUX_ROC_MAX_DELAY_ON_CHANNEL 5000
|
||||
static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
|
||||
struct ieee80211_channel *channel,
|
||||
struct ieee80211_vif *vif,
|
||||
int duration)
|
||||
{
|
||||
int res, time_reg = DEVICE_SYSTEM_TIME_REG;
|
||||
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
|
||||
struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data;
|
||||
static const u8 time_event_response[] = { HOT_SPOT_CMD };
|
||||
struct iwl_notification_wait wait_time_event;
|
||||
struct iwl_hs20_roc_req aux_roc_req = {
|
||||
.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
|
||||
.id_and_color =
|
||||
cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)),
|
||||
.sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id),
|
||||
/* Set the channel info data */
|
||||
.channel_info.band = (channel->band == IEEE80211_BAND_2GHZ) ?
|
||||
PHY_BAND_24 : PHY_BAND_5,
|
||||
.channel_info.channel = channel->hw_value,
|
||||
.channel_info.width = PHY_VHT_CHANNEL_MODE20,
|
||||
/* Set the time and duration */
|
||||
.apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)),
|
||||
.apply_time_max_delay =
|
||||
cpu_to_le32(MSEC_TO_TU(AUX_ROC_MAX_DELAY_ON_CHANNEL)),
|
||||
.duration = cpu_to_le32(MSEC_TO_TU(duration)),
|
||||
};
|
||||
|
||||
/* Set the node address */
|
||||
memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN);
|
||||
|
||||
te_data->vif = vif;
|
||||
te_data->duration = duration;
|
||||
te_data->id = HOT_SPOT_CMD;
|
||||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
spin_lock_bh(&mvm->time_event_lock);
|
||||
list_add_tail(&te_data->list, &mvm->time_event_list);
|
||||
spin_unlock_bh(&mvm->time_event_lock);
|
||||
|
||||
/*
|
||||
* Use a notification wait, which really just processes the
|
||||
* command response and doesn't wait for anything, in order
|
||||
* to be able to process the response and get the UID inside
|
||||
* the RX path. Using CMD_WANT_SKB doesn't work because it
|
||||
* stores the buffer and then wakes up this thread, by which
|
||||
* time another notification (that the time event started)
|
||||
* might already be processed unsuccessfully.
|
||||
*/
|
||||
iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
|
||||
time_event_response,
|
||||
ARRAY_SIZE(time_event_response),
|
||||
iwl_mvm_rx_aux_roc, te_data);
|
||||
|
||||
res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, sizeof(aux_roc_req),
|
||||
&aux_roc_req);
|
||||
|
||||
if (res) {
|
||||
IWL_ERR(mvm, "Couldn't send HOT_SPOT_CMD: %d\n", res);
|
||||
iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
|
||||
goto out_clear_te;
|
||||
}
|
||||
|
||||
/* No need to wait for anything, so just pass 1 (0 isn't valid) */
|
||||
res = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
|
||||
/* should never fail */
|
||||
WARN_ON_ONCE(res);
|
||||
|
||||
if (res) {
|
||||
out_clear_te:
|
||||
spin_lock_bh(&mvm->time_event_lock);
|
||||
iwl_mvm_te_clear_data(mvm, te_data);
|
||||
spin_unlock_bh(&mvm->time_event_lock);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int iwl_mvm_roc(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_channel *channel,
|
||||
|
@ -2281,8 +2461,17 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
|
|||
IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
|
||||
duration, type);
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_P2P_DEVICE) {
|
||||
IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type);
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
/* Use aux roc framework (HS20) */
|
||||
ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
|
||||
vif, duration);
|
||||
return ret;
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
/* handle below */
|
||||
break;
|
||||
default:
|
||||
IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -2661,6 +2850,10 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
|
|||
goto out_remove;
|
||||
}
|
||||
|
||||
/* we don't support TDLS during DCM - can be caused by channel switch */
|
||||
if (iwl_mvm_phy_ctx_count(mvm) > 1)
|
||||
iwl_mvm_teardown_tdls_peers(mvm);
|
||||
|
||||
goto out;
|
||||
|
||||
out_remove:
|
||||
|
|
|
@ -82,6 +82,8 @@
|
|||
/* RSSI offset for WkP */
|
||||
#define IWL_RSSI_OFFSET 50
|
||||
#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8
|
||||
/* A TimeUnit is 1024 microsecond */
|
||||
#define MSEC_TO_TU(_msec) (_msec*1000/1024)
|
||||
|
||||
/*
|
||||
* The CSA NoA is scheduled IWL_MVM_CHANNEL_SWITCH_TIME TUs before "beacon 0"
|
||||
|
@ -126,6 +128,21 @@ struct iwl_mvm_mod_params {
|
|||
};
|
||||
extern struct iwl_mvm_mod_params iwlmvm_mod_params;
|
||||
|
||||
/**
|
||||
* struct iwl_mvm_dump_ptrs - set of pointers needed for the fw-error-dump
|
||||
*
|
||||
* @op_mode_ptr: pointer to the buffer coming from the mvm op_mode
|
||||
* @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the
|
||||
* transport's data.
|
||||
* @trans_len: length of the valid data in trans_ptr
|
||||
* @op_mode_len: length of the valid data in op_mode_ptr
|
||||
*/
|
||||
struct iwl_mvm_dump_ptrs {
|
||||
struct iwl_trans_dump_data *trans_ptr;
|
||||
void *op_mode_ptr;
|
||||
u32 op_mode_len;
|
||||
};
|
||||
|
||||
struct iwl_mvm_phy_ctxt {
|
||||
u16 id;
|
||||
u16 color;
|
||||
|
@ -249,6 +266,15 @@ enum iwl_mvm_ref_type {
|
|||
IWL_MVM_REF_TX,
|
||||
IWL_MVM_REF_TX_AGG,
|
||||
IWL_MVM_REF_ADD_IF,
|
||||
IWL_MVM_REF_START_AP,
|
||||
IWL_MVM_REF_BSS_CHANGED,
|
||||
IWL_MVM_REF_PREPARE_TX,
|
||||
IWL_MVM_REF_PROTECT_TDLS,
|
||||
IWL_MVM_REF_CHECK_CTKILL,
|
||||
IWL_MVM_REF_PRPH_READ,
|
||||
IWL_MVM_REF_PRPH_WRITE,
|
||||
IWL_MVM_REF_NMI,
|
||||
IWL_MVM_REF_TM_CMD,
|
||||
IWL_MVM_REF_EXIT_WORK,
|
||||
|
||||
IWL_MVM_REF_COUNT,
|
||||
|
@ -327,6 +353,7 @@ struct iwl_mvm_vif {
|
|||
*/
|
||||
struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
|
||||
struct iwl_mvm_time_event_data time_event_data;
|
||||
struct iwl_mvm_time_event_data hs_time_event_data;
|
||||
|
||||
struct iwl_mvm_int_sta bcast_sta;
|
||||
|
||||
|
@ -606,14 +633,15 @@ struct iwl_mvm {
|
|||
*/
|
||||
unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
|
||||
|
||||
/* A bitmap of reference types taken by the driver. */
|
||||
unsigned long ref_bitmap[BITS_TO_LONGS(IWL_MVM_REF_COUNT)];
|
||||
/* references taken by the driver and spinlock protecting them */
|
||||
spinlock_t refs_lock;
|
||||
u8 refs[IWL_MVM_REF_COUNT];
|
||||
|
||||
u8 vif_count;
|
||||
|
||||
/* -1 for always, 0 for never, >0 for that many times */
|
||||
s8 restart_fw;
|
||||
void *fw_error_dump;
|
||||
struct iwl_mvm_dump_ptrs *fw_error_dump;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_LEDS
|
||||
struct led_classdev led;
|
||||
|
@ -647,7 +675,8 @@ struct iwl_mvm {
|
|||
wait_queue_head_t d0i3_exit_waitq;
|
||||
|
||||
/* BT-Coex */
|
||||
u8 bt_kill_msk;
|
||||
u8 bt_ack_kill_msk[NUM_PHY_CTX];
|
||||
u8 bt_cts_kill_msk[NUM_PHY_CTX];
|
||||
|
||||
struct iwl_bt_coex_profile_notif_old last_bt_notif_old;
|
||||
struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old;
|
||||
|
@ -659,6 +688,9 @@ struct iwl_mvm {
|
|||
u8 bt_tx_prio;
|
||||
enum iwl_bt_force_ant_mode bt_force_ant_mode;
|
||||
|
||||
/* Aux ROC */
|
||||
struct list_head aux_roc_te_list;
|
||||
|
||||
/* Thermal Throttling and CTkill */
|
||||
struct iwl_mvm_tt_mgmt thermal_throttle;
|
||||
s32 temperature; /* Celsius */
|
||||
|
@ -697,6 +729,7 @@ enum iwl_mvm_status {
|
|||
IWL_MVM_STATUS_ROC_RUNNING,
|
||||
IWL_MVM_STATUS_IN_HW_RESTART,
|
||||
IWL_MVM_STATUS_IN_D0I3,
|
||||
IWL_MVM_STATUS_ROC_AUX_RUNNING,
|
||||
};
|
||||
|
||||
static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
|
||||
|
@ -988,6 +1021,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
|
|||
/* D0i3 */
|
||||
void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
|
||||
void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
|
||||
int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
|
||||
void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
|
||||
int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
|
||||
|
||||
|
@ -1029,12 +1063,14 @@ int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
|
|||
|
||||
enum iwl_bt_kill_msk {
|
||||
BT_KILL_MSK_DEFAULT,
|
||||
BT_KILL_MSK_SCO_HID_A2DP,
|
||||
BT_KILL_MSK_REDUCED_TXPOW,
|
||||
BT_KILL_MSK_NEVER,
|
||||
BT_KILL_MSK_ALWAYS,
|
||||
BT_KILL_MSK_MAX,
|
||||
};
|
||||
extern const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX];
|
||||
extern const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX];
|
||||
|
||||
extern const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT];
|
||||
extern const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT];
|
||||
extern const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX];
|
||||
|
||||
/* beacon filtering */
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
|
|
|
@ -265,7 +265,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
|
|||
if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
|
||||
if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
|
||||
!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) {
|
||||
IWL_ERR(mvm, "Can't parse empty NVM sections\n");
|
||||
IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
|
@ -273,7 +273,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
|
|||
if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
|
||||
!mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) {
|
||||
IWL_ERR(mvm,
|
||||
"Can't parse empty family 8000 NVM sections\n");
|
||||
"Can't parse empty family 8000 OTP/NVM sections\n");
|
||||
return NULL;
|
||||
}
|
||||
/* MAC_OVERRIDE or at least HW section must exist */
|
||||
|
|
|
@ -289,6 +289,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
|
|||
CMD(MATCH_FOUND_NOTIFICATION),
|
||||
CMD(SCAN_OFFLOAD_REQUEST_CMD),
|
||||
CMD(SCAN_OFFLOAD_ABORT_CMD),
|
||||
CMD(HOT_SPOT_CMD),
|
||||
CMD(SCAN_OFFLOAD_COMPLETE),
|
||||
CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
|
||||
CMD(SCAN_ITERATION_COMPLETE),
|
||||
|
@ -391,6 +392,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
if (!hw)
|
||||
return NULL;
|
||||
|
||||
if (cfg->max_rx_agg_size)
|
||||
hw->max_rx_aggregation_subframes = cfg->max_rx_agg_size;
|
||||
|
||||
op_mode = hw->priv;
|
||||
op_mode->ops = &iwl_mvm_ops;
|
||||
|
||||
|
@ -416,6 +420,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
mutex_init(&mvm->d0i3_suspend_mutex);
|
||||
spin_lock_init(&mvm->async_handlers_lock);
|
||||
INIT_LIST_HEAD(&mvm->time_event_list);
|
||||
INIT_LIST_HEAD(&mvm->aux_roc_te_list);
|
||||
INIT_LIST_HEAD(&mvm->async_handlers_list);
|
||||
spin_lock_init(&mvm->time_event_lock);
|
||||
|
||||
|
@ -425,6 +430,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
|
||||
|
||||
spin_lock_init(&mvm->d0i3_tx_lock);
|
||||
spin_lock_init(&mvm->refs_lock);
|
||||
skb_queue_head_init(&mvm->d0i3_tx);
|
||||
init_waitqueue_head(&mvm->d0i3_exit_waitq);
|
||||
|
||||
|
@ -539,7 +545,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
|
|||
memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
|
||||
|
||||
/* rpm starts with a taken ref. only set the appropriate bit here. */
|
||||
set_bit(IWL_MVM_REF_UCODE_DOWN, mvm->ref_bitmap);
|
||||
mvm->refs[IWL_MVM_REF_UCODE_DOWN] = 1;
|
||||
|
||||
return op_mode;
|
||||
|
||||
|
@ -567,7 +573,11 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
|
|||
ieee80211_unregister_hw(mvm->hw);
|
||||
|
||||
kfree(mvm->scan_cmd);
|
||||
vfree(mvm->fw_error_dump);
|
||||
if (mvm->fw_error_dump) {
|
||||
vfree(mvm->fw_error_dump->op_mode_ptr);
|
||||
vfree(mvm->fw_error_dump->trans_ptr);
|
||||
kfree(mvm->fw_error_dump);
|
||||
}
|
||||
kfree(mvm->mcast_filter_cmd);
|
||||
mvm->mcast_filter_cmd = NULL;
|
||||
|
||||
|
|
|
@ -98,23 +98,21 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
|
|||
bool update)
|
||||
{
|
||||
struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
|
||||
struct iwl_mvm_add_sta_cmd add_sta_cmd;
|
||||
struct iwl_mvm_add_sta_cmd add_sta_cmd = {
|
||||
.sta_id = mvm_sta->sta_id,
|
||||
.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color),
|
||||
.add_modify = update ? 1 : 0,
|
||||
.station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK |
|
||||
STA_FLG_MIMO_EN_MSK),
|
||||
};
|
||||
int ret;
|
||||
u32 status;
|
||||
u32 agg_size = 0, mpdu_dens = 0;
|
||||
|
||||
memset(&add_sta_cmd, 0, sizeof(add_sta_cmd));
|
||||
|
||||
add_sta_cmd.sta_id = mvm_sta->sta_id;
|
||||
add_sta_cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
|
||||
if (!update) {
|
||||
add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk);
|
||||
memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN);
|
||||
}
|
||||
add_sta_cmd.add_modify = update ? 1 : 0;
|
||||
|
||||
add_sta_cmd.station_flags_msk |= cpu_to_le32(STA_FLG_FAT_EN_MSK |
|
||||
STA_FLG_MIMO_EN_MSK);
|
||||
|
||||
switch (sta->bandwidth) {
|
||||
case IEEE80211_STA_RX_BW_160:
|
||||
|
@ -528,8 +526,12 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
|
|||
|
||||
lockdep_assert_held(&mvm->mutex);
|
||||
|
||||
/* Add the aux station, but without any queues */
|
||||
ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0,
|
||||
/* Map Aux queue to fifo - needs to happen before adding Aux station */
|
||||
iwl_trans_ac_txq_enable(mvm->trans, mvm->aux_queue,
|
||||
IWL_MVM_TX_FIFO_MCAST);
|
||||
|
||||
/* Allocate aux station and assign to it the aux queue */
|
||||
ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue),
|
||||
NL80211_IFTYPE_UNSPECIFIED);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -72,9 +72,6 @@
|
|||
#include "iwl-io.h"
|
||||
#include "iwl-prph.h"
|
||||
|
||||
/* A TimeUnit is 1024 microsecond */
|
||||
#define MSEC_TO_TU(_msec) (_msec*1000/1024)
|
||||
|
||||
/*
|
||||
* For the high priority TE use a time event type that has similar priority to
|
||||
* the FW's action scan priority.
|
||||
|
@ -100,6 +97,21 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
|
|||
void iwl_mvm_roc_done_wk(struct work_struct *wk)
|
||||
{
|
||||
struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
|
||||
u32 queues = 0;
|
||||
|
||||
/*
|
||||
* Clear the ROC_RUNNING /ROC_AUX_RUNNING status bit.
|
||||
* This will cause the TX path to drop offchannel transmissions.
|
||||
* That would also be done by mac80211, but it is racy, in particular
|
||||
* in the case that the time event actually completed in the firmware
|
||||
* (which is handled in iwl_mvm_te_handle_notif).
|
||||
*/
|
||||
if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
|
||||
queues |= BIT(IWL_MVM_OFFCHANNEL_QUEUE);
|
||||
if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
|
||||
queues |= BIT(mvm->aux_queue);
|
||||
|
||||
iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
|
||||
|
||||
synchronize_net();
|
||||
|
||||
|
@ -113,21 +125,11 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)
|
|||
* issue as it will have to complete before the next command is
|
||||
* executed, and a new time event means a new command.
|
||||
*/
|
||||
iwl_mvm_flush_tx_path(mvm, BIT(IWL_MVM_OFFCHANNEL_QUEUE), false);
|
||||
iwl_mvm_flush_tx_path(mvm, queues, false);
|
||||
}
|
||||
|
||||
static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
|
||||
{
|
||||
/*
|
||||
* First, clear the ROC_RUNNING status bit. This will cause the TX
|
||||
* path to drop offchannel transmissions. That would also be done
|
||||
* by mac80211, but it is racy, in particular in the case that the
|
||||
* time event actually completed in the firmware (which is handled
|
||||
* in iwl_mvm_te_handle_notif).
|
||||
*/
|
||||
clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
|
||||
iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
|
||||
|
||||
/*
|
||||
* Of course, our status bit is just as racy as mac80211, so in
|
||||
* addition, fire off the work struct which will drop all frames
|
||||
|
@ -262,6 +264,60 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle A Aux ROC time event
|
||||
*/
|
||||
static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
|
||||
struct iwl_time_event_notif *notif)
|
||||
{
|
||||
struct iwl_mvm_time_event_data *te_data, *tmp;
|
||||
bool aux_roc_te = false;
|
||||
|
||||
list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) {
|
||||
if (le32_to_cpu(notif->unique_id) == te_data->uid) {
|
||||
aux_roc_te = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!aux_roc_te) /* Not a Aux ROC time event */
|
||||
return -EINVAL;
|
||||
|
||||
if (!le32_to_cpu(notif->status)) {
|
||||
IWL_DEBUG_TE(mvm,
|
||||
"ERROR: Aux ROC Time Event %s notification failure\n",
|
||||
(le32_to_cpu(notif->action) &
|
||||
TE_V2_NOTIF_HOST_EVENT_START) ? "start" : "end");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
IWL_DEBUG_TE(mvm,
|
||||
"Aux ROC time event notification - UID = 0x%x action %d\n",
|
||||
le32_to_cpu(notif->unique_id),
|
||||
le32_to_cpu(notif->action));
|
||||
|
||||
if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
|
||||
/* End TE, notify mac80211 */
|
||||
ieee80211_remain_on_channel_expired(mvm->hw);
|
||||
iwl_mvm_roc_finished(mvm); /* flush aux queue */
|
||||
list_del(&te_data->list); /* remove from list */
|
||||
te_data->running = false;
|
||||
te_data->vif = NULL;
|
||||
te_data->uid = 0;
|
||||
} else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
|
||||
set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
|
||||
set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
|
||||
te_data->running = true;
|
||||
ieee80211_ready_on_channel(mvm->hw); /* Start TE */
|
||||
} else {
|
||||
IWL_DEBUG_TE(mvm,
|
||||
"ERROR: Unknown Aux ROC Time Event (action = %d)\n",
|
||||
le32_to_cpu(notif->action));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The Rx handler for time event notifications
|
||||
*/
|
||||
|
@ -278,10 +334,15 @@ int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
|
|||
le32_to_cpu(notif->action));
|
||||
|
||||
spin_lock_bh(&mvm->time_event_lock);
|
||||
/* This time event is triggered for Aux ROC request */
|
||||
if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif))
|
||||
goto unlock;
|
||||
|
||||
list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
|
||||
if (le32_to_cpu(notif->unique_id) == te_data->uid)
|
||||
iwl_mvm_te_handle_notif(mvm, te_data, notif);
|
||||
}
|
||||
unlock:
|
||||
spin_unlock_bh(&mvm->time_event_lock);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -140,9 +140,9 @@ static u16 iwl_mvm_dts_get_ptat_deviation_offset(struct iwl_mvm *mvm)
|
|||
|
||||
/* TODO: move parsing to NVM code */
|
||||
calib = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION].data;
|
||||
ptat = calib[OTP_DTS_DIODE_DEVIATION];
|
||||
pa1 = calib[OTP_DTS_DIODE_DEVIATION + 1];
|
||||
pa2 = calib[OTP_DTS_DIODE_DEVIATION + 2];
|
||||
ptat = calib[OTP_DTS_DIODE_DEVIATION * 2];
|
||||
pa1 = calib[OTP_DTS_DIODE_DEVIATION * 2 + 1];
|
||||
pa2 = calib[OTP_DTS_DIODE_DEVIATION * 2 + 2];
|
||||
|
||||
/* get the median: */
|
||||
if (ptat > pa1) {
|
||||
|
@ -338,10 +338,16 @@ static void check_exit_ctkill(struct work_struct *work)
|
|||
|
||||
duration = tt->params->ct_kill_duration;
|
||||
|
||||
/* make sure the device is available for direct read/writes */
|
||||
if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL))
|
||||
goto reschedule;
|
||||
|
||||
iwl_trans_start_hw(mvm->trans);
|
||||
temp = check_nic_temperature(mvm);
|
||||
iwl_trans_stop_device(mvm->trans);
|
||||
|
||||
iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL);
|
||||
|
||||
if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) {
|
||||
IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n");
|
||||
goto reschedule;
|
||||
|
|
|
@ -310,6 +310,16 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
|
|||
info->hw_queue != info->control.vif->cab_queue)))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used
|
||||
* in 2 different types of vifs, P2P & STATION. P2P uses the offchannel
|
||||
* queue. STATION (HS2.0) uses the auxiliary context of the FW,
|
||||
* and hence needs to be sent on the aux queue
|
||||
*/
|
||||
if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
|
||||
info->control.vif->type == NL80211_IFTYPE_STATION)
|
||||
IEEE80211_SKB_CB(skb)->hw_queue = mvm->aux_queue;
|
||||
|
||||
/*
|
||||
* If the interface on which frame is sent is the P2P_DEVICE
|
||||
* or an AP/GO interface use the broadcast station associated
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "iwl-drv.h"
|
||||
#include "iwl-trans.h"
|
||||
|
@ -1773,28 +1774,207 @@ static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
|
|||
return cmdlen;
|
||||
}
|
||||
|
||||
static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
|
||||
void *buf, u32 buflen)
|
||||
static const struct {
|
||||
u32 start, end;
|
||||
} iwl_prph_dump_addr[] = {
|
||||
{ .start = 0x00a00000, .end = 0x00a00000 },
|
||||
{ .start = 0x00a0000c, .end = 0x00a00024 },
|
||||
{ .start = 0x00a0002c, .end = 0x00a0003c },
|
||||
{ .start = 0x00a00410, .end = 0x00a00418 },
|
||||
{ .start = 0x00a00420, .end = 0x00a00420 },
|
||||
{ .start = 0x00a00428, .end = 0x00a00428 },
|
||||
{ .start = 0x00a00430, .end = 0x00a0043c },
|
||||
{ .start = 0x00a00444, .end = 0x00a00444 },
|
||||
{ .start = 0x00a004c0, .end = 0x00a004cc },
|
||||
{ .start = 0x00a004d8, .end = 0x00a004d8 },
|
||||
{ .start = 0x00a004e0, .end = 0x00a004f0 },
|
||||
{ .start = 0x00a00840, .end = 0x00a00840 },
|
||||
{ .start = 0x00a00850, .end = 0x00a00858 },
|
||||
{ .start = 0x00a01004, .end = 0x00a01008 },
|
||||
{ .start = 0x00a01010, .end = 0x00a01010 },
|
||||
{ .start = 0x00a01018, .end = 0x00a01018 },
|
||||
{ .start = 0x00a01024, .end = 0x00a01024 },
|
||||
{ .start = 0x00a0102c, .end = 0x00a01034 },
|
||||
{ .start = 0x00a0103c, .end = 0x00a01040 },
|
||||
{ .start = 0x00a01048, .end = 0x00a01094 },
|
||||
{ .start = 0x00a01c00, .end = 0x00a01c20 },
|
||||
{ .start = 0x00a01c58, .end = 0x00a01c58 },
|
||||
{ .start = 0x00a01c7c, .end = 0x00a01c7c },
|
||||
{ .start = 0x00a01c28, .end = 0x00a01c54 },
|
||||
{ .start = 0x00a01c5c, .end = 0x00a01c5c },
|
||||
{ .start = 0x00a01c84, .end = 0x00a01c84 },
|
||||
{ .start = 0x00a01ce0, .end = 0x00a01d0c },
|
||||
{ .start = 0x00a01d18, .end = 0x00a01d20 },
|
||||
{ .start = 0x00a01d2c, .end = 0x00a01d30 },
|
||||
{ .start = 0x00a01d40, .end = 0x00a01d5c },
|
||||
{ .start = 0x00a01d80, .end = 0x00a01d80 },
|
||||
{ .start = 0x00a01d98, .end = 0x00a01d98 },
|
||||
{ .start = 0x00a01dc0, .end = 0x00a01dfc },
|
||||
{ .start = 0x00a01e00, .end = 0x00a01e2c },
|
||||
{ .start = 0x00a01e40, .end = 0x00a01e60 },
|
||||
{ .start = 0x00a01e84, .end = 0x00a01e90 },
|
||||
{ .start = 0x00a01e9c, .end = 0x00a01ec4 },
|
||||
{ .start = 0x00a01ed0, .end = 0x00a01ed0 },
|
||||
{ .start = 0x00a01f00, .end = 0x00a01f14 },
|
||||
{ .start = 0x00a01f44, .end = 0x00a01f58 },
|
||||
{ .start = 0x00a01f80, .end = 0x00a01fa8 },
|
||||
{ .start = 0x00a01fb0, .end = 0x00a01fbc },
|
||||
{ .start = 0x00a01ff8, .end = 0x00a01ffc },
|
||||
{ .start = 0x00a02000, .end = 0x00a02048 },
|
||||
{ .start = 0x00a02068, .end = 0x00a020f0 },
|
||||
{ .start = 0x00a02100, .end = 0x00a02118 },
|
||||
{ .start = 0x00a02140, .end = 0x00a0214c },
|
||||
{ .start = 0x00a02168, .end = 0x00a0218c },
|
||||
{ .start = 0x00a021c0, .end = 0x00a021c0 },
|
||||
{ .start = 0x00a02400, .end = 0x00a02410 },
|
||||
{ .start = 0x00a02418, .end = 0x00a02420 },
|
||||
{ .start = 0x00a02428, .end = 0x00a0242c },
|
||||
{ .start = 0x00a02434, .end = 0x00a02434 },
|
||||
{ .start = 0x00a02440, .end = 0x00a02460 },
|
||||
{ .start = 0x00a02468, .end = 0x00a024b0 },
|
||||
{ .start = 0x00a024c8, .end = 0x00a024cc },
|
||||
{ .start = 0x00a02500, .end = 0x00a02504 },
|
||||
{ .start = 0x00a0250c, .end = 0x00a02510 },
|
||||
{ .start = 0x00a02540, .end = 0x00a02554 },
|
||||
{ .start = 0x00a02580, .end = 0x00a025f4 },
|
||||
{ .start = 0x00a02600, .end = 0x00a0260c },
|
||||
{ .start = 0x00a02648, .end = 0x00a02650 },
|
||||
{ .start = 0x00a02680, .end = 0x00a02680 },
|
||||
{ .start = 0x00a026c0, .end = 0x00a026d0 },
|
||||
{ .start = 0x00a02700, .end = 0x00a0270c },
|
||||
{ .start = 0x00a02804, .end = 0x00a02804 },
|
||||
{ .start = 0x00a02818, .end = 0x00a0281c },
|
||||
{ .start = 0x00a02c00, .end = 0x00a02db4 },
|
||||
{ .start = 0x00a02df4, .end = 0x00a02fb0 },
|
||||
{ .start = 0x00a03000, .end = 0x00a03014 },
|
||||
{ .start = 0x00a0301c, .end = 0x00a0302c },
|
||||
{ .start = 0x00a03034, .end = 0x00a03038 },
|
||||
{ .start = 0x00a03040, .end = 0x00a03048 },
|
||||
{ .start = 0x00a03060, .end = 0x00a03068 },
|
||||
{ .start = 0x00a03070, .end = 0x00a03074 },
|
||||
{ .start = 0x00a0307c, .end = 0x00a0307c },
|
||||
{ .start = 0x00a03080, .end = 0x00a03084 },
|
||||
{ .start = 0x00a0308c, .end = 0x00a03090 },
|
||||
{ .start = 0x00a03098, .end = 0x00a03098 },
|
||||
{ .start = 0x00a030a0, .end = 0x00a030a0 },
|
||||
{ .start = 0x00a030a8, .end = 0x00a030b4 },
|
||||
{ .start = 0x00a030bc, .end = 0x00a030bc },
|
||||
{ .start = 0x00a030c0, .end = 0x00a0312c },
|
||||
{ .start = 0x00a03c00, .end = 0x00a03c5c },
|
||||
{ .start = 0x00a04400, .end = 0x00a04454 },
|
||||
{ .start = 0x00a04460, .end = 0x00a04474 },
|
||||
{ .start = 0x00a044c0, .end = 0x00a044ec },
|
||||
{ .start = 0x00a04500, .end = 0x00a04504 },
|
||||
{ .start = 0x00a04510, .end = 0x00a04538 },
|
||||
{ .start = 0x00a04540, .end = 0x00a04548 },
|
||||
{ .start = 0x00a04560, .end = 0x00a0457c },
|
||||
{ .start = 0x00a04590, .end = 0x00a04598 },
|
||||
{ .start = 0x00a045c0, .end = 0x00a045f4 },
|
||||
};
|
||||
|
||||
static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans,
|
||||
struct iwl_fw_error_dump_data **data)
|
||||
{
|
||||
struct iwl_fw_error_dump_prph *prph;
|
||||
unsigned long flags;
|
||||
u32 prph_len = 0, i;
|
||||
|
||||
if (!iwl_trans_grab_nic_access(trans, false, &flags))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
|
||||
/* The range includes both boundaries */
|
||||
int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
|
||||
iwl_prph_dump_addr[i].start + 4;
|
||||
int reg;
|
||||
__le32 *val;
|
||||
|
||||
prph_len += sizeof(*data) + sizeof(*prph) +
|
||||
num_bytes_in_chunk;
|
||||
|
||||
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
|
||||
(*data)->len = cpu_to_le32(sizeof(*prph) +
|
||||
num_bytes_in_chunk);
|
||||
prph = (void *)(*data)->data;
|
||||
prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
|
||||
val = (void *)prph->data;
|
||||
|
||||
for (reg = iwl_prph_dump_addr[i].start;
|
||||
reg <= iwl_prph_dump_addr[i].end;
|
||||
reg += 4)
|
||||
*val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans,
|
||||
reg));
|
||||
*data = iwl_fw_error_next_data(*data);
|
||||
}
|
||||
|
||||
iwl_trans_release_nic_access(trans, &flags);
|
||||
|
||||
return prph_len;
|
||||
}
|
||||
|
||||
#define IWL_CSR_TO_DUMP (0x250)
|
||||
|
||||
static u32 iwl_trans_pcie_dump_csr(struct iwl_trans *trans,
|
||||
struct iwl_fw_error_dump_data **data)
|
||||
{
|
||||
u32 csr_len = sizeof(**data) + IWL_CSR_TO_DUMP;
|
||||
__le32 *val;
|
||||
int i;
|
||||
|
||||
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_CSR);
|
||||
(*data)->len = cpu_to_le32(IWL_CSR_TO_DUMP);
|
||||
val = (void *)(*data)->data;
|
||||
|
||||
for (i = 0; i < IWL_CSR_TO_DUMP; i += 4)
|
||||
*val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
|
||||
|
||||
*data = iwl_fw_error_next_data(*data);
|
||||
|
||||
return csr_len;
|
||||
}
|
||||
|
||||
static
|
||||
struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
struct iwl_fw_error_dump_data *data;
|
||||
struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue];
|
||||
struct iwl_fw_error_dump_txcmd *txcmd;
|
||||
struct iwl_trans_dump_data *dump_data;
|
||||
u32 len;
|
||||
int i, ptr;
|
||||
|
||||
len = sizeof(*data) +
|
||||
/* transport dump header */
|
||||
len = sizeof(*dump_data);
|
||||
|
||||
/* host commands */
|
||||
len += sizeof(*data) +
|
||||
cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE);
|
||||
|
||||
/* CSR registers */
|
||||
len += sizeof(*data) + IWL_CSR_TO_DUMP;
|
||||
|
||||
/* PRPH registers */
|
||||
for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
|
||||
/* The range includes both boundaries */
|
||||
int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
|
||||
iwl_prph_dump_addr[i].start + 4;
|
||||
|
||||
len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_prph) +
|
||||
num_bytes_in_chunk;
|
||||
}
|
||||
|
||||
/* FW monitor */
|
||||
if (trans_pcie->fw_mon_page)
|
||||
len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
|
||||
trans_pcie->fw_mon_size;
|
||||
|
||||
if (!buf)
|
||||
return len;
|
||||
dump_data = vzalloc(len);
|
||||
if (!dump_data)
|
||||
return NULL;
|
||||
|
||||
len = 0;
|
||||
data = buf;
|
||||
data = (void *)dump_data->data;
|
||||
data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
|
||||
txcmd = (void *)data->data;
|
||||
spin_lock_bh(&cmdq->lock);
|
||||
|
@ -1820,11 +2000,15 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
|
|||
|
||||
data->len = cpu_to_le32(len);
|
||||
len += sizeof(*data);
|
||||
data = iwl_fw_error_next_data(data);
|
||||
|
||||
len += iwl_trans_pcie_dump_prph(trans, &data);
|
||||
len += iwl_trans_pcie_dump_csr(trans, &data);
|
||||
/* data is already pointing to the next section */
|
||||
|
||||
if (trans_pcie->fw_mon_page) {
|
||||
struct iwl_fw_error_dump_fw_mon *fw_mon_data;
|
||||
|
||||
data = iwl_fw_error_next_data(data);
|
||||
data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
|
||||
data->len = cpu_to_le32(trans_pcie->fw_mon_size +
|
||||
sizeof(*fw_mon_data));
|
||||
|
@ -1852,7 +2036,9 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
|
|||
trans_pcie->fw_mon_size;
|
||||
}
|
||||
|
||||
return len;
|
||||
dump_data->len = len;
|
||||
|
||||
return dump_data;
|
||||
}
|
||||
#else
|
||||
static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
|
||||
|
|
|
@ -685,11 +685,16 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
|
|||
struct mac80211_hwsim_data *data = hw->priv;
|
||||
u64 now = mac80211_hwsim_get_tsf(hw, vif);
|
||||
u32 bcn_int = data->beacon_int;
|
||||
s64 delta = tsf - now;
|
||||
u64 delta = abs64(tsf - now);
|
||||
|
||||
data->tsf_offset += delta;
|
||||
/* adjust after beaconing with new timestamp at old TBTT */
|
||||
data->bcn_delta = do_div(delta, bcn_int);
|
||||
if (tsf > now) {
|
||||
data->tsf_offset += delta;
|
||||
data->bcn_delta = do_div(delta, bcn_int);
|
||||
} else {
|
||||
data->tsf_offset -= delta;
|
||||
data->bcn_delta = -do_div(delta, bcn_int);
|
||||
}
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
|
||||
|
|
|
@ -1954,6 +1954,7 @@ static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
|
|||
mmc_remove_host(target);
|
||||
/* 20ms delay is based on experiment with sdhci controller */
|
||||
mdelay(20);
|
||||
target->rescan_entered = 0; /* rescan non-removable cards */
|
||||
mmc_add_host(target);
|
||||
}
|
||||
|
||||
|
|
|
@ -1001,6 +1001,26 @@ struct ieee80211_vendor_ie {
|
|||
u8 oui_type;
|
||||
} __packed;
|
||||
|
||||
struct ieee80211_wmm_ac_param {
|
||||
u8 aci_aifsn; /* AIFSN, ACM, ACI */
|
||||
u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
|
||||
__le16 txop_limit;
|
||||
} __packed;
|
||||
|
||||
struct ieee80211_wmm_param_ie {
|
||||
u8 element_id; /* Element ID: 221 (0xdd); */
|
||||
u8 len; /* Length: 24 */
|
||||
/* required fields for WMM version 1 */
|
||||
u8 oui[3]; /* 00:50:f2 */
|
||||
u8 oui_type; /* 2 */
|
||||
u8 oui_subtype; /* 1 */
|
||||
u8 version; /* 1 for WMM version 1.0 */
|
||||
u8 qos_info; /* AP/STA specific QoS info */
|
||||
u8 reserved; /* 0 */
|
||||
/* AC_BE, AC_BK, AC_VI, AC_VO */
|
||||
struct ieee80211_wmm_ac_param ac[4];
|
||||
} __packed;
|
||||
|
||||
/* Control frames */
|
||||
struct ieee80211_rts {
|
||||
__le16 frame_control;
|
||||
|
|
|
@ -401,6 +401,9 @@ enum {
|
|||
/* The core spec defines 127 as the "not available" value */
|
||||
#define HCI_TX_POWER_INVALID 127
|
||||
|
||||
#define HCI_ROLE_MASTER 0x00
|
||||
#define HCI_ROLE_SLAVE 0x01
|
||||
|
||||
/* Extended Inquiry Response field types */
|
||||
#define EIR_FLAGS 0x01 /* flags */
|
||||
#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
|
||||
|
@ -1713,9 +1716,6 @@ struct hci_ev_sync_train_complete {
|
|||
|
||||
#define HCI_EV_SLAVE_PAGE_RESP_TIMEOUT 0x54
|
||||
|
||||
/* Low energy meta events */
|
||||
#define LE_CONN_ROLE_MASTER 0x00
|
||||
|
||||
#define HCI_EV_LE_CONN_COMPLETE 0x01
|
||||
struct hci_ev_le_conn_complete {
|
||||
__u8 status;
|
||||
|
|
|
@ -83,6 +83,7 @@ struct hci_conn_hash {
|
|||
unsigned int amp_num;
|
||||
unsigned int sco_num;
|
||||
unsigned int le_num;
|
||||
unsigned int le_num_slave;
|
||||
};
|
||||
|
||||
struct bdaddr_list {
|
||||
|
@ -371,6 +372,7 @@ struct hci_conn {
|
|||
__u16 state;
|
||||
__u8 mode;
|
||||
__u8 type;
|
||||
__u8 role;
|
||||
bool out;
|
||||
__u8 attempt;
|
||||
__u8 dev_class[3];
|
||||
|
@ -540,12 +542,12 @@ enum {
|
|||
HCI_CONN_POWER_SAVE,
|
||||
HCI_CONN_REMOTE_OOB,
|
||||
HCI_CONN_FLUSH_KEY,
|
||||
HCI_CONN_MASTER,
|
||||
HCI_CONN_ENCRYPT,
|
||||
HCI_CONN_AUTH,
|
||||
HCI_CONN_SECURE,
|
||||
HCI_CONN_FIPS,
|
||||
HCI_CONN_STK_ENCRYPT,
|
||||
HCI_CONN_AUTH_INITIATOR,
|
||||
};
|
||||
|
||||
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
|
||||
|
@ -575,6 +577,8 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
|
|||
break;
|
||||
case LE_LINK:
|
||||
h->le_num++;
|
||||
if (c->role == HCI_ROLE_SLAVE)
|
||||
h->le_num_slave++;
|
||||
break;
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
|
@ -599,6 +603,8 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
|
|||
break;
|
||||
case LE_LINK:
|
||||
h->le_num--;
|
||||
if (c->role == HCI_ROLE_SLAVE)
|
||||
h->le_num_slave--;
|
||||
break;
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
|
@ -695,7 +701,8 @@ void hci_disconnect(struct hci_conn *conn, __u8 reason);
|
|||
bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
|
||||
void hci_sco_setup(struct hci_conn *conn, __u8 status);
|
||||
|
||||
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
|
||||
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
u8 role);
|
||||
int hci_conn_del(struct hci_conn *conn);
|
||||
void hci_conn_hash_flush(struct hci_dev *hdev);
|
||||
void hci_conn_check_pending(struct hci_dev *hdev);
|
||||
|
@ -707,14 +714,15 @@ struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle);
|
|||
|
||||
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
u8 dst_type, u8 sec_level, u16 conn_timeout,
|
||||
bool master);
|
||||
u8 role);
|
||||
struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
u8 sec_level, u8 auth_type);
|
||||
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
__u16 setting);
|
||||
int hci_conn_check_link_mode(struct hci_conn *conn);
|
||||
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
|
||||
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type);
|
||||
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
|
||||
bool initiator);
|
||||
int hci_conn_change_link_key(struct hci_conn *conn);
|
||||
int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
|
||||
|
||||
|
@ -881,12 +889,12 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
bdaddr_t *bdaddr, u8 *val, u8 type,
|
||||
u8 pin_len, bool *persistent);
|
||||
struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
|
||||
bool master);
|
||||
u8 role);
|
||||
struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 addr_type, u8 type, u8 authenticated,
|
||||
u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand);
|
||||
struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 addr_type, bool master);
|
||||
u8 addr_type, u8 role);
|
||||
int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type);
|
||||
void hci_smp_ltks_clear(struct hci_dev *hdev);
|
||||
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
|
|
|
@ -905,7 +905,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||
bdaddr_t *dst, u8 dst_type);
|
||||
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len);
|
||||
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
|
||||
int l2cap_chan_check_security(struct l2cap_chan *chan);
|
||||
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
|
||||
void l2cap_chan_set_defaults(struct l2cap_chan *chan);
|
||||
int l2cap_ertm_init(struct l2cap_chan *chan);
|
||||
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
|
||||
|
|
|
@ -4552,6 +4552,40 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
|
|||
*/
|
||||
void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
|
||||
|
||||
/**
|
||||
* ieee80211_start_rx_ba_session_offl - start a Rx BA session
|
||||
*
|
||||
* Some device drivers may offload part of the Rx aggregation flow including
|
||||
* AddBa/DelBa negotiation but may otherwise be incapable of full Rx
|
||||
* reordering.
|
||||
*
|
||||
* Create structures responsible for reordering so device drivers may call here
|
||||
* when they complete AddBa negotiation.
|
||||
*
|
||||
* @vif: &struct ieee80211_vif pointer from the add_interface callback
|
||||
* @addr: station mac address
|
||||
* @tid: the rx tid
|
||||
*/
|
||||
void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
|
||||
const u8 *addr, u16 tid);
|
||||
|
||||
/**
|
||||
* ieee80211_stop_rx_ba_session_offl - stop a Rx BA session
|
||||
*
|
||||
* Some device drivers may offload part of the Rx aggregation flow including
|
||||
* AddBa/DelBa negotiation but may otherwise be incapable of full Rx
|
||||
* reordering.
|
||||
*
|
||||
* Destroy structures responsible for reordering so device drivers may call here
|
||||
* when they complete DelBa negotiation.
|
||||
*
|
||||
* @vif: &struct ieee80211_vif pointer from the add_interface callback
|
||||
* @addr: station mac address
|
||||
* @tid: the rx tid
|
||||
*/
|
||||
void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
|
||||
const u8 *addr, u16 tid);
|
||||
|
||||
/* Rate control API */
|
||||
|
||||
/**
|
||||
|
|
|
@ -113,8 +113,9 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
|
|||
{
|
||||
bdaddr_t *dst = &mgr->l2cap_conn->hcon->dst;
|
||||
struct hci_conn *hcon;
|
||||
u8 role = out ? HCI_ROLE_MASTER : HCI_ROLE_SLAVE;
|
||||
|
||||
hcon = hci_conn_add(hdev, AMP_LINK, dst);
|
||||
hcon = hci_conn_add(hdev, AMP_LINK, dst, role);
|
||||
if (!hcon)
|
||||
return NULL;
|
||||
|
||||
|
@ -125,7 +126,6 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
|
|||
hcon->handle = __next_handle(mgr);
|
||||
hcon->remote_id = remote_id;
|
||||
hcon->amp_mgr = amp_mgr_get(mgr);
|
||||
hcon->out = out;
|
||||
|
||||
return hcon;
|
||||
}
|
||||
|
@ -133,8 +133,8 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
|
|||
/* AMP crypto key generation interface */
|
||||
static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
|
||||
{
|
||||
int ret = 0;
|
||||
struct crypto_shash *tfm;
|
||||
int ret;
|
||||
|
||||
if (!ksize)
|
||||
return -EINVAL;
|
||||
|
|
|
@ -66,8 +66,7 @@ static void hci_acl_create_connection(struct hci_conn *conn)
|
|||
|
||||
conn->state = BT_CONNECT;
|
||||
conn->out = true;
|
||||
|
||||
set_bit(HCI_CONN_MASTER, &conn->flags);
|
||||
conn->role = HCI_ROLE_MASTER;
|
||||
|
||||
conn->attempt++;
|
||||
|
||||
|
@ -335,7 +334,7 @@ static void hci_conn_timeout(struct work_struct *work)
|
|||
* event handling and hci_clock_offset_evt function.
|
||||
*/
|
||||
if (conn->type == ACL_LINK &&
|
||||
test_bit(HCI_CONN_MASTER, &conn->flags)) {
|
||||
conn->role == HCI_ROLE_MASTER) {
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_read_clock_offset cp;
|
||||
|
||||
|
@ -422,13 +421,14 @@ static void le_conn_timeout(struct work_struct *work)
|
|||
hci_le_create_connection_cancel(conn);
|
||||
}
|
||||
|
||||
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
|
||||
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
u8 role)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s dst %pMR", hdev->name, dst);
|
||||
|
||||
conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL);
|
||||
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
|
@ -436,6 +436,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
|
|||
bacpy(&conn->src, &hdev->bdaddr);
|
||||
conn->hdev = hdev;
|
||||
conn->type = type;
|
||||
conn->role = role;
|
||||
conn->mode = HCI_CM_ACTIVE;
|
||||
conn->state = BT_OPEN;
|
||||
conn->auth_type = HCI_AT_GENERAL_BONDING;
|
||||
|
@ -448,6 +449,9 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
|
|||
set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
|
||||
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
|
||||
if (conn->role == HCI_ROLE_MASTER)
|
||||
conn->out = true;
|
||||
|
||||
switch (type) {
|
||||
case ACL_LINK:
|
||||
conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
|
||||
|
@ -698,7 +702,7 @@ static void hci_req_directed_advertising(struct hci_request *req,
|
|||
|
||||
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
u8 dst_type, u8 sec_level, u16 conn_timeout,
|
||||
bool master)
|
||||
u8 role)
|
||||
{
|
||||
struct hci_conn_params *params;
|
||||
struct hci_conn *conn;
|
||||
|
@ -747,7 +751,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
|||
dst_type = ADDR_LE_DEV_RANDOM;
|
||||
}
|
||||
|
||||
conn = hci_conn_add(hdev, LE_LINK, dst);
|
||||
conn = hci_conn_add(hdev, LE_LINK, dst, role);
|
||||
if (!conn)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -771,7 +775,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
|||
}
|
||||
|
||||
/* If requested to connect as slave use directed advertising */
|
||||
if (!master) {
|
||||
if (conn->role == HCI_ROLE_SLAVE) {
|
||||
/* If we're active scanning most controllers are unable
|
||||
* to initiate advertising. Simply reject the attempt.
|
||||
*/
|
||||
|
@ -786,9 +790,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
|||
goto create_conn;
|
||||
}
|
||||
|
||||
conn->out = true;
|
||||
set_bit(HCI_CONN_MASTER, &conn->flags);
|
||||
|
||||
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
|
||||
if (params) {
|
||||
conn->le_conn_min_interval = params->conn_min_interval;
|
||||
|
@ -833,11 +834,11 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
|
|||
struct hci_conn *acl;
|
||||
|
||||
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
|
||||
if (!acl) {
|
||||
acl = hci_conn_add(hdev, ACL_LINK, dst);
|
||||
acl = hci_conn_add(hdev, ACL_LINK, dst, HCI_ROLE_MASTER);
|
||||
if (!acl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
@ -866,7 +867,7 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
|||
|
||||
sco = hci_conn_hash_lookup_ba(hdev, type, dst);
|
||||
if (!sco) {
|
||||
sco = hci_conn_add(hdev, type, dst);
|
||||
sco = hci_conn_add(hdev, type, dst, HCI_ROLE_MASTER);
|
||||
if (!sco) {
|
||||
hci_conn_drop(acl);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
@ -972,7 +973,8 @@ static void hci_conn_encrypt(struct hci_conn *conn)
|
|||
}
|
||||
|
||||
/* Enable security */
|
||||
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
||||
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
|
||||
bool initiator)
|
||||
{
|
||||
BT_DBG("hcon %p", conn);
|
||||
|
||||
|
@ -1025,6 +1027,9 @@ auth:
|
|||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
|
||||
return 0;
|
||||
|
||||
if (initiator)
|
||||
set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
|
||||
|
||||
if (!hci_conn_auth(conn, sec_level, auth_type))
|
||||
return 0;
|
||||
|
||||
|
@ -1076,7 +1081,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
|
|||
{
|
||||
BT_DBG("hcon %p", conn);
|
||||
|
||||
if (!role && test_bit(HCI_CONN_MASTER, &conn->flags))
|
||||
if (role == conn->role)
|
||||
return 1;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) {
|
||||
|
@ -1151,7 +1156,7 @@ static u32 get_link_mode(struct hci_conn *conn)
|
|||
{
|
||||
u32 link_mode = 0;
|
||||
|
||||
if (test_bit(HCI_CONN_MASTER, &conn->flags))
|
||||
if (conn->role == HCI_ROLE_MASTER)
|
||||
link_mode |= HCI_LM_MASTER;
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
|
||||
|
@ -1277,7 +1282,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
|
|||
|
||||
BT_DBG("%s hcon %p", hdev->name, conn);
|
||||
|
||||
chan = kzalloc(sizeof(struct hci_chan), GFP_KERNEL);
|
||||
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
|
||||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -2088,7 +2088,7 @@ u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
|
|||
}
|
||||
|
||||
/* Entry not in the cache. Add new one. */
|
||||
ie = kzalloc(sizeof(struct inquiry_entry), GFP_KERNEL);
|
||||
ie = kzalloc(sizeof(*ie), GFP_KERNEL);
|
||||
if (!ie) {
|
||||
flags |= MGMT_DEV_FOUND_CONFIRM_NAME;
|
||||
goto done;
|
||||
|
@ -3121,13 +3121,16 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool ltk_type_master(u8 type)
|
||||
static u8 ltk_role(u8 type)
|
||||
{
|
||||
return (type == SMP_LTK);
|
||||
if (type == SMP_LTK)
|
||||
return HCI_ROLE_MASTER;
|
||||
|
||||
return HCI_ROLE_SLAVE;
|
||||
}
|
||||
|
||||
struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
|
||||
bool master)
|
||||
u8 role)
|
||||
{
|
||||
struct smp_ltk *k;
|
||||
|
||||
|
@ -3135,7 +3138,7 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
|
|||
if (k->ediv != ediv || k->rand != rand)
|
||||
continue;
|
||||
|
||||
if (ltk_type_master(k->type) != master)
|
||||
if (ltk_role(k->type) != role)
|
||||
continue;
|
||||
|
||||
return k;
|
||||
|
@ -3145,14 +3148,14 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
|
|||
}
|
||||
|
||||
struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 addr_type, bool master)
|
||||
u8 addr_type, u8 role)
|
||||
{
|
||||
struct smp_ltk *k;
|
||||
|
||||
list_for_each_entry(k, &hdev->long_term_keys, list)
|
||||
if (addr_type == k->bdaddr_type &&
|
||||
bacmp(bdaddr, &k->bdaddr) == 0 &&
|
||||
ltk_type_master(k->type) == master)
|
||||
ltk_role(k->type) == role)
|
||||
return k;
|
||||
|
||||
return NULL;
|
||||
|
@ -3247,9 +3250,9 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand)
|
||||
{
|
||||
struct smp_ltk *key, *old_key;
|
||||
bool master = ltk_type_master(type);
|
||||
u8 role = ltk_role(type);
|
||||
|
||||
old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, master);
|
||||
old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, role);
|
||||
if (old_key)
|
||||
key = old_key;
|
||||
else {
|
||||
|
@ -3489,7 +3492,7 @@ int hci_bdaddr_list_add(struct list_head *list, bdaddr_t *bdaddr, u8 type)
|
|||
if (hci_bdaddr_list_lookup(list, bdaddr, type))
|
||||
return -EEXIST;
|
||||
|
||||
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -3894,7 +3897,7 @@ struct hci_dev *hci_alloc_dev(void)
|
|||
{
|
||||
struct hci_dev *hdev;
|
||||
|
||||
hdev = kzalloc(sizeof(struct hci_dev), GFP_KERNEL);
|
||||
hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
|
||||
if (!hdev)
|
||||
return NULL;
|
||||
|
||||
|
@ -5462,8 +5465,7 @@ void hci_update_background_scan(struct hci_dev *hdev)
|
|||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags) &&
|
||||
list_empty(&hdev->pend_le_conns) &&
|
||||
if (list_empty(&hdev->pend_le_conns) &&
|
||||
list_empty(&hdev->pend_le_reports)) {
|
||||
/* If there is no pending LE connections or devices
|
||||
* to be scanned for, we should stop the background
|
||||
|
|
|
@ -101,12 +101,8 @@ static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
|
||||
if (conn) {
|
||||
if (rp->role)
|
||||
clear_bit(HCI_CONN_MASTER, &conn->flags);
|
||||
else
|
||||
set_bit(HCI_CONN_MASTER, &conn->flags);
|
||||
}
|
||||
if (conn)
|
||||
conn->role = rp->role;
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
@ -1418,11 +1414,9 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
|
|||
}
|
||||
} else {
|
||||
if (!conn) {
|
||||
conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr);
|
||||
if (conn) {
|
||||
conn->out = true;
|
||||
set_bit(HCI_CONN_MASTER, &conn->flags);
|
||||
} else
|
||||
conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr,
|
||||
HCI_ROLE_MASTER);
|
||||
if (!conn)
|
||||
BT_ERR("No memory for new connection");
|
||||
}
|
||||
}
|
||||
|
@ -1651,6 +1645,8 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
|
|||
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
|
||||
struct hci_cp_auth_requested auth_cp;
|
||||
|
||||
set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
|
||||
|
||||
auth_cp.handle = __cpu_to_le16(conn->handle);
|
||||
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED,
|
||||
sizeof(auth_cp), &auth_cp);
|
||||
|
@ -2135,18 +2131,17 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
return;
|
||||
}
|
||||
|
||||
if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) {
|
||||
if (hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr,
|
||||
BDADDR_BREDR)) {
|
||||
hci_reject_conn(hdev, &ev->bdaddr);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!hci_bdaddr_list_lookup(&hdev->whitelist, &ev->bdaddr,
|
||||
BDADDR_BREDR)) {
|
||||
hci_reject_conn(hdev, &ev->bdaddr);
|
||||
return;
|
||||
}
|
||||
if (hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr,
|
||||
BDADDR_BREDR)) {
|
||||
hci_reject_conn(hdev, &ev->bdaddr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags) &&
|
||||
!hci_bdaddr_list_lookup(&hdev->whitelist, &ev->bdaddr,
|
||||
BDADDR_BREDR)) {
|
||||
hci_reject_conn(hdev, &ev->bdaddr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Connection accepted */
|
||||
|
@ -2160,7 +2155,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type,
|
||||
&ev->bdaddr);
|
||||
if (!conn) {
|
||||
conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr);
|
||||
conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr,
|
||||
HCI_ROLE_SLAVE);
|
||||
if (!conn) {
|
||||
BT_ERR("No memory for new connection");
|
||||
hci_dev_unlock(hdev);
|
||||
|
@ -2393,6 +2389,9 @@ check_auth:
|
|||
|
||||
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
|
||||
struct hci_cp_auth_requested cp;
|
||||
|
||||
set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
|
||||
|
||||
cp.handle = __cpu_to_le16(conn->handle);
|
||||
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
|
||||
}
|
||||
|
@ -2924,12 +2923,8 @@ static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
||||
if (conn) {
|
||||
if (!ev->status) {
|
||||
if (ev->role)
|
||||
clear_bit(HCI_CONN_MASTER, &conn->flags);
|
||||
else
|
||||
set_bit(HCI_CONN_MASTER, &conn->flags);
|
||||
}
|
||||
if (!ev->status)
|
||||
conn->role = ev->role;
|
||||
|
||||
clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
|
||||
|
||||
|
@ -3123,10 +3118,11 @@ static void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_conn_drop(conn);
|
||||
}
|
||||
|
||||
if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags))
|
||||
if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags) &&
|
||||
!test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags)) {
|
||||
hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
|
||||
sizeof(ev->bdaddr), &ev->bdaddr);
|
||||
else if (test_bit(HCI_MGMT, &hdev->dev_flags)) {
|
||||
} else if (test_bit(HCI_MGMT, &hdev->dev_flags)) {
|
||||
u8 secure;
|
||||
|
||||
if (conn->pending_sec_level == BT_SECURITY_HIGH)
|
||||
|
@ -3652,7 +3648,11 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
if (!test_bit(HCI_MGMT, &hdev->dev_flags))
|
||||
goto unlock;
|
||||
|
||||
/* Allow pairing if we're pairable, the initiators of the
|
||||
* pairing or if the remote is not requesting bonding.
|
||||
*/
|
||||
if (test_bit(HCI_PAIRABLE, &hdev->dev_flags) ||
|
||||
test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags) ||
|
||||
(conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
|
||||
struct hci_cp_io_capability_reply cp;
|
||||
|
||||
|
@ -3668,7 +3668,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
* except for the no-bonding case.
|
||||
*/
|
||||
if (conn->io_capability != HCI_IO_NO_INPUT_OUTPUT &&
|
||||
cp.authentication != HCI_AT_NO_BONDING)
|
||||
conn->auth_type != HCI_AT_NO_BONDING)
|
||||
conn->auth_type |= 0x01;
|
||||
|
||||
cp.authentication = conn->auth_type;
|
||||
|
@ -3762,9 +3762,11 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
|
|||
/* If we're not the initiators request authorization to
|
||||
* proceed from user space (mgmt_user_confirm with
|
||||
* confirm_hint set to 1). The exception is if neither
|
||||
* side had MITM in which case we do auto-accept.
|
||||
* side had MITM or if the local IO capability is
|
||||
* NoInputNoOutput, in which case we do auto-accept
|
||||
*/
|
||||
if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) &&
|
||||
conn->io_capability != HCI_IO_NO_INPUT_OUTPUT &&
|
||||
(loc_mitm || rem_mitm)) {
|
||||
BT_DBG("Confirming auto-accept as acceptor");
|
||||
confirm_hint = 1;
|
||||
|
@ -3878,6 +3880,9 @@ static void hci_simple_pair_complete_evt(struct hci_dev *hdev,
|
|||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
/* Reset the authentication requirement to unknown */
|
||||
conn->remote_auth = 0xff;
|
||||
|
||||
/* To avoid duplicate auth_failed events to user space we check
|
||||
* the HCI_CONN_AUTH_PEND flag which will be set if we
|
||||
* initiated the authentication. A traditional auth_complete
|
||||
|
@ -4108,7 +4113,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
|
||||
if (!conn) {
|
||||
conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr);
|
||||
conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr, ev->role);
|
||||
if (!conn) {
|
||||
BT_ERR("No memory for new connection");
|
||||
goto unlock;
|
||||
|
@ -4116,11 +4121,6 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
conn->dst_type = ev->bdaddr_type;
|
||||
|
||||
if (ev->role == LE_CONN_ROLE_MASTER) {
|
||||
conn->out = true;
|
||||
set_bit(HCI_CONN_MASTER, &conn->flags);
|
||||
}
|
||||
|
||||
/* If we didn't have a hci_conn object previously
|
||||
* but we're in master role this must be something
|
||||
* initiated using a white list. Since white list based
|
||||
|
@ -4187,14 +4187,14 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
else
|
||||
addr_type = BDADDR_LE_RANDOM;
|
||||
|
||||
/* Drop the connection if he device is blocked */
|
||||
if (hci_bdaddr_list_lookup(&hdev->blacklist, &conn->dst, addr_type)) {
|
||||
hci_conn_drop(conn);
|
||||
if (ev->status) {
|
||||
hci_le_conn_failed(conn, ev->status);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (ev->status) {
|
||||
hci_le_conn_failed(conn, ev->status);
|
||||
/* Drop the connection if the device is blocked */
|
||||
if (hci_bdaddr_list_lookup(&hdev->blacklist, &conn->dst, addr_type)) {
|
||||
hci_conn_drop(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
|
@ -4260,6 +4260,12 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
|
|||
if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type))
|
||||
return;
|
||||
|
||||
/* Most controller will fail if we try to create new connections
|
||||
* while we have an existing one in slave role.
|
||||
*/
|
||||
if (hdev->conn_hash.le_num_slave > 0)
|
||||
return;
|
||||
|
||||
/* If we're connectable, always connect any ADV_DIRECT_IND event */
|
||||
if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) &&
|
||||
adv_type == LE_ADV_DIRECT_IND)
|
||||
|
@ -4272,9 +4278,8 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
|
|||
return;
|
||||
|
||||
connect:
|
||||
/* Request connection in master = true role */
|
||||
conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
|
||||
HCI_LE_AUTOCONN_TIMEOUT, true);
|
||||
HCI_LE_AUTOCONN_TIMEOUT, HCI_ROLE_MASTER);
|
||||
if (!IS_ERR(conn))
|
||||
return;
|
||||
|
||||
|
@ -4314,14 +4319,11 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
|||
* device found events.
|
||||
*/
|
||||
if (hdev->le_scan_type == LE_SCAN_PASSIVE) {
|
||||
struct hci_conn_params *param;
|
||||
|
||||
if (type == LE_ADV_DIRECT_IND)
|
||||
return;
|
||||
|
||||
param = hci_pend_le_action_lookup(&hdev->pend_le_reports,
|
||||
bdaddr, bdaddr_type);
|
||||
if (!param)
|
||||
if (!hci_pend_le_action_lookup(&hdev->pend_le_reports,
|
||||
bdaddr, bdaddr_type))
|
||||
return;
|
||||
|
||||
if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND)
|
||||
|
@ -4455,7 +4457,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
if (conn == NULL)
|
||||
goto not_found;
|
||||
|
||||
ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->out);
|
||||
ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->role);
|
||||
if (ltk == NULL)
|
||||
goto not_found;
|
||||
|
||||
|
@ -4530,7 +4532,7 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev,
|
|||
return send_conn_param_neg_reply(hdev, handle,
|
||||
HCI_ERROR_INVALID_LL_PARAMS);
|
||||
|
||||
if (test_bit(HCI_CONN_MASTER, &hcon->flags)) {
|
||||
if (hcon->role == HCI_ROLE_MASTER) {
|
||||
struct hci_conn_params *params;
|
||||
u8 store_hint;
|
||||
|
||||
|
|
|
@ -775,7 +775,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
|
|||
}
|
||||
|
||||
/* Service level security */
|
||||
int l2cap_chan_check_security(struct l2cap_chan *chan)
|
||||
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator)
|
||||
{
|
||||
struct l2cap_conn *conn = chan->conn;
|
||||
__u8 auth_type;
|
||||
|
@ -785,7 +785,8 @@ int l2cap_chan_check_security(struct l2cap_chan *chan)
|
|||
|
||||
auth_type = l2cap_get_auth_type(chan);
|
||||
|
||||
return hci_conn_security(conn->hcon, chan->sec_level, auth_type);
|
||||
return hci_conn_security(conn->hcon, chan->sec_level, auth_type,
|
||||
initiator);
|
||||
}
|
||||
|
||||
static u8 l2cap_get_ident(struct l2cap_conn *conn)
|
||||
|
@ -1278,7 +1279,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
|
|||
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
|
||||
return;
|
||||
|
||||
if (l2cap_chan_check_security(chan) &&
|
||||
if (l2cap_chan_check_security(chan, true) &&
|
||||
__l2cap_no_conn_pending(chan)) {
|
||||
l2cap_start_connection(chan);
|
||||
}
|
||||
|
@ -1357,7 +1358,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||
}
|
||||
|
||||
if (chan->state == BT_CONNECT) {
|
||||
if (!l2cap_chan_check_security(chan) ||
|
||||
if (!l2cap_chan_check_security(chan, true) ||
|
||||
!__l2cap_no_conn_pending(chan)) {
|
||||
l2cap_chan_unlock(chan);
|
||||
continue;
|
||||
|
@ -1379,7 +1380,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_chan_check_security(chan)) {
|
||||
if (l2cap_chan_check_security(chan, false)) {
|
||||
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
|
||||
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
|
||||
rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
|
||||
|
@ -1487,7 +1488,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
|||
* been configured for this connection. If not, then trigger
|
||||
* the connection update procedure.
|
||||
*/
|
||||
if (!test_bit(HCI_CONN_MASTER, &hcon->flags) &&
|
||||
if (hcon->role == HCI_ROLE_SLAVE &&
|
||||
(hcon->le_conn_interval < hcon->le_conn_min_interval ||
|
||||
hcon->le_conn_interval > hcon->le_conn_max_interval)) {
|
||||
struct l2cap_conn_param_update_req req;
|
||||
|
@ -3849,7 +3850,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
|
|||
chan->ident = cmd->ident;
|
||||
|
||||
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
|
||||
if (l2cap_chan_check_security(chan)) {
|
||||
if (l2cap_chan_check_security(chan, false)) {
|
||||
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
|
||||
l2cap_state_change(chan, BT_CONNECT2);
|
||||
result = L2CAP_CR_PEND;
|
||||
|
@ -5227,7 +5228,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
|
|||
u16 min, max, latency, to_multiplier;
|
||||
int err;
|
||||
|
||||
if (!test_bit(HCI_CONN_MASTER, &hcon->flags))
|
||||
if (hcon->role != HCI_ROLE_MASTER)
|
||||
return -EINVAL;
|
||||
|
||||
if (cmd_len != sizeof(struct l2cap_conn_param_update_req))
|
||||
|
@ -6984,7 +6985,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
|
|||
if (!hchan)
|
||||
return NULL;
|
||||
|
||||
conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
|
||||
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
|
||||
if (!conn) {
|
||||
hci_chan_del(hchan);
|
||||
return NULL;
|
||||
|
@ -7093,7 +7094,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||
break;
|
||||
/* fall through */
|
||||
default:
|
||||
err = -ENOTSUPP;
|
||||
err = -EOPNOTSUPP;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -7128,7 +7129,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||
chan->dcid = cid;
|
||||
|
||||
if (bdaddr_type_is_le(dst_type)) {
|
||||
bool master;
|
||||
u8 role;
|
||||
|
||||
/* Convert from L2CAP channel address type to HCI address type
|
||||
*/
|
||||
|
@ -7137,10 +7138,13 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||
else
|
||||
dst_type = ADDR_LE_DEV_RANDOM;
|
||||
|
||||
master = !test_bit(HCI_ADVERTISING, &hdev->dev_flags);
|
||||
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
|
||||
role = HCI_ROLE_SLAVE;
|
||||
else
|
||||
role = HCI_ROLE_MASTER;
|
||||
|
||||
hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level,
|
||||
HCI_LE_CONN_TIMEOUT, master);
|
||||
HCI_LE_CONN_TIMEOUT, role);
|
||||
} else {
|
||||
u8 auth_type = l2cap_get_auth_type(chan);
|
||||
hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type);
|
||||
|
@ -7188,7 +7192,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||
if (hcon->state == BT_CONNECTED) {
|
||||
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
||||
__clear_chan_timer(chan);
|
||||
if (l2cap_chan_check_security(chan))
|
||||
if (l2cap_chan_check_security(chan, true))
|
||||
l2cap_state_change(chan, BT_CONNECTED);
|
||||
} else
|
||||
l2cap_do_start(chan);
|
||||
|
|
|
@ -279,7 +279,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
|
|||
break;
|
||||
/* fall through */
|
||||
default:
|
||||
err = -ENOTSUPP;
|
||||
err = -EOPNOTSUPP;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -797,7 +797,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||
} else if ((sk->sk_state == BT_CONNECT2 &&
|
||||
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) ||
|
||||
sk->sk_state == BT_CONNECTED) {
|
||||
if (!l2cap_chan_check_security(chan))
|
||||
if (!l2cap_chan_check_security(chan, true))
|
||||
set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
|
||||
else
|
||||
sk->sk_state_change(sk);
|
||||
|
@ -1112,7 +1112,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
|
|||
l2cap_chan_close(chan, 0);
|
||||
lock_sock(sk);
|
||||
|
||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
|
||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
|
||||
!(current->flags & PF_EXITING))
|
||||
err = bt_sock_wait_state(sk, BT_CLOSED,
|
||||
sk->sk_lingertime);
|
||||
}
|
||||
|
|
|
@ -3154,9 +3154,9 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
*/
|
||||
hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type);
|
||||
|
||||
/* Request a connection with master = true role */
|
||||
conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type,
|
||||
sec_level, HCI_LE_CONN_TIMEOUT, true);
|
||||
sec_level, HCI_LE_CONN_TIMEOUT,
|
||||
HCI_ROLE_MASTER);
|
||||
}
|
||||
|
||||
if (IS_ERR(conn)) {
|
||||
|
@ -3202,7 +3202,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
cmd->user_data = conn;
|
||||
|
||||
if (conn->state == BT_CONNECTED &&
|
||||
hci_conn_security(conn, sec_level, auth_type))
|
||||
hci_conn_security(conn, sec_level, auth_type, true))
|
||||
pairing_complete(cmd, 0);
|
||||
|
||||
err = 0;
|
||||
|
|
|
@ -227,7 +227,8 @@ static int rfcomm_check_security(struct rfcomm_dlc *d)
|
|||
break;
|
||||
}
|
||||
|
||||
return hci_conn_security(conn->hcon, d->sec_level, auth_type);
|
||||
return hci_conn_security(conn->hcon, d->sec_level, auth_type,
|
||||
d->out);
|
||||
}
|
||||
|
||||
static void rfcomm_session_timeout(unsigned long arg)
|
||||
|
|
|
@ -918,7 +918,8 @@ static int rfcomm_sock_shutdown(struct socket *sock, int how)
|
|||
sk->sk_shutdown = SHUTDOWN_MASK;
|
||||
__rfcomm_sock_close(sk);
|
||||
|
||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
|
||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
|
||||
!(current->flags & PF_EXITING))
|
||||
err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
|
||||
}
|
||||
release_sock(sk);
|
||||
|
|
|
@ -970,7 +970,8 @@ static int sco_sock_shutdown(struct socket *sock, int how)
|
|||
sco_sock_clear_timer(sk);
|
||||
__sco_sock_close(sk);
|
||||
|
||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
|
||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
|
||||
!(current->flags & PF_EXITING))
|
||||
err = bt_sock_wait_state(sk, BT_CLOSED,
|
||||
sk->sk_lingertime);
|
||||
}
|
||||
|
@ -990,7 +991,8 @@ static int sco_sock_release(struct socket *sock)
|
|||
|
||||
sco_sock_close(sk);
|
||||
|
||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) {
|
||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
|
||||
!(current->flags & PF_EXITING)) {
|
||||
lock_sock(sk);
|
||||
err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
|
||||
release_sock(sk);
|
||||
|
|
|
@ -431,6 +431,10 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
|
|||
if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
|
||||
method = JUST_WORKS;
|
||||
|
||||
/* Don't bother user space with no IO capabilities */
|
||||
if (method == JUST_CFM && hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
|
||||
method = JUST_WORKS;
|
||||
|
||||
/* If Just Works, Continue with Zero TK */
|
||||
if (method == JUST_WORKS) {
|
||||
set_bit(SMP_FLAG_TK_VALID, &smp->flags);
|
||||
|
@ -445,7 +449,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
|
|||
* Confirms and the slave Enters the passkey.
|
||||
*/
|
||||
if (method == OVERLAP) {
|
||||
if (test_bit(HCI_CONN_MASTER, &hcon->flags))
|
||||
if (hcon->role == HCI_ROLE_MASTER)
|
||||
method = CFM_PASSKEY;
|
||||
else
|
||||
method = REQ_PASSKEY;
|
||||
|
@ -686,7 +690,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
if (skb->len < sizeof(*req))
|
||||
return SMP_INVALID_PARAMS;
|
||||
|
||||
if (test_bit(HCI_CONN_MASTER, &conn->hcon->flags))
|
||||
if (conn->hcon->role != HCI_ROLE_SLAVE)
|
||||
return SMP_CMD_NOTSUPP;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
|
||||
|
@ -755,7 +759,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
if (skb->len < sizeof(*rsp))
|
||||
return SMP_INVALID_PARAMS;
|
||||
|
||||
if (!test_bit(HCI_CONN_MASTER, &conn->hcon->flags))
|
||||
if (conn->hcon->role != HCI_ROLE_MASTER)
|
||||
return SMP_CMD_NOTSUPP;
|
||||
|
||||
skb_pull(skb, sizeof(*rsp));
|
||||
|
@ -849,7 +853,7 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
|
|||
struct hci_conn *hcon = conn->hcon;
|
||||
|
||||
key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
|
||||
hcon->out);
|
||||
hcon->role);
|
||||
if (!key)
|
||||
return false;
|
||||
|
||||
|
@ -881,7 +885,7 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
|
|||
*/
|
||||
if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
|
||||
hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
|
||||
hcon->out))
|
||||
hcon->role))
|
||||
return false;
|
||||
|
||||
if (hcon->sec_level >= sec_level)
|
||||
|
@ -903,7 +907,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
if (skb->len < sizeof(*rp))
|
||||
return SMP_INVALID_PARAMS;
|
||||
|
||||
if (!test_bit(HCI_CONN_MASTER, &conn->hcon->flags))
|
||||
if (hcon->role != HCI_ROLE_MASTER)
|
||||
return SMP_CMD_NOTSUPP;
|
||||
|
||||
sec_level = authreq_to_seclevel(rp->auth_req);
|
||||
|
@ -961,7 +965,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
|
|||
if (sec_level > hcon->pending_sec_level)
|
||||
hcon->pending_sec_level = sec_level;
|
||||
|
||||
if (test_bit(HCI_CONN_MASTER, &hcon->flags))
|
||||
if (hcon->role == HCI_ROLE_MASTER)
|
||||
if (smp_ltk_encrypt(conn, hcon->pending_sec_level))
|
||||
return 0;
|
||||
|
||||
|
@ -981,7 +985,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
|
|||
hcon->pending_sec_level > BT_SECURITY_MEDIUM)
|
||||
authreq |= SMP_AUTH_MITM;
|
||||
|
||||
if (test_bit(HCI_CONN_MASTER, &hcon->flags)) {
|
||||
if (hcon->role == HCI_ROLE_MASTER) {
|
||||
struct smp_cmd_pairing cp;
|
||||
|
||||
build_pairing_cmd(conn, &cp, NULL, authreq);
|
||||
|
@ -1185,7 +1189,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) {
|
||||
err = -ENOTSUPP;
|
||||
err = -EOPNOTSUPP;
|
||||
reason = SMP_PAIRING_NOTSUPP;
|
||||
goto done;
|
||||
}
|
||||
|
@ -1203,7 +1207,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
!conn->smp_chan) {
|
||||
BT_ERR("Unexpected SMP command 0x%02x. Disconnecting.", code);
|
||||
kfree_skb(skb);
|
||||
return -ENOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
|
|
|
@ -52,7 +52,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h)
|
|||
del_timer_sync(&tid_rx->reorder_timer);
|
||||
|
||||
for (i = 0; i < tid_rx->buf_size; i++)
|
||||
dev_kfree_skb(tid_rx->reorder_buf[i]);
|
||||
__skb_queue_purge(&tid_rx->reorder_buf[i]);
|
||||
kfree(tid_rx->reorder_buf);
|
||||
kfree(tid_rx->reorder_time);
|
||||
kfree(tid_rx);
|
||||
|
@ -224,28 +224,15 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
|
|||
ieee80211_tx_skb(sdata, skb);
|
||||
}
|
||||
|
||||
void ieee80211_process_addba_request(struct ieee80211_local *local,
|
||||
struct sta_info *sta,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
void __ieee80211_start_rx_ba_session(struct sta_info *sta,
|
||||
u8 dialog_token, u16 timeout,
|
||||
u16 start_seq_num, u16 ba_policy, u16 tid,
|
||||
u16 buf_size, bool tx)
|
||||
{
|
||||
struct ieee80211_local *local = sta->sdata->local;
|
||||
struct tid_ampdu_rx *tid_agg_rx;
|
||||
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
|
||||
u8 dialog_token;
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
/* extract session parameters from addba request frame */
|
||||
dialog_token = mgmt->u.action.u.addba_req.dialog_token;
|
||||
timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
|
||||
start_seq_num =
|
||||
le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
|
||||
|
||||
capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
|
||||
ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
|
||||
tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
|
||||
buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
|
||||
|
||||
status = WLAN_STATUS_REQUEST_DECLINED;
|
||||
int i, ret = -EOPNOTSUPP;
|
||||
u16 status = WLAN_STATUS_REQUEST_DECLINED;
|
||||
|
||||
if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
|
||||
ht_dbg(sta->sdata,
|
||||
|
@ -264,7 +251,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
|||
status = WLAN_STATUS_INVALID_QOS_PARAM;
|
||||
ht_dbg_ratelimited(sta->sdata,
|
||||
"AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
|
||||
mgmt->sa, tid, ba_policy, buf_size);
|
||||
sta->sta.addr, tid, ba_policy, buf_size);
|
||||
goto end_no_lock;
|
||||
}
|
||||
/* determine default buffer size */
|
||||
|
@ -281,7 +268,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
|||
if (sta->ampdu_mlme.tid_rx[tid]) {
|
||||
ht_dbg_ratelimited(sta->sdata,
|
||||
"unexpected AddBA Req from %pM on tid %u\n",
|
||||
mgmt->sa, tid);
|
||||
sta->sta.addr, tid);
|
||||
|
||||
/* delete existing Rx BA session on the same tid */
|
||||
___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
|
||||
|
@ -308,7 +295,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
|||
|
||||
/* prepare reordering buffer */
|
||||
tid_agg_rx->reorder_buf =
|
||||
kcalloc(buf_size, sizeof(struct sk_buff *), GFP_KERNEL);
|
||||
kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL);
|
||||
tid_agg_rx->reorder_time =
|
||||
kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
|
||||
if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
|
||||
|
@ -318,6 +305,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
|||
goto end;
|
||||
}
|
||||
|
||||
for (i = 0; i < buf_size; i++)
|
||||
__skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
|
||||
|
||||
ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
|
||||
&sta->sta, tid, &start_seq_num, 0);
|
||||
ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
|
||||
|
@ -350,6 +340,74 @@ end:
|
|||
mutex_unlock(&sta->ampdu_mlme.mtx);
|
||||
|
||||
end_no_lock:
|
||||
ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
|
||||
dialog_token, status, 1, buf_size, timeout);
|
||||
if (tx)
|
||||
ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
|
||||
dialog_token, status, 1, buf_size,
|
||||
timeout);
|
||||
}
|
||||
|
||||
void ieee80211_process_addba_request(struct ieee80211_local *local,
|
||||
struct sta_info *sta,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
{
|
||||
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
|
||||
u8 dialog_token;
|
||||
|
||||
/* extract session parameters from addba request frame */
|
||||
dialog_token = mgmt->u.action.u.addba_req.dialog_token;
|
||||
timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
|
||||
start_seq_num =
|
||||
le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
|
||||
|
||||
capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
|
||||
ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
|
||||
tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
|
||||
buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
|
||||
|
||||
__ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
|
||||
start_seq_num, ba_policy, tid,
|
||||
buf_size, true);
|
||||
}
|
||||
|
||||
void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
|
||||
const u8 *addr, u16 tid)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_rx_agg *rx_agg;
|
||||
struct sk_buff *skb = dev_alloc_skb(0);
|
||||
|
||||
if (unlikely(!skb))
|
||||
return;
|
||||
|
||||
rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
|
||||
memcpy(&rx_agg->addr, addr, ETH_ALEN);
|
||||
rx_agg->tid = tid;
|
||||
|
||||
skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_START;
|
||||
skb_queue_tail(&sdata->skb_queue, skb);
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_start_rx_ba_session_offl);
|
||||
|
||||
void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
|
||||
const u8 *addr, u16 tid)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_rx_agg *rx_agg;
|
||||
struct sk_buff *skb = dev_alloc_skb(0);
|
||||
|
||||
if (unlikely(!skb))
|
||||
return;
|
||||
|
||||
rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
|
||||
memcpy(&rx_agg->addr, addr, ETH_ALEN);
|
||||
rx_agg->tid = tid;
|
||||
|
||||
skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_STOP;
|
||||
skb_queue_tail(&sdata->skb_queue, skb);
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_stop_rx_ba_session_offl);
|
||||
|
|
|
@ -66,7 +66,7 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
|
|||
static struct ieee80211_chanctx *
|
||||
ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_local *local __maybe_unused = sdata->local;
|
||||
struct ieee80211_chanctx_conf *conf;
|
||||
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
|
|
|
@ -150,13 +150,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
/*
|
||||
* If user has specified capability over-rides, take care
|
||||
* of that if the station we're setting up is the AP that
|
||||
* of that if the station we're setting up is the AP or TDLS peer that
|
||||
* we advertised a restricted capability set to. Override
|
||||
* our own capabilities and then use those below.
|
||||
*/
|
||||
if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
|
||||
sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
|
||||
!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION ||
|
||||
sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
ieee80211_apply_htcap_overrides(sdata, &own_cap);
|
||||
|
||||
/*
|
||||
|
@ -228,6 +227,9 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
|
||||
ht_cap.mcs.rx_mask[32/8] |= 1;
|
||||
|
||||
/* set Rx highest rate */
|
||||
ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
|
||||
|
||||
apply:
|
||||
changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
|
||||
|
||||
|
|
|
@ -189,17 +189,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
|
|||
chandef, 0);
|
||||
}
|
||||
|
||||
if (local->hw.queues >= IEEE80211_NUM_ACS) {
|
||||
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*pos++ = 7; /* len */
|
||||
*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
|
||||
*pos++ = 0x50;
|
||||
*pos++ = 0xf2;
|
||||
*pos++ = 2; /* WME */
|
||||
*pos++ = 0; /* WME info */
|
||||
*pos++ = 1; /* WME ver */
|
||||
*pos++ = 0; /* U-APSD no in use */
|
||||
}
|
||||
if (local->hw.queues >= IEEE80211_NUM_ACS)
|
||||
pos = ieee80211_add_wmm_info_ie(pos, 0); /* U-APSD not in use */
|
||||
|
||||
presp->head_len = pos - presp->head;
|
||||
if (WARN_ON(presp->head_len > frame_len))
|
||||
|
|
|
@ -345,7 +345,6 @@ enum ieee80211_sta_flags {
|
|||
IEEE80211_STA_CONNECTION_POLL = BIT(1),
|
||||
IEEE80211_STA_CONTROL_PORT = BIT(2),
|
||||
IEEE80211_STA_DISABLE_HT = BIT(4),
|
||||
IEEE80211_STA_CSA_RECEIVED = BIT(5),
|
||||
IEEE80211_STA_MFP_ENABLED = BIT(6),
|
||||
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
|
||||
IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
|
||||
|
@ -503,6 +502,9 @@ struct ieee80211_if_managed {
|
|||
struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
|
||||
struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
|
||||
struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
|
||||
|
||||
u8 tdls_peer[ETH_ALEN] __aligned(2);
|
||||
struct delayed_work tdls_peer_del_work;
|
||||
};
|
||||
|
||||
struct ieee80211_if_ibss {
|
||||
|
@ -815,9 +817,6 @@ struct ieee80211_sub_if_data {
|
|||
bool radar_required;
|
||||
struct delayed_work dfs_cac_timer_work;
|
||||
|
||||
u8 tdls_peer[ETH_ALEN] __aligned(2);
|
||||
struct delayed_work tdls_peer_del_work;
|
||||
|
||||
/*
|
||||
* AP this belongs to: self in AP mode and
|
||||
* corresponding AP in VLAN mode, NULL for
|
||||
|
@ -926,10 +925,17 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif)
|
|||
return shift;
|
||||
}
|
||||
|
||||
struct ieee80211_rx_agg {
|
||||
u8 addr[ETH_ALEN];
|
||||
u16 tid;
|
||||
};
|
||||
|
||||
enum sdata_queue_type {
|
||||
IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0,
|
||||
IEEE80211_SDATA_QUEUE_AGG_START = 1,
|
||||
IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
|
||||
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
|
||||
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -1578,6 +1584,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
|
|||
u16 initiator, u16 reason, bool stop);
|
||||
void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
|
||||
u16 initiator, u16 reason, bool stop);
|
||||
void __ieee80211_start_rx_ba_session(struct sta_info *sta,
|
||||
u8 dialog_token, u16 timeout,
|
||||
u16 start_seq_num, u16 ba_policy, u16 tid,
|
||||
u16 buf_size, bool tx);
|
||||
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
|
||||
enum ieee80211_agg_stop_reason reason);
|
||||
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
|
||||
|
@ -1730,6 +1740,21 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
|
|||
ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
|
||||
}
|
||||
|
||||
static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
|
||||
{
|
||||
struct sk_buff *tail = skb_peek_tail(frames);
|
||||
struct ieee80211_rx_status *status;
|
||||
|
||||
if (!tail)
|
||||
return false;
|
||||
|
||||
status = IEEE80211_SKB_RXCB(tail);
|
||||
if (status->flag & RX_FLAG_AMSDU_MORE)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
|
||||
void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
|
||||
void ieee80211_dynamic_ps_timer(unsigned long data);
|
||||
|
@ -1824,6 +1849,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
|
|||
int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, bool need_basic,
|
||||
enum ieee80211_band band);
|
||||
u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
|
||||
|
||||
/* channel management */
|
||||
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
|
||||
|
|
|
@ -1140,6 +1140,7 @@ static void ieee80211_iface_work(struct work_struct *work)
|
|||
struct sk_buff *skb;
|
||||
struct sta_info *sta;
|
||||
struct ieee80211_ra_tid *ra_tid;
|
||||
struct ieee80211_rx_agg *rx_agg;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
return;
|
||||
|
@ -1167,6 +1168,34 @@ static void ieee80211_iface_work(struct work_struct *work)
|
|||
ra_tid = (void *)&skb->cb;
|
||||
ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra,
|
||||
ra_tid->tid);
|
||||
} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_START) {
|
||||
rx_agg = (void *)&skb->cb;
|
||||
mutex_lock(&local->sta_mtx);
|
||||
sta = sta_info_get_bss(sdata, rx_agg->addr);
|
||||
if (sta) {
|
||||
u16 last_seq;
|
||||
|
||||
last_seq = le16_to_cpu(
|
||||
sta->last_seq_ctrl[rx_agg->tid]);
|
||||
|
||||
__ieee80211_start_rx_ba_session(sta,
|
||||
0, 0,
|
||||
ieee80211_sn_inc(last_seq),
|
||||
1, rx_agg->tid,
|
||||
IEEE80211_MAX_AMPDU_BUF,
|
||||
false);
|
||||
}
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) {
|
||||
rx_agg = (void *)&skb->cb;
|
||||
mutex_lock(&local->sta_mtx);
|
||||
sta = sta_info_get_bss(sdata, rx_agg->addr);
|
||||
if (sta)
|
||||
__ieee80211_stop_rx_ba_session(sta,
|
||||
rx_agg->tid,
|
||||
WLAN_BACK_RECIPIENT, 0,
|
||||
false);
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
} else if (ieee80211_is_action(mgmt->frame_control) &&
|
||||
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
|
||||
int len = skb->len;
|
||||
|
@ -1672,8 +1701,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
|||
ieee80211_dfs_cac_timer_work);
|
||||
INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
|
||||
ieee80211_delayed_tailroom_dec);
|
||||
INIT_DELAYED_WORK(&sdata->tdls_peer_del_work,
|
||||
ieee80211_tdls_peer_del_work);
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
|
|
@ -482,9 +482,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
|
|||
int idx, ret;
|
||||
bool pairwise;
|
||||
|
||||
if (WARN_ON(!sdata || !key))
|
||||
return -EINVAL;
|
||||
|
||||
pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
|
||||
idx = key->conf.keyidx;
|
||||
key->local = sdata->local;
|
||||
|
|
|
@ -830,16 +830,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
|||
qos_info = 0;
|
||||
}
|
||||
|
||||
pos = skb_put(skb, 9);
|
||||
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*pos++ = 7; /* len */
|
||||
*pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
|
||||
*pos++ = 0x50;
|
||||
*pos++ = 0xf2;
|
||||
*pos++ = 2; /* WME */
|
||||
*pos++ = 0; /* WME info */
|
||||
*pos++ = 1; /* WME ver */
|
||||
*pos++ = qos_info;
|
||||
pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
|
||||
}
|
||||
|
||||
/* add any remaining custom (i.e. vendor specific here) IEs */
|
||||
|
@ -1005,8 +996,6 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
|||
sdata->csa_block_tx = false;
|
||||
}
|
||||
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
|
||||
ieee80211_sta_reset_beacon_monitor(sdata);
|
||||
ieee80211_sta_reset_conn_monitor(sdata);
|
||||
|
||||
|
@ -1064,7 +1053,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||
return;
|
||||
|
||||
/* disregard subsequent announcements if we are already processing */
|
||||
if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
|
||||
if (sdata->vif.csa_active)
|
||||
return;
|
||||
|
||||
current_band = cbss->channel->band;
|
||||
|
@ -1091,8 +1080,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||
return;
|
||||
}
|
||||
|
||||
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||
|
@ -2108,8 +2095,6 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
|||
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
||||
true, frame_buf);
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
sdata->vif.csa_active = false;
|
||||
if (sdata->csa_block_tx) {
|
||||
|
@ -3722,6 +3707,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
|||
INIT_WORK(&ifmgd->csa_connection_drop_work,
|
||||
ieee80211_csa_connection_drop_work);
|
||||
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
|
||||
INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
|
||||
ieee80211_tdls_peer_del_work);
|
||||
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
|
||||
(unsigned long) sdata);
|
||||
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
|
||||
|
@ -4585,6 +4572,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
|
|||
cancel_work_sync(&ifmgd->request_smps_work);
|
||||
cancel_work_sync(&ifmgd->csa_connection_drop_work);
|
||||
cancel_work_sync(&ifmgd->chswitch_work);
|
||||
cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work);
|
||||
|
||||
sdata_lock(sdata);
|
||||
if (ifmgd->assoc_data) {
|
||||
|
|
|
@ -688,20 +688,27 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
|
|||
int index,
|
||||
struct sk_buff_head *frames)
|
||||
{
|
||||
struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
|
||||
struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index];
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_rx_status *status;
|
||||
|
||||
lockdep_assert_held(&tid_agg_rx->reorder_lock);
|
||||
|
||||
if (!skb)
|
||||
if (skb_queue_empty(skb_list))
|
||||
goto no_frame;
|
||||
|
||||
/* release the frame from the reorder ring buffer */
|
||||
if (!ieee80211_rx_reorder_ready(skb_list)) {
|
||||
__skb_queue_purge(skb_list);
|
||||
goto no_frame;
|
||||
}
|
||||
|
||||
/* release frames from the reorder ring buffer */
|
||||
tid_agg_rx->stored_mpdu_num--;
|
||||
tid_agg_rx->reorder_buf[index] = NULL;
|
||||
status = IEEE80211_SKB_RXCB(skb);
|
||||
status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
|
||||
__skb_queue_tail(frames, skb);
|
||||
while ((skb = __skb_dequeue(skb_list))) {
|
||||
status = IEEE80211_SKB_RXCB(skb);
|
||||
status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
|
||||
__skb_queue_tail(frames, skb);
|
||||
}
|
||||
|
||||
no_frame:
|
||||
tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
|
||||
|
@ -738,13 +745,13 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
|
|||
struct tid_ampdu_rx *tid_agg_rx,
|
||||
struct sk_buff_head *frames)
|
||||
{
|
||||
int index, j;
|
||||
int index, i, j;
|
||||
|
||||
lockdep_assert_held(&tid_agg_rx->reorder_lock);
|
||||
|
||||
/* release the buffer until next missing frame */
|
||||
index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
|
||||
if (!tid_agg_rx->reorder_buf[index] &&
|
||||
if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) &&
|
||||
tid_agg_rx->stored_mpdu_num) {
|
||||
/*
|
||||
* No buffers ready to be released, but check whether any
|
||||
|
@ -753,7 +760,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
|
|||
int skipped = 1;
|
||||
for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
|
||||
j = (j + 1) % tid_agg_rx->buf_size) {
|
||||
if (!tid_agg_rx->reorder_buf[j]) {
|
||||
if (!ieee80211_rx_reorder_ready(
|
||||
&tid_agg_rx->reorder_buf[j])) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
@ -762,6 +770,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
|
|||
HT_RX_REORDER_BUF_TIMEOUT))
|
||||
goto set_release_timer;
|
||||
|
||||
/* don't leave incomplete A-MSDUs around */
|
||||
for (i = (index + 1) % tid_agg_rx->buf_size; i != j;
|
||||
i = (i + 1) % tid_agg_rx->buf_size)
|
||||
__skb_queue_purge(&tid_agg_rx->reorder_buf[i]);
|
||||
|
||||
ht_dbg_ratelimited(sdata,
|
||||
"release an RX reorder frame due to timeout on earlier frames\n");
|
||||
ieee80211_release_reorder_frame(sdata, tid_agg_rx, j,
|
||||
|
@ -775,7 +788,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
|
|||
skipped) & IEEE80211_SN_MASK;
|
||||
skipped = 0;
|
||||
}
|
||||
} else while (tid_agg_rx->reorder_buf[index]) {
|
||||
} else while (ieee80211_rx_reorder_ready(
|
||||
&tid_agg_rx->reorder_buf[index])) {
|
||||
ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
|
||||
frames);
|
||||
index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
|
||||
|
@ -786,7 +800,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
for (; j != (index - 1) % tid_agg_rx->buf_size;
|
||||
j = (j + 1) % tid_agg_rx->buf_size) {
|
||||
if (tid_agg_rx->reorder_buf[j])
|
||||
if (ieee80211_rx_reorder_ready(
|
||||
&tid_agg_rx->reorder_buf[j]))
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -811,6 +826,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
|
|||
struct sk_buff_head *frames)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
||||
u16 sc = le16_to_cpu(hdr->seq_ctrl);
|
||||
u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
|
||||
u16 head_seq_num, buf_size;
|
||||
|
@ -845,7 +861,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
|
|||
index = mpdu_seq_num % tid_agg_rx->buf_size;
|
||||
|
||||
/* check if we already stored this frame */
|
||||
if (tid_agg_rx->reorder_buf[index]) {
|
||||
if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) {
|
||||
dev_kfree_skb(skb);
|
||||
goto out;
|
||||
}
|
||||
|
@ -858,17 +874,20 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
|
|||
*/
|
||||
if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
|
||||
tid_agg_rx->stored_mpdu_num == 0) {
|
||||
tid_agg_rx->head_seq_num =
|
||||
ieee80211_sn_inc(tid_agg_rx->head_seq_num);
|
||||
if (!(status->flag & RX_FLAG_AMSDU_MORE))
|
||||
tid_agg_rx->head_seq_num =
|
||||
ieee80211_sn_inc(tid_agg_rx->head_seq_num);
|
||||
ret = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* put the frame in the reordering buffer */
|
||||
tid_agg_rx->reorder_buf[index] = skb;
|
||||
tid_agg_rx->reorder_time[index] = jiffies;
|
||||
tid_agg_rx->stored_mpdu_num++;
|
||||
ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
|
||||
__skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb);
|
||||
if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
|
||||
tid_agg_rx->reorder_time[index] = jiffies;
|
||||
tid_agg_rx->stored_mpdu_num++;
|
||||
ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&tid_agg_rx->reorder_lock);
|
||||
|
@ -3129,6 +3148,14 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
|
|||
if (!ieee80211_is_beacon(hdr->frame_control))
|
||||
return false;
|
||||
status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
|
||||
} else if (!ieee80211_has_tods(hdr->frame_control)) {
|
||||
/* ignore data frames to TDLS-peers */
|
||||
if (ieee80211_is_data(hdr->frame_control))
|
||||
return false;
|
||||
/* ignore action frames to TDLS-peers */
|
||||
if (ieee80211_is_action(hdr->frame_control) &&
|
||||
!ether_addr_equal(bssid, hdr->addr1))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_WDS:
|
||||
|
|
|
@ -47,6 +47,8 @@
|
|||
* @WLAN_STA_TDLS_PEER: Station is a TDLS peer.
|
||||
* @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct
|
||||
* packets. This means the link is enabled.
|
||||
* @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
|
||||
* station.
|
||||
* @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
|
||||
* keeping station in power-save mode, reply when the driver
|
||||
* unblocks the station.
|
||||
|
@ -76,6 +78,7 @@ enum ieee80211_sta_info_flags {
|
|||
WLAN_STA_PSPOLL,
|
||||
WLAN_STA_TDLS_PEER,
|
||||
WLAN_STA_TDLS_PEER_AUTH,
|
||||
WLAN_STA_TDLS_INITIATOR,
|
||||
WLAN_STA_UAPSD,
|
||||
WLAN_STA_SP,
|
||||
WLAN_STA_4ADDR_EVENT,
|
||||
|
@ -152,7 +155,8 @@ struct tid_ampdu_tx {
|
|||
/**
|
||||
* struct tid_ampdu_rx - TID aggregation information (Rx).
|
||||
*
|
||||
* @reorder_buf: buffer to reorder incoming aggregated MPDUs
|
||||
* @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
|
||||
* A-MSDU with individually reported subframes.
|
||||
* @reorder_time: jiffies when skb was added
|
||||
* @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
|
||||
* @reorder_timer: releases expired frames from the reorder buffer.
|
||||
|
@ -177,7 +181,7 @@ struct tid_ampdu_tx {
|
|||
struct tid_ampdu_rx {
|
||||
struct rcu_head rcu_head;
|
||||
spinlock_t reorder_lock;
|
||||
struct sk_buff **reorder_buf;
|
||||
struct sk_buff_head *reorder_buf;
|
||||
unsigned long *reorder_time;
|
||||
struct timer_list session_timer;
|
||||
struct timer_list reorder_timer;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
#include <linux/log2.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "ieee80211_i.h"
|
||||
#include "driver-ops.h"
|
||||
|
@ -21,14 +22,14 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
|
|||
struct ieee80211_local *local;
|
||||
|
||||
sdata = container_of(wk, struct ieee80211_sub_if_data,
|
||||
tdls_peer_del_work.work);
|
||||
u.mgd.tdls_peer_del_work.work);
|
||||
local = sdata->local;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
if (!is_zero_ether_addr(sdata->tdls_peer)) {
|
||||
tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer);
|
||||
sta_info_destroy_addr(sdata, sdata->tdls_peer);
|
||||
eth_zero_addr(sdata->tdls_peer);
|
||||
if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) {
|
||||
tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer);
|
||||
sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer);
|
||||
eth_zero_addr(sdata->u.mgd.tdls_peer);
|
||||
}
|
||||
mutex_unlock(&local->mtx);
|
||||
}
|
||||
|
@ -46,11 +47,16 @@ static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
|
|||
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
|
||||
}
|
||||
|
||||
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
|
||||
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
|
||||
u16 status_code)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
u16 capab;
|
||||
|
||||
/* The capability will be 0 when sending a failure code */
|
||||
if (status_code != 0)
|
||||
return 0;
|
||||
|
||||
capab = 0;
|
||||
if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
|
||||
return capab;
|
||||
|
@ -63,19 +69,332 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
|
|||
return capab;
|
||||
}
|
||||
|
||||
static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
|
||||
const u8 *peer, const u8 *bssid)
|
||||
static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
bool initiator)
|
||||
{
|
||||
struct ieee80211_tdls_lnkie *lnkid;
|
||||
const u8 *init_addr, *rsp_addr;
|
||||
|
||||
if (initiator) {
|
||||
init_addr = sdata->vif.addr;
|
||||
rsp_addr = peer;
|
||||
} else {
|
||||
init_addr = peer;
|
||||
rsp_addr = sdata->vif.addr;
|
||||
}
|
||||
|
||||
lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
|
||||
|
||||
lnkid->ie_type = WLAN_EID_LINK_ID;
|
||||
lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
|
||||
|
||||
memcpy(lnkid->bssid, bssid, ETH_ALEN);
|
||||
memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
|
||||
memcpy(lnkid->resp_sta, peer, ETH_ALEN);
|
||||
memcpy(lnkid->bssid, sdata->u.mgd.bssid, ETH_ALEN);
|
||||
memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
|
||||
memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
|
||||
}
|
||||
|
||||
/* translate numbering in the WMM parameter IE to the mac80211 notation */
|
||||
static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
|
||||
{
|
||||
switch (ac) {
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
case 0:
|
||||
return IEEE80211_AC_BE;
|
||||
case 1:
|
||||
return IEEE80211_AC_BK;
|
||||
case 2:
|
||||
return IEEE80211_AC_VI;
|
||||
case 3:
|
||||
return IEEE80211_AC_VO;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci)
|
||||
{
|
||||
u8 ret;
|
||||
|
||||
ret = aifsn & 0x0f;
|
||||
if (acm)
|
||||
ret |= 0x10;
|
||||
ret |= (aci << 5) & 0x60;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max)
|
||||
{
|
||||
return ((ilog2(cw_min + 1) << 0x0) & 0x0f) |
|
||||
((ilog2(cw_max + 1) << 0x4) & 0xf0);
|
||||
}
|
||||
|
||||
static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_wmm_param_ie *wmm;
|
||||
struct ieee80211_tx_queue_params *txq;
|
||||
int i;
|
||||
|
||||
wmm = (void *)skb_put(skb, sizeof(*wmm));
|
||||
memset(wmm, 0, sizeof(*wmm));
|
||||
|
||||
wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
|
||||
wmm->len = sizeof(*wmm) - 2;
|
||||
|
||||
wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
|
||||
wmm->oui[1] = 0x50;
|
||||
wmm->oui[2] = 0xf2;
|
||||
wmm->oui_type = 2; /* WME */
|
||||
wmm->oui_subtype = 1; /* WME param */
|
||||
wmm->version = 1; /* WME ver */
|
||||
wmm->qos_info = 0; /* U-APSD not in use */
|
||||
|
||||
/*
|
||||
* Use the EDCA parameters defined for the BSS, or default if the AP
|
||||
* doesn't support it, as mandated by 802.11-2012 section 10.22.4
|
||||
*/
|
||||
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
|
||||
txq = &sdata->tx_conf[ieee80211_ac_from_wmm(i)];
|
||||
wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs,
|
||||
txq->acm, i);
|
||||
wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max);
|
||||
wmm->ac[i].txop_limit = cpu_to_le16(txq->txop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
u8 action_code, bool initiator,
|
||||
const u8 *extra_ies, size_t extra_ies_len)
|
||||
{
|
||||
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_sta_ht_cap ht_cap;
|
||||
struct sta_info *sta = NULL;
|
||||
size_t offset = 0, noffset;
|
||||
u8 *pos;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* we should have the peer STA if we're already responding */
|
||||
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
|
||||
sta = sta_info_get(sdata, peer);
|
||||
if (WARN_ON_ONCE(!sta)) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ieee80211_add_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
||||
|
||||
/* add any custom IEs that go before Extended Capabilities */
|
||||
if (extra_ies_len) {
|
||||
static const u8 before_ext_cap[] = {
|
||||
WLAN_EID_SUPP_RATES,
|
||||
WLAN_EID_COUNTRY,
|
||||
WLAN_EID_EXT_SUPP_RATES,
|
||||
WLAN_EID_SUPPORTED_CHANNELS,
|
||||
WLAN_EID_RSN,
|
||||
};
|
||||
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
|
||||
before_ext_cap,
|
||||
ARRAY_SIZE(before_ext_cap),
|
||||
offset);
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_ext_capab(skb);
|
||||
|
||||
/* add the QoS element if we support it */
|
||||
if (local->hw.queues >= IEEE80211_NUM_ACS &&
|
||||
action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
|
||||
ieee80211_add_wmm_info_ie(skb_put(skb, 9), 0); /* no U-APSD */
|
||||
|
||||
/* add any custom IEs that go before HT capabilities */
|
||||
if (extra_ies_len) {
|
||||
static const u8 before_ht_cap[] = {
|
||||
WLAN_EID_SUPP_RATES,
|
||||
WLAN_EID_COUNTRY,
|
||||
WLAN_EID_EXT_SUPP_RATES,
|
||||
WLAN_EID_SUPPORTED_CHANNELS,
|
||||
WLAN_EID_RSN,
|
||||
WLAN_EID_EXT_CAPABILITY,
|
||||
WLAN_EID_QOS_CAPA,
|
||||
WLAN_EID_FAST_BSS_TRANSITION,
|
||||
WLAN_EID_TIMEOUT_INTERVAL,
|
||||
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
||||
};
|
||||
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
|
||||
before_ht_cap,
|
||||
ARRAY_SIZE(before_ht_cap),
|
||||
offset);
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
/*
|
||||
* with TDLS we can switch channels, and HT-caps are not necessarily
|
||||
* the same on all bands. The specification limits the setup to a
|
||||
* single HT-cap, so use the current band for now.
|
||||
*/
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
|
||||
if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
|
||||
action_code == WLAN_TDLS_SETUP_RESPONSE) &&
|
||||
ht_cap.ht_supported && (!sta || sta->sta.ht_cap.ht_supported)) {
|
||||
if (action_code == WLAN_TDLS_SETUP_REQUEST) {
|
||||
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
|
||||
|
||||
/* disable SMPS in TDLS initiator */
|
||||
ht_cap.cap |= (WLAN_HT_CAP_SM_PS_DISABLED
|
||||
<< IEEE80211_HT_CAP_SM_PS_SHIFT);
|
||||
} else {
|
||||
/* disable SMPS in TDLS responder */
|
||||
sta->sta.ht_cap.cap |=
|
||||
(WLAN_HT_CAP_SM_PS_DISABLED
|
||||
<< IEEE80211_HT_CAP_SM_PS_SHIFT);
|
||||
|
||||
/* the peer caps are already intersected with our own */
|
||||
memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
|
||||
}
|
||||
|
||||
pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
|
||||
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
/* add any remaining IEs */
|
||||
if (extra_ies_len) {
|
||||
noffset = extra_ies_len;
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
size_t offset = 0, noffset;
|
||||
struct sta_info *sta, *ap_sta;
|
||||
u8 *pos;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
sta = sta_info_get(sdata, peer);
|
||||
ap_sta = sta_info_get(sdata, ifmgd->bssid);
|
||||
if (WARN_ON_ONCE(!sta || !ap_sta)) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
/* add any custom IEs that go before the QoS IE */
|
||||
if (extra_ies_len) {
|
||||
static const u8 before_qos[] = {
|
||||
WLAN_EID_RSN,
|
||||
};
|
||||
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
|
||||
before_qos,
|
||||
ARRAY_SIZE(before_qos),
|
||||
offset);
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
/* add the QoS param IE if both the peer and we support it */
|
||||
if (local->hw.queues >= IEEE80211_NUM_ACS &&
|
||||
test_sta_flag(sta, WLAN_STA_WME))
|
||||
ieee80211_tdls_add_wmm_param_ie(sdata, skb);
|
||||
|
||||
/* add any custom IEs that go before HT operation */
|
||||
if (extra_ies_len) {
|
||||
static const u8 before_ht_op[] = {
|
||||
WLAN_EID_RSN,
|
||||
WLAN_EID_QOS_CAPA,
|
||||
WLAN_EID_FAST_BSS_TRANSITION,
|
||||
WLAN_EID_TIMEOUT_INTERVAL,
|
||||
};
|
||||
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
|
||||
before_ht_op,
|
||||
ARRAY_SIZE(before_ht_op),
|
||||
offset);
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
/* if HT support is only added in TDLS, we need an HT-operation IE */
|
||||
if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
|
||||
struct ieee80211_chanctx_conf *chanctx_conf =
|
||||
rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!WARN_ON(!chanctx_conf)) {
|
||||
pos = skb_put(skb, 2 +
|
||||
sizeof(struct ieee80211_ht_operation));
|
||||
/* send an empty HT operation IE */
|
||||
ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap,
|
||||
&chanctx_conf->def, 0);
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
/* add any remaining IEs */
|
||||
if (extra_ies_len) {
|
||||
noffset = extra_ies_len;
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
|
||||
}
|
||||
|
||||
static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
u8 action_code, u16 status_code,
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len)
|
||||
{
|
||||
switch (action_code) {
|
||||
case WLAN_TDLS_SETUP_REQUEST:
|
||||
case WLAN_TDLS_SETUP_RESPONSE:
|
||||
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
||||
if (status_code == 0)
|
||||
ieee80211_tdls_add_setup_start_ies(sdata, skb, peer,
|
||||
action_code,
|
||||
initiator,
|
||||
extra_ies,
|
||||
extra_ies_len);
|
||||
break;
|
||||
case WLAN_TDLS_SETUP_CONFIRM:
|
||||
if (status_code == 0)
|
||||
ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer,
|
||||
initiator, extra_ies,
|
||||
extra_ies_len);
|
||||
break;
|
||||
case WLAN_TDLS_TEARDOWN:
|
||||
case WLAN_TDLS_DISCOVERY_REQUEST:
|
||||
if (extra_ies_len)
|
||||
memcpy(skb_put(skb, extra_ies_len), extra_ies,
|
||||
extra_ies_len);
|
||||
if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
|
||||
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -84,7 +403,6 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
|
|||
u16 status_code, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
||||
struct ieee80211_tdls_data *tf;
|
||||
|
||||
tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
|
||||
|
@ -102,11 +420,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
|
|||
skb_put(skb, sizeof(tf->u.setup_req));
|
||||
tf->u.setup_req.dialog_token = dialog_token;
|
||||
tf->u.setup_req.capability =
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
|
||||
|
||||
ieee80211_add_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_tdls_add_ext_capab(skb);
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
|
||||
status_code));
|
||||
break;
|
||||
case WLAN_TDLS_SETUP_RESPONSE:
|
||||
tf->category = WLAN_CATEGORY_TDLS;
|
||||
|
@ -116,11 +431,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
|
|||
tf->u.setup_resp.status_code = cpu_to_le16(status_code);
|
||||
tf->u.setup_resp.dialog_token = dialog_token;
|
||||
tf->u.setup_resp.capability =
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
|
||||
|
||||
ieee80211_add_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_tdls_add_ext_capab(skb);
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
|
||||
status_code));
|
||||
break;
|
||||
case WLAN_TDLS_SETUP_CONFIRM:
|
||||
tf->category = WLAN_CATEGORY_TDLS;
|
||||
|
@ -157,7 +469,6 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
|
|||
u16 status_code, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
|
||||
mgmt = (void *)skb_put(skb, 24);
|
||||
|
@ -178,11 +489,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
|
|||
mgmt->u.action.u.tdls_discover_resp.dialog_token =
|
||||
dialog_token;
|
||||
mgmt->u.action.u.tdls_discover_resp.capability =
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
|
||||
|
||||
ieee80211_add_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_tdls_add_ext_capab(skb);
|
||||
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
|
||||
status_code));
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -202,7 +510,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
|||
struct ieee80211_local *local = sdata->local;
|
||||
struct sk_buff *skb = NULL;
|
||||
bool send_direct;
|
||||
const u8 *init_addr, *rsp_addr;
|
||||
struct sta_info *sta;
|
||||
int ret;
|
||||
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
||||
|
@ -210,6 +518,9 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
|||
sizeof(struct ieee80211_tdls_data)) +
|
||||
50 + /* supported rates */
|
||||
7 + /* ext capab */
|
||||
26 + /* max(WMM-info, WMM-param) */
|
||||
2 + max(sizeof(struct ieee80211_ht_cap),
|
||||
sizeof(struct ieee80211_ht_operation)) +
|
||||
extra_ies_len +
|
||||
sizeof(struct ieee80211_tdls_lnkie));
|
||||
if (!skb)
|
||||
|
@ -242,45 +553,48 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
|||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
if (extra_ies_len)
|
||||
memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
|
||||
rcu_read_lock();
|
||||
sta = sta_info_get(sdata, peer);
|
||||
|
||||
/* sanity check for initiator */
|
||||
/* infer the initiator if we can, to support old userspace */
|
||||
switch (action_code) {
|
||||
case WLAN_TDLS_SETUP_REQUEST:
|
||||
if (sta)
|
||||
set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
|
||||
/* fall-through */
|
||||
case WLAN_TDLS_SETUP_CONFIRM:
|
||||
case WLAN_TDLS_DISCOVERY_REQUEST:
|
||||
if (!initiator) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
initiator = true;
|
||||
break;
|
||||
case WLAN_TDLS_SETUP_RESPONSE:
|
||||
/*
|
||||
* In some testing scenarios, we send a request and response.
|
||||
* Make the last packet sent take effect for the initiator
|
||||
* value.
|
||||
*/
|
||||
if (sta)
|
||||
clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
|
||||
/* fall-through */
|
||||
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
||||
if (initiator) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
initiator = false;
|
||||
break;
|
||||
case WLAN_TDLS_TEARDOWN:
|
||||
/* any value is ok */
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))
|
||||
initiator = true;
|
||||
|
||||
rcu_read_unlock();
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (initiator) {
|
||||
init_addr = sdata->vif.addr;
|
||||
rsp_addr = peer;
|
||||
} else {
|
||||
init_addr = peer;
|
||||
rsp_addr = sdata->vif.addr;
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
|
||||
sdata->u.mgd.bssid);
|
||||
|
||||
ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
|
||||
initiator, extra_ies, extra_ies_len);
|
||||
if (send_direct) {
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
return 0;
|
||||
|
@ -327,8 +641,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
|
|||
mutex_lock(&local->mtx);
|
||||
|
||||
/* we don't support concurrent TDLS peer setups */
|
||||
if (!is_zero_ether_addr(sdata->tdls_peer) &&
|
||||
!ether_addr_equal(sdata->tdls_peer, peer)) {
|
||||
if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
|
||||
!ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
|
||||
ret = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
|
@ -336,15 +650,19 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
|
|||
/*
|
||||
* make sure we have a STA representing the peer so we drop or buffer
|
||||
* non-TDLS-setup frames to the peer. We can't send other packets
|
||||
* during setup through the AP path
|
||||
* during setup through the AP path.
|
||||
* Allow error packets to be sent - sometimes we don't even add a STA
|
||||
* before failing the setup.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
if (!sta_info_get(sdata, peer)) {
|
||||
if (status_code == 0) {
|
||||
rcu_read_lock();
|
||||
if (!sta_info_get(sdata, peer)) {
|
||||
rcu_read_unlock();
|
||||
ret = -ENOLINK;
|
||||
goto exit;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
ret = -ENOLINK;
|
||||
goto exit;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
ieee80211_flush_queues(local, sdata);
|
||||
|
||||
|
@ -355,9 +673,9 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
|
|||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
memcpy(sdata->tdls_peer, peer, ETH_ALEN);
|
||||
memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
|
||||
ieee80211_queue_delayed_work(&sdata->local->hw,
|
||||
&sdata->tdls_peer_del_work,
|
||||
&sdata->u.mgd.tdls_peer_del_work,
|
||||
TDLS_PEER_SETUP_TIMEOUT);
|
||||
|
||||
exit:
|
||||
|
@ -513,11 +831,22 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
|||
set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
|
||||
rcu_read_unlock();
|
||||
|
||||
WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) ||
|
||||
!ether_addr_equal(sdata->tdls_peer, peer));
|
||||
WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) ||
|
||||
!ether_addr_equal(sdata->u.mgd.tdls_peer, peer));
|
||||
ret = 0;
|
||||
break;
|
||||
case NL80211_TDLS_DISABLE_LINK:
|
||||
/*
|
||||
* The teardown message in ieee80211_tdls_mgmt_teardown() was
|
||||
* created while the queues were stopped, so it might still be
|
||||
* pending. Before flushing the queues we need to be sure the
|
||||
* message is handled by the tasklet handling pending messages,
|
||||
* otherwise we might start destroying the station before
|
||||
* sending the teardown packet.
|
||||
* Note that this only forces the tasklet to flush pendings -
|
||||
* not to stop the tasklet from rescheduling itself.
|
||||
*/
|
||||
tasklet_kill(&local->tx_pending_tasklet);
|
||||
/* flush a potentially queued teardown packet */
|
||||
ieee80211_flush_queues(local, sdata);
|
||||
|
||||
|
@ -528,9 +857,9 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
|||
break;
|
||||
}
|
||||
|
||||
if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) {
|
||||
cancel_delayed_work(&sdata->tdls_peer_del_work);
|
||||
eth_zero_addr(sdata->tdls_peer);
|
||||
if (ret == 0 && ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
|
||||
cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work);
|
||||
eth_zero_addr(sdata->u.mgd.tdls_peer);
|
||||
}
|
||||
|
||||
mutex_unlock(&local->mtx);
|
||||
|
|
|
@ -3083,3 +3083,18 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
|
|||
|
||||
return max_num_different_channels;
|
||||
}
|
||||
|
||||
u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
|
||||
{
|
||||
*buf++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*buf++ = 7; /* len */
|
||||
*buf++ = 0x00; /* Microsoft OUI 00:50:F2 */
|
||||
*buf++ = 0x50;
|
||||
*buf++ = 0xf2;
|
||||
*buf++ = 2; /* WME */
|
||||
*buf++ = 0; /* WME info */
|
||||
*buf++ = 1; /* WME ver */
|
||||
*buf++ = qosinfo; /* U-APSD no in use */
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
|
|
@ -129,6 +129,10 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
if (!vht_cap_ie || !sband->vht_cap.vht_supported)
|
||||
return;
|
||||
|
||||
/* don't support VHT for TDLS peers for now */
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
|
||||
return;
|
||||
|
||||
/*
|
||||
* A VHT STA must support 40 MHz, but if we verify that here
|
||||
* then we break a few things - some APs (e.g. Netgear R6300v2
|
||||
|
|
|
@ -811,7 +811,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
|
|||
ieee80211_rx_result
|
||||
ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
if (rx->sta->cipher_scheme)
|
||||
if (rx->sta && rx->sta->cipher_scheme)
|
||||
return ieee80211_crypto_cs_decrypt(rx);
|
||||
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
|
|
@ -162,6 +162,12 @@ config CFG80211_INTERNAL_REGDB
|
|||
and includes code to query that database. This is an alternative
|
||||
to using CRDA for defining regulatory rules for the kernel.
|
||||
|
||||
Using this option requires some parsing of the db.txt at build time,
|
||||
the parser will be upkept with the latest wireless-regdb updates but
|
||||
older wireless-regdb formats will be ignored. The parser may later
|
||||
be replaced to avoid issues with conflicts on versions of
|
||||
wireless-regdb.
|
||||
|
||||
For details see:
|
||||
|
||||
http://wireless.kernel.org/en/developers/Regulatory
|
||||
|
|
|
@ -51,32 +51,41 @@ function parse_country_head() {
|
|||
|
||||
function parse_reg_rule()
|
||||
{
|
||||
flag_starts_at = 7
|
||||
|
||||
start = $1
|
||||
sub(/\(/, "", start)
|
||||
end = $3
|
||||
bw = $5
|
||||
sub(/\),/, "", bw)
|
||||
gain = $6
|
||||
sub(/\(/, "", gain)
|
||||
sub(/,/, "", gain)
|
||||
power = $7
|
||||
sub(/\)/, "", power)
|
||||
sub(/,/, "", power)
|
||||
gain = 0
|
||||
power = $6
|
||||
# power might be in mW...
|
||||
units = $8
|
||||
units = $7
|
||||
dfs_cac = 0
|
||||
|
||||
sub(/\(/, "", power)
|
||||
sub(/\),/, "", power)
|
||||
sub(/\),/, "", units)
|
||||
sub(/\)/, "", units)
|
||||
sub(/,/, "", units)
|
||||
dfs_cac = $9
|
||||
|
||||
if (units == "mW") {
|
||||
flag_starts_at = 8
|
||||
power = 10 * log(power)/log(10)
|
||||
if ($8 ~ /[[:digit:]]/) {
|
||||
flag_starts_at = 9
|
||||
dfs_cac = $8
|
||||
}
|
||||
} else {
|
||||
dfs_cac = $8
|
||||
if ($7 ~ /[[:digit:]]/) {
|
||||
flag_starts_at = 8
|
||||
dfs_cac = $7
|
||||
}
|
||||
}
|
||||
sub(/,/, "", dfs_cac)
|
||||
sub(/\(/, "", dfs_cac)
|
||||
sub(/\)/, "", dfs_cac)
|
||||
sub(/\),/, "", dfs_cac)
|
||||
flagstr = ""
|
||||
for (i=8; i<=NF; i++)
|
||||
for (i=flag_starts_at; i<=NF; i++)
|
||||
flagstr = flagstr $i
|
||||
split(flagstr, flagarray, ",")
|
||||
flags = ""
|
||||
|
|
|
@ -3814,7 +3814,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
|
|||
{
|
||||
if (params->listen_interval != -1)
|
||||
return -EINVAL;
|
||||
if (params->aid)
|
||||
if (params->aid &&
|
||||
!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
|
||||
return -EINVAL;
|
||||
|
||||
/* When you run into this, adjust the code below for the new flag */
|
||||
|
|
Loading…
Reference in New Issue