Merge tag 'mac80211-next-for-john-2014-11-20' of git://git.kernel.org/pub/scm/linux...
authorJohn W. Linville <linville@tuxdriver.com>
Thu, 20 Nov 2014 21:09:30 +0000 (16:09 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 20 Nov 2014 21:09:30 +0000 (16:09 -0500)
Johannes Berg <johannes@sipsolutions.net> says:

"It has been a while since my last pull request, so we accumulated
another relatively large set of changes:
 * TDLS off-channel support set from Arik/Liad, with some support
   patches I did
 * custom regulatory fixes from Arik
 * minstrel VHT fix (and a small optimisation) from Felix
 * add back radiotap vendor namespace support (myself)
 * random MAC address scanning for cfg80211/mac80211/hwsim (myself)
 * CSA improvements (Luca)
 * WoWLAN Net Detect (wake on network found) support (Luca)
 * and lots of other smaller changes from many people"

Signed-off-by: John W. Linville <linville@tuxdriver.com>
100 files changed:
MAINTAINERS
drivers/bcma/host_pci.c
drivers/bcma/main.c
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_ath.c
drivers/net/ieee802154/at86rf230.c
drivers/net/ieee802154/fakelb.c
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/common.c
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/regd.c
drivers/net/wireless/b43/phy_common.c
drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11n_rxreorder.c
drivers/net/wireless/mwifiex/11n_rxreorder.h
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rtlwifi/base.c
drivers/net/wireless/rtlwifi/core.c
drivers/net/wireless/rtlwifi/core.h
drivers/net/wireless/rtlwifi/pci.c
drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
drivers/net/wireless/rtlwifi/rtl8192ce/def.h
drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
drivers/net/wireless/rtlwifi/rtl8192de/sw.c
drivers/net/wireless/rtlwifi/rtl8192ee/hw.c
drivers/net/wireless/rtlwifi/rtl8192se/def.h
drivers/net/wireless/rtlwifi/rtl8192se/hw.c
drivers/net/wireless/rtlwifi/rtl8192se/phy.c
drivers/net/wireless/rtlwifi/rtl8192se/sw.c
drivers/net/wireless/rtlwifi/rtl8192se/trx.c
drivers/net/wireless/rtlwifi/rtl8821ae/phy.c
drivers/net/wireless/rtlwifi/usb.c
include/linux/ieee802154.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/bluetooth/mgmt.h
include/net/cfg802154.h
include/net/mac802154.h
include/net/nl802154.h [new file with mode: 0644]
net/bluetooth/6lowpan.c
net/bluetooth/amp.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hidp/core.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/smp.c
net/bluetooth/smp.h
net/ieee802154/Makefile
net/ieee802154/core.c
net/ieee802154/core.h
net/ieee802154/ieee802154.h
net/ieee802154/netlink.c
net/ieee802154/nl-mac.c
net/ieee802154/nl802154.c [new file with mode: 0644]
net/ieee802154/nl802154.h [new file with mode: 0644]
net/ieee802154/rdev-ops.h
net/ieee802154/sysfs.c
net/mac80211/aes_ccm.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mesh.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/spectmgmt.c
net/mac802154/cfg.c
net/mac802154/driver-ops.h
net/mac802154/ieee802154_i.h
net/mac802154/iface.c
net/mac802154/mac_cmd.c
net/mac802154/main.c
net/mac802154/mib.c
net/mac802154/rx.c
net/mac802154/tx.c
net/mac802154/util.c

index b42eb50b7426cc26527a8263e43f3eabf9dafbd9..7ec37a396ffedd2fd4f5c2072f8968f39081120c 100644 (file)
@@ -4699,6 +4699,7 @@ F:        net/mac802154/
 F:     drivers/net/ieee802154/
 F:     include/linux/nl802154.h
 F:     include/linux/ieee802154.h
+F:     include/net/nl802154.h
 F:     include/net/mac802154.h
 F:     include/net/af_ieee802154.h
 F:     include/net/cfg802154.h
index 1e5ac0a796968b61e3de79a4ae2a82f9c2339916..cd9161a8b3a196bcc67634dc86bb84ae5b6190d1 100644 (file)
@@ -275,7 +275,7 @@ static SIMPLE_DEV_PM_OPS(bcma_pm_ops, bcma_host_pci_suspend,
 static const struct pci_device_id bcma_pci_bridge_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4313) },
-       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) },  /* 0xa8d8 */
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
@@ -285,7 +285,8 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
-       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) },  /* 0xA8DB */
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) },  /* 0xa8db, BCM43217 (sic!) */
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43228) },  /* 0xa8dc */
        { 0, },
 };
 MODULE_DEVICE_TABLE(pci, bcma_pci_bridge_tbl);
index 122086ef9fe1b78accfeffecb97ccbbcd35e4751..534e1337766d2d0ba9d7e8d220a30434da23ef5d 100644 (file)
@@ -133,7 +133,7 @@ static bool bcma_is_core_needed_early(u16 core_id)
        return false;
 }
 
-#ifdef CONFIG_OF
+#if defined(CONFIG_OF) && defined(CONFIG_OF_ADDRESS)
 static struct device_node *bcma_of_find_child_device(struct platform_device *parent,
                                                     struct bcma_device *core)
 {
index cd634f3b76d3a7cadd72c5f27c75d505afd66d45..7c13d7a8d83cb035fe396f770bc883cf14703d04 100644 (file)
@@ -106,6 +106,8 @@ static const struct usb_device_id btusb_table[] = {
        { USB_DEVICE(0x0b05, 0x17b5) },
        { USB_DEVICE(0x0b05, 0x17cb) },
        { USB_DEVICE(0x413c, 0x8197) },
+       { USB_DEVICE(0x13d3, 0x3404),
+         .driver_info = BTUSB_BCM_PATCHRAM },
 
        /* Foxconn - Hon Hai */
        { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
index 0bc8a6a6a14850744d27788b9850ddf9bacac99f..2ff6dfd2d3f0657e13853ec13b04a7b51cf765a6 100644 (file)
@@ -74,7 +74,7 @@ static int ath_wakeup_ar3k(struct tty_struct *tty)
 
        status = tty->driver->ops->tiocmget(tty);
 
-       /* Disable Automatic RTSCTS */
+       /* Enable Automatic RTSCTS */
        ktermios.c_cflag |= CRTSCTS;
        status = tty_set_termios(tty, &ktermios);
 
index ebcbeb3a06e833477b239d783108c5d79e6b9190..1c0135620c62261dca42b32c3958d43c3dca3ded 100644 (file)
@@ -46,10 +46,6 @@ struct at86rf2xx_chip_data {
        u16 t_off_to_tx_on;
        u16 t_frame;
        u16 t_p_ack;
-       /* short interframe spacing time */
-       u16 t_sifs;
-       /* long interframe spacing time */
-       u16 t_lifs;
        /* completion timeout for tx in msecs */
        u16 t_tx_timeout;
        int rssi_base_val;
@@ -719,19 +715,10 @@ at86rf230_tx_complete(void *context)
 
        enable_irq(lp->spi->irq);
 
-       if (lp->max_frame_retries <= 0) {
-               /* Interfame spacing time, which is phy depend.
-                * TODO
-                * Move this handling in MAC 802.15.4 layer.
-                * This is currently a workaround to avoid fragmenation issues.
-                */
-               if (skb->len > 18)
-                       udelay(lp->data->t_lifs);
-               else
-                       udelay(lp->data->t_sifs);
-       }
-
-       ieee802154_xmit_complete(lp->hw, skb);
+       if (lp->max_frame_retries <= 0)
+               ieee802154_xmit_complete(lp->hw, skb, true);
+       else
+               ieee802154_xmit_complete(lp->hw, skb, false);
 }
 
 static void
@@ -1038,6 +1025,36 @@ at86rf212_set_channel(struct at86rf230_local *lp, u8 page, u8 channel)
        if (rc < 0)
                return rc;
 
+       /* This sets the symbol_duration according frequency on the 212.
+        * TODO move this handling while set channel and page in cfg802154.
+        * We can do that, this timings are according 802.15.4 standard.
+        * If we do that in cfg802154, this is a more generic calculation.
+        *
+        * This should also protected from ifs_timer. Means cancel timer and
+        * init with a new value. For now, this is okay.
+        */
+       if (channel == 0) {
+               if (page == 0) {
+                       /* SUB:0 and BPSK:0 -> BPSK-20 */
+                       lp->hw->phy->symbol_duration = 50;
+               } else {
+                       /* SUB:1 and BPSK:0 -> BPSK-40 */
+                       lp->hw->phy->symbol_duration = 25;
+               }
+       } else {
+               if (page == 0)
+                       /* SUB:0 and BPSK:1 -> OQPSK-100/200/400 */
+                       lp->hw->phy->symbol_duration = 40;
+               else
+                       /* SUB:1 and BPSK:1 -> OQPSK-250/500/1000 */
+                       lp->hw->phy->symbol_duration = 16;
+       }
+
+       lp->hw->phy->lifs_period = IEEE802154_LIFS_PERIOD *
+                                  lp->hw->phy->symbol_duration;
+       lp->hw->phy->sifs_period = IEEE802154_SIFS_PERIOD *
+                                  lp->hw->phy->symbol_duration;
+
        return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
 }
 
@@ -1047,23 +1064,11 @@ at86rf230_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
        struct at86rf230_local *lp = hw->priv;
        int rc;
 
-       if (page > 31 ||
-           !(lp->hw->phy->channels_supported[page] & BIT(channel))) {
-               WARN_ON(1);
-               return -EINVAL;
-       }
-
        rc = lp->data->set_channel(lp, page, channel);
-       if (rc < 0)
-               return rc;
-
        /* Wait for PLL */
        usleep_range(lp->data->t_channel_switch,
                     lp->data->t_channel_switch + 10);
-       hw->phy->current_channel = channel;
-       hw->phy->current_page = page;
-
-       return 0;
+       return rc;
 }
 
 static int
@@ -1179,9 +1184,6 @@ at86rf230_set_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be,
        struct at86rf230_local *lp = hw->priv;
        int rc;
 
-       if (min_be > max_be || max_be > 8 || retries > 5)
-               return -EINVAL;
-
        rc = at86rf230_write_subreg(lp, SR_MIN_BE, min_be);
        if (rc)
                return rc;
@@ -1199,9 +1201,6 @@ at86rf230_set_frame_retries(struct ieee802154_hw *hw, s8 retries)
        struct at86rf230_local *lp = hw->priv;
        int rc = 0;
 
-       if (retries < -1 || retries > 15)
-               return -EINVAL;
-
        lp->tx_aret = retries >= 0;
        lp->max_frame_retries = retries;
 
@@ -1263,8 +1262,6 @@ static struct at86rf2xx_chip_data at86rf233_data = {
        .t_off_to_tx_on = 80,
        .t_frame = 4096,
        .t_p_ack = 545,
-       .t_sifs = 192,
-       .t_lifs = 640,
        .t_tx_timeout = 2000,
        .rssi_base_val = -91,
        .set_channel = at86rf23x_set_channel,
@@ -1279,8 +1276,6 @@ static struct at86rf2xx_chip_data at86rf231_data = {
        .t_off_to_tx_on = 110,
        .t_frame = 4096,
        .t_p_ack = 545,
-       .t_sifs = 192,
-       .t_lifs = 640,
        .t_tx_timeout = 2000,
        .rssi_base_val = -91,
        .set_channel = at86rf23x_set_channel,
@@ -1295,8 +1290,6 @@ static struct at86rf2xx_chip_data at86rf212_data = {
        .t_off_to_tx_on = 200,
        .t_frame = 4096,
        .t_p_ack = 545,
-       .t_sifs = 192,
-       .t_lifs = 640,
        .t_tx_timeout = 2000,
        .rssi_base_val = -100,
        .set_channel = at86rf212_set_channel,
@@ -1432,6 +1425,7 @@ at86rf230_detect_device(struct at86rf230_local *lp)
                lp->data = &at86rf231_data;
                lp->hw->phy->channels_supported[0] = 0x7FFF800;
                lp->hw->phy->current_channel = 11;
+               lp->hw->phy->symbol_duration = 16;
                break;
        case 7:
                chip = "at86rf212";
@@ -1441,6 +1435,7 @@ at86rf230_detect_device(struct at86rf230_local *lp)
                        lp->hw->phy->channels_supported[0] = 0x00007FF;
                        lp->hw->phy->channels_supported[2] = 0x00007FF;
                        lp->hw->phy->current_channel = 5;
+                       lp->hw->phy->symbol_duration = 25;
                } else {
                        rc = -ENOTSUPP;
                }
@@ -1450,6 +1445,7 @@ at86rf230_detect_device(struct at86rf230_local *lp)
                lp->data = &at86rf233_data;
                lp->hw->phy->channels_supported[0] = 0x7FFF800;
                lp->hw->phy->current_channel = 13;
+               lp->hw->phy->symbol_duration = 16;
                break;
        default:
                chip = "unkown";
index 6e62286cef7849c4523e1334015729ca7f8fa39a..96947d724189d88862d0af034c8d486ec4bbb428 100644 (file)
@@ -58,9 +58,6 @@ fakelb_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
 {
        pr_debug("set channel to %d\n", channel);
 
-       hw->phy->current_page = page;
-       hw->phy->current_channel = channel;
-
        return 0;
 }
 
index 76668dc79c0e4b444a2239925bed0ea0876d7587..ccba4fea7269e35ecf2df7d95fb8b6af1aee507a 100644 (file)
@@ -80,6 +80,7 @@ struct reg_dmn_pair_mapping {
 
 struct ath_regulatory {
        char alpha2[2];
+       enum nl80211_dfs_regions region;
        u16 country_code;
        u16 max_power_level;
        u16 current_rd;
index ca101d7cb99faf09b269c4f517c009c046d7466a..fee0cadb0f5ed592fba313f1040b2e1f5ed1ebde 100644 (file)
@@ -3,6 +3,8 @@ config ATH9K_HW
 config ATH9K_COMMON
        tristate
        select ATH_COMMON
+       select DEBUG_FS
+       select RELAY
 config ATH9K_DFS_DEBUGFS
        def_bool y
        depends on ATH9K_DEBUGFS && ATH9K_DFS_CERTIFIED
index 9bdaa0afc37f7bed18594e7cab050e1467bfb279..2df6d2ee70c25283f3c03b3957e8e4f0fce6f618 100644 (file)
@@ -664,6 +664,19 @@ static void ar9003_hw_override_ini(struct ath_hw *ah)
                ah->enabled_cals |= TX_CL_CAL;
        else
                ah->enabled_cals &= ~TX_CL_CAL;
+
+       if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
+               if (ah->is_clk_25mhz) {
+                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
+                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
+                       REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
+               } else {
+                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
+                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
+                       REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
+               }
+               udelay(100);
+       }
 }
 
 static void ar9003_hw_prog_ini(struct ath_hw *ah,
index eb62c58dd0f7ade60b018cc54207dd57ce47a121..e8c699446470cb9e33dc5f407ebfc81b5197af1b 100644 (file)
@@ -368,11 +368,11 @@ void ath9k_cmn_update_txpow(struct ath_hw *ah, u16 cur_txpow,
 {
        struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
 
-       if (reg->power_limit != new_txpow) {
+       if (reg->power_limit != new_txpow)
                ath9k_hw_set_txpowerlimit(ah, new_txpow, false);
-               /* read back in case value is clamped */
-               *txpower = reg->max_power_level;
-       }
+
+       /* read back in case value is clamped */
+       *txpower = reg->max_power_level;
 }
 EXPORT_SYMBOL(ath9k_cmn_update_txpow);
 
index 3f21b1bbc52e9efa4e2c019487e9d4269587b3e6..696e3d5309c6bdfc5f890c6dd2189ceee28f51c1 100644 (file)
@@ -455,7 +455,7 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf,
                         "%2d          %2x      %1x     %2x           %2x\n",
                         i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
                         (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3),
-                        val[2] & (0x7 << (i * 3)) >> (i * 3),
+                        (val[2] & (0x7 << (i * 3))) >> (i * 3),
                         (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
        }
 
index fbc78d80c559c3676cea7bb6d29533c4edf2b8e4..6d4b273469b1914ab019c7cdeb4ec00f1ddd329a 100644 (file)
@@ -870,19 +870,6 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
        udelay(RTC_PLL_SETTLE_DELAY);
 
        REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
-
-       if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) {
-               if (ah->is_clk_25mhz) {
-                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
-                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
-                       REG_WRITE(ah,  AR_SLP32_INC, 0x0001e7ae);
-               } else {
-                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
-                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
-                       REG_WRITE(ah,  AR_SLP32_INC, 0x0001e800);
-               }
-               udelay(100);
-       }
 }
 
 static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
index 39157ca723d6108992b9dca46a9593048546a63e..59d679cebc89e1132fdb313e4fea7e953a35728d 100644 (file)
@@ -764,6 +764,32 @@ static const struct ieee80211_iface_combination if_comb[] = {
 #endif
 };
 
+#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
+static void ath9k_set_mcc_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       if (!ath9k_is_chanctx_enabled())
+               return;
+
+       hw->flags |= IEEE80211_HW_QUEUE_CONTROL;
+       hw->queues = ATH9K_NUM_TX_QUEUES;
+       hw->offchannel_tx_hw_queue = hw->queues - 1;
+       hw->wiphy->interface_modes &= ~ BIT(NL80211_IFTYPE_WDS);
+       hw->wiphy->iface_combinations = if_comb_multi;
+       hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_multi);
+       hw->wiphy->max_scan_ssids = 255;
+       hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+       hw->wiphy->max_remain_on_channel_duration = 10000;
+       hw->chanctx_data_size = sizeof(void *);
+       hw->extra_beacon_tailroom =
+               sizeof(struct ieee80211_p2p_noa_attr) + 9;
+
+       ath_dbg(common, CHAN_CTX, "Use channel contexts\n");
+}
+#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
+
 static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 {
        struct ath_hw *ah = sc->sc_ah;
@@ -776,7 +802,6 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                IEEE80211_HW_SPECTRUM_MGMT |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_SUPPORTS_RC_TABLE |
-               IEEE80211_HW_QUEUE_CONTROL |
                IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
 
        if (ath9k_ps_enable)
@@ -811,24 +836,6 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                        hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
        }
 
-#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
-
-       if (ath9k_is_chanctx_enabled()) {
-               hw->wiphy->interface_modes &= ~ BIT(NL80211_IFTYPE_WDS);
-               hw->wiphy->iface_combinations = if_comb_multi;
-               hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_multi);
-               hw->wiphy->max_scan_ssids = 255;
-               hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
-               hw->wiphy->max_remain_on_channel_duration = 10000;
-               hw->chanctx_data_size = sizeof(void *);
-               hw->extra_beacon_tailroom =
-                       sizeof(struct ieee80211_p2p_noa_attr) + 9;
-
-               ath_dbg(common, CHAN_CTX, "Use channel contexts\n");
-       }
-
-#endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
-
        hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
@@ -838,12 +845,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
        hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
 
-       /* allow 4 queues per channel context +
-        * 1 cab queue + 1 offchannel tx queue
-        */
-       hw->queues = ATH9K_NUM_TX_QUEUES;
-       /* last queue for offchannel */
-       hw->offchannel_tx_hw_queue = hw->queues - 1;
+       hw->queues = 4;
        hw->max_rates = 4;
        hw->max_listen_interval = 10;
        hw->max_rate_tries = 10;
@@ -867,6 +869,9 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
                        &common->sbands[IEEE80211_BAND_5GHZ];
 
+#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
+       ath9k_set_mcc_capab(sc, hw);
+#endif
        ath9k_init_wow(hw);
        ath9k_cmn_reload_chainmask(ah);
 
index 027ad715ffb2e48b2ae6c13a27a4ea61b4aadc63..7c63976b5b0c4182c9fe72284a3c6860ff3b0630 100644 (file)
@@ -1184,6 +1184,9 @@ static void ath9k_assign_hw_queues(struct ieee80211_hw *hw,
 {
        int i;
 
+       if (!ath9k_is_chanctx_enabled())
+               return;
+
        for (i = 0; i < IEEE80211_NUM_ACS; i++)
                vif->hw_queue[i] = i;
 
index 493a183d0aaf7d04c77103a8c4ea2b2c477089ad..d6e54a3c88f671f08ae3174ddabf6721fb39e051 100644 (file)
@@ -169,7 +169,10 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
 
        if (txq->stopped &&
            txq->pending_frames < sc->tx.txq_max_pending[q]) {
-               ieee80211_wake_queue(sc->hw, info->hw_queue);
+               if (ath9k_is_chanctx_enabled())
+                       ieee80211_wake_queue(sc->hw, info->hw_queue);
+               else
+                       ieee80211_wake_queue(sc->hw, q);
                txq->stopped = false;
        }
 }
@@ -2247,7 +2250,10 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                fi->txq = q;
                if (++txq->pending_frames > sc->tx.txq_max_pending[q] &&
                    !txq->stopped) {
-                       ieee80211_stop_queue(sc->hw, info->hw_queue);
+                       if (ath9k_is_chanctx_enabled())
+                               ieee80211_stop_queue(sc->hw, info->hw_queue);
+                       else
+                               ieee80211_stop_queue(sc->hw, q);
                        txq->stopped = true;
                }
        }
index 415393dfb6fc17b51d33f4d24e70c6700620d4b5..06ea6cc9e30a5e07c379116a8e1ec0996e3f189e 100644 (file)
@@ -515,6 +515,7 @@ void ath_reg_notifier_apply(struct wiphy *wiphy,
        if (!request)
                return;
 
+       reg->region = request->dfs_region;
        switch (request->initiator) {
        case NL80211_REGDOM_SET_BY_CORE:
                /*
@@ -779,6 +780,19 @@ u32 ath_regd_get_band_ctl(struct ath_regulatory *reg,
                return SD_NO_CTL;
        }
 
+       if (ath_regd_get_eepromRD(reg) == CTRY_DEFAULT) {
+               switch (reg->region) {
+               case NL80211_DFS_FCC:
+                       return CTL_FCC;
+               case NL80211_DFS_ETSI:
+                       return CTL_ETSI;
+               case NL80211_DFS_JP:
+                       return CTL_MKK;
+               default:
+                       break;
+               }
+       }
+
        switch (band) {
        case IEEE80211_BAND_2GHZ:
                return reg->regpair->reg_2ghz_ctl;
index 1dfc682a805513f5e5d160f6198f6d17bec644fc..ee27b06074e1213f68bb2e3f6bb632b61aeb5fd5 100644 (file)
@@ -300,9 +300,7 @@ void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value)
 
 void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg)
 {
-       assert_mac_suspended(dev);
-       dev->phy.ops->phy_write(dev, destreg,
-               dev->phy.ops->phy_read(dev, srcreg));
+       b43_phy_write(dev, destreg, b43_phy_read(dev, srcreg));
 }
 
 void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask)
index e418969679c9c558cb5045691d9d195957625542..f8a9dfa657ba6a4a2e4a54d2ba218c0ff862be26 100644 (file)
@@ -299,6 +299,7 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
        primary_offset = ch->center_freq1 - ch->chan->center_freq;
        switch (ch->width) {
        case NL80211_CHAN_WIDTH_20:
+       case NL80211_CHAN_WIDTH_20_NOHT:
                ch_inf.bw = BRCMU_CHAN_BW_20;
                WARN_ON(primary_offset != 0);
                break;
@@ -323,6 +324,10 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
                                ch_inf.sb = BRCMU_CHAN_SB_LU;
                }
                break;
+       case NL80211_CHAN_WIDTH_80P80:
+       case NL80211_CHAN_WIDTH_160:
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
        default:
                WARN_ON_ONCE(1);
        }
@@ -333,6 +338,7 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
        case IEEE80211_BAND_5GHZ:
                ch_inf.band = BRCMU_CHAN_BAND_5G;
                break;
+       case IEEE80211_BAND_60GHZ:
        default:
                WARN_ON_ONCE(1);
        }
index 72e87b51f9993c0f1d896bdb9f06644cfafd7678..0b0d51a61060cbc42f8f755ba9b2e543b08b9426 100644 (file)
@@ -670,7 +670,6 @@ static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci,
                                  struct brcmf_sdio_dev *sdiodev)
 {
        int i;
-       uint fw_len, nv_len;
        char end;
 
        for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
@@ -684,25 +683,25 @@ static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci,
                return -ENODEV;
        }
 
-       fw_len = sizeof(sdiodev->fw_name) - 1;
-       nv_len = sizeof(sdiodev->nvram_name) - 1;
        /* check if firmware path is provided by module parameter */
        if (brcmf_firmware_path[0] != '\0') {
-               strncpy(sdiodev->fw_name, brcmf_firmware_path, fw_len);
-               strncpy(sdiodev->nvram_name, brcmf_firmware_path, nv_len);
-               fw_len -= strlen(sdiodev->fw_name);
-               nv_len -= strlen(sdiodev->nvram_name);
+               strlcpy(sdiodev->fw_name, brcmf_firmware_path,
+                       sizeof(sdiodev->fw_name));
+               strlcpy(sdiodev->nvram_name, brcmf_firmware_path,
+                       sizeof(sdiodev->nvram_name));
 
                end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1];
                if (end != '/') {
-                       strncat(sdiodev->fw_name, "/", fw_len);
-                       strncat(sdiodev->nvram_name, "/", nv_len);
-                       fw_len--;
-                       nv_len--;
+                       strlcat(sdiodev->fw_name, "/",
+                               sizeof(sdiodev->fw_name));
+                       strlcat(sdiodev->nvram_name, "/",
+                               sizeof(sdiodev->nvram_name));
                }
        }
-       strncat(sdiodev->fw_name, brcmf_fwname_data[i].bin, fw_len);
-       strncat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, nv_len);
+       strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin,
+               sizeof(sdiodev->fw_name));
+       strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv,
+               sizeof(sdiodev->nvram_name));
 
        return 0;
 }
index e0d9f19650b07e5df8b56342ec5e6d1bef88194f..eb03943f846326207061a2ed63e95df09af93caf 100644 (file)
@@ -284,7 +284,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (WARN_ON_ONCE(mvm->init_ucode_complete))
+       if (WARN_ON_ONCE(mvm->init_ucode_complete || mvm->calibrating))
                return 0;
 
        iwl_init_notification_wait(&mvm->notif_wait,
@@ -334,6 +334,8 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                goto out;
        }
 
+       mvm->calibrating = true;
+
        /* Send TX valid antennas before triggering calibrations */
        ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
        if (ret)
@@ -358,11 +360,17 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                        MVM_UCODE_CALIB_TIMEOUT);
        if (!ret)
                mvm->init_ucode_complete = true;
+
+       if (ret && iwl_mvm_is_radio_killed(mvm)) {
+               IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n");
+               ret = 1;
+       }
        goto out;
 
 error:
        iwl_remove_notification(&mvm->notif_wait, &calib_wait);
 out:
+       mvm->calibrating = false;
        if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
                /* we want to debug INIT and we have no NVM - fake */
                mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
index f308e52781f6f060e61d21608b313505b145ac48..57325589ee5bcba07d315983e517b3e73ff82499 100644 (file)
@@ -825,6 +825,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 
        mvm->scan_status = IWL_MVM_SCAN_NONE;
        mvm->ps_disabled = false;
+       mvm->calibrating = false;
 
        /* just in case one was running */
        ieee80211_remain_on_channel_expired(mvm->hw);
index 256765accbc659f9d431899d110ced3afa26c8f0..d015fac06a62ae2fc144b38ffbebe59d060b5617 100644 (file)
@@ -548,6 +548,7 @@ struct iwl_mvm {
        enum iwl_ucode_type cur_ucode;
        bool ucode_loaded;
        bool init_ucode_complete;
+       bool calibrating;
        u32 error_event_table;
        u32 log_event_table;
        u32 umac_error_event_table;
index bd52ecfabedba1b3d6d36c416e42a6a011e06d08..7a9578567f4f0853a0e560fb05c7ebb6377492c1 100644 (file)
@@ -427,6 +427,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        }
        mvm->sf_state = SF_UNINIT;
        mvm->low_latency_agg_frame_limit = 6;
+       mvm->cur_ucode = IWL_UCODE_INIT;
 
        mutex_init(&mvm->mutex);
        mutex_init(&mvm->d0i3_suspend_mutex);
@@ -757,6 +758,7 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
 static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       bool calibrating = ACCESS_ONCE(mvm->calibrating);
 
        if (state)
                set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
@@ -765,7 +767,15 @@ static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
 
        wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
 
-       return state && mvm->cur_ucode != IWL_UCODE_INIT;
+       /* iwl_run_init_mvm_ucode is waiting for results, abort it */
+       if (calibrating)
+               iwl_abort_notification_waits(&mvm->notif_wait);
+
+       /*
+        * Stop the device if we run OPERATIONAL firmware or if we are in the
+        * middle of the calibrations.
+        */
+       return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating);
 }
 
 static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
index 5cd59a43e1da51acb1fd4debb21ce8e9f38be1c0..fb2a8628b8fca3ef75184471bf40faf89f7801d4 100644 (file)
@@ -603,16 +603,6 @@ static int iwl_mvm_cancel_regular_scan(struct iwl_mvm *mvm)
                                               SCAN_COMPLETE_NOTIFICATION };
        int ret;
 
-       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
-               return 0;
-
-       if (iwl_mvm_is_radio_killed(mvm)) {
-               ieee80211_scan_completed(mvm->hw, true);
-               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
-               mvm->scan_status = IWL_MVM_SCAN_NONE;
-               return 0;
-       }
-
        iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
                                   scan_abort_notif,
                                   ARRAY_SIZE(scan_abort_notif),
@@ -1431,6 +1421,16 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
 
 int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
 {
+       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
+               return 0;
+
+       if (iwl_mvm_is_radio_killed(mvm)) {
+               ieee80211_scan_completed(mvm->hw, true);
+               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               return 0;
+       }
+
        if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
                return iwl_mvm_scan_offload_stop(mvm, true);
        return iwl_mvm_cancel_regular_scan(mvm);
index 40a290603eade3ffc9eb173e846ad9dd2a711a9f..ea8efed25c6abadbe97001ce04e7823f2da8d157 100644 (file)
@@ -913,7 +913,8 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
         * restart. So don't process again if the device is
         * already dead.
         */
-       if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+       if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+               IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n");
                iwl_pcie_tx_stop(trans);
                iwl_pcie_rx_stop(trans);
 
@@ -943,7 +944,6 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        /* clear all status bits */
        clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
        clear_bit(STATUS_INT_ENABLED, &trans->status);
-       clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
        clear_bit(STATUS_TPOWER_PMI, &trans->status);
        clear_bit(STATUS_RFKILL, &trans->status);
 
@@ -1898,8 +1898,7 @@ static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans,
                int reg;
                __le32 *val;
 
-               prph_len += sizeof(*data) + sizeof(*prph) +
-                       num_bytes_in_chunk;
+               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) +
index e23a8d14578a211b3c81da83380b9c802edfccad..2371d11e4190b718c21520305130d8bddb1c4bb5 100644 (file)
@@ -2259,7 +2259,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
        if (err != 0) {
                printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n",
                       err);
-               goto failed_hw;
+               goto failed_bind;
        }
 
        skb_queue_head_init(&data->pending);
@@ -2470,6 +2470,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
        return idx;
 
 failed_hw:
+       device_release_driver(data->dev);
+failed_bind:
        device_unregister(data->dev);
 failed_drvdata:
        ieee80211_free_hw(hw);
index 40057079ffb9537eae9549518be3e5df29f1a16b..5ef5a0eeba50eb682d30e5879f565ac48ad217e4 100644 (file)
@@ -196,6 +196,7 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv,
        mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
 
        del_timer_sync(&tbl->timer_context.timer);
+       tbl->timer_context.timer_is_set = false;
 
        spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        list_del(&tbl->list);
@@ -297,6 +298,7 @@ mwifiex_flush_data(unsigned long context)
                (struct reorder_tmr_cnxt *) context;
        int start_win, seq_num;
 
+       ctx->timer_is_set = false;
        seq_num = mwifiex_11n_find_last_seq_num(ctx);
 
        if (seq_num < 0)
@@ -385,6 +387,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
 
        new_node->timer_context.ptr = new_node;
        new_node->timer_context.priv = priv;
+       new_node->timer_context.timer_is_set = false;
 
        init_timer(&new_node->timer_context.timer);
        new_node->timer_context.timer.function = mwifiex_flush_data;
@@ -399,6 +402,22 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
        spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 }
 
+static void
+mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl)
+{
+       u32 min_flush_time;
+
+       if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32)
+               min_flush_time = MIN_FLUSH_TIMER_15_MS;
+       else
+               min_flush_time = MIN_FLUSH_TIMER_MS;
+
+       mod_timer(&tbl->timer_context.timer,
+                 jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size));
+
+       tbl->timer_context.timer_is_set = true;
+}
+
 /*
  * This function prepares command for adding a BA request.
  *
@@ -523,31 +542,31 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
                                u8 *ta, u8 pkt_type, void *payload)
 {
        struct mwifiex_rx_reorder_tbl *tbl;
-       int start_win, end_win, win_size;
+       int prev_start_win, start_win, end_win, win_size;
        u16 pkt_index;
        bool init_window_shift = false;
+       int ret = 0;
 
        tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
        if (!tbl) {
                if (pkt_type != PKT_TYPE_BAR)
                        mwifiex_11n_dispatch_pkt(priv, payload);
-               return 0;
+               return ret;
        }
 
        if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) {
                mwifiex_11n_dispatch_pkt(priv, payload);
-               return 0;
+               return ret;
        }
 
        start_win = tbl->start_win;
+       prev_start_win = start_win;
        win_size = tbl->win_size;
        end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
        if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
                init_window_shift = true;
                tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
        }
-       mod_timer(&tbl->timer_context.timer,
-                 jiffies + msecs_to_jiffies(MIN_FLUSH_TIMER_MS * win_size));
 
        if (tbl->flags & RXREOR_FORCE_NO_DROP) {
                dev_dbg(priv->adapter->dev,
@@ -568,11 +587,14 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
                if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
                        if (seq_num >= ((start_win + TWOPOW11) &
                                        (MAX_TID_VALUE - 1)) &&
-                           seq_num < start_win)
-                               return -1;
+                           seq_num < start_win) {
+                               ret = -1;
+                               goto done;
+                       }
                } else if ((seq_num < start_win) ||
-                          (seq_num > (start_win + TWOPOW11))) {
-                       return -1;
+                          (seq_num >= (start_win + TWOPOW11))) {
+                       ret = -1;
+                       goto done;
                }
        }
 
@@ -601,8 +623,10 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
                else
                        pkt_index = (seq_num+MAX_TID_VALUE) - start_win;
 
-               if (tbl->rx_reorder_ptr[pkt_index])
-                       return -1;
+               if (tbl->rx_reorder_ptr[pkt_index]) {
+                       ret = -1;
+                       goto done;
+               }
 
                tbl->rx_reorder_ptr[pkt_index] = payload;
        }
@@ -613,7 +637,11 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
         */
        mwifiex_11n_scan_and_dispatch(priv, tbl);
 
-       return 0;
+done:
+       if (!tbl->timer_context.timer_is_set ||
+           prev_start_win != tbl->start_win)
+               mwifiex_11n_rxreorder_timer_restart(tbl);
+       return ret;
 }
 
 /*
index 3a87bb0e3a62adb477784a94feba13cefd32626b..63ecea89b4ab562cc00942b2542fac63ed8202e0 100644 (file)
@@ -21,6 +21,8 @@
 #define _MWIFIEX_11N_RXREORDER_H_
 
 #define MIN_FLUSH_TIMER_MS             50
+#define MIN_FLUSH_TIMER_15_MS          15
+#define MWIFIEX_BA_WIN_SIZE_32         32
 
 #define PKT_TYPE_BAR 0xE7
 #define MAX_TID_VALUE                  (2 << 11)
index 51a67f34c8cb093c0c022b779e56c71056929cd0..5a690d5210f02b5206ca139006adc534c4b88617 100644 (file)
@@ -600,6 +600,7 @@ struct reorder_tmr_cnxt {
        struct timer_list timer;
        struct mwifiex_rx_reorder_tbl *ptr;
        struct mwifiex_private *priv;
+       u8 timer_is_set;
 };
 
 struct mwifiex_rx_reorder_tbl {
index 573897b8e878a2e6bfc3aa61a9df72d9b99e0372..8444313eabe2bbc1bdcb660c5a82bff17f3fdb90 100644 (file)
@@ -1111,6 +1111,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        /* Ovislink */
        { USB_DEVICE(0x1b75, 0x3071) },
        { USB_DEVICE(0x1b75, 0x3072) },
+       { USB_DEVICE(0x1b75, 0xa200) },
        /* Para */
        { USB_DEVICE(0x20b8, 0x8888) },
        /* Pegatron */
index 8e68f87ab13c3081f062acc69fb71f59601e836f..66ff36447b9473799e27899bf44c3c3c8eb3994b 100644 (file)
@@ -158,55 +158,29 @@ void rt2x00queue_align_frame(struct sk_buff *skb)
        skb_trim(skb, frame_length);
 }
 
-void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length)
+/*
+ * H/W needs L2 padding between the header and the paylod if header size
+ * is not 4 bytes aligned.
+ */
+void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int hdr_len)
 {
-       unsigned int payload_length = skb->len - header_length;
-       unsigned int header_align = ALIGN_SIZE(skb, 0);
-       unsigned int payload_align = ALIGN_SIZE(skb, header_length);
-       unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0;
+       unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
 
-       /*
-        * Adjust the header alignment if the payload needs to be moved more
-        * than the header.
-        */
-       if (payload_align > header_align)
-               header_align += 4;
-
-       /* There is nothing to do if no alignment is needed */
-       if (!header_align)
+       if (!l2pad)
                return;
 
-       /* Reserve the amount of space needed in front of the frame */
-       skb_push(skb, header_align);
-
-       /*
-        * Move the header.
-        */
-       memmove(skb->data, skb->data + header_align, header_length);
-
-       /* Move the payload, if present and if required */
-       if (payload_length && payload_align)
-               memmove(skb->data + header_length + l2pad,
-                       skb->data + header_length + l2pad + payload_align,
-                       payload_length);
-
-       /* Trim the skb to the correct size */
-       skb_trim(skb, header_length + l2pad + payload_length);
+       skb_push(skb, l2pad);
+       memmove(skb->data, skb->data + l2pad, hdr_len);
 }
 
-void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
+void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int hdr_len)
 {
-       /*
-        * L2 padding is only present if the skb contains more than just the
-        * IEEE 802.11 header.
-        */
-       unsigned int l2pad = (skb->len > header_length) ?
-                               L2PAD_SIZE(header_length) : 0;
+       unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
 
        if (!l2pad)
                return;
 
-       memmove(skb->data + l2pad, skb->data, header_length);
+       memmove(skb->data + l2pad, skb->data, hdr_len);
        skb_pull(skb, l2pad);
 }
 
index 58ba718308862391b108a41e0eab1e7ff7fa226a..40b6d1d006d7ab7dc9076a967fedae75259dc1fd 100644 (file)
@@ -467,7 +467,7 @@ static void _rtl_init_deferred_work(struct ieee80211_hw *hw)
                    rtl_easy_concurrent_retrytimer_callback, (unsigned long)hw);
        /* <2> work queue */
        rtlpriv->works.hw = hw;
-       rtlpriv->works.rtl_wq = alloc_workqueue(rtlpriv->cfg->name, 0, 0);
+       rtlpriv->works.rtl_wq = alloc_workqueue("%s", 0, 0, rtlpriv->cfg->name);
        INIT_DELAYED_WORK(&rtlpriv->works.watchdog_wq,
                          (void *)rtl_watchdog_wq_callback);
        INIT_DELAYED_WORK(&rtlpriv->works.ips_nic_off_wq,
index 884d90526f9e13c4f9601e66146a5af3e473fcf6..af52f0bdb71e979766c600f7f75da3cc452ccf64 100644 (file)
@@ -1831,3 +1831,9 @@ const struct ieee80211_ops rtl_ops = {
        .flush = rtl_op_flush,
 };
 EXPORT_SYMBOL_GPL(rtl_ops);
+
+bool rtl_btc_status_false(void)
+{
+       return false;
+}
+EXPORT_SYMBOL_GPL(rtl_btc_status_false);
index 59cd3b9dca25f895bcd6ee24f43f366867c8e41a..624e1dc16d31d383ba92cbc8cd08e28d180df882 100644 (file)
@@ -42,5 +42,6 @@ void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr,
                     u32 mask, u32 data);
 void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data);
 bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb);
+bool rtl_btc_status_false(void);
 
 #endif
index 667aba81246c9ac094db11a5c455be9c29d273ad..61f5d36eca6aaa50c5b33da090736662cc27b77a 100644 (file)
@@ -842,7 +842,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
                        break;
                }
                /* handle command packet here */
-               if (rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
+               if (rtlpriv->cfg->ops->rx_command_packet &&
+                   rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
                                dev_kfree_skb_any(skb);
                                goto end;
                }
@@ -1127,9 +1128,14 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw)
 
        __skb_queue_tail(&ring->queue, pskb);
 
-       rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
-                                   &temp_one);
-
+       if (rtlpriv->use_new_trx_flow) {
+               temp_one = 4;
+               rtlpriv->cfg->ops->set_desc(hw, (u8 *)pbuffer_desc, true,
+                                           HW_DESC_OWN, (u8 *)&temp_one);
+       } else {
+               rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
+                                           &temp_one);
+       }
        return;
 }
 
@@ -1370,9 +1376,9 @@ static void _rtl_pci_free_tx_ring(struct ieee80211_hw *hw,
        ring->desc = NULL;
        if (rtlpriv->use_new_trx_flow) {
                pci_free_consistent(rtlpci->pdev,
-                                   sizeof(*ring->desc) * ring->entries,
+                                   sizeof(*ring->buffer_desc) * ring->entries,
                                    ring->buffer_desc, ring->buffer_desc_dma);
-               ring->desc = NULL;
+               ring->buffer_desc = NULL;
        }
 }
 
@@ -1543,7 +1549,6 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw)
                                                         true,
                                                         HW_DESC_TXBUFF_ADDR),
                                                 skb->len, PCI_DMA_TODEVICE);
-                               ring->idx = (ring->idx + 1) % ring->entries;
                                kfree_skb(skb);
                                ring->idx = (ring->idx + 1) % ring->entries;
                        }
@@ -1796,7 +1801,8 @@ static int rtl_pci_start(struct ieee80211_hw *hw)
        rtl_pci_reset_trx_ring(hw);
 
        rtlpci->driver_is_goingto_unload = false;
-       if (rtlpriv->cfg->ops->get_btc_status()) {
+       if (rtlpriv->cfg->ops->get_btc_status &&
+           rtlpriv->cfg->ops->get_btc_status()) {
                rtlpriv->btcoexist.btc_ops->btc_init_variables(rtlpriv);
                rtlpriv->btcoexist.btc_ops->btc_init_hal_vars(rtlpriv);
        }
index a00861b26ece7ae6b2c6168ca22bdaed42c71033..29983bc96a89289187617a5cf202386f74386639 100644 (file)
@@ -656,7 +656,8 @@ static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 };
 
-void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished)
+void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw,
+        bool (*cmd_send_packet)(struct ieee80211_hw *, struct sk_buff *))
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
@@ -722,7 +723,10 @@ void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished)
        memcpy((u8 *)skb_put(skb, totalpacketlen),
               &reserved_page_packet, totalpacketlen);
 
-       rtstatus = rtl_cmd_send_packet(hw, skb);
+       if (cmd_send_packet)
+               rtstatus = cmd_send_packet(hw, skb);
+       else
+               rtstatus = rtl_cmd_send_packet(hw, skb);
 
        if (rtstatus)
                b_dlok = true;
index a815bd6273da0645cf86f6f77050f220875ec6ff..b64ae45dc6743869da3746a7382683936e86f88c 100644 (file)
@@ -109,7 +109,9 @@ void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id,
                         u32 cmd_len, u8 *p_cmdbuffer);
 void rtl92c_firmware_selfreset(struct ieee80211_hw *hw);
 void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
-void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished);
+void rtl92c_set_fw_rsvdpagepkt
+       (struct ieee80211_hw *hw,
+        bool (*cmd_send_packet)(struct ieee80211_hw *, struct sk_buff *));
 void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
 void usb_writeN_async(struct rtl_priv *rtlpriv, u32 addr, void *data, u16 len);
 void rtl92c_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state);
index 831df101d7b7a97866b55283c8e998e3c9152806..9b660df6fd712fd517638723bb0616b17b9df257 100644 (file)
        LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 16, 4)
 #define        GET_C2H_CMD_FEEDBACK_CCX_SEQ(__pcmdfbhdr)       \
        LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 20, 12)
+#define GET_RX_STATUS_DESC_BUFF_ADDR(__pdesc)                  \
+       SHIFT_AND_MASK_LE(__pdesc + 24, 0, 32)
 
 #define CHIP_VER_B                     BIT(4)
 #define CHIP_BONDING_IDENTIFIER(_value) (((_value) >> 22) & 0x3)
index 8ec0f031f48a575035dbe59af4b2f236d4260c72..55357d69397a3370d8f4081ecec3d578daa3b8b9 100644 (file)
@@ -459,7 +459,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
                                rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
                                               tmp_reg422 & (~BIT(6)));
 
-                               rtl92c_set_fw_rsvdpagepkt(hw, 0);
+                               rtl92c_set_fw_rsvdpagepkt(hw, NULL);
 
                                _rtl92ce_set_bcn_ctrl_reg(hw, BIT(3), 0);
                                _rtl92ce_set_bcn_ctrl_reg(hw, 0, BIT(4));
index d86b5b566444e1098a666909e3d86865c00f0b7c..46ea07605eb47f449f7850dea3b8b0862555ffde 100644 (file)
@@ -244,6 +244,7 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = {
        .phy_lc_calibrate = _rtl92ce_phy_lc_calibrate,
        .phy_set_bw_mode_callback = rtl92ce_phy_set_bw_mode_callback,
        .dm_dynamic_txpower = rtl92ce_dm_dynamic_txpower,
+       .get_btc_status = rtl_btc_status_false,
 };
 
 static struct rtl_mod_params rtl92ce_mod_params = {
index 2fb9c7acb76a3bf7d6fa1feee728b15365b86207..dc3d20b17a265479906fa9d6777cc6e29e533f34 100644 (file)
@@ -728,6 +728,9 @@ u32 rtl92ce_get_desc(u8 *p_desc, bool istx, u8 desc_name)
                case HW_DESC_RXPKT_LEN:
                        ret = GET_RX_DESC_PKT_LEN(pdesc);
                        break;
+               case HW_DESC_RXBUFF_ADDR:
+                       ret = GET_RX_STATUS_DESC_BUFF_ADDR(pdesc);
+                       break;
                default:
                        RT_ASSERT(false, "ERR rxdesc :%d not process\n",
                                  desc_name);
index 04aa0b5f5b3d10a1ae16eb8db5ea74d4f38e1890..873363acbacfac12df0d0c724dc962c74b2a2fc7 100644 (file)
@@ -1592,6 +1592,20 @@ void rtl92cu_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
        }
 }
 
+bool usb_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+  /* Currently nothing happens here.
+   * Traffic stops after some seconds in WPA2 802.11n mode.
+   * Maybe because rtl8192cu chip should be set from here?
+   * If I understand correctly, the realtek vendor driver sends some urbs
+   * if its "here".
+   *
+   * This is maybe necessary:
+   * rtlpriv->cfg->ops->fill_tx_cmddesc(hw, buffer, 1, 1, skb);
+   */
+       return true;
+}
+
 void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -1939,7 +1953,8 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
                                        recover = true;
                                rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
                                               tmp_reg422 & (~BIT(6)));
-                               rtl92c_set_fw_rsvdpagepkt(hw, 0);
+                               rtl92c_set_fw_rsvdpagepkt(hw,
+                                                         &usb_cmd_send_packet);
                                _rtl92cu_set_bcn_ctrl_reg(hw, BIT(3), 0);
                                _rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(4));
                                if (recover)
index 0f7812e0c8aa0c75d5382c52b4872431447c8108..c1e33b0228c0176e1ae31f5a8f99085a4f840397 100644 (file)
@@ -104,7 +104,6 @@ bool rtl92cu_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 * valid);
 void rtl92cu_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid);
 int rtl92c_download_fw(struct ieee80211_hw *hw);
 void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
-void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool dl_finished);
 void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
 void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw,
                         u8 element_id, u32 cmd_len, u8 *p_cmdbuffer);
index 7c5fbaf5fee0d790d1cfbeaa563a1c360a1a976b..e06bafee37f9764b4041bd64665c46e3a889d238 100644 (file)
@@ -101,6 +101,12 @@ static void rtl92cu_deinit_sw_vars(struct ieee80211_hw *hw)
        }
 }
 
+/* get bt coexist status */
+static bool rtl92cu_get_btc_status(void)
+{
+       return false;
+}
+
 static struct rtl_hal_ops rtl8192cu_hal_ops = {
        .init_sw_vars = rtl92cu_init_sw_vars,
        .deinit_sw_vars = rtl92cu_deinit_sw_vars,
@@ -148,6 +154,7 @@ static struct rtl_hal_ops rtl8192cu_hal_ops = {
        .phy_set_bw_mode_callback = rtl92cu_phy_set_bw_mode_callback,
        .dm_dynamic_txpower = rtl92cu_dm_dynamic_txpower,
        .fill_h2c_cmd = rtl92c_fill_h2c_cmd,
+       .get_btc_status = rtl92cu_get_btc_status,
 };
 
 static struct rtl_mod_params rtl92cu_mod_params = {
index edab5a5351b52a814ec10833109666309279e2b1..a0aba088259aa97b6f80fb37e02d6f0c881b039b 100644 (file)
@@ -251,6 +251,7 @@ static struct rtl_hal_ops rtl8192de_hal_ops = {
        .get_rfreg = rtl92d_phy_query_rf_reg,
        .set_rfreg = rtl92d_phy_set_rf_reg,
        .linked_set_reg = rtl92d_linked_set_reg,
+       .get_btc_status = rtl_btc_status_false,
 };
 
 static struct rtl_mod_params rtl92de_mod_params = {
index dfdc9b20e4ad4d868012c80f4233559e58050052..1a87edca2c3f36a1d540eabc4c3247da5b1959d6 100644 (file)
@@ -362,7 +362,7 @@ void rtl92ee_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
                }
                break;
        default:
-               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
                         "switch case not process %x\n", variable);
                break;
        }
@@ -591,7 +591,7 @@ void rtl92ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
                                acm_ctrl &= (~ACMHW_BEQEN);
                                break;
                        default:
-                               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                               RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
                                         "switch case not process\n");
                                break;
                        }
@@ -710,7 +710,7 @@ void rtl92ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
                }
                break;
        default:
-               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
                         "switch case not process %x\n", variable);
                break;
        }
@@ -2424,7 +2424,7 @@ void rtl92ee_set_key(struct ieee80211_hw *hw, u32 key_index,
                        enc_algo = CAM_AES;
                        break;
                default:
-                       RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                       RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
                                 "switch case not process\n");
                        enc_algo = CAM_TKIP;
                        break;
index 83c98674bfd3176f8fc512f80b7a31be83ba87da..6e7a70b43949eab504d70a70742dc216fb6e27d1 100644 (file)
 /* DWORD 6 */
 #define SET_RX_STATUS__DESC_BUFF_ADDR(__pdesc, __val)  \
        SET_BITS_OFFSET_LE(__pdesc + 24, 0, 32, __val)
+#define GET_RX_STATUS_DESC_BUFF_ADDR(__pdesc)                  \
+       SHIFT_AND_MASK_LE(__pdesc + 24, 0, 32)
 
 #define SE_RX_HAL_IS_CCK_RATE(_pdesc)\
        (GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE1M ||  \
index 00e067044c08d0a9cafa9009466eb22c21965d56..5761d5b49e39e4e9cb2ce703f39578ad1cf99e74 100644 (file)
@@ -1201,6 +1201,9 @@ static int _rtl92se_set_media_status(struct ieee80211_hw *hw,
 
        }
 
+       if (type != NL80211_IFTYPE_AP &&
+           rtlpriv->mac80211.link_state < MAC80211_LINKED)
+               bt_msr = rtl_read_byte(rtlpriv, MSR) & ~MSR_LINK_MASK;
        rtl_write_byte(rtlpriv, (MSR), bt_msr);
 
        temp = rtl_read_dword(rtlpriv, TCR);
@@ -1262,6 +1265,7 @@ void rtl92se_enable_interrupt(struct ieee80211_hw *hw)
        rtl_write_dword(rtlpriv, INTA_MASK, rtlpci->irq_mask[0]);
        /* Support Bit 32-37(Assign as Bit 0-5) interrupt setting now */
        rtl_write_dword(rtlpriv, INTA_MASK + 4, rtlpci->irq_mask[1] & 0x3F);
+       rtlpci->irq_enabled = true;
 }
 
 void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
@@ -1276,8 +1280,7 @@ void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
        rtlpci = rtl_pcidev(rtl_pcipriv(hw));
        rtl_write_dword(rtlpriv, INTA_MASK, 0);
        rtl_write_dword(rtlpriv, INTA_MASK + 4, 0);
-
-       synchronize_irq(rtlpci->pdev->irq);
+       rtlpci->irq_enabled = false;
 }
 
 static u8 _rtl92s_set_sysclk(struct ieee80211_hw *hw, u8 data)
index 77c5b5f352441a3a2a2d0c26b5627266aa15d5d9..4b4612fe2fdbdf318bad0b0a6e36873a6340d60b 100644 (file)
@@ -399,6 +399,8 @@ static bool _rtl92s_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw,
                case 2:
                        currentcmd = &postcommoncmd[*step];
                        break;
+               default:
+                       return true;
                }
 
                if (currentcmd->cmdid == CMDID_END) {
index 1bff2a0f760087d0a510e247f01d1c6a33eee891..fb003868bdef7ed9bbb775042a380190ce68162d 100644 (file)
@@ -87,11 +87,8 @@ static void rtl92s_init_aspm_vars(struct ieee80211_hw *hw)
 static void rtl92se_fw_cb(const struct firmware *firmware, void *context)
 {
        struct ieee80211_hw *hw = context;
-       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
        struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
        struct rt_firmware *pfirmware = NULL;
-       int err;
 
        RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
                         "Firmware callback routine entered!\n");
@@ -112,20 +109,6 @@ static void rtl92se_fw_cb(const struct firmware *firmware, void *context)
        memcpy(pfirmware->sz_fw_tmpbuffer, firmware->data, firmware->size);
        pfirmware->sz_fw_tmpbufferlen = firmware->size;
        release_firmware(firmware);
-
-       err = ieee80211_register_hw(hw);
-       if (err) {
-               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
-                        "Can't register mac80211 hw\n");
-               return;
-       } else {
-               rtlpriv->mac80211.mac80211_registered = 1;
-       }
-       rtlpci->irq_alloc = 1;
-       set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
-
-       /*init rfkill */
-       rtl_init_rfkill(hw);
 }
 
 static int rtl92s_init_sw_vars(struct ieee80211_hw *hw)
@@ -226,8 +209,8 @@ static int rtl92s_init_sw_vars(struct ieee80211_hw *hw)
        if (!rtlpriv->rtlhal.pfirmware)
                return 1;
 
-       rtlpriv->max_fw_size = RTL8190_MAX_RAW_FIRMWARE_CODE_SIZE;
-
+       rtlpriv->max_fw_size = RTL8190_MAX_FIRMWARE_CODE_SIZE*2 +
+                              sizeof(struct fw_hdr);
        pr_info("Driver for Realtek RTL8192SE/RTL8191SE\n"
                "Loading firmware %s\n", rtlpriv->cfg->fw_name);
        /* request fw */
@@ -253,6 +236,19 @@ static void rtl92s_deinit_sw_vars(struct ieee80211_hw *hw)
        }
 }
 
+static bool rtl92se_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue,
+                                     u16 index)
+{
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
+       u8 *entry = (u8 *)(&ring->desc[ring->idx]);
+       u8 own = (u8)rtl92se_get_desc(entry, true, HW_DESC_OWN);
+
+       if (own)
+               return false;
+       return true;
+}
+
 static struct rtl_hal_ops rtl8192se_hal_ops = {
        .init_sw_vars = rtl92s_init_sw_vars,
        .deinit_sw_vars = rtl92s_deinit_sw_vars,
@@ -286,6 +282,7 @@ static struct rtl_hal_ops rtl8192se_hal_ops = {
        .led_control = rtl92se_led_control,
        .set_desc = rtl92se_set_desc,
        .get_desc = rtl92se_get_desc,
+       .is_tx_desc_closed = rtl92se_is_tx_desc_closed,
        .tx_polling = rtl92se_tx_polling,
        .enable_hw_sec = rtl92se_enable_hw_security_config,
        .set_key = rtl92se_set_key,
@@ -294,6 +291,7 @@ static struct rtl_hal_ops rtl8192se_hal_ops = {
        .set_bbreg = rtl92s_phy_set_bb_reg,
        .get_rfreg = rtl92s_phy_query_rf_reg,
        .set_rfreg = rtl92s_phy_set_rf_reg,
+       .get_btc_status = rtl_btc_status_false,
 };
 
 static struct rtl_mod_params rtl92se_mod_params = {
@@ -322,6 +320,8 @@ static struct rtl_hal_cfg rtl92se_hal_cfg = {
        .maps[MAC_RCR_ACRC32] = RCR_ACRC32,
        .maps[MAC_RCR_ACF] = RCR_ACF,
        .maps[MAC_RCR_AAP] = RCR_AAP,
+       .maps[MAC_HIMR] = INTA_MASK,
+       .maps[MAC_HIMRE] = INTA_MASK + 4,
 
        .maps[EFUSE_TEST] = REG_EFUSE_TEST,
        .maps[EFUSE_CTRL] = REG_EFUSE_CTRL,
index b358ebce8942127413105b39e3799006d559eed8..672fd3b02835e30414044311af02a3383886e554 100644 (file)
@@ -640,6 +640,9 @@ u32 rtl92se_get_desc(u8 *desc, bool istx, u8 desc_name)
                case HW_DESC_RXPKT_LEN:
                        ret = GET_RX_STATUS_DESC_PKT_LEN(desc);
                        break;
+               case HW_DESC_RXBUFF_ADDR:
+                       ret = GET_RX_STATUS_DESC_BUFF_ADDR(desc);
+                       break;
                default:
                        RT_ASSERT(false, "ERR rxdesc :%d not process\n",
                                  desc_name);
index 4be278f7df51fa210416bc05d93a895c8187102c..9b4d8a63791511ae54b143dae0209f65cd2cb387 100644 (file)
@@ -1889,15 +1889,18 @@ static void _rtl8821ae_store_tx_power_by_rate(struct ieee80211_hw *hw,
        struct rtl_phy *rtlphy = &rtlpriv->phy;
        u8 rate_section = _rtl8821ae_get_rate_section_index(regaddr);
 
-       if (band != BAND_ON_2_4G && band != BAND_ON_5G)
+       if (band != BAND_ON_2_4G && band != BAND_ON_5G) {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid Band %d\n", band);
-
-       if (rfpath >= MAX_RF_PATH)
+               band = BAND_ON_2_4G;
+       }
+       if (rfpath >= MAX_RF_PATH) {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid RfPath %d\n", rfpath);
-
-       if (txnum >= MAX_RF_PATH)
+               rfpath = MAX_RF_PATH - 1;
+       }
+       if (txnum >= MAX_RF_PATH) {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid TxNum %d\n", txnum);
-
+               txnum = MAX_RF_PATH - 1;
+       }
        rtlphy->tx_power_by_rate_offset[band][rfpath][txnum][rate_section] = data;
        RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
                 "TxPwrByRateOffset[Band %d][RfPath %d][TxNum %d][RateSection %d] = 0x%x\n",
index 10cf69c4bc42ea93adefdab5569cbe90a19a4a63..46ee956d0235dc79dbb8130242b5b2586fd4db5e 100644 (file)
@@ -1117,7 +1117,18 @@ int rtl_usb_probe(struct usb_interface *intf,
        }
        rtlpriv->cfg->ops->init_sw_leds(hw);
 
+       err = ieee80211_register_hw(hw);
+       if (err) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+                        "Can't register mac80211 hw.\n");
+               err = -ENODEV;
+               goto error_out;
+       }
+       rtlpriv->mac80211.mac80211_registered = 1;
+
+       set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
        return 0;
+
 error_out:
        rtl_deinit_core(hw);
        _rtl_usb_io_handler_release(hw);
index d043449a079d190ff87a15748a3f54466a755be0..6e82d888287c280e096bc4c8c4cd585f786fb507 100644 (file)
 #define IEEE802154_MTU                 127
 #define IEEE802154_MIN_PSDU_LEN                5
 
+#define IEEE802154_PAN_ID_BROADCAST    0xffff
+#define IEEE802154_ADDR_SHORT_BROADCAST        0xffff
+#define IEEE802154_ADDR_SHORT_UNSPEC   0xfffe
+
 #define IEEE802154_EXTENDED_ADDR_LEN   8
 
+#define IEEE802154_LIFS_PERIOD         40
+#define IEEE802154_SIFS_PERIOD         12
+
+#define IEEE802154_MAX_CHANNEL         26
+#define IEEE802154_MAX_PAGE            31
+
 #define IEEE802154_FC_TYPE_BEACON      0x0     /* Frame is beacon */
 #define        IEEE802154_FC_TYPE_DATA         0x1     /* Frame is data */
 #define IEEE802154_FC_TYPE_ACK         0x2     /* Frame is acknowledgment */
index d5f85d7746bc5ad14ac146abc003270f291ad505..e56f9099f8e34b6f2970928da3d17de9c12de019 100644 (file)
@@ -639,7 +639,7 @@ struct hci_cp_user_passkey_reply {
 struct hci_cp_remote_oob_data_reply {
        bdaddr_t bdaddr;
        __u8     hash[16];
-       __u8     randomizer[16];
+       __u8     rand[16];
 } __packed;
 
 #define HCI_OP_REMOTE_OOB_DATA_NEG_REPLY       0x0433
@@ -731,9 +731,9 @@ struct hci_rp_set_csb {
 struct hci_cp_remote_oob_ext_data_reply {
        bdaddr_t bdaddr;
        __u8     hash192[16];
-       __u8     randomizer192[16];
+       __u8     rand192[16];
        __u8     hash256[16];
-       __u8     randomizer256[16];
+       __u8     rand256[16];
 } __packed;
 
 #define HCI_OP_SNIFF_MODE              0x0803
@@ -940,7 +940,7 @@ struct hci_cp_write_ssp_mode {
 struct hci_rp_read_local_oob_data {
        __u8     status;
        __u8     hash[16];
-       __u8     randomizer[16];
+       __u8     rand[16];
 } __packed;
 
 #define HCI_OP_READ_INQ_RSP_TX_POWER   0x0c58
@@ -1024,9 +1024,9 @@ struct hci_cp_write_sc_support {
 struct hci_rp_read_local_oob_ext_data {
        __u8     status;
        __u8     hash192[16];
-       __u8     randomizer192[16];
+       __u8     rand192[16];
        __u8     hash256[16];
-       __u8     randomizer256[16];
+       __u8     rand256[16];
 } __packed;
 
 #define HCI_OP_READ_LOCAL_VERSION      0x1001
index 4e39a5adfcabbf3087fa2b0daa8e0d12ce1cf5a0..a805b3d97c0b467d55fc7812c280a61e03771558 100644 (file)
@@ -108,6 +108,7 @@ struct smp_csrk {
 
 struct smp_ltk {
        struct list_head list;
+       struct rcu_head rcu;
        bdaddr_t bdaddr;
        u8 bdaddr_type;
        u8 authenticated;
@@ -120,6 +121,7 @@ struct smp_ltk {
 
 struct smp_irk {
        struct list_head list;
+       struct rcu_head rcu;
        bdaddr_t rpa;
        bdaddr_t bdaddr;
        u8 addr_type;
@@ -138,9 +140,9 @@ struct oob_data {
        struct list_head list;
        bdaddr_t bdaddr;
        u8 hash192[16];
-       u8 randomizer192[16];
+       u8 rand192[16];
        u8 hash256[16];
-       u8 randomizer256[16];
+       u8 rand256[16];
 };
 
 #define HCI_MAX_SHORT_NAME_LENGTH      10
@@ -941,10 +943,10 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev);
 struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
                                          bdaddr_t *bdaddr);
 int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                           u8 *hash, u8 *randomizer);
+                           u8 *hash, u8 *rand);
 int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                               u8 *hash192, u8 *randomizer192,
-                               u8 *hash256, u8 *randomizer256);
+                               u8 *hash192, u8 *rand192,
+                               u8 *hash256, u8 *rand256);
 int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
 
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
@@ -1372,8 +1374,8 @@ void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
                                    u8 status);
 void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
 void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
-                                      u8 *randomizer192, u8 *hash256,
-                                      u8 *randomizer256, u8 status);
+                                      u8 *rand192, u8 *hash256, u8 *rand256,
+                                      u8 status);
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                       u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
                       u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
index ead99f032f7ab381a0559cf5f4ee23600632d479..061e648052c8745f5457fba577e6183e16ef2779 100644 (file)
@@ -28,6 +28,7 @@
 #define __L2CAP_H
 
 #include <asm/unaligned.h>
+#include <linux/atomic.h>
 
 /* L2CAP defaults */
 #define L2CAP_DEFAULT_MTU              672
@@ -481,6 +482,7 @@ struct l2cap_chan {
        struct hci_conn         *hs_hcon;
        struct hci_chan         *hs_hchan;
        struct kref     kref;
+       atomic_t        nesting;
 
        __u8            state;
 
@@ -713,6 +715,17 @@ enum {
        FLAG_HOLD_HCI_CONN,
 };
 
+/* Lock nesting levels for L2CAP channels. We need these because lockdep
+ * otherwise considers all channels equal and will e.g. complain about a
+ * connection oriented channel triggering SMP procedures or a listening
+ * channel creating and locking a child channel.
+ */
+enum {
+       L2CAP_NESTING_SMP,
+       L2CAP_NESTING_NORMAL,
+       L2CAP_NESTING_PARENT,
+};
+
 enum {
        L2CAP_TX_STATE_XMIT,
        L2CAP_TX_STATE_WAIT_F,
@@ -778,7 +791,7 @@ void l2cap_chan_put(struct l2cap_chan *c);
 
 static inline void l2cap_chan_lock(struct l2cap_chan *chan)
 {
-       mutex_lock(&chan->lock);
+       mutex_lock_nested(&chan->lock, atomic_read(&chan->nesting));
 }
 
 static inline void l2cap_chan_unlock(struct l2cap_chan *chan)
index 414cd2f9a437f6fde742c5c05f1d901455b0b9bb..b391fd6634689f816af155e3731c925a205275da 100644 (file)
@@ -299,28 +299,28 @@ struct mgmt_cp_user_passkey_neg_reply {
 #define MGMT_READ_LOCAL_OOB_DATA_SIZE  0
 struct mgmt_rp_read_local_oob_data {
        __u8    hash[16];
-       __u8    randomizer[16];
+       __u8    rand[16];
 } __packed;
 struct mgmt_rp_read_local_oob_ext_data {
        __u8    hash192[16];
-       __u8    randomizer192[16];
+       __u8    rand192[16];
        __u8    hash256[16];
-       __u8    randomizer256[16];
+       __u8    rand256[16];
 } __packed;
 
 #define MGMT_OP_ADD_REMOTE_OOB_DATA    0x0021
 struct mgmt_cp_add_remote_oob_data {
        struct mgmt_addr_info addr;
        __u8    hash[16];
-       __u8    randomizer[16];
+       __u8    rand[16];
 } __packed;
 #define MGMT_ADD_REMOTE_OOB_DATA_SIZE  (MGMT_ADDR_INFO_SIZE + 32)
 struct mgmt_cp_add_remote_oob_ext_data {
        struct mgmt_addr_info addr;
        __u8    hash192[16];
-       __u8    randomizer192[16];
+       __u8    rand192[16];
        __u8    hash256[16];
-       __u8    randomizer256[16];
+       __u8    rand256[16];
 } __packed;
 #define MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 64)
 
index 9d99b965576083cc7a20cdec5e848f239d07bada..7f713acfa106c9bd564b4c748a1d9b7b8978d8ae 100644 (file)
 #ifndef __NET_CFG802154_H
 #define __NET_CFG802154_H
 
+#include <linux/ieee802154.h>
 #include <linux/netdevice.h>
 #include <linux/mutex.h>
 #include <linux/bug.h>
 
-/* According to the IEEE 802.15.4 stadard the upper most significant bits of
- * the 32-bit channel bitmaps shall be used as an integer value to specify 32
- * possible channel pages. The lower 27 bits of the channel bit map shall be
- * used as a bit mask to specify channel numbers within a channel page.
- */
-#define WPAN_NUM_CHANNELS      27
-#define WPAN_NUM_PAGES         32
+#include <net/nl802154.h>
 
 struct wpan_phy;
 
@@ -35,13 +30,43 @@ struct cfg802154_ops {
        struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
                                                           const char *name,
                                                           int type);
-       void (*del_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
-                                           struct net_device *dev);
+       void    (*del_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
+                                              struct net_device *dev);
+       int     (*add_virtual_intf)(struct wpan_phy *wpan_phy,
+                                   const char *name,
+                                   enum nl802154_iftype type,
+                                   __le64 extended_addr);
+       int     (*del_virtual_intf)(struct wpan_phy *wpan_phy,
+                                   struct wpan_dev *wpan_dev);
+       int     (*set_channel)(struct wpan_phy *wpan_phy, u8 page, u8 channel);
+       int     (*set_pan_id)(struct wpan_phy *wpan_phy,
+                             struct wpan_dev *wpan_dev, __le16 pan_id);
+       int     (*set_short_addr)(struct wpan_phy *wpan_phy,
+                                 struct wpan_dev *wpan_dev, __le16 short_addr);
+       int     (*set_backoff_exponent)(struct wpan_phy *wpan_phy,
+                                       struct wpan_dev *wpan_dev, u8 min_be,
+                                       u8 max_be);
+       int     (*set_max_csma_backoffs)(struct wpan_phy *wpan_phy,
+                                        struct wpan_dev *wpan_dev,
+                                        u8 max_csma_backoffs);
+       int     (*set_max_frame_retries)(struct wpan_phy *wpan_phy,
+                                        struct wpan_dev *wpan_dev,
+                                        s8 max_frame_retries);
+       int     (*set_lbt_mode)(struct wpan_phy *wpan_phy,
+                               struct wpan_dev *wpan_dev, bool mode);
 };
 
 struct wpan_phy {
        struct mutex pib_lock;
 
+       /* If multiple wpan_phys are registered and you're handed e.g.
+        * a regular netdev with assigned ieee802154_ptr, you won't
+        * know whether it points to a wpan_phy your driver has registered
+        * or not. Assign this to something global to your driver to
+        * help determine whether you own this wpan_phy or not.
+        */
+       const void *privid;
+
        /*
         * This is a PIB according to 802.15.4-2011.
         * We do not provide timing-related variables, as they
@@ -49,19 +74,22 @@ struct wpan_phy {
         */
        u8 current_channel;
        u8 current_page;
-       u32 channels_supported[32];
+       u32 channels_supported[IEEE802154_MAX_PAGE + 1];
        s8 transmit_power;
        u8 cca_mode;
-       u8 min_be;
-       u8 max_be;
-       u8 csma_retries;
-       s8 frame_retries;
 
        __le64 perm_extended_addr;
 
-       bool lbt;
        s32 cca_ed_level;
 
+       /* PHY depended MAC PIB values */
+
+       /* 802.15.4 acronym: Tdsym in usec */
+       u8 symbol_duration;
+       /* lifs and sifs periods timing */
+       u16 lifs_period;
+       u16 sifs_period;
+
        struct device dev;
 
        char priv[0] __aligned(NETDEV_ALIGN);
@@ -69,12 +97,38 @@ struct wpan_phy {
 
 struct wpan_dev {
        struct wpan_phy *wpan_phy;
+       int iftype;
+
+       /* the remainder of this struct should be private to cfg802154 */
+       struct list_head list;
+       struct net_device *netdev;
+
+       u32 identifier;
+
+       /* MAC PIB */
+       __le16 pan_id;
+       __le16 short_addr;
+       __le64 extended_addr;
+
+       /* MAC BSN field */
+       u8 bsn;
+       /* MAC DSN field */
+       u8 dsn;
+
+       u8 min_be;
+       u8 max_be;
+       u8 csma_retries;
+       s8 frame_retries;
+
+       bool lbt;
+
+       bool promiscuous_mode;
 };
 
 #define to_phy(_dev)   container_of(_dev, struct wpan_phy, dev)
 
 struct wpan_phy *
-wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size);
+wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size);
 static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev)
 {
        phy->dev.parent = dev;
index 632f6566adb5894c8c164f4d0380d2167e490873..c823d910b46ca7da89866226ba5e1be7c7c93529 100644 (file)
@@ -260,6 +260,7 @@ void ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb,
 
 void ieee802154_wake_queue(struct ieee802154_hw *hw);
 void ieee802154_stop_queue(struct ieee802154_hw *hw);
-void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb);
+void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
+                             bool ifs_handling);
 
 #endif /* NET_MAC802154_H */
diff --git a/include/net/nl802154.h b/include/net/nl802154.h
new file mode 100644 (file)
index 0000000..6dbd406
--- /dev/null
@@ -0,0 +1,122 @@
+#ifndef __NL802154_H
+#define __NL802154_H
+/*
+ * 802.15.4 netlink interface public header
+ *
+ * Copyright 2014 Alexander Aring <aar@pengutronix.de>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#define NL802154_GENL_NAME "nl802154"
+
+enum nl802154_commands {
+/* don't change the order or add anything between, this is ABI! */
+/* currently we don't shipping this file via uapi, ignore the above one */
+       NL802154_CMD_UNSPEC,
+
+       NL802154_CMD_GET_WPAN_PHY,              /* can dump */
+       NL802154_CMD_SET_WPAN_PHY,
+       NL802154_CMD_NEW_WPAN_PHY,
+       NL802154_CMD_DEL_WPAN_PHY,
+
+       NL802154_CMD_GET_INTERFACE,             /* can dump */
+       NL802154_CMD_SET_INTERFACE,
+       NL802154_CMD_NEW_INTERFACE,
+       NL802154_CMD_DEL_INTERFACE,
+
+       NL802154_CMD_SET_CHANNEL,
+
+       NL802154_CMD_SET_PAN_ID,
+       NL802154_CMD_SET_SHORT_ADDR,
+
+       NL802154_CMD_SET_TX_POWER,
+       NL802154_CMD_SET_CCA_MODE,
+       NL802154_CMD_SET_CCA_ED_LEVEL,
+
+       NL802154_CMD_SET_MAX_FRAME_RETRIES,
+
+       NL802154_CMD_SET_BACKOFF_EXPONENT,
+       NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
+
+       NL802154_CMD_SET_LBT_MODE,
+
+       /* add new commands above here */
+
+       /* used to define NL802154_CMD_MAX below */
+       __NL802154_CMD_AFTER_LAST,
+       NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
+};
+
+enum nl802154_attrs {
+/* don't change the order or add anything between, this is ABI! */
+/* currently we don't shipping this file via uapi, ignore the above one */
+       NL802154_ATTR_UNSPEC,
+
+       NL802154_ATTR_WPAN_PHY,
+       NL802154_ATTR_WPAN_PHY_NAME,
+
+       NL802154_ATTR_IFINDEX,
+       NL802154_ATTR_IFNAME,
+       NL802154_ATTR_IFTYPE,
+
+       NL802154_ATTR_WPAN_DEV,
+
+       NL802154_ATTR_PAGE,
+       NL802154_ATTR_CHANNEL,
+
+       NL802154_ATTR_PAN_ID,
+       NL802154_ATTR_SHORT_ADDR,
+
+       NL802154_ATTR_TX_POWER,
+
+       NL802154_ATTR_CCA_MODE,
+       NL802154_ATTR_CCA_MODE3_AND,
+       NL802154_ATTR_CCA_ED_LEVEL,
+
+       NL802154_ATTR_MAX_FRAME_RETRIES,
+
+       NL802154_ATTR_MAX_BE,
+       NL802154_ATTR_MIN_BE,
+       NL802154_ATTR_MAX_CSMA_BACKOFFS,
+
+       NL802154_ATTR_LBT_MODE,
+
+       NL802154_ATTR_GENERATION,
+
+       NL802154_ATTR_CHANNELS_SUPPORTED,
+       NL802154_ATTR_SUPPORTED_CHANNEL,
+
+       NL802154_ATTR_EXTENDED_ADDR,
+
+       /* add attributes here, update the policy in nl802154.c */
+
+       __NL802154_ATTR_AFTER_LAST,
+       NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_iftype {
+       /* for backwards compatibility TODO */
+       NL802154_IFTYPE_UNSPEC = -1,
+
+       NL802154_IFTYPE_NODE,
+       NL802154_IFTYPE_MONITOR,
+       NL802154_IFTYPE_COORD,
+
+       /* keep last */
+       NUM_NL802154_IFTYPES,
+       NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1
+};
+
+#endif /* __NL802154_H */
index dc23c55f1ab66331a478a2bcc6b0a52c803929ae..bdcaefd2db127c97b6d0e4556dd3494835f01d89 100644 (file)
@@ -87,13 +87,6 @@ struct lowpan_dev {
        struct delayed_work notify_peers;
 };
 
-static inline void peer_free(struct rcu_head *head)
-{
-       struct lowpan_peer *e = container_of(head, struct lowpan_peer, rcu);
-
-       kfree(e);
-}
-
 static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev)
 {
        return netdev_priv(netdev);
@@ -108,7 +101,7 @@ static inline void peer_add(struct lowpan_dev *dev, struct lowpan_peer *peer)
 static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
 {
        list_del_rcu(&peer->list);
-       call_rcu(&peer->rcu, peer_free);
+       kfree_rcu(peer, rcu);
 
        module_put(THIS_MODULE);
 
@@ -614,17 +607,13 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
        int err = 0;
        bdaddr_t addr;
        u8 addr_type;
-       struct sk_buff *tmpskb;
 
        /* We must take a copy of the skb before we modify/replace the ipv6
         * header as the header could be used elsewhere
         */
-       tmpskb = skb_unshare(skb, GFP_ATOMIC);
-       if (!tmpskb) {
-               kfree_skb(skb);
+       skb = skb_unshare(skb, GFP_ATOMIC);
+       if (!skb)
                return NET_XMIT_DROP;
-       }
-       skb = tmpskb;
 
        /* Return values from setup_header()
         *  <0 - error, packet is dropped
@@ -1141,6 +1130,8 @@ static struct l2cap_chan *bt_6lowpan_listen(void)
        pchan->state = BT_LISTEN;
        pchan->src_type = BDADDR_LE_PUBLIC;
 
+       atomic_set(&pchan->nesting, L2CAP_NESTING_PARENT);
+
        BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan,
               pchan->src_type);
 
@@ -1223,7 +1214,7 @@ static void disconnect_all_peers(void)
                l2cap_chan_close(peer->chan, ENOENT);
 
                list_del_rcu(&peer->list);
-               call_rcu(&peer->rcu, peer_free);
+               kfree_rcu(peer, rcu);
 
                module_put(THIS_MODULE);
        }
index 2640d78f30b80080f5b240c8a25cf668392e2961..ee016f03910005de87cc45a7d04b2a3989d90c2e 100644 (file)
@@ -134,6 +134,7 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
 static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
 {
        struct crypto_shash *tfm;
+       struct shash_desc *shash;
        int ret;
 
        if (!ksize)
@@ -148,18 +149,24 @@ static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
        ret = crypto_shash_setkey(tfm, key, ksize);
        if (ret) {
                BT_DBG("crypto_ahash_setkey failed: err %d", ret);
-       } else {
-               char desc[sizeof(struct shash_desc) +
-                       crypto_shash_descsize(tfm)] CRYPTO_MINALIGN_ATTR;
-               struct shash_desc *shash = (struct shash_desc *)desc;
-
-               shash->tfm = tfm;
-               shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+               goto failed;
+       }
 
-               ret = crypto_shash_digest(shash, plaintext, psize,
-                                         output);
+       shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
+                       GFP_KERNEL);
+       if (!shash) {
+               ret = -ENOMEM;
+               goto failed;
        }
 
+       shash->tfm = tfm;
+       shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+       ret = crypto_shash_digest(shash, plaintext, psize, output);
+
+       kfree(shash);
+
+failed:
        crypto_free_shash(tfm);
        return ret;
 }
index 6c162c8809cf8d5f60592742dd6ffff0cd6afe28..d786958a1decd58a8236701f48f6a74850138b48 100644 (file)
@@ -748,16 +748,15 @@ static const struct file_operations white_list_fops = {
 static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
 {
        struct hci_dev *hdev = f->private;
-       struct list_head *p, *n;
+       struct smp_irk *irk;
 
-       hci_dev_lock(hdev);
-       list_for_each_safe(p, n, &hdev->identity_resolving_keys) {
-               struct smp_irk *irk = list_entry(p, struct smp_irk, list);
+       rcu_read_lock();
+       list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
                seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
                           &irk->bdaddr, irk->addr_type,
                           16, irk->val, &irk->rpa);
        }
-       hci_dev_unlock(hdev);
+       rcu_read_unlock();
 
        return 0;
 }
@@ -778,17 +777,15 @@ static const struct file_operations identity_resolving_keys_fops = {
 static int long_term_keys_show(struct seq_file *f, void *ptr)
 {
        struct hci_dev *hdev = f->private;
-       struct list_head *p, *n;
+       struct smp_ltk *ltk;
 
-       hci_dev_lock(hdev);
-       list_for_each_safe(p, n, &hdev->long_term_keys) {
-               struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
+       rcu_read_lock();
+       list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
                seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
                           &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
                           ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
                           __le64_to_cpu(ltk->rand), 16, ltk->val);
-       }
-       hci_dev_unlock(hdev);
+       rcu_read_unlock();
 
        return 0;
 }
@@ -2564,6 +2561,11 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        if (test_bit(HCI_MGMT, &hdev->dev_flags))
                cancel_delayed_work_sync(&hdev->rpa_expired);
 
+       /* Avoid potential lockdep warnings from the *_flush() calls by
+        * ensuring the workqueue is empty up front.
+        */
+       drain_workqueue(hdev->workqueue);
+
        hci_dev_lock(hdev);
        hci_inquiry_cache_flush(hdev);
        hci_pend_le_actions_clear(hdev);
@@ -2687,6 +2689,11 @@ int hci_dev_reset(__u16 dev)
        skb_queue_purge(&hdev->rx_q);
        skb_queue_purge(&hdev->cmd_q);
 
+       /* Avoid potential lockdep warnings from the *_flush() calls by
+        * ensuring the workqueue is empty up front.
+        */
+       drain_workqueue(hdev->workqueue);
+
        hci_dev_lock(hdev);
        hci_inquiry_cache_flush(hdev);
        hci_conn_hash_flush(hdev);
@@ -3106,21 +3113,21 @@ void hci_link_keys_clear(struct hci_dev *hdev)
 
 void hci_smp_ltks_clear(struct hci_dev *hdev)
 {
-       struct smp_ltk *k, *tmp;
+       struct smp_ltk *k;
 
-       list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
-               list_del(&k->list);
-               kfree(k);
+       list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
+               list_del_rcu(&k->list);
+               kfree_rcu(k, rcu);
        }
 }
 
 void hci_smp_irks_clear(struct hci_dev *hdev)
 {
-       struct smp_irk *k, *tmp;
+       struct smp_irk *k;
 
-       list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
-               list_del(&k->list);
-               kfree(k);
+       list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
+               list_del_rcu(&k->list);
+               kfree_rcu(k, rcu);
        }
 }
 
@@ -3184,15 +3191,18 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
 {
        struct smp_ltk *k;
 
-       list_for_each_entry(k, &hdev->long_term_keys, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
                if (k->ediv != ediv || k->rand != rand)
                        continue;
 
                if (ltk_role(k->type) != role)
                        continue;
 
+               rcu_read_unlock();
                return k;
        }
+       rcu_read_unlock();
 
        return NULL;
 }
@@ -3202,11 +3212,16 @@ struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
 {
        struct smp_ltk *k;
 
-       list_for_each_entry(k, &hdev->long_term_keys, list)
+       rcu_read_lock();
+       list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
                if (addr_type == k->bdaddr_type &&
                    bacmp(bdaddr, &k->bdaddr) == 0 &&
-                   ltk_role(k->type) == role)
+                   ltk_role(k->type) == role) {
+                       rcu_read_unlock();
                        return k;
+               }
+       }
+       rcu_read_unlock();
 
        return NULL;
 }
@@ -3215,17 +3230,22 @@ struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa)
 {
        struct smp_irk *irk;
 
-       list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
-               if (!bacmp(&irk->rpa, rpa))
+       rcu_read_lock();
+       list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
+               if (!bacmp(&irk->rpa, rpa)) {
+                       rcu_read_unlock();
                        return irk;
+               }
        }
 
-       list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+       list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
                if (smp_irk_matches(hdev, irk->val, rpa)) {
                        bacpy(&irk->rpa, rpa);
+                       rcu_read_unlock();
                        return irk;
                }
        }
+       rcu_read_unlock();
 
        return NULL;
 }
@@ -3239,11 +3259,15 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
        if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0)
                return NULL;
 
-       list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
                if (addr_type == irk->addr_type &&
-                   bacmp(bdaddr, &irk->bdaddr) == 0)
+                   bacmp(bdaddr, &irk->bdaddr) == 0) {
+                       rcu_read_unlock();
                        return irk;
+               }
        }
+       rcu_read_unlock();
 
        return NULL;
 }
@@ -3309,7 +3333,7 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
                key = kzalloc(sizeof(*key), GFP_KERNEL);
                if (!key)
                        return NULL;
-               list_add(&key->list, &hdev->long_term_keys);
+               list_add_rcu(&key->list, &hdev->long_term_keys);
        }
 
        bacpy(&key->bdaddr, bdaddr);
@@ -3338,7 +3362,7 @@ struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
                bacpy(&irk->bdaddr, bdaddr);
                irk->addr_type = addr_type;
 
-               list_add(&irk->list, &hdev->identity_resolving_keys);
+               list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
        }
 
        memcpy(irk->val, val, 16);
@@ -3365,17 +3389,17 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
 
 int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
 {
-       struct smp_ltk *k, *tmp;
+       struct smp_ltk *k;
        int removed = 0;
 
-       list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
+       list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
                if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type)
                        continue;
 
                BT_DBG("%s removing %pMR", hdev->name, bdaddr);
 
-               list_del(&k->list);
-               kfree(k);
+               list_del_rcu(&k->list);
+               kfree_rcu(k, rcu);
                removed++;
        }
 
@@ -3384,16 +3408,16 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
 
 void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
 {
-       struct smp_irk *k, *tmp;
+       struct smp_irk *k;
 
-       list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
+       list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
                if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type)
                        continue;
 
                BT_DBG("%s removing %pMR", hdev->name, bdaddr);
 
-               list_del(&k->list);
-               kfree(k);
+               list_del_rcu(&k->list);
+               kfree_rcu(k, rcu);
        }
 }
 
@@ -3455,7 +3479,7 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev)
 }
 
 int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                           u8 *hash, u8 *randomizer)
+                           u8 *hash, u8 *rand)
 {
        struct oob_data *data;
 
@@ -3470,10 +3494,10 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
        }
 
        memcpy(data->hash192, hash, sizeof(data->hash192));
-       memcpy(data->randomizer192, randomizer, sizeof(data->randomizer192));
+       memcpy(data->rand192, rand, sizeof(data->rand192));
 
        memset(data->hash256, 0, sizeof(data->hash256));
-       memset(data->randomizer256, 0, sizeof(data->randomizer256));
+       memset(data->rand256, 0, sizeof(data->rand256));
 
        BT_DBG("%s for %pMR", hdev->name, bdaddr);
 
@@ -3481,8 +3505,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
 }
 
 int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                               u8 *hash192, u8 *randomizer192,
-                               u8 *hash256, u8 *randomizer256)
+                               u8 *hash192, u8 *rand192,
+                               u8 *hash256, u8 *rand256)
 {
        struct oob_data *data;
 
@@ -3497,10 +3521,10 @@ int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
        }
 
        memcpy(data->hash192, hash192, sizeof(data->hash192));
-       memcpy(data->randomizer192, randomizer192, sizeof(data->randomizer192));
+       memcpy(data->rand192, rand192, sizeof(data->rand192));
 
        memcpy(data->hash256, hash256, sizeof(data->hash256));
-       memcpy(data->randomizer256, randomizer256, sizeof(data->randomizer256));
+       memcpy(data->rand256, rand256, sizeof(data->rand256));
 
        BT_DBG("%s for %pMR", hdev->name, bdaddr);
 
index 5e7be804c709d30f1eacaa46a5c29ebf80593529..844f7d1ff1cd27aeb051577559fc0ae7fa1b3f7f 100644 (file)
@@ -994,8 +994,8 @@ static void hci_cc_read_local_oob_data(struct hci_dev *hdev,
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
        hci_dev_lock(hdev);
-       mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer,
-                                         NULL, NULL, rp->status);
+       mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
+                                         rp->status);
        hci_dev_unlock(hdev);
 }
 
@@ -1007,8 +1007,8 @@ static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
        hci_dev_lock(hdev);
-       mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192,
-                                         rp->hash256, rp->randomizer256,
+       mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
+                                         rp->hash256, rp->rand256,
                                          rp->status);
        hci_dev_unlock(hdev);
 }
@@ -1581,7 +1581,14 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn,
        struct discovery_state *discov = &hdev->discovery;
        struct inquiry_entry *e;
 
-       if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+       /* Update the mgmt connected state if necessary. Be careful with
+        * conn objects that exist but are not (yet) connected however.
+        * Only those in BT_CONFIG or BT_CONNECTED states can be
+        * considered connected.
+        */
+       if (conn &&
+           (conn->state == BT_CONFIG || conn->state == BT_CONNECTED) &&
+           !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
                mgmt_device_connected(hdev, conn, 0, name, name_len);
 
        if (discov->state == DISCOVERY_STOPPED)
@@ -3989,11 +3996,9 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
 
                        bacpy(&cp.bdaddr, &ev->bdaddr);
                        memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
-                       memcpy(cp.randomizer192, data->randomizer192,
-                              sizeof(cp.randomizer192));
+                       memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
                        memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
-                       memcpy(cp.randomizer256, data->randomizer256,
-                              sizeof(cp.randomizer256));
+                       memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
 
                        hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
                                     sizeof(cp), &cp);
@@ -4002,8 +4007,7 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
 
                        bacpy(&cp.bdaddr, &ev->bdaddr);
                        memcpy(cp.hash, data->hash192, sizeof(cp.hash));
-                       memcpy(cp.randomizer, data->randomizer192,
-                              sizeof(cp.randomizer));
+                       memcpy(cp.rand, data->rand192, sizeof(cp.rand));
 
                        hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
                                     sizeof(cp), &cp);
@@ -4571,8 +4575,8 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
         */
        if (ltk->type == SMP_STK) {
                set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
-               list_del(&ltk->list);
-               kfree(ltk);
+               list_del_rcu(&ltk->list);
+               kfree_rcu(ltk, rcu);
        } else {
                clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
        }
index 1b7d605706aa5720b45e056d3a75f235c9c4ab60..cc25d0b74b3609ef800453484eef7e0f55767162 100644 (file)
@@ -736,14 +736,10 @@ static int hidp_setup_hid(struct hidp_session *session,
        struct hid_device *hid;
        int err;
 
-       session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
-       if (!session->rd_data)
-               return -ENOMEM;
+       session->rd_data = memdup_user(req->rd_data, req->rd_size);
+       if (IS_ERR(session->rd_data))
+               return PTR_ERR(session->rd_data);
 
-       if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
-               err = -EFAULT;
-               goto fault;
-       }
        session->rd_size = req->rd_size;
 
        hid = hid_allocate_device();
index fc15174c612c3d087c43ccf736eda23aa2c60054..8e127317302096959f64f2fc5af4556252b903b5 100644 (file)
@@ -424,6 +424,9 @@ struct l2cap_chan *l2cap_chan_create(void)
 
        mutex_init(&chan->lock);
 
+       /* Set default lock nesting level */
+       atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL);
+
        write_lock(&chan_list_lock);
        list_add(&chan->global_l, &chan_list);
        write_unlock(&chan_list_lock);
@@ -567,7 +570,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
 
        __clear_chan_timer(chan);
 
-       BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
+       BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
+              state_to_string(chan->state));
 
        chan->ops->teardown(chan, err);
 
@@ -5215,9 +5219,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
                                u8 *data)
 {
        struct l2cap_le_conn_rsp *rsp = (struct l2cap_le_conn_rsp *) data;
+       struct hci_conn *hcon = conn->hcon;
        u16 dcid, mtu, mps, credits, result;
        struct l2cap_chan *chan;
-       int err;
+       int err, sec_level;
 
        if (cmd_len < sizeof(*rsp))
                return -EPROTO;
@@ -5256,6 +5261,26 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
                l2cap_chan_ready(chan);
                break;
 
+       case L2CAP_CR_AUTHENTICATION:
+       case L2CAP_CR_ENCRYPTION:
+               /* If we already have MITM protection we can't do
+                * anything.
+                */
+               if (hcon->sec_level > BT_SECURITY_MEDIUM) {
+                       l2cap_chan_del(chan, ECONNREFUSED);
+                       break;
+               }
+
+               sec_level = hcon->sec_level + 1;
+               if (chan->sec_level < sec_level)
+                       chan->sec_level = sec_level;
+
+               /* We'll need to send a new Connect Request */
+               clear_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags);
+
+               smp_conn_security(hcon, chan->sec_level);
+               break;
+
        default:
                l2cap_chan_del(chan, ECONNREFUSED);
                break;
@@ -5388,7 +5413,8 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
        mutex_lock(&conn->chan_lock);
        l2cap_chan_lock(pchan);
 
-       if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) {
+       if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
+                                    SMP_ALLOW_STK)) {
                result = L2CAP_CR_AUTHENTICATION;
                chan = NULL;
                goto response_unlock;
@@ -7329,7 +7355,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
                                l2cap_start_connection(chan);
                        else
                                __set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
-               } else if (chan->state == BT_CONNECT2) {
+               } else if (chan->state == BT_CONNECT2 &&
+                          chan->mode != L2CAP_MODE_LE_FLOWCTL) {
                        struct l2cap_conn_rsp rsp;
                        __u16 res, stat;
 
index 31f106e61ca20fd15420830e653f81e7b1f87a20..b0efb7202957cc4ae61781fb8642e1b30c9dd1f8 100644 (file)
@@ -285,6 +285,12 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
        sk->sk_max_ack_backlog = backlog;
        sk->sk_ack_backlog = 0;
 
+       /* Listening channels need to use nested locking in order not to
+        * cause lockdep warnings when the created child channels end up
+        * being locked in the same thread as the parent channel.
+        */
+       atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
+
        chan->state = BT_LISTEN;
        sk->sk_state = BT_LISTEN;
 
@@ -301,7 +307,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
        long timeo;
        int err = 0;
 
-       lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
+       lock_sock_nested(sk, L2CAP_NESTING_PARENT);
 
        timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
 
@@ -333,7 +339,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
 
                release_sock(sk);
                timeo = schedule_timeout(timeo);
-               lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
+               lock_sock_nested(sk, L2CAP_NESTING_PARENT);
        }
        __set_current_state(TASK_RUNNING);
        remove_wait_queue(sk_sleep(sk), &wait);
@@ -1096,6 +1102,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
        chan = l2cap_pi(sk)->chan;
        conn = chan->conn;
 
+       BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+
        if (conn)
                mutex_lock(&conn->chan_lock);
 
@@ -1153,12 +1161,16 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
 {
        struct sock *sk;
 
-       BT_DBG("parent %p", parent);
+       BT_DBG("parent %p state %s", parent,
+              state_to_string(parent->sk_state));
 
        /* Close not yet accepted channels */
        while ((sk = bt_accept_dequeue(parent, NULL))) {
                struct l2cap_chan *chan = l2cap_pi(sk)->chan;
 
+               BT_DBG("child chan %p state %s", chan,
+                      state_to_string(chan->state));
+
                l2cap_chan_lock(chan);
                __clear_chan_timer(chan);
                l2cap_chan_close(chan, ECONNRESET);
@@ -1246,7 +1258,16 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
        struct sock *sk = chan->data;
        struct sock *parent;
 
-       lock_sock(sk);
+       BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+
+       /* This callback can be called both for server (BT_LISTEN)
+        * sockets as well as "normal" ones. To avoid lockdep warnings
+        * with child socket locking (through l2cap_sock_cleanup_listen)
+        * we need separation into separate nesting levels. The simplest
+        * way to accomplish this is to inherit the nesting level used
+        * for the channel.
+        */
+       lock_sock_nested(sk, atomic_read(&chan->nesting));
 
        parent = bt_sk(sk)->parent;
 
index ce0272c6f71fdccefb26c49e7fffbd075ffab12d..cbeef5f62f3b25380fbde84c853bca2b30aab820 100644 (file)
@@ -3589,8 +3589,16 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
                struct mgmt_cp_add_remote_oob_data *cp = data;
                u8 status;
 
+               if (cp->addr.type != BDADDR_BREDR) {
+                       err = cmd_complete(sk, hdev->id,
+                                          MGMT_OP_ADD_REMOTE_OOB_DATA,
+                                          MGMT_STATUS_INVALID_PARAMS,
+                                          &cp->addr, sizeof(cp->addr));
+                       goto unlock;
+               }
+
                err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
-                                             cp->hash, cp->randomizer);
+                                             cp->hash, cp->rand);
                if (err < 0)
                        status = MGMT_STATUS_FAILED;
                else
@@ -3602,11 +3610,17 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
                struct mgmt_cp_add_remote_oob_ext_data *cp = data;
                u8 status;
 
+               if (cp->addr.type != BDADDR_BREDR) {
+                       err = cmd_complete(sk, hdev->id,
+                                          MGMT_OP_ADD_REMOTE_OOB_DATA,
+                                          MGMT_STATUS_INVALID_PARAMS,
+                                          &cp->addr, sizeof(cp->addr));
+                       goto unlock;
+               }
+
                err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr,
-                                                 cp->hash192,
-                                                 cp->randomizer192,
-                                                 cp->hash256,
-                                                 cp->randomizer256);
+                                                 cp->hash192, cp->rand192,
+                                                 cp->hash256, cp->rand256);
                if (err < 0)
                        status = MGMT_STATUS_FAILED;
                else
@@ -3620,6 +3634,7 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
                                 MGMT_STATUS_INVALID_PARAMS);
        }
 
+unlock:
        hci_dev_unlock(hdev);
        return err;
 }
@@ -3633,14 +3648,26 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
 
        BT_DBG("%s", hdev->name);
 
+       if (cp->addr.type != BDADDR_BREDR)
+               return cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &cp->addr, sizeof(cp->addr));
+
        hci_dev_lock(hdev);
 
+       if (!bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
+               hci_remote_oob_data_clear(hdev);
+               status = MGMT_STATUS_SUCCESS;
+               goto done;
+       }
+
        err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
        if (err < 0)
                status = MGMT_STATUS_INVALID_PARAMS;
        else
                status = MGMT_STATUS_SUCCESS;
 
+done:
        err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
                           status, &cp->addr, sizeof(cp->addr));
 
@@ -6742,8 +6769,8 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
 }
 
 void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
-                                      u8 *randomizer192, u8 *hash256,
-                                      u8 *randomizer256, u8 status)
+                                      u8 *rand192, u8 *hash256, u8 *rand256,
+                                      u8 status)
 {
        struct pending_cmd *cmd;
 
@@ -6758,16 +6785,14 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
                           mgmt_status(status));
        } else {
                if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
-                   hash256 && randomizer256) {
+                   hash256 && rand256) {
                        struct mgmt_rp_read_local_oob_ext_data rp;
 
                        memcpy(rp.hash192, hash192, sizeof(rp.hash192));
-                       memcpy(rp.randomizer192, randomizer192,
-                              sizeof(rp.randomizer192));
+                       memcpy(rp.rand192, rand192, sizeof(rp.rand192));
 
                        memcpy(rp.hash256, hash256, sizeof(rp.hash256));
-                       memcpy(rp.randomizer256, randomizer256,
-                              sizeof(rp.randomizer256));
+                       memcpy(rp.rand256, rand256, sizeof(rp.rand256));
 
                        cmd_complete(cmd->sk, hdev->id,
                                     MGMT_OP_READ_LOCAL_OOB_DATA, 0,
@@ -6776,8 +6801,7 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
                        struct mgmt_rp_read_local_oob_data rp;
 
                        memcpy(rp.hash, hash192, sizeof(rp.hash));
-                       memcpy(rp.randomizer, randomizer192,
-                              sizeof(rp.randomizer));
+                       memcpy(rp.rand, rand192, sizeof(rp.rand));
 
                        cmd_complete(cmd->sk, hdev->id,
                                     MGMT_OP_READ_LOCAL_OOB_DATA, 0,
index 3ebf65b508813b57c42ec3c2b4f75c1b97e9122d..069b76e03b57950aa979db165f4cc913489ea8ef 100644 (file)
@@ -383,18 +383,18 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
        /* If pairing failed clean up any keys we might have */
        if (!complete) {
                if (smp->ltk) {
-                       list_del(&smp->ltk->list);
-                       kfree(smp->ltk);
+                       list_del_rcu(&smp->ltk->list);
+                       kfree_rcu(smp->ltk, rcu);
                }
 
                if (smp->slave_ltk) {
-                       list_del(&smp->slave_ltk->list);
-                       kfree(smp->slave_ltk);
+                       list_del_rcu(&smp->slave_ltk->list);
+                       kfree_rcu(smp->slave_ltk, rcu);
                }
 
                if (smp->remote_irk) {
-                       list_del(&smp->remote_irk->list);
-                       kfree(smp->remote_irk);
+                       list_del_rcu(&smp->remote_irk->list);
+                       kfree_rcu(smp->remote_irk, rcu);
                }
        }
 
@@ -514,8 +514,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
                set_bit(SMP_FLAG_TK_VALID, &smp->flags);
        }
 
-       hci_dev_lock(hcon->hdev);
-
        if (method == REQ_PASSKEY)
                ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
                                                hcon->type, hcon->dst_type);
@@ -528,8 +526,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
                                                hcon->type, hcon->dst_type,
                                                passkey, 0);
 
-       hci_dev_unlock(hcon->hdev);
-
        return ret;
 }
 
@@ -659,8 +655,8 @@ static void smp_notify_keys(struct l2cap_conn *conn)
                 * just remove it.
                 */
                if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
-                       list_del(&smp->remote_irk->list);
-                       kfree(smp->remote_irk);
+                       list_del_rcu(&smp->remote_irk->list);
+                       kfree_rcu(smp->remote_irk, rcu);
                        smp->remote_irk = NULL;
                }
        }
@@ -1126,18 +1122,20 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
        return true;
 }
 
-bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
+                            enum smp_key_pref key_pref)
 {
        if (sec_level == BT_SECURITY_LOW)
                return true;
 
-       /* If we're encrypted with an STK always claim insufficient
-        * security. This way we allow the connection to be re-encrypted
-        * with an LTK, even if the LTK provides the same level of
-        * security. Only exception is if we don't have an LTK (e.g.
-        * because of key distribution bits).
+       /* If we're encrypted with an STK but the caller prefers using
+        * LTK claim insufficient security. This way we allow the
+        * connection to be re-encrypted with an LTK, even if the LTK
+        * provides the same level of security. Only exception is if we
+        * don't have an LTK (e.g. because of key distribution bits).
         */
-       if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
+       if (key_pref == SMP_USE_LTK &&
+           test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
            hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
                                 hcon->role))
                return false;
@@ -1171,7 +1169,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        else
                sec_level = authreq_to_seclevel(auth);
 
-       if (smp_sufficient_security(hcon, sec_level))
+       if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
                return 0;
 
        if (sec_level > hcon->pending_sec_level)
@@ -1221,7 +1219,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
        if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
                return 1;
 
-       if (smp_sufficient_security(hcon, sec_level))
+       if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
                return 1;
 
        if (sec_level > hcon->pending_sec_level)
@@ -1323,7 +1321,6 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
 
        skb_pull(skb, sizeof(*rp));
 
-       hci_dev_lock(hdev);
        authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
        ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK,
                          authenticated, smp->tk, smp->enc_key_size,
@@ -1331,7 +1328,6 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
        smp->ltk = ltk;
        if (!(smp->remote_key_dist & KEY_DIST_MASK))
                smp_distribute_keys(smp);
-       hci_dev_unlock(hdev);
 
        return 0;
 }
@@ -1378,8 +1374,6 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
 
        skb_pull(skb, sizeof(*info));
 
-       hci_dev_lock(hcon->hdev);
-
        /* Strictly speaking the Core Specification (4.1) allows sending
         * an empty address which would force us to rely on just the IRK
         * as "identity information". However, since such
@@ -1407,8 +1401,6 @@ distribute:
        if (!(smp->remote_key_dist & KEY_DIST_MASK))
                smp_distribute_keys(smp);
 
-       hci_dev_unlock(hcon->hdev);
-
        return 0;
 }
 
@@ -1417,7 +1409,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
        struct smp_cmd_sign_info *rp = (void *) skb->data;
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
-       struct hci_dev *hdev = conn->hcon->hdev;
        struct smp_csrk *csrk;
 
        BT_DBG("conn %p", conn);
@@ -1430,7 +1421,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
 
        skb_pull(skb, sizeof(*rp));
 
-       hci_dev_lock(hdev);
        csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
        if (csrk) {
                csrk->master = 0x01;
@@ -1438,7 +1428,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
        }
        smp->csrk = csrk;
        smp_distribute_keys(smp);
-       hci_dev_unlock(hdev);
 
        return 0;
 }
@@ -1662,6 +1651,13 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
        chan->omtu      = pchan->omtu;
        chan->mode      = pchan->mode;
 
+       /* Other L2CAP channels may request SMP routines in order to
+        * change the security level. This means that the SMP channel
+        * lock must be considered in its own category to avoid lockdep
+        * warnings.
+        */
+       atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
+
        BT_DBG("created chan %p", chan);
 
        return chan;
@@ -1693,7 +1689,7 @@ int smp_register(struct hci_dev *hdev)
 
        BT_DBG("%s", hdev->name);
 
-       tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+       tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
        if (IS_ERR(tfm_aes)) {
                int err = PTR_ERR(tfm_aes);
                BT_ERR("Unable to create crypto context");
@@ -1719,6 +1715,9 @@ int smp_register(struct hci_dev *hdev)
        chan->imtu = L2CAP_DEFAULT_MTU;
        chan->ops = &smp_root_chan_ops;
 
+       /* Set correct nesting level for a parent/listening channel */
+       atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
+
        hdev->smp_data = chan;
 
        return 0;
index 86a683a8b4917fe7ba0567254da8da210909f13c..f76083b8500535395ee5c063aa609bcc5e69d1cd 100644 (file)
@@ -133,8 +133,15 @@ static inline u8 smp_ltk_sec_level(struct smp_ltk *key)
        return BT_SECURITY_MEDIUM;
 }
 
+/* Key preferences for smp_sufficient security */
+enum smp_key_pref {
+       SMP_ALLOW_STK,
+       SMP_USE_LTK,
+};
+
 /* SMP Commands */
-bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
+                            enum smp_key_pref key_pref);
 int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
 int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
 
index 38354d4a70cbe1ac0bcf4b5c8042f5c4a86612a3..9f6970f2a28b9e9d6840ccbef56efdf3d19b2531 100644 (file)
@@ -3,7 +3,7 @@ obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
 
 ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
 ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
-                header_ops.o sysfs.o
+                header_ops.o sysfs.o nl802154.o
 af_802154-y := af_ieee802154.o raw.o dgram.o
 
 ccflags-y += -D__CHECK_ENDIAN__
index d1cd0edfb14989bd3bb7bad6eee197314e758b04..18bc7e7385074c4b62e5e7c87c1d2bba3981e7a4 100644 (file)
 #include <linux/device.h>
 
 #include <net/cfg802154.h>
+#include <net/rtnetlink.h>
 
 #include "ieee802154.h"
+#include "nl802154.h"
 #include "sysfs.h"
 #include "core.h"
 
+/* RCU-protected (and RTNL for writers) */
+LIST_HEAD(cfg802154_rdev_list);
+int cfg802154_rdev_list_generation;
+
 static int wpan_phy_match(struct device *dev, const void *data)
 {
        return !strcmp(dev_name(dev), (const char *)data);
@@ -69,8 +75,25 @@ int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
 }
 EXPORT_SYMBOL(wpan_phy_for_each);
 
+struct cfg802154_registered_device *
+cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
+{
+       struct cfg802154_registered_device *result = NULL, *rdev;
+
+       ASSERT_RTNL();
+
+       list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+               if (rdev->wpan_phy_idx == wpan_phy_idx) {
+                       result = rdev;
+                       break;
+               }
+       }
+
+       return result;
+}
+
 struct wpan_phy *
-wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size)
+wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
 {
        static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
        struct cfg802154_registered_device *rdev;
@@ -97,25 +120,71 @@ wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size)
 
        mutex_init(&rdev->wpan_phy.pib_lock);
 
+       INIT_LIST_HEAD(&rdev->wpan_dev_list);
        device_initialize(&rdev->wpan_phy.dev);
        dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx);
 
        rdev->wpan_phy.dev.class = &wpan_phy_class;
        rdev->wpan_phy.dev.platform_data = rdev;
 
+       init_waitqueue_head(&rdev->dev_wait);
+
        return &rdev->wpan_phy;
 }
-EXPORT_SYMBOL(wpan_phy_alloc);
+EXPORT_SYMBOL(wpan_phy_new);
 
 int wpan_phy_register(struct wpan_phy *phy)
 {
-       return device_add(&phy->dev);
+       struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
+       int ret;
+
+       rtnl_lock();
+       ret = device_add(&phy->dev);
+       if (ret) {
+               rtnl_unlock();
+               return ret;
+       }
+
+       list_add_rcu(&rdev->list, &cfg802154_rdev_list);
+       cfg802154_rdev_list_generation++;
+
+       /* TODO phy registered lock */
+       rtnl_unlock();
+
+       /* TODO nl802154 phy notify */
+
+       return 0;
 }
 EXPORT_SYMBOL(wpan_phy_register);
 
 void wpan_phy_unregister(struct wpan_phy *phy)
 {
+       struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
+
+       wait_event(rdev->dev_wait, ({
+               int __count;
+               rtnl_lock();
+               __count = rdev->opencount;
+               rtnl_unlock();
+               __count == 0; }));
+
+       rtnl_lock();
+       /* TODO nl802154 phy notify */
+       /* TODO phy registered lock */
+
+       WARN_ON(!list_empty(&rdev->wpan_dev_list));
+
+       /* First remove the hardware from everywhere, this makes
+        * it impossible to find from userspace.
+        */
+       list_del_rcu(&rdev->list);
+       synchronize_rcu();
+
+       cfg802154_rdev_list_generation++;
+
        device_del(&phy->dev);
+
+       rtnl_unlock();
 }
 EXPORT_SYMBOL(wpan_phy_unregister);
 
@@ -130,6 +199,79 @@ void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
        kfree(rdev);
 }
 
+static void
+cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
+                          int iftype, int num)
+{
+       ASSERT_RTNL();
+
+       rdev->num_running_ifaces += num;
+}
+
+static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
+                                         unsigned long state, void *ptr)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       struct cfg802154_registered_device *rdev;
+
+       if (!wpan_dev)
+               return NOTIFY_DONE;
+
+       rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+
+       /* TODO WARN_ON unspec type */
+
+       switch (state) {
+               /* TODO NETDEV_DEVTYPE */
+       case NETDEV_REGISTER:
+               wpan_dev->identifier = ++rdev->wpan_dev_id;
+               list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
+               rdev->devlist_generation++;
+
+               wpan_dev->netdev = dev;
+               break;
+       case NETDEV_DOWN:
+               cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
+
+               rdev->opencount--;
+               wake_up(&rdev->dev_wait);
+               break;
+       case NETDEV_UP:
+               cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
+
+               rdev->opencount++;
+               break;
+       case NETDEV_UNREGISTER:
+               /* It is possible to get NETDEV_UNREGISTER
+                * multiple times. To detect that, check
+                * that the interface is still on the list
+                * of registered interfaces, and only then
+                * remove and clean it up.
+                */
+               if (!list_empty(&wpan_dev->list)) {
+                       list_del_rcu(&wpan_dev->list);
+                       rdev->devlist_generation++;
+               }
+               /* synchronize (so that we won't find this netdev
+                * from other code any more) and then clear the list
+                * head so that the above code can safely check for
+                * !list_empty() to avoid double-cleanup.
+                */
+               synchronize_rcu();
+               INIT_LIST_HEAD(&wpan_dev->list);
+               break;
+       default:
+               return NOTIFY_DONE;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block cfg802154_netdev_notifier = {
+       .notifier_call = cfg802154_netdev_notifier_call,
+};
+
 static int __init wpan_phy_class_init(void)
 {
        int rc;
@@ -138,11 +280,25 @@ static int __init wpan_phy_class_init(void)
        if (rc)
                goto err;
 
-       rc = ieee802154_nl_init();
+       rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
        if (rc)
                goto err_nl;
 
+       rc = ieee802154_nl_init();
+       if (rc)
+               goto err_notifier;
+
+       rc = nl802154_init();
+       if (rc)
+               goto err_ieee802154_nl;
+
        return 0;
+
+err_ieee802154_nl:
+       ieee802154_nl_exit();
+
+err_notifier:
+       unregister_netdevice_notifier(&cfg802154_netdev_notifier);
 err_nl:
        wpan_phy_sysfs_exit();
 err:
@@ -152,7 +308,9 @@ subsys_initcall(wpan_phy_class_init);
 
 static void __exit wpan_phy_class_exit(void)
 {
+       nl802154_exit();
        ieee802154_nl_exit();
+       unregister_netdevice_notifier(&cfg802154_netdev_notifier);
        wpan_phy_sysfs_exit();
 }
 module_exit(wpan_phy_class_exit);
index fea60b3a884609eca84c012610f83f93562e708b..f3e95580caee0e96ada8bee0bb2a2e72aa21eda3 100644 (file)
@@ -5,10 +5,22 @@
 
 struct cfg802154_registered_device {
        const struct cfg802154_ops *ops;
+       struct list_head list;
 
        /* wpan_phy index, internal only */
        int wpan_phy_idx;
 
+       /* also protected by devlist_mtx */
+       int opencount;
+       wait_queue_head_t dev_wait;
+
+       /* protected by RTNL only */
+       int num_running_ifaces;
+
+       /* associated wpan interfaces, protected by rtnl or RCU */
+       struct list_head wpan_dev_list;
+       int devlist_generation, wpan_dev_id;
+
        /* must be last because of the way we do wpan_phy_priv(),
         * and it should at least be aligned to NETDEV_ALIGN
         */
@@ -23,7 +35,12 @@ wpan_phy_to_rdev(struct wpan_phy *wpan_phy)
                            wpan_phy);
 }
 
+extern struct list_head cfg802154_rdev_list;
+extern int cfg802154_rdev_list_generation;
+
 /* free object */
 void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
+struct cfg802154_registered_device *
+cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx);
 
 #endif /* __IEEE802154_CORE_H */
index 42ae63a345abb83296627c20aadda4239fa7db47..a5d7515b7f6256424eb26ce4f07250571ce7f6f7 100644 (file)
@@ -15,7 +15,7 @@
 #define IEEE_802154_LOCAL_H
 
 int __init ieee802154_nl_init(void);
-void __exit ieee802154_nl_exit(void);
+void ieee802154_nl_exit(void);
 
 #define IEEE802154_OP(_cmd, _func)                     \
        {                                               \
index 6c3c2595a201822f18c86b5e9080a640017cbc08..63ee7d66950e85d92e692016fbb187184ddbbf64 100644 (file)
@@ -155,7 +155,7 @@ int __init ieee802154_nl_init(void)
                                                    ieee802154_mcgrps);
 }
 
-void __exit ieee802154_nl_exit(void)
+void ieee802154_nl_exit(void)
 {
        genl_unregister_family(&nl802154_family);
 }
index 7127b9d1a68409457a1d570117ca1a962b6f2293..fe77f0c770b810b77b86fbbd089c440e76e02516 100644 (file)
@@ -113,7 +113,9 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
        if (ops->get_mac_params) {
                struct ieee802154_mac_params params;
 
+               rtnl_lock();
                ops->get_mac_params(dev, &params);
+               rtnl_unlock();
 
                if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER,
                               params.transmit_power) ||
@@ -164,7 +166,10 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
        if (!dev)
                return NULL;
 
-       if (dev->type != ARPHRD_IEEE802154) {
+       /* Check on mtu is currently a hacked solution because lowpan
+        * and wpan have the same ARPHRD type.
+        */
+       if (dev->type != ARPHRD_IEEE802154 || dev->mtu != IEEE802154_MTU) {
                dev_put(dev);
                return NULL;
        }
@@ -348,8 +353,10 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
        }
 
+       rtnl_lock();
        ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
                bcn_ord, sf_ord, pan_coord, blx, coord_realign);
+       rtnl_unlock();
 
        /* FIXME: add validation for unused parameters to be sane
         * for SoftMAC
@@ -444,7 +451,11 @@ int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
 
        idx = 0;
        for_each_netdev(net, dev) {
-               if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
+               /* Check on mtu is currently a hacked solution because lowpan
+                * and wpan have the same ARPHRD type.
+                */
+               if (idx < s_idx || dev->type != ARPHRD_IEEE802154 ||
+                   dev->mtu != IEEE802154_MTU)
                        goto cont;
 
                if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
@@ -497,6 +508,7 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
        phy = dev->ieee802154_ptr->wpan_phy;
        get_device(&phy->dev);
 
+       rtnl_lock();
        ops->get_mac_params(dev, &params);
 
        if (info->attrs[IEEE802154_ATTR_TXPOWER])
@@ -524,6 +536,7 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
                params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
 
        rc = ops->set_mac_params(dev, &params);
+       rtnl_unlock();
 
        wpan_phy_put(phy);
        dev_put(dev);
@@ -776,7 +789,11 @@ ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb,
        int rc;
 
        for_each_netdev(net, dev) {
-               if (idx < first_dev || dev->type != ARPHRD_IEEE802154)
+               /* Check on mtu is currently a hacked solution because lowpan
+                * and wpan have the same ARPHRD type.
+                */
+               if (idx < first_dev || dev->type != ARPHRD_IEEE802154 ||
+                   dev->mtu != IEEE802154_MTU)
                        goto skip;
 
                data.ops = ieee802154_mlme_ops(dev);
diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c
new file mode 100644 (file)
index 0000000..8896477
--- /dev/null
@@ -0,0 +1,957 @@
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/wireless/nl80211.c
+ */
+
+#include <linux/rtnetlink.h>
+
+#include <net/cfg802154.h>
+#include <net/genetlink.h>
+#include <net/mac802154.h>
+#include <net/netlink.h>
+#include <net/nl802154.h>
+#include <net/sock.h>
+
+#include "nl802154.h"
+#include "rdev-ops.h"
+#include "core.h"
+
+static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+                            struct genl_info *info);
+
+static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+                              struct genl_info *info);
+
+/* the netlink family */
+static struct genl_family nl802154_fam = {
+       .id = GENL_ID_GENERATE,         /* don't bother with a hardcoded ID */
+       .name = NL802154_GENL_NAME,     /* have users key off the name instead */
+       .hdrsize = 0,                   /* no private header */
+       .version = 1,                   /* no particular meaning now */
+       .maxattr = NL802154_ATTR_MAX,
+       .netnsok = true,
+       .pre_doit = nl802154_pre_doit,
+       .post_doit = nl802154_post_doit,
+};
+
+/* multicast groups */
+enum nl802154_multicast_groups {
+       NL802154_MCGRP_CONFIG,
+};
+
+static const struct genl_multicast_group nl802154_mcgrps[] = {
+       [NL802154_MCGRP_CONFIG] = { .name = "config", },
+};
+
+/* returns ERR_PTR values */
+static struct wpan_dev *
+__cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
+{
+       struct cfg802154_registered_device *rdev;
+       struct wpan_dev *result = NULL;
+       bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
+       bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
+       u64 wpan_dev_id;
+       int wpan_phy_idx = -1;
+       int ifidx = -1;
+
+       ASSERT_RTNL();
+
+       if (!have_ifidx && !have_wpan_dev_id)
+               return ERR_PTR(-EINVAL);
+
+       if (have_ifidx)
+               ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
+       if (have_wpan_dev_id) {
+               wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
+               wpan_phy_idx = wpan_dev_id >> 32;
+       }
+
+       list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+               struct wpan_dev *wpan_dev;
+
+               /* TODO netns compare */
+
+               if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
+                       continue;
+
+               list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
+                       if (have_ifidx && wpan_dev->netdev &&
+                           wpan_dev->netdev->ifindex == ifidx) {
+                               result = wpan_dev;
+                               break;
+                       }
+                       if (have_wpan_dev_id &&
+                           wpan_dev->identifier == (u32)wpan_dev_id) {
+                               result = wpan_dev;
+                               break;
+                       }
+               }
+
+               if (result)
+                       break;
+       }
+
+       if (result)
+               return result;
+
+       return ERR_PTR(-ENODEV);
+}
+
+static struct cfg802154_registered_device *
+__cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
+{
+       struct cfg802154_registered_device *rdev = NULL, *tmp;
+       struct net_device *netdev;
+
+       ASSERT_RTNL();
+
+       if (!attrs[NL802154_ATTR_WPAN_PHY] &&
+           !attrs[NL802154_ATTR_IFINDEX] &&
+           !attrs[NL802154_ATTR_WPAN_DEV])
+               return ERR_PTR(-EINVAL);
+
+       if (attrs[NL802154_ATTR_WPAN_PHY])
+               rdev = cfg802154_rdev_by_wpan_phy_idx(
+                               nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
+
+       if (attrs[NL802154_ATTR_WPAN_DEV]) {
+               u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
+               struct wpan_dev *wpan_dev;
+               bool found = false;
+
+               tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
+               if (tmp) {
+                       /* make sure wpan_dev exists */
+                       list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
+                               if (wpan_dev->identifier != (u32)wpan_dev_id)
+                                       continue;
+                               found = true;
+                               break;
+                       }
+
+                       if (!found)
+                               tmp = NULL;
+
+                       if (rdev && tmp != rdev)
+                               return ERR_PTR(-EINVAL);
+                       rdev = tmp;
+               }
+       }
+
+       if (attrs[NL802154_ATTR_IFINDEX]) {
+               int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
+
+               netdev = __dev_get_by_index(netns, ifindex);
+               if (netdev) {
+                       if (netdev->ieee802154_ptr)
+                               tmp = wpan_phy_to_rdev(
+                                               netdev->ieee802154_ptr->wpan_phy);
+                       else
+                               tmp = NULL;
+
+                       /* not wireless device -- return error */
+                       if (!tmp)
+                               return ERR_PTR(-EINVAL);
+
+                       /* mismatch -- return error */
+                       if (rdev && tmp != rdev)
+                               return ERR_PTR(-EINVAL);
+
+                       rdev = tmp;
+               }
+       }
+
+       if (!rdev)
+               return ERR_PTR(-ENODEV);
+
+       /* TODO netns compare */
+
+       return rdev;
+}
+
+/* This function returns a pointer to the driver
+ * that the genl_info item that is passed refers to.
+ *
+ * The result of this can be a PTR_ERR and hence must
+ * be checked with IS_ERR() for errors.
+ */
+static struct cfg802154_registered_device *
+cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
+{
+       return __cfg802154_rdev_from_attrs(netns, info->attrs);
+}
+
+/* policy for the attributes */
+static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
+       [NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 },
+       [NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING,
+                                         .len = 20-1 },
+
+       [NL802154_ATTR_IFINDEX] = { .type = NLA_U32 },
+       [NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
+       [NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
+
+       [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
+
+       [NL802154_ATTR_PAGE] = { .type = NLA_U8, },
+       [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, },
+
+       [NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
+
+       [NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
+
+       [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
+
+       [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
+       [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
+       [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
+
+       [NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
+       [NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
+       [NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
+
+       [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
+
+       [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
+};
+
+/* message building helper */
+static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
+                                   int flags, u8 cmd)
+{
+       /* since there is no private header just add the generic one */
+       return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
+}
+
+static int
+nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
+                               struct sk_buff *msg)
+{
+       struct nlattr *nl_page;
+       unsigned long page;
+
+       nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
+       if (!nl_page)
+               return -ENOBUFS;
+
+       for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
+               if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
+                               rdev->wpan_phy.channels_supported[page]))
+                       return -ENOBUFS;
+       }
+       nla_nest_end(msg, nl_page);
+
+       return 0;
+}
+
+static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
+                                 enum nl802154_commands cmd,
+                                 struct sk_buff *msg, u32 portid, u32 seq,
+                                 int flags)
+{
+       void *hdr;
+
+       hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+       if (!hdr)
+               return -ENOBUFS;
+
+       if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
+           nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
+                          wpan_phy_name(&rdev->wpan_phy)) ||
+           nla_put_u32(msg, NL802154_ATTR_GENERATION,
+                       cfg802154_rdev_list_generation))
+               goto nla_put_failure;
+
+       if (cmd != NL802154_CMD_NEW_WPAN_PHY)
+               goto finish;
+
+       /* DUMP PHY PIB */
+
+       /* current channel settings */
+       if (nla_put_u8(msg, NL802154_ATTR_PAGE,
+                      rdev->wpan_phy.current_page) ||
+           nla_put_u8(msg, NL802154_ATTR_CHANNEL,
+                      rdev->wpan_phy.current_channel))
+               goto nla_put_failure;
+
+       /* supported channels array */
+       if (nl802154_send_wpan_phy_channels(rdev, msg))
+               goto nla_put_failure;
+
+       /* cca mode */
+       if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE,
+                      rdev->wpan_phy.cca_mode))
+               goto nla_put_failure;
+
+       if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
+                      rdev->wpan_phy.transmit_power))
+               goto nla_put_failure;
+
+finish:
+       return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+struct nl802154_dump_wpan_phy_state {
+       s64 filter_wpan_phy;
+       long start;
+
+};
+
+static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
+                                       struct netlink_callback *cb,
+                                       struct nl802154_dump_wpan_phy_state *state)
+{
+       struct nlattr **tb = nl802154_fam.attrbuf;
+       int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
+                             tb, nl802154_fam.maxattr, nl802154_policy);
+
+       /* TODO check if we can handle error here,
+        * we have no backward compatibility
+        */
+       if (ret)
+               return 0;
+
+       if (tb[NL802154_ATTR_WPAN_PHY])
+               state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
+       if (tb[NL802154_ATTR_WPAN_DEV])
+               state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
+       if (tb[NL802154_ATTR_IFINDEX]) {
+               struct net_device *netdev;
+               struct cfg802154_registered_device *rdev;
+               int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
+
+               /* TODO netns */
+               netdev = __dev_get_by_index(&init_net, ifidx);
+               if (!netdev)
+                       return -ENODEV;
+               if (netdev->ieee802154_ptr) {
+                       rdev = wpan_phy_to_rdev(
+                                       netdev->ieee802154_ptr->wpan_phy);
+                       state->filter_wpan_phy = rdev->wpan_phy_idx;
+               }
+       }
+
+       return 0;
+}
+
+static int
+nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       int idx = 0, ret;
+       struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
+       struct cfg802154_registered_device *rdev;
+
+       rtnl_lock();
+       if (!state) {
+               state = kzalloc(sizeof(*state), GFP_KERNEL);
+               if (!state) {
+                       rtnl_unlock();
+                       return -ENOMEM;
+               }
+               state->filter_wpan_phy = -1;
+               ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
+               if (ret) {
+                       kfree(state);
+                       rtnl_unlock();
+                       return ret;
+               }
+               cb->args[0] = (long)state;
+       }
+
+       list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+               /* TODO net ns compare */
+               if (++idx <= state->start)
+                       continue;
+               if (state->filter_wpan_phy != -1 &&
+                   state->filter_wpan_phy != rdev->wpan_phy_idx)
+                       continue;
+               /* attempt to fit multiple wpan_phy data chunks into the skb */
+               ret = nl802154_send_wpan_phy(rdev,
+                                            NL802154_CMD_NEW_WPAN_PHY,
+                                            skb,
+                                            NETLINK_CB(cb->skb).portid,
+                                            cb->nlh->nlmsg_seq, NLM_F_MULTI);
+               if (ret < 0) {
+                       if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
+                           !skb->len && cb->min_dump_alloc < 4096) {
+                               cb->min_dump_alloc = 4096;
+                               rtnl_unlock();
+                               return 1;
+                       }
+                       idx--;
+                       break;
+               }
+               break;
+       }
+       rtnl_unlock();
+
+       state->start = idx;
+
+       return skb->len;
+}
+
+static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
+{
+       kfree((void *)cb->args[0]);
+       return 0;
+}
+
+static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
+{
+       struct sk_buff *msg;
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
+                                  info->snd_portid, info->snd_seq, 0) < 0) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
+{
+       return (u64)wpan_dev->identifier |
+              ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
+}
+
+static int
+nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
+                   struct cfg802154_registered_device *rdev,
+                   struct wpan_dev *wpan_dev)
+{
+       struct net_device *dev = wpan_dev->netdev;
+       void *hdr;
+
+       hdr = nl802154hdr_put(msg, portid, seq, flags,
+                             NL802154_CMD_NEW_INTERFACE);
+       if (!hdr)
+               return -1;
+
+       if (dev &&
+           (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
+            nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
+           nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
+           nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
+           nla_put_u32(msg, NL802154_ATTR_GENERATION,
+                       rdev->devlist_generation ^
+                       (cfg802154_rdev_list_generation << 2)))
+               goto nla_put_failure;
+
+       /* address settings */
+       if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
+                        wpan_dev->extended_addr) ||
+           nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
+                        wpan_dev->short_addr) ||
+           nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
+               goto nla_put_failure;
+
+       /* ARET handling */
+       if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
+                      wpan_dev->frame_retries) ||
+           nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
+           nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
+                      wpan_dev->csma_retries) ||
+           nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
+               goto nla_put_failure;
+
+       /* listen before transmit */
+       if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
+               goto nla_put_failure;
+
+       return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       int wp_idx = 0;
+       int if_idx = 0;
+       int wp_start = cb->args[0];
+       int if_start = cb->args[1];
+       struct cfg802154_registered_device *rdev;
+       struct wpan_dev *wpan_dev;
+
+       rtnl_lock();
+       list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+               /* TODO netns compare */
+               if (wp_idx < wp_start) {
+                       wp_idx++;
+                       continue;
+               }
+               if_idx = 0;
+
+               list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
+                       if (if_idx < if_start) {
+                               if_idx++;
+                               continue;
+                       }
+                       if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
+                                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                               rdev, wpan_dev) < 0) {
+                               goto out;
+                       }
+                       if_idx++;
+               }
+
+               wp_idx++;
+       }
+out:
+       rtnl_unlock();
+
+       cb->args[0] = wp_idx;
+       cb->args[1] = if_idx;
+
+       return skb->len;
+}
+
+static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
+{
+       struct sk_buff *msg;
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct wpan_dev *wdev = info->user_ptr[1];
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
+                               rdev, wdev) < 0) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
+       __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
+
+       /* TODO avoid failing a new interface
+        * creation due to pending removal?
+        */
+
+       if (!info->attrs[NL802154_ATTR_IFNAME])
+               return -EINVAL;
+
+       if (info->attrs[NL802154_ATTR_IFTYPE]) {
+               type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
+               if (type > NL802154_IFTYPE_MAX)
+                       return -EINVAL;
+       }
+
+       /* TODO add nla_get_le64 to netlink */
+       if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
+               extended_addr = (__force __le64)nla_get_u64(
+                               info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
+
+       if (!rdev->ops->add_virtual_intf)
+               return -EOPNOTSUPP;
+
+       return rdev_add_virtual_intf(rdev,
+                                    nla_data(info->attrs[NL802154_ATTR_IFNAME]),
+                                    type, extended_addr);
+}
+
+static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct wpan_dev *wpan_dev = info->user_ptr[1];
+
+       if (!rdev->ops->del_virtual_intf)
+               return -EOPNOTSUPP;
+
+       /* If we remove a wpan device without a netdev then clear
+        * user_ptr[1] so that nl802154_post_doit won't dereference it
+        * to check if it needs to do dev_put(). Otherwise it crashes
+        * since the wpan_dev has been freed, unlike with a netdev where
+        * we need the dev_put() for the netdev to really be freed.
+        */
+       if (!wpan_dev->netdev)
+               info->user_ptr[1] = NULL;
+
+       return rdev_del_virtual_intf(rdev, wpan_dev);
+}
+
+static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       u8 channel, page;
+
+       if (!info->attrs[NL802154_ATTR_PAGE] ||
+           !info->attrs[NL802154_ATTR_CHANNEL])
+               return -EINVAL;
+
+       page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
+       channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
+
+       /* check 802.15.4 constraints */
+       if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL)
+               return -EINVAL;
+
+       return rdev_set_channel(rdev, page, channel);
+}
+
+static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       __le16 pan_id;
+
+       /* conflict here while tx/rx calls */
+       if (netif_running(dev))
+               return -EBUSY;
+
+       /* don't change address fields on monitor */
+       if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
+               return -EINVAL;
+
+       if (!info->attrs[NL802154_ATTR_PAN_ID])
+               return -EINVAL;
+
+       pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
+
+       return rdev_set_pan_id(rdev, wpan_dev, pan_id);
+}
+
+static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       __le16 short_addr;
+
+       /* conflict here while tx/rx calls */
+       if (netif_running(dev))
+               return -EBUSY;
+
+       /* don't change address fields on monitor */
+       if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
+               return -EINVAL;
+
+       if (!info->attrs[NL802154_ATTR_SHORT_ADDR])
+               return -EINVAL;
+
+       short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
+
+       return rdev_set_short_addr(rdev, wpan_dev, short_addr);
+}
+
+static int
+nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       u8 min_be, max_be;
+
+       /* should be set on netif open inside phy settings */
+       if (netif_running(dev))
+               return -EBUSY;
+
+       if (!info->attrs[NL802154_ATTR_MIN_BE] ||
+           !info->attrs[NL802154_ATTR_MAX_BE])
+               return -EINVAL;
+
+       min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
+       max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
+
+       /* check 802.15.4 constraints */
+       if (max_be < 3 || max_be > 8 || min_be > max_be)
+               return -EINVAL;
+
+       return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
+}
+
+static int
+nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       u8 max_csma_backoffs;
+
+       /* conflict here while other running iface settings */
+       if (netif_running(dev))
+               return -EBUSY;
+
+       if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
+               return -EINVAL;
+
+       max_csma_backoffs = nla_get_u8(
+                       info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
+
+       /* check 802.15.4 constraints */
+       if (max_csma_backoffs > 5)
+               return -EINVAL;
+
+       return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
+}
+
+static int
+nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       s8 max_frame_retries;
+
+       if (netif_running(dev))
+               return -EBUSY;
+
+       if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
+               return -EINVAL;
+
+       max_frame_retries = nla_get_s8(
+                       info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
+
+       /* check 802.15.4 constraints */
+       if (max_frame_retries < -1 || max_frame_retries > 7)
+               return -EINVAL;
+
+       return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
+}
+
+static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+       bool mode;
+
+       if (netif_running(dev))
+               return -EBUSY;
+
+       if (!info->attrs[NL802154_ATTR_LBT_MODE])
+               return -EINVAL;
+
+       mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
+       return rdev_set_lbt_mode(rdev, wpan_dev, mode);
+}
+
+#define NL802154_FLAG_NEED_WPAN_PHY    0x01
+#define NL802154_FLAG_NEED_NETDEV      0x02
+#define NL802154_FLAG_NEED_RTNL                0x04
+#define NL802154_FLAG_CHECK_NETDEV_UP  0x08
+#define NL802154_FLAG_NEED_NETDEV_UP   (NL802154_FLAG_NEED_NETDEV |\
+                                        NL802154_FLAG_CHECK_NETDEV_UP)
+#define NL802154_FLAG_NEED_WPAN_DEV    0x10
+#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
+                                        NL802154_FLAG_CHECK_NETDEV_UP)
+
+static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+                            struct genl_info *info)
+{
+       struct cfg802154_registered_device *rdev;
+       struct wpan_dev *wpan_dev;
+       struct net_device *dev;
+       bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
+
+       if (rtnl)
+               rtnl_lock();
+
+       if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
+               rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
+               if (IS_ERR(rdev)) {
+                       if (rtnl)
+                               rtnl_unlock();
+                       return PTR_ERR(rdev);
+               }
+               info->user_ptr[0] = rdev;
+       } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
+                  ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+               ASSERT_RTNL();
+               wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
+                                                          info->attrs);
+               if (IS_ERR(wpan_dev)) {
+                       if (rtnl)
+                               rtnl_unlock();
+                       return PTR_ERR(wpan_dev);
+               }
+
+               dev = wpan_dev->netdev;
+               rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+
+               if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
+                       if (!dev) {
+                               if (rtnl)
+                                       rtnl_unlock();
+                               return -EINVAL;
+                       }
+
+                       info->user_ptr[1] = dev;
+               } else {
+                       info->user_ptr[1] = wpan_dev;
+               }
+
+               if (dev) {
+                       if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
+                           !netif_running(dev)) {
+                               if (rtnl)
+                                       rtnl_unlock();
+                               return -ENETDOWN;
+                       }
+
+                       dev_hold(dev);
+               }
+
+               info->user_ptr[0] = rdev;
+       }
+
+       return 0;
+}
+
+static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+                              struct genl_info *info)
+{
+       if (info->user_ptr[1]) {
+               if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+                       struct wpan_dev *wpan_dev = info->user_ptr[1];
+
+                       if (wpan_dev->netdev)
+                               dev_put(wpan_dev->netdev);
+               } else {
+                       dev_put(info->user_ptr[1]);
+               }
+       }
+
+       if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
+               rtnl_unlock();
+}
+
+static const struct genl_ops nl802154_ops[] = {
+       {
+               .cmd = NL802154_CMD_GET_WPAN_PHY,
+               .doit = nl802154_get_wpan_phy,
+               .dumpit = nl802154_dump_wpan_phy,
+               .done = nl802154_dump_wpan_phy_done,
+               .policy = nl802154_policy,
+               /* can be retrieved by unprivileged users */
+               .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_GET_INTERFACE,
+               .doit = nl802154_get_interface,
+               .dumpit = nl802154_dump_interface,
+               .policy = nl802154_policy,
+               /* can be retrieved by unprivileged users */
+               .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_NEW_INTERFACE,
+               .doit = nl802154_new_interface,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_DEL_INTERFACE,
+               .doit = nl802154_del_interface,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_SET_CHANNEL,
+               .doit = nl802154_set_channel,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_SET_PAN_ID,
+               .doit = nl802154_set_pan_id,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_SET_SHORT_ADDR,
+               .doit = nl802154_set_short_addr,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
+               .doit = nl802154_set_backoff_exponent,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
+               .doit = nl802154_set_max_csma_backoffs,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
+               .doit = nl802154_set_max_frame_retries,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL802154_CMD_SET_LBT_MODE,
+               .doit = nl802154_set_lbt_mode,
+               .policy = nl802154_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL802154_FLAG_NEED_NETDEV |
+                                 NL802154_FLAG_NEED_RTNL,
+       },
+};
+
+/* initialisation/exit functions */
+int nl802154_init(void)
+{
+       return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
+                                                   nl802154_mcgrps);
+}
+
+void nl802154_exit(void)
+{
+       genl_unregister_family(&nl802154_fam);
+}
diff --git a/net/ieee802154/nl802154.h b/net/ieee802154/nl802154.h
new file mode 100644 (file)
index 0000000..3846a89
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __IEEE802154_NL802154_H
+#define __IEEE802154_NL802154_H
+
+int nl802154_init(void);
+void nl802154_exit(void);
+
+#endif /* __IEEE802154_NL802154_H */
index ac8824ec168c9941166af6dfad1006292e0ae4ba..aff54fbd9264e92fd03578d6ecb5298d53226a42 100644 (file)
@@ -20,4 +20,70 @@ rdev_del_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
        rdev->ops->del_virtual_intf_deprecated(&rdev->wpan_phy, dev);
 }
 
+static inline int
+rdev_add_virtual_intf(struct cfg802154_registered_device *rdev, char *name,
+                     enum nl802154_iftype type, __le64 extended_addr)
+{
+       return rdev->ops->add_virtual_intf(&rdev->wpan_phy, name, type,
+                                          extended_addr);
+}
+
+static inline int
+rdev_del_virtual_intf(struct cfg802154_registered_device *rdev,
+                     struct wpan_dev *wpan_dev)
+{
+       return rdev->ops->del_virtual_intf(&rdev->wpan_phy, wpan_dev);
+}
+
+static inline int
+rdev_set_channel(struct cfg802154_registered_device *rdev, u8 page, u8 channel)
+{
+       return rdev->ops->set_channel(&rdev->wpan_phy, page, channel);
+}
+
+static inline int
+rdev_set_pan_id(struct cfg802154_registered_device *rdev,
+               struct wpan_dev *wpan_dev, __le16 pan_id)
+{
+       return rdev->ops->set_pan_id(&rdev->wpan_phy, wpan_dev, pan_id);
+}
+
+static inline int
+rdev_set_short_addr(struct cfg802154_registered_device *rdev,
+                   struct wpan_dev *wpan_dev, __le16 short_addr)
+{
+       return rdev->ops->set_short_addr(&rdev->wpan_phy, wpan_dev, short_addr);
+}
+
+static inline int
+rdev_set_backoff_exponent(struct cfg802154_registered_device *rdev,
+                         struct wpan_dev *wpan_dev, u8 min_be, u8 max_be)
+{
+       return rdev->ops->set_backoff_exponent(&rdev->wpan_phy, wpan_dev,
+                                              min_be, max_be);
+}
+
+static inline int
+rdev_set_max_csma_backoffs(struct cfg802154_registered_device *rdev,
+                          struct wpan_dev *wpan_dev, u8 max_csma_backoffs)
+{
+       return rdev->ops->set_max_csma_backoffs(&rdev->wpan_phy, wpan_dev,
+                                               max_csma_backoffs);
+}
+
+static inline int
+rdev_set_max_frame_retries(struct cfg802154_registered_device *rdev,
+                          struct wpan_dev *wpan_dev, s8 max_frame_retries)
+{
+       return rdev->ops->set_max_frame_retries(&rdev->wpan_phy, wpan_dev,
+                                               max_frame_retries);
+}
+
+static inline int
+rdev_set_lbt_mode(struct cfg802154_registered_device *rdev,
+                 struct wpan_dev *wpan_dev, bool mode)
+{
+       return rdev->ops->set_lbt_mode(&rdev->wpan_phy, wpan_dev, mode);
+}
+
 #endif /* __CFG802154_RDEV_OPS */
index 88199980dae9398a78008bb34981e6977cb4a203..1613b9c65dfa15aafd154042e60abc44517bc9d6 100644 (file)
@@ -27,6 +27,27 @@ dev_to_rdev(struct device *dev)
                            wpan_phy.dev);
 }
 
+#define SHOW_FMT(name, fmt, member)                                    \
+static ssize_t name ## _show(struct device *dev,                       \
+                            struct device_attribute *attr,             \
+                            char *buf)                                 \
+{                                                                      \
+       return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member);        \
+}                                                                      \
+static DEVICE_ATTR_RO(name)
+
+SHOW_FMT(index, "%d", wpan_phy_idx);
+
+static ssize_t name_show(struct device *dev,
+                        struct device_attribute *attr,
+                        char *buf)
+{
+       struct wpan_phy *wpan_phy = &dev_to_rdev(dev)->wpan_phy;
+
+       return sprintf(buf, "%s\n", dev_name(&wpan_phy->dev));
+}
+static DEVICE_ATTR_RO(name);
+
 #define MASTER_SHOW_COMPLEX(name, format_string, args...)              \
 static ssize_t name ## _show(struct device *dev,                       \
                            struct device_attribute *attr, char *buf)   \
@@ -78,6 +99,9 @@ static void wpan_phy_release(struct device *dev)
 }
 
 static struct attribute *pmib_attrs[] = {
+       &dev_attr_index.attr,
+       &dev_attr_name.attr,
+       /* below will be removed soon */
        &dev_attr_current_channel.attr,
        &dev_attr_current_page.attr,
        &dev_attr_channels_supported.attr,
index ec24378caaafaf333152e856aa0e2e920ddbb13f..09d9caaec59112f40b060951ae16796388e2e741 100644 (file)
@@ -53,6 +53,9 @@ int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
                __aligned(__alignof__(struct aead_request));
        struct aead_request *aead_req = (void *) aead_req_data;
 
+       if (data_len == 0)
+               return -EINVAL;
+
        memset(aead_req, 0, sizeof(aead_req_data));
 
        sg_init_one(&pt, data, data_len);
index 56b53571c8077ae5a3660449cb0afeb81375028c..509bc157ce5551700db59e380d180c486b44f097 100644 (file)
@@ -805,7 +805,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        memset(&params, 0, sizeof(params));
        memset(&csa_ie, 0, sizeof(csa_ie));
-       err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
+       err = ieee80211_parse_ch_switch_ie(sdata, elems,
                                           ifibss->chandef.chan->band,
                                           sta_flags, ifibss->bssid, &csa_ie);
        /* can't switch to destination channel, fail */
index cf95d033bcbfda4013f98b9f09be6610aeab2f3d..cc6e964d98370e8d07b640f6d8c01ddfbce2be1b 100644 (file)
@@ -1723,7 +1723,6 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
  * ieee80211_parse_ch_switch_ie - parses channel switch IEs
  * @sdata: the sdata of the interface which has received the frame
  * @elems: parsed 802.11 elements received with the frame
- * @beacon: indicates if the frame was a beacon or probe response
  * @current_band: indicates the current band
  * @sta_flags: contains information about own capabilities and restrictions
  *     to decide which channel switch announcements can be accepted. Only the
@@ -1737,7 +1736,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
  * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
  */
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
-                                struct ieee802_11_elems *elems, bool beacon,
+                                struct ieee802_11_elems *elems,
                                 enum ieee80211_band current_band,
                                 u32 sta_flags, u8 *bssid,
                                 struct ieee80211_csa_ie *csa_ie);
index 82473d909bb683a5e1467e555d28e280740706a1..538fe4ef5c85dd7e285f8fb0f8166073cebcfac6 100644 (file)
@@ -777,10 +777,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        int i, flushed;
        struct ps_data *ps;
        struct cfg80211_chan_def chandef;
+       bool cancel_scan;
 
        clear_bit(SDATA_STATE_RUNNING, &sdata->state);
 
-       if (rcu_access_pointer(local->scan_sdata) == sdata)
+       cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
+       if (cancel_scan)
                ieee80211_scan_cancel(local);
 
        /*
@@ -911,6 +913,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                list_del(&sdata->u.vlan.list);
                mutex_unlock(&local->mtx);
                RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
+               /* see comment in the default case below */
+               ieee80211_free_keys(sdata, true);
                /* no need to tell driver */
                break;
        case NL80211_IFTYPE_MONITOR:
@@ -936,17 +940,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                /*
                 * When we get here, the interface is marked down.
                 * Free the remaining keys, if there are any
-                * (shouldn't be, except maybe in WDS mode?)
+                * (which can happen in AP mode if userspace sets
+                * keys before the interface is operating, and maybe
+                * also in WDS mode)
                 *
                 * Force the key freeing to always synchronize_net()
                 * to wait for the RX path in case it is using this
-                * interface enqueuing frames at this very time on
+                * interface enqueuing frames at this very time on
                 * another CPU.
                 */
                ieee80211_free_keys(sdata, true);
-
-               /* fall through */
-       case NL80211_IFTYPE_AP:
                skb_queue_purge(&sdata->skb_queue);
        }
 
@@ -1004,6 +1007,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_recalc_ps(local, -1);
 
+       if (cancel_scan)
+               flush_delayed_work(&local->scan_work);
+
        if (local->open_count == 0) {
                ieee80211_stop_device(local);
 
index e9f99c1e3fad5905682a61f978d9897833426fd0..0c8b2a77d312d5e3ad18f975ce808c44755c820b 100644 (file)
@@ -874,7 +874,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
 
        memset(&params, 0, sizeof(params));
        memset(&csa_ie, 0, sizeof(csa_ie));
-       err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
+       err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
                                           sta_flags, sdata->vif.addr,
                                           &csa_ie);
        if (err < 0)
index d29589a090658436427e7d153879df7978c534e9..ba06cd003375bf9592603823b8f452dd97a5ec63 100644 (file)
@@ -1119,7 +1119,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        current_band = cbss->channel->band;
        memset(&csa_ie, 0, sizeof(csa_ie));
-       res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
+       res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
                                           ifmgd->flags,
                                           ifmgd->associated->bssid, &csa_ie);
        if (res < 0)
@@ -1221,7 +1221,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
        else
                mod_timer(&ifmgd->chswitch_timer,
-                         TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
+                         TU_TO_EXP_TIME((csa_ie.count - 1) *
+                                        cbss->beacon_interval));
 }
 
 static bool
index d9bbb73d4436cb13193f5dde7745cb7a71ae97de..49c23bdf08bb2f65786269ccb11ccf8b520a6d9a 100644 (file)
@@ -1761,11 +1761,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
        sc = le16_to_cpu(hdr->seq_ctrl);
        frag = sc & IEEE80211_SCTL_FRAG;
 
-       if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
-                  is_multicast_ether_addr(hdr->addr1))) {
-               /* not fragmented */
+       if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
+               goto out;
+
+       if (is_multicast_ether_addr(hdr->addr1)) {
+               rx->local->dot11MulticastReceivedFrameCount++;
                goto out;
        }
+
        I802_DEBUG_INC(rx->local->rx_handlers_fragments);
 
        if (skb_linearize(rx->skb))
@@ -1858,10 +1861,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
  out:
        if (rx->sta)
                rx->sta->rx_packets++;
-       if (is_multicast_ether_addr(hdr->addr1))
-               rx->local->dot11MulticastReceivedFrameCount++;
-       else
-               ieee80211_led_rx(rx->local);
+       ieee80211_led_rx(rx->local);
        return RX_CONTINUE;
 }
 
index 6ab00907008461fb14d551b2633a8dbcd4fef623..efeba56c913bae0d7284bfc74862599a2ad2298b 100644 (file)
@@ -22,7 +22,7 @@
 #include "wme.h"
 
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
-                                struct ieee802_11_elems *elems, bool beacon,
+                                struct ieee802_11_elems *elems,
                                 enum ieee80211_band current_band,
                                 u32 sta_flags, u8 *bssid,
                                 struct ieee80211_csa_ie *csa_ie)
@@ -91,19 +91,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                return -EINVAL;
        }
 
-       if (!beacon && sec_chan_offs) {
+       if (sec_chan_offs) {
                secondary_channel_offset = sec_chan_offs->sec_chan_offs;
-       } else if (beacon && ht_oper) {
-               secondary_channel_offset =
-                       ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
        } else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
-               /* If it's not a beacon, HT is enabled and the IE not present,
-                * it's 20 MHz, 802.11-2012 8.5.2.6:
-                *      This element [the Secondary Channel Offset Element] is
-                *      present when switching to a 40 MHz channel. It may be
-                *      present when switching to a 20 MHz channel (in which
-                *      case the secondary channel offset is set to SCN).
-                */
+               /* If the secondary channel offset IE is not present,
+                * we can't know what's the post-CSA offset, so the
+                * best we can do is use 20MHz.
+               */
                secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
        }
 
index d2c4e8f89720a8288043628ad894c7fa32964203..c035708ada160d5364417db8a4c3179819016d49 100644 (file)
@@ -17,6 +17,7 @@
 #include <net/cfg802154.h>
 
 #include "ieee802154_i.h"
+#include "driver-ops.h"
 #include "cfg.h"
 
 static struct net_device *
@@ -27,7 +28,8 @@ ieee802154_add_iface_deprecated(struct wpan_phy *wpan_phy,
        struct net_device *dev;
 
        rtnl_lock();
-       dev = ieee802154_if_add(local, name, NULL, type);
+       dev = ieee802154_if_add(local, name, type,
+                               cpu_to_le64(0x0000000000000000ULL));
        rtnl_unlock();
 
        return dev;
@@ -41,7 +43,168 @@ static void ieee802154_del_iface_deprecated(struct wpan_phy *wpan_phy,
        ieee802154_if_remove(sdata);
 }
 
+static int
+ieee802154_add_iface(struct wpan_phy *phy, const char *name,
+                    enum nl802154_iftype type, __le64 extended_addr)
+{
+       struct ieee802154_local *local = wpan_phy_priv(phy);
+       struct net_device *err;
+
+       err = ieee802154_if_add(local, name, type, extended_addr);
+       if (IS_ERR(err))
+               return PTR_ERR(err);
+
+       return 0;
+}
+
+static int
+ieee802154_del_iface(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev)
+{
+       ieee802154_if_remove(IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev));
+
+       return 0;
+}
+
+static int
+ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel)
+{
+       struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+       int ret;
+
+       ASSERT_RTNL();
+
+       /* check if phy support this setting */
+       if (!(wpan_phy->channels_supported[page] & BIT(channel)))
+               return -EINVAL;
+
+       ret = drv_set_channel(local, page, channel);
+       if (!ret) {
+               wpan_phy->current_page = page;
+               wpan_phy->current_channel = channel;
+       }
+
+       return ret;
+}
+
+static int
+ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                     __le16 pan_id)
+{
+       ASSERT_RTNL();
+
+       /* TODO
+        * I am not sure about to check here on broadcast pan_id.
+        * Broadcast is a valid setting, comment from 802.15.4:
+        * If this value is 0xffff, the device is not associated.
+        *
+        * This could useful to simple deassociate an device.
+        */
+       if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
+               return -EINVAL;
+
+       wpan_dev->pan_id = pan_id;
+       return 0;
+}
+
+static int
+ieee802154_set_backoff_exponent(struct wpan_phy *wpan_phy,
+                               struct wpan_dev *wpan_dev,
+                               u8 min_be, u8 max_be)
+{
+       struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+       ASSERT_RTNL();
+
+       if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS))
+               return -EOPNOTSUPP;
+
+       wpan_dev->min_be = min_be;
+       wpan_dev->max_be = max_be;
+       return 0;
+}
+
+static int
+ieee802154_set_short_addr(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                         __le16 short_addr)
+{
+       ASSERT_RTNL();
+
+       /* TODO
+        * I am not sure about to check here on broadcast short_addr.
+        * Broadcast is a valid setting, comment from 802.15.4:
+        * A value of 0xfffe indicates that the device has
+        * associated but has not been allocated an address. A
+        * value of 0xffff indicates that the device does not
+        * have a short address.
+        *
+        * I think we should allow to set these settings but
+        * don't allow to allow socket communication with it.
+        */
+       if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) ||
+           short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST))
+               return -EINVAL;
+
+       wpan_dev->short_addr = short_addr;
+       return 0;
+}
+
+static int
+ieee802154_set_max_csma_backoffs(struct wpan_phy *wpan_phy,
+                                struct wpan_dev *wpan_dev,
+                                u8 max_csma_backoffs)
+{
+       struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+       ASSERT_RTNL();
+
+       if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS))
+               return -EOPNOTSUPP;
+
+       wpan_dev->csma_retries = max_csma_backoffs;
+       return 0;
+}
+
+static int
+ieee802154_set_max_frame_retries(struct wpan_phy *wpan_phy,
+                                struct wpan_dev *wpan_dev,
+                                s8 max_frame_retries)
+{
+       struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+       ASSERT_RTNL();
+
+       if (!(local->hw.flags & IEEE802154_HW_FRAME_RETRIES))
+               return -EOPNOTSUPP;
+
+       wpan_dev->frame_retries = max_frame_retries;
+       return 0;
+}
+
+static int
+ieee802154_set_lbt_mode(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+                       bool mode)
+{
+       struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+       ASSERT_RTNL();
+
+       if (!(local->hw.flags & IEEE802154_HW_LBT))
+               return -EOPNOTSUPP;
+
+       wpan_dev->lbt = mode;
+       return 0;
+}
+
 const struct cfg802154_ops mac802154_config_ops = {
        .add_virtual_intf_deprecated = ieee802154_add_iface_deprecated,
        .del_virtual_intf_deprecated = ieee802154_del_iface_deprecated,
+       .add_virtual_intf = ieee802154_add_iface,
+       .del_virtual_intf = ieee802154_del_iface,
+       .set_channel = ieee802154_set_channel,
+       .set_pan_id = ieee802154_set_pan_id,
+       .set_short_addr = ieee802154_set_short_addr,
+       .set_backoff_exponent = ieee802154_set_backoff_exponent,
+       .set_max_csma_backoffs = ieee802154_set_max_csma_backoffs,
+       .set_max_frame_retries = ieee802154_set_max_frame_retries,
+       .set_lbt_mode = ieee802154_set_lbt_mode,
 };
index dfd29ffb8fee392074bb1d84b5e1861adfd71798..f21e864613d09b6d02c4ffe8f2b7e72e78b75e16 100644 (file)
@@ -50,16 +50,15 @@ static inline void drv_stop(struct ieee802154_local *local)
        local->started = false;
 }
 
-static inline int drv_set_channel(struct ieee802154_local *local,
-                                 const u8 page, const u8 channel)
+static inline int
+drv_set_channel(struct ieee802154_local *local, u8 page, u8 channel)
 {
        might_sleep();
 
        return local->ops->set_channel(&local->hw, page, channel);
 }
 
-static inline int drv_set_tx_power(struct ieee802154_local *local,
-                                  const s8 dbm)
+static inline int drv_set_tx_power(struct ieee802154_local *local, s8 dbm)
 {
        might_sleep();
 
@@ -71,8 +70,7 @@ static inline int drv_set_tx_power(struct ieee802154_local *local,
        return local->ops->set_txpower(&local->hw, dbm);
 }
 
-static inline int drv_set_cca_mode(struct ieee802154_local *local,
-                                  const u8 cca_mode)
+static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode)
 {
        might_sleep();
 
@@ -84,8 +82,7 @@ static inline int drv_set_cca_mode(struct ieee802154_local *local,
        return local->ops->set_cca_mode(&local->hw, cca_mode);
 }
 
-static inline int drv_set_lbt_mode(struct ieee802154_local *local,
-                                  const bool mode)
+static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode)
 {
        might_sleep();
 
@@ -97,8 +94,8 @@ static inline int drv_set_lbt_mode(struct ieee802154_local *local,
        return local->ops->set_lbt(&local->hw, mode);
 }
 
-static inline int drv_set_cca_ed_level(struct ieee802154_local *local,
-                                      const s32 ed_level)
+static inline int
+drv_set_cca_ed_level(struct ieee802154_local *local, s32 ed_level)
 {
        might_sleep();
 
@@ -110,8 +107,7 @@ static inline int drv_set_cca_ed_level(struct ieee802154_local *local,
        return local->ops->set_cca_ed_level(&local->hw, ed_level);
 }
 
-static inline int drv_set_pan_id(struct ieee802154_local *local,
-                                const __le16 pan_id)
+static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id)
 {
        struct ieee802154_hw_addr_filt filt;
 
@@ -128,8 +124,8 @@ static inline int drv_set_pan_id(struct ieee802154_local *local,
                                            IEEE802154_AFILT_PANID_CHANGED);
 }
 
-static inline int drv_set_extended_addr(struct ieee802154_local *local,
-                                       const __le64 extended_addr)
+static inline int
+drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr)
 {
        struct ieee802154_hw_addr_filt filt;
 
@@ -146,8 +142,8 @@ static inline int drv_set_extended_addr(struct ieee802154_local *local,
                                            IEEE802154_AFILT_IEEEADDR_CHANGED);
 }
 
-static inline int drv_set_short_addr(struct ieee802154_local *local,
-                                    const __le16 short_addr)
+static inline int
+drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr)
 {
        struct ieee802154_hw_addr_filt filt;
 
@@ -164,8 +160,8 @@ static inline int drv_set_short_addr(struct ieee802154_local *local,
                                            IEEE802154_AFILT_SADDR_CHANGED);
 }
 
-static inline int drv_set_pan_coord(struct ieee802154_local *local,
-                                   const bool is_coord)
+static inline int
+drv_set_pan_coord(struct ieee802154_local *local, bool is_coord)
 {
        struct ieee802154_hw_addr_filt filt;
 
@@ -182,9 +178,9 @@ static inline int drv_set_pan_coord(struct ieee802154_local *local,
                                            IEEE802154_AFILT_PANC_CHANGED);
 }
 
-static inline int drv_set_csma_params(struct ieee802154_local *local,
-                                     u8 min_be, u8 max_be,
-                                     u8 max_csma_backoffs)
+static inline int
+drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be,
+                   u8 max_csma_backoffs)
 {
        might_sleep();
 
@@ -197,8 +193,8 @@ static inline int drv_set_csma_params(struct ieee802154_local *local,
                                           max_csma_backoffs);
 }
 
-static inline int drv_set_max_frame_retries(struct ieee802154_local *local,
-                                           s8 max_frame_retries)
+static inline int
+drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries)
 {
        might_sleep();
 
@@ -210,8 +206,8 @@ static inline int drv_set_max_frame_retries(struct ieee802154_local *local,
        return local->ops->set_frame_retries(&local->hw, max_frame_retries);
 }
 
-static inline int drv_set_promiscuous_mode(struct ieee802154_local *local,
-                                          const bool on)
+static inline int
+drv_set_promiscuous_mode(struct ieee802154_local *local, bool on)
 {
        might_sleep();
 
index 4acacea0d37179219a542945108cf5c91e99aa5d..bebd70ffc7a3d101551f023e515db24033883f0b 100644 (file)
 #define __IEEE802154_I_H
 
 #include <linux/mutex.h>
+#include <linux/hrtimer.h>
 #include <net/cfg802154.h>
 #include <net/mac802154.h>
+#include <net/nl802154.h>
 #include <net/ieee802154_netdev.h>
 
 #include "llsec.h"
@@ -51,6 +53,8 @@ struct ieee802154_local {
         */
        struct workqueue_struct *workqueue;
 
+       struct hrtimer ifs_timer;
+
        bool started;
 
        struct tasklet_struct tasklet;
@@ -84,18 +88,6 @@ struct ieee802154_sub_if_data {
 
        spinlock_t mib_lock;
 
-       __le16 pan_id;
-       __le16 short_addr;
-       __le64 extended_addr;
-       bool promiscuous_mode;
-
-       struct ieee802154_mac_params mac_params;
-
-       /* MAC BSN field */
-       u8 bsn;
-       /* MAC DSN field */
-       u8 dsn;
-
        /* protects sec from concurrent access by netlink. access by
         * encrypt/decrypt/header_create safe without additional protection.
         */
@@ -108,6 +100,9 @@ struct ieee802154_sub_if_data {
 
 #define MAC802154_CHAN_NONE            0xff /* No channel is assigned */
 
+/* utility functions/constants */
+extern const void *const mac802154_wpan_phy_privid; /*  for wpan_phy privid */
+
 static inline struct ieee802154_local *
 hw_to_local(struct ieee802154_hw *hw)
 {
@@ -120,22 +115,25 @@ IEEE802154_DEV_TO_SUB_IF(const struct net_device *dev)
        return netdev_priv(dev);
 }
 
+static inline struct ieee802154_sub_if_data *
+IEEE802154_WPAN_DEV_TO_SUB_IF(struct wpan_dev *wpan_dev)
+{
+       return container_of(wpan_dev, struct ieee802154_sub_if_data, wpan_dev);
+}
+
 static inline bool
 ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata)
 {
        return test_bit(SDATA_STATE_RUNNING, &sdata->state);
 }
 
-extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced;
 extern struct ieee802154_mlme_ops mac802154_mlme_wpan;
 
-void mac802154_monitor_setup(struct net_device *dev);
 netdev_tx_t
 ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
-
-void mac802154_wpan_setup(struct net_device *dev);
 netdev_tx_t
 ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
+enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer);
 
 /* MIB callbacks */
 void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val);
@@ -178,11 +176,13 @@ void mac802154_get_table(struct net_device *dev,
                         struct ieee802154_llsec_table **t);
 void mac802154_unlock_table(struct net_device *dev);
 
-struct net_device *
-mac802154_add_iface(struct wpan_phy *phy, const char *name, int type);
+/* interface handling */
+int ieee802154_iface_init(void);
+void ieee802154_iface_exit(void);
 void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata);
 struct net_device *
 ieee802154_if_add(struct ieee802154_local *local, const char *name,
-                 struct wpan_dev **new_wpan_dev, int type);
+                 enum nl802154_iftype type, __le64 extended_addr);
+void ieee802154_remove_interfaces(struct ieee802154_local *local);
 
 #endif /* __IEEE802154_I_H */
index 384f4bb3c99bb9a414f57d344140d7634d0cb74a..38dfc72d24b689483facf8404d12970978cb3e56 100644 (file)
@@ -22,8 +22,7 @@
 #include <linux/if_arp.h>
 #include <linux/ieee802154.h>
 
-#include <net/rtnetlink.h>
-#include <linux/nl802154.h>
+#include <net/nl802154.h>
 #include <net/mac802154.h>
 #include <net/ieee802154_netdev.h>
 #include <net/cfg802154.h>
@@ -35,16 +34,17 @@ static int mac802154_wpan_update_llsec(struct net_device *dev)
 {
        struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
        struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct wpan_dev *wpan_dev = &sdata->wpan_dev;
        int rc = 0;
 
        if (ops->llsec) {
                struct ieee802154_llsec_params params;
                int changed = 0;
 
-               params.pan_id = sdata->pan_id;
+               params.pan_id = wpan_dev->pan_id;
                changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
 
-               params.hwaddr = sdata->extended_addr;
+               params.hwaddr = wpan_dev->extended_addr;
                changed |= IEEE802154_LLSEC_PARAM_HWADDR;
 
                rc = ops->llsec->set_params(dev, &params, changed);
@@ -57,10 +57,13 @@ static int
 mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       struct wpan_dev *wpan_dev = &sdata->wpan_dev;
        struct sockaddr_ieee802154 *sa =
                (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
        int err = -ENOIOCTLCMD;
 
+       ASSERT_RTNL();
+
        spin_lock_bh(&sdata->mib_lock);
 
        switch (cmd) {
@@ -68,8 +71,8 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        {
                u16 pan_id, short_addr;
 
-               pan_id = le16_to_cpu(sdata->pan_id);
-               short_addr = le16_to_cpu(sdata->short_addr);
+               pan_id = le16_to_cpu(wpan_dev->pan_id);
+               short_addr = le16_to_cpu(wpan_dev->short_addr);
                if (pan_id == IEEE802154_PANID_BROADCAST ||
                    short_addr == IEEE802154_ADDR_BROADCAST) {
                        err = -EADDRNOTAVAIL;
@@ -85,6 +88,11 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                break;
        }
        case SIOCSIFADDR:
+               if (netif_running(dev)) {
+                       spin_unlock_bh(&sdata->mib_lock);
+                       return -EBUSY;
+               }
+
                dev_warn(&dev->dev,
                         "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
                if (sa->family != AF_IEEE802154 ||
@@ -96,8 +104,8 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                        break;
                }
 
-               sdata->pan_id = cpu_to_le16(sa->addr.pan_id);
-               sdata->short_addr = cpu_to_le16(sa->addr.short_addr);
+               wpan_dev->pan_id = cpu_to_le16(sa->addr.pan_id);
+               wpan_dev->short_addr = cpu_to_le16(sa->addr.short_addr);
 
                err = mac802154_wpan_update_llsec(dev);
                break;
@@ -121,7 +129,7 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
                return -EINVAL;
 
        memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
-       sdata->extended_addr = extended_addr;
+       sdata->wpan_dev.extended_addr = extended_addr;
 
        return mac802154_wpan_update_llsec(dev);
 }
@@ -135,7 +143,7 @@ static int mac802154_slave_open(struct net_device *dev)
 
        ASSERT_RTNL();
 
-       if (sdata->vif.type == IEEE802154_DEV_WPAN) {
+       if (sdata->vif.type == NL802154_IFTYPE_NODE) {
                mutex_lock(&sdata->local->iflist_mtx);
                list_for_each_entry(subif, &sdata->local->interfaces, list) {
                        if (subif != sdata &&
@@ -172,6 +180,7 @@ static int mac802154_wpan_open(struct net_device *dev)
        int rc;
        struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
        struct ieee802154_local *local = sdata->local;
+       struct wpan_dev *wpan_dev = &sdata->wpan_dev;
        struct wpan_phy *phy = sdata->local->phy;
 
        rc = mac802154_slave_open(dev);
@@ -181,42 +190,42 @@ static int mac802154_wpan_open(struct net_device *dev)
        mutex_lock(&phy->pib_lock);
 
        if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
-               rc = drv_set_promiscuous_mode(local, sdata->promiscuous_mode);
+               rc = drv_set_promiscuous_mode(local,
+                                             wpan_dev->promiscuous_mode);
                if (rc < 0)
                        goto out;
        }
 
        if (local->hw.flags & IEEE802154_HW_AFILT) {
-               rc = drv_set_pan_id(local, sdata->pan_id);
+               rc = drv_set_pan_id(local, wpan_dev->pan_id);
                if (rc < 0)
                        goto out;
 
-               rc = drv_set_extended_addr(local, sdata->extended_addr);
+               rc = drv_set_extended_addr(local, wpan_dev->extended_addr);
                if (rc < 0)
                        goto out;
 
-               rc = drv_set_short_addr(local, sdata->short_addr);
+               rc = drv_set_short_addr(local, wpan_dev->short_addr);
                if (rc < 0)
                        goto out;
        }
 
        if (local->hw.flags & IEEE802154_HW_LBT) {
-               rc = drv_set_lbt_mode(local, sdata->mac_params.lbt);
+               rc = drv_set_lbt_mode(local, wpan_dev->lbt);
                if (rc < 0)
                        goto out;
        }
 
        if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
-               rc = drv_set_csma_params(local, sdata->mac_params.min_be,
-                                        sdata->mac_params.max_be,
-                                        sdata->mac_params.csma_retries);
+               rc = drv_set_csma_params(local, wpan_dev->min_be,
+                                        wpan_dev->max_be,
+                                        wpan_dev->csma_retries);
                if (rc < 0)
                        goto out;
        }
 
        if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
-               rc = drv_set_max_frame_retries(local,
-                                              sdata->mac_params.frame_retries);
+               rc = drv_set_max_frame_retries(local, wpan_dev->frame_retries);
                if (rc < 0)
                        goto out;
        }
@@ -236,6 +245,8 @@ static int mac802154_slave_close(struct net_device *dev)
 
        ASSERT_RTNL();
 
+       hrtimer_cancel(&local->ifs_timer);
+
        netif_stop_queue(dev);
        local->open_count--;
 
@@ -288,6 +299,7 @@ static int mac802154_header_create(struct sk_buff *skb,
 {
        struct ieee802154_hdr hdr;
        struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       struct wpan_dev *wpan_dev = &sdata->wpan_dev;
        struct ieee802154_mac_cb *cb = mac_cb(skb);
        int hlen;
 
@@ -306,17 +318,17 @@ static int mac802154_header_create(struct sk_buff *skb,
        if (!saddr) {
                spin_lock_bh(&sdata->mib_lock);
 
-               if (sdata->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
-                   sdata->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
-                   sdata->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
+               if (wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
+                   wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
+                   wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
                        hdr.source.mode = IEEE802154_ADDR_LONG;
-                       hdr.source.extended_addr = sdata->extended_addr;
+                       hdr.source.extended_addr = wpan_dev->extended_addr;
                } else {
                        hdr.source.mode = IEEE802154_ADDR_SHORT;
-                       hdr.source.short_addr = sdata->short_addr;
+                       hdr.source.short_addr = wpan_dev->short_addr;
                }
 
-               hdr.source.pan_id = sdata->pan_id;
+               hdr.source.pan_id = wpan_dev->pan_id;
 
                spin_unlock_bh(&sdata->mib_lock);
        } else {
@@ -394,42 +406,48 @@ static void ieee802154_if_setup(struct net_device *dev)
 }
 
 static int
-ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, int type)
+ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
+                      enum nl802154_iftype type)
 {
+       struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+
        /* set some type-dependent values */
        sdata->vif.type = type;
+       sdata->wpan_dev.iftype = type;
 
-       get_random_bytes(&sdata->bsn, 1);
-       get_random_bytes(&sdata->dsn, 1);
+       get_random_bytes(&wpan_dev->bsn, 1);
+       get_random_bytes(&wpan_dev->dsn, 1);
 
        /* defaults per 802.15.4-2011 */
-       sdata->mac_params.min_be = 3;
-       sdata->mac_params.max_be = 5;
-       sdata->mac_params.csma_retries = 4;
+       wpan_dev->min_be = 3;
+       wpan_dev->max_be = 5;
+       wpan_dev->csma_retries = 4;
        /* for compatibility, actual default is 3 */
-       sdata->mac_params.frame_retries = -1;
+       wpan_dev->frame_retries = -1;
 
-       ieee802154_be64_to_le64(&sdata->extended_addr, sdata->dev->dev_addr);
-       sdata->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
-       sdata->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+       wpan_dev->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
+       wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
 
        switch (type) {
-       case IEEE802154_DEV_WPAN:
+       case NL802154_IFTYPE_NODE:
+               ieee802154_be64_to_le64(&wpan_dev->extended_addr,
+                                       sdata->dev->dev_addr);
+
                sdata->dev->header_ops = &mac802154_header_ops;
                sdata->dev->destructor = mac802154_wpan_free;
                sdata->dev->netdev_ops = &mac802154_wpan_ops;
                sdata->dev->ml_priv = &mac802154_mlme_wpan;
-               sdata->promiscuous_mode = false;
+               wpan_dev->promiscuous_mode = false;
 
                spin_lock_init(&sdata->mib_lock);
                mutex_init(&sdata->sec_mtx);
 
                mac802154_llsec_init(&sdata->sec);
                break;
-       case IEEE802154_DEV_MONITOR:
+       case NL802154_IFTYPE_MONITOR:
                sdata->dev->destructor = free_netdev;
                sdata->dev->netdev_ops = &mac802154_monitor_ops;
-               sdata->promiscuous_mode = true;
+               wpan_dev->promiscuous_mode = true;
                break;
        default:
                BUG();
@@ -440,7 +458,7 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, int type)
 
 struct net_device *
 ieee802154_if_add(struct ieee802154_local *local, const char *name,
-                 struct wpan_dev **new_wpan_dev, int type)
+                 enum nl802154_iftype type, __le64 extended_addr)
 {
        struct net_device *ndev = NULL;
        struct ieee802154_sub_if_data *sdata = NULL;
@@ -459,11 +477,18 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
        if (ret < 0)
                goto err;
 
+       ieee802154_le64_to_be64(ndev->perm_addr,
+                               &local->hw.phy->perm_extended_addr);
        switch (type) {
-       case IEEE802154_DEV_WPAN:
+       case NL802154_IFTYPE_NODE:
                ndev->type = ARPHRD_IEEE802154;
+               if (ieee802154_is_valid_extended_addr(extended_addr))
+                       ieee802154_le64_to_be64(ndev->dev_addr, &extended_addr);
+               else
+                       memcpy(ndev->dev_addr, ndev->perm_addr,
+                              IEEE802154_EXTENDED_ADDR_LEN);
                break;
-       case IEEE802154_DEV_MONITOR:
+       case NL802154_IFTYPE_MONITOR:
                ndev->type = ARPHRD_IEEE802154_MONITOR;
                break;
        default:
@@ -471,9 +496,6 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
                goto err;
        }
 
-       ieee802154_le64_to_be64(ndev->perm_addr,
-                               &local->hw.phy->perm_extended_addr);
-       memcpy(ndev->dev_addr, ndev->perm_addr, IEEE802154_EXTENDED_ADDR_LEN);
        /* TODO check this */
        SET_NETDEV_DEV(ndev, &local->phy->dev);
        sdata = netdev_priv(ndev);
@@ -498,9 +520,6 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
        list_add_tail_rcu(&sdata->list, &local->interfaces);
        mutex_unlock(&local->iflist_mtx);
 
-       if (new_wpan_dev)
-               *new_wpan_dev = &sdata->wpan_dev;
-
        return ndev;
 
 err:
@@ -519,3 +538,51 @@ void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata)
        synchronize_rcu();
        unregister_netdevice(sdata->dev);
 }
+
+void ieee802154_remove_interfaces(struct ieee802154_local *local)
+{
+       struct ieee802154_sub_if_data *sdata, *tmp;
+
+       mutex_lock(&local->iflist_mtx);
+       list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
+               list_del(&sdata->list);
+
+               unregister_netdevice(sdata->dev);
+       }
+       mutex_unlock(&local->iflist_mtx);
+}
+
+static int netdev_notify(struct notifier_block *nb,
+                        unsigned long state, void *ptr)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct ieee802154_sub_if_data *sdata;
+
+       if (state != NETDEV_CHANGENAME)
+               return NOTIFY_DONE;
+
+       if (!dev->ieee802154_ptr || !dev->ieee802154_ptr->wpan_phy)
+               return NOTIFY_DONE;
+
+       if (dev->ieee802154_ptr->wpan_phy->privid != mac802154_wpan_phy_privid)
+               return NOTIFY_DONE;
+
+       sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       memcpy(sdata->name, dev->name, IFNAMSIZ);
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block mac802154_netdev_notifier = {
+       .notifier_call = netdev_notify,
+};
+
+int ieee802154_iface_init(void)
+{
+       return register_netdevice_notifier(&mac802154_netdev_notifier);
+}
+
+void ieee802154_iface_exit(void)
+{
+       unregister_netdevice_notifier(&mac802154_netdev_notifier);
+}
index 00b2b214770eb3b7e37ba0b6179ec7054e88df33..6aacb181688961178bab9849c23425a7a3e25478 100644 (file)
@@ -39,6 +39,8 @@ static int mac802154_mlme_start_req(struct net_device *dev,
        struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
        int rc = 0;
 
+       ASSERT_RTNL();
+
        BUG_ON(addr->mode != IEEE802154_ADDR_SHORT);
 
        mac802154_dev_set_pan_id(dev, addr->pan_id);
@@ -72,11 +74,22 @@ static int mac802154_set_mac_params(struct net_device *dev,
 {
        struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
        struct ieee802154_local *local = sdata->local;
+       struct wpan_dev *wpan_dev = &sdata->wpan_dev;
        int ret;
 
-       mutex_lock(&sdata->local->iflist_mtx);
-       sdata->mac_params = *params;
-       mutex_unlock(&sdata->local->iflist_mtx);
+       ASSERT_RTNL();
+
+       /* PHY */
+       wpan_dev->wpan_phy->transmit_power = params->transmit_power;
+       wpan_dev->wpan_phy->cca_mode = params->cca_mode;
+       wpan_dev->wpan_phy->cca_ed_level = params->cca_ed_level;
+
+       /* MAC */
+       wpan_dev->min_be = params->min_be;
+       wpan_dev->max_be = params->max_be;
+       wpan_dev->csma_retries = params->csma_retries;
+       wpan_dev->frame_retries = params->frame_retries;
+       wpan_dev->lbt = params->lbt;
 
        if (local->hw.flags & IEEE802154_HW_TXPOWER) {
                ret = drv_set_tx_power(local, params->transmit_power);
@@ -103,10 +116,21 @@ static void mac802154_get_mac_params(struct net_device *dev,
                                     struct ieee802154_mac_params *params)
 {
        struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+       struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+
+       ASSERT_RTNL();
+
+       /* PHY */
+       params->transmit_power = wpan_dev->wpan_phy->transmit_power;
+       params->cca_mode = wpan_dev->wpan_phy->cca_mode;
+       params->cca_ed_level = wpan_dev->wpan_phy->cca_ed_level;
 
-       mutex_lock(&sdata->local->iflist_mtx);
-       *params = sdata->mac_params;
-       mutex_unlock(&sdata->local->iflist_mtx);
+       /* MAC */
+       params->min_be = wpan_dev->min_be;
+       params->max_be = wpan_dev->max_be;
+       params->csma_retries = wpan_dev->csma_retries;
+       params->frame_retries = wpan_dev->frame_retries;
+       params->lbt = wpan_dev->lbt;
 }
 
 static struct ieee802154_llsec_ops mac802154_llsec_ops = {
index 7d0ff7fd2cd462f83a0b77c13e4640f14a822ccb..8500378c8318cd3b5b7e3a368f44080b40c5900f 100644 (file)
@@ -4,8 +4,6 @@
  * Written by:
  * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
  *
- * Based on the code from 'linux-zigbee.sourceforge.net' project.
- *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
  * as published by the Free Software Foundation.
@@ -21,7 +19,7 @@
 #include <linux/netdevice.h>
 
 #include <net/netlink.h>
-#include <linux/nl802154.h>
+#include <net/nl802154.h>
 #include <net/mac802154.h>
 #include <net/ieee802154_netdev.h>
 #include <net/route.h>
@@ -86,12 +84,14 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
 
        priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
 
-       phy = wpan_phy_alloc(&mac802154_config_ops, priv_size);
+       phy = wpan_phy_new(&mac802154_config_ops, priv_size);
        if (!phy) {
                pr_err("failure to allocate master IEEE802.15.4 device\n");
                return NULL;
        }
 
+       phy->privid = mac802154_wpan_phy_privid;
+
        local = wpan_phy_priv(phy);
        local->phy = phy;
        local->hw.phy = local->phy;
@@ -123,6 +123,18 @@ void ieee802154_free_hw(struct ieee802154_hw *hw)
 }
 EXPORT_SYMBOL(ieee802154_free_hw);
 
+static void ieee802154_setup_wpan_phy_pib(struct wpan_phy *wpan_phy)
+{
+       /* TODO warn on empty symbol_duration
+        * Should be done when all drivers sets this value.
+        */
+
+       wpan_phy->lifs_period = IEEE802154_LIFS_PERIOD *
+                               wpan_phy->symbol_duration;
+       wpan_phy->sifs_period = IEEE802154_SIFS_PERIOD *
+                               wpan_phy->symbol_duration;
+}
+
 int ieee802154_register_hw(struct ieee802154_hw *hw)
 {
        struct ieee802154_local *local = hw_to_local(hw);
@@ -136,15 +148,21 @@ int ieee802154_register_hw(struct ieee802154_hw *hw)
                goto out;
        }
 
+       hrtimer_init(&local->ifs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       local->ifs_timer.function = ieee802154_xmit_ifs_timer;
+
        wpan_phy_set_dev(local->phy, local->hw.parent);
 
+       ieee802154_setup_wpan_phy_pib(local->phy);
+
        rc = wpan_phy_register(local->phy);
        if (rc < 0)
                goto out_wq;
 
        rtnl_lock();
 
-       dev = ieee802154_if_add(local, "wpan%d", NULL, IEEE802154_DEV_WPAN);
+       dev = ieee802154_if_add(local, "wpan%d", NL802154_IFTYPE_NODE,
+                               cpu_to_le64(0x0000000000000000ULL));
        if (IS_ERR(dev)) {
                rtnl_unlock();
                rc = PTR_ERR(dev);
@@ -165,7 +183,6 @@ EXPORT_SYMBOL(ieee802154_register_hw);
 void ieee802154_unregister_hw(struct ieee802154_hw *hw)
 {
        struct ieee802154_local *local = hw_to_local(hw);
-       struct ieee802154_sub_if_data *sdata, *next;
 
        tasklet_kill(&local->tasklet);
        flush_workqueue(local->workqueue);
@@ -173,13 +190,7 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw)
 
        rtnl_lock();
 
-       list_for_each_entry_safe(sdata, next, &local->interfaces, list) {
-               mutex_lock(&sdata->local->iflist_mtx);
-               list_del(&sdata->list);
-               mutex_unlock(&sdata->local->iflist_mtx);
-
-               unregister_netdevice(sdata->dev);
-       }
+       ieee802154_remove_interfaces(local);
 
        rtnl_unlock();
 
@@ -187,5 +198,20 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw)
 }
 EXPORT_SYMBOL(ieee802154_unregister_hw);
 
-MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
+static int __init ieee802154_init(void)
+{
+       return ieee802154_iface_init();
+}
+
+static void __exit ieee802154_exit(void)
+{
+       ieee802154_iface_exit();
+
+       rcu_barrier();
+}
+
+subsys_initcall(ieee802154_init);
+module_exit(ieee802154_exit);
+
+MODULE_DESCRIPTION("IEEE 802.15.4 subsystem");
 MODULE_LICENSE("GPL v2");
index 6fa749154bafa8732b8a6f7d498411cc1c406111..3596b29ead6b8716aded81244bee99d9626acca5 100644 (file)
@@ -33,7 +33,7 @@ void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val)
        BUG_ON(dev->type != ARPHRD_IEEE802154);
 
        spin_lock_bh(&sdata->mib_lock);
-       sdata->short_addr = val;
+       sdata->wpan_dev.short_addr = val;
        spin_unlock_bh(&sdata->mib_lock);
 }
 
@@ -45,7 +45,7 @@ __le16 mac802154_dev_get_short_addr(const struct net_device *dev)
        BUG_ON(dev->type != ARPHRD_IEEE802154);
 
        spin_lock_bh(&sdata->mib_lock);
-       ret = sdata->short_addr;
+       ret = sdata->wpan_dev.short_addr;
        spin_unlock_bh(&sdata->mib_lock);
 
        return ret;
@@ -59,7 +59,7 @@ __le16 mac802154_dev_get_pan_id(const struct net_device *dev)
        BUG_ON(dev->type != ARPHRD_IEEE802154);
 
        spin_lock_bh(&sdata->mib_lock);
-       ret = sdata->pan_id;
+       ret = sdata->wpan_dev.pan_id;
        spin_unlock_bh(&sdata->mib_lock);
 
        return ret;
@@ -72,7 +72,7 @@ void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val)
        BUG_ON(dev->type != ARPHRD_IEEE802154);
 
        spin_lock_bh(&sdata->mib_lock);
-       sdata->pan_id = val;
+       sdata->wpan_dev.pan_id = val;
        spin_unlock_bh(&sdata->mib_lock);
 }
 
@@ -82,7 +82,7 @@ u8 mac802154_dev_get_dsn(const struct net_device *dev)
 
        BUG_ON(dev->type != ARPHRD_IEEE802154);
 
-       return sdata->dsn++;
+       return sdata->wpan_dev.dsn++;
 }
 
 void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
index 4b54cf33e5624cd34d09f17656e2fd75161b139d..041dbd5958d4a8905440f42f3c2d843d89e07447 100644 (file)
@@ -25,8 +25,7 @@
 
 #include <net/mac802154.h>
 #include <net/ieee802154_netdev.h>
-#include <net/rtnetlink.h>
-#include <linux/nl802154.h>
+#include <net/nl802154.h>
 
 #include "ieee802154_i.h"
 
@@ -42,6 +41,7 @@ static int
 ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
                       struct sk_buff *skb, const struct ieee802154_hdr *hdr)
 {
+       struct wpan_dev *wpan_dev = &sdata->wpan_dev;
        __le16 span, sshort;
        int rc;
 
@@ -49,8 +49,8 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
 
        spin_lock_bh(&sdata->mib_lock);
 
-       span = sdata->pan_id;
-       sshort = sdata->short_addr;
+       span = wpan_dev->pan_id;
+       sshort = wpan_dev->short_addr;
 
        switch (mac_cb(skb)->dest.mode) {
        case IEEE802154_ADDR_NONE:
@@ -65,7 +65,7 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
                if (mac_cb(skb)->dest.pan_id != span &&
                    mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
                        skb->pkt_type = PACKET_OTHERHOST;
-               else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr)
+               else if (mac_cb(skb)->dest.extended_addr == wpan_dev->extended_addr)
                        skb->pkt_type = PACKET_HOST;
                else
                        skb->pkt_type = PACKET_OTHERHOST;
@@ -208,7 +208,7 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local,
        }
 
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               if (sdata->vif.type != IEEE802154_DEV_WPAN ||
+               if (sdata->vif.type != NL802154_IFTYPE_NODE ||
                    !netif_running(sdata->dev))
                        continue;
 
@@ -233,7 +233,7 @@ ieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb)
        skb->protocol = htons(ETH_P_IEEE802154);
 
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               if (sdata->vif.type != IEEE802154_DEV_MONITOR)
+               if (sdata->vif.type != NL802154_IFTYPE_MONITOR)
                        continue;
 
                if (!ieee802154_sdata_running(sdata))
index cc37b77f26321ca9a5f4f960a0f72608d48c1013..c62e95695c7843947c8643cb268f95f2e64c3da9 100644 (file)
@@ -60,7 +60,7 @@ static void ieee802154_xmit_worker(struct work_struct *work)
        if (res)
                goto err_tx;
 
-       ieee802154_xmit_complete(&local->hw, skb);
+       ieee802154_xmit_complete(&local->hw, skb, false);
 
        dev->stats.tx_packets++;
        dev->stats.tx_bytes += skb->len;
index 117e4eff4ca86646f31206b64753c1ad0a3ba1a9..5fc979027919749a604a47bcd45148fbe5505f03 100644 (file)
@@ -15,6 +15,9 @@
 
 #include "ieee802154_i.h"
 
+/* privid for wpan_phys to determine whether they belong to us or not */
+const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid;
+
 void ieee802154_wake_queue(struct ieee802154_hw *hw)
 {
        struct ieee802154_local *local = hw_to_local(hw);
@@ -47,9 +50,35 @@ void ieee802154_stop_queue(struct ieee802154_hw *hw)
 }
 EXPORT_SYMBOL(ieee802154_stop_queue);
 
-void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb)
+enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer)
 {
-       ieee802154_wake_queue(hw);
-       consume_skb(skb);
+       struct ieee802154_local *local =
+               container_of(timer, struct ieee802154_local, ifs_timer);
+
+       ieee802154_wake_queue(&local->hw);
+
+       return HRTIMER_NORESTART;
+}
+
+void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
+                             bool ifs_handling)
+{
+       if (ifs_handling) {
+               struct ieee802154_local *local = hw_to_local(hw);
+
+               if (skb->len > 18)
+                       hrtimer_start(&local->ifs_timer,
+                                     ktime_set(0, hw->phy->lifs_period * NSEC_PER_USEC),
+                                     HRTIMER_MODE_REL);
+               else
+                       hrtimer_start(&local->ifs_timer,
+                                     ktime_set(0, hw->phy->sifs_period * NSEC_PER_USEC),
+                                     HRTIMER_MODE_REL);
+
+               consume_skb(skb);
+       } else {
+               ieee802154_wake_queue(hw);
+               consume_skb(skb);
+       }
 }
 EXPORT_SYMBOL(ieee802154_xmit_complete);