ieee802154: add support for setting CCA energy detection levels
authorPhoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
Mon, 17 Feb 2014 10:34:12 +0000 (11:34 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 17 Feb 2014 21:42:38 +0000 (16:42 -0500)
Since three of the four clear channel assesment modes make use of energy
detection, provide an API to set the energy detection threshold.
Driver support for this is available in at86rf230 for the RF212 chips.
Since for these chips the minimal energy detection threshold depends on
page and channel used, add a field to struct at86rf230_local that stores
the minimal threshold. Actual ED thresholds are configured as offsets
from this value.

For RF212, setting the ED threshold will not work before a channel/page
has been set due to the dependency of energy detection in the chip and
the actual channel/page selected.

Signed-off-by: Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ieee802154/at86rf230.c
include/linux/nl802154.h
include/net/mac802154.h
include/net/wpan-phy.h
net/ieee802154/nl-phy.c
net/ieee802154/nl_policy.c
net/mac802154/ieee802154_dev.c

index c60871aff333e3043df73771ca10ef6602408cb4..20596be61028ca02a6423ced7eed9a65a640f3e1 100644 (file)
@@ -52,6 +52,8 @@ struct at86rf230_local {
        spinlock_t lock;
        bool irq_busy;
        bool is_tx;
+
+       int rssi_base_val;
 };
 
 static inline int is_rf212(struct at86rf230_local *local)
@@ -580,6 +582,8 @@ at86rf230_stop(struct ieee802154_dev *dev)
 static int
 at86rf230_set_channel(struct at86rf230_local *lp, int page, int channel)
 {
+       lp->rssi_base_val = -91;
+
        return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
 }
 
@@ -595,10 +599,13 @@ at86rf212_set_channel(struct at86rf230_local *lp, int page, int channel)
        if (rc < 0)
                return rc;
 
-       if (page == 0)
+       if (page == 0) {
                rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 0);
-       else
+               lp->rssi_base_val = -100;
+       } else {
                rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 1);
+               lp->rssi_base_val = -98;
+       }
        if (rc < 0)
                return rc;
 
@@ -802,6 +809,20 @@ at86rf212_set_cca_mode(struct ieee802154_dev *dev, u8 mode)
        return at86rf230_write_subreg(lp, SR_CCA_MODE, mode);
 }
 
+static int
+at86rf212_set_cca_ed_level(struct ieee802154_dev *dev, s32 level)
+{
+       struct at86rf230_local *lp = dev->priv;
+       int desens_steps;
+
+       if (level < lp->rssi_base_val || level > 30)
+               return -EINVAL;
+
+       desens_steps = (level - lp->rssi_base_val) * 100 / 207;
+
+       return at86rf230_write_subreg(lp, SR_CCA_ED_THRES, desens_steps);
+}
+
 static struct ieee802154_ops at86rf230_ops = {
        .owner = THIS_MODULE,
        .xmit = at86rf230_xmit,
@@ -823,6 +844,7 @@ static struct ieee802154_ops at86rf212_ops = {
        .set_txpower = at86rf212_set_txpower,
        .set_lbt = at86rf212_set_lbt,
        .set_cca_mode = at86rf212_set_cca_mode,
+       .set_cca_ed_level = at86rf212_set_cca_ed_level,
 };
 
 static void at86rf230_irqwork(struct work_struct *work)
index 5edefc14bd83a338b159cb90621b08cef8b02f46..0594a0ae71bae8f49cc971f451ad9ba7656c9516 100644 (file)
@@ -73,6 +73,7 @@ enum {
        IEEE802154_ATTR_TXPOWER,
        IEEE802154_ATTR_LBT_ENABLED,
        IEEE802154_ATTR_CCA_MODE,
+       IEEE802154_ATTR_CCA_ED_LEVEL,
 
        __IEEE802154_ATTR_MAX,
 };
index 1a98e5014e665dc19ddd6b3266125b7edc354dd3..15fe6bca80f0919608d140da5428b38796005a75 100644 (file)
@@ -126,6 +126,11 @@ struct ieee802154_dev {
  * set_cca_mode
  *       Sets the CCA mode used by the device. Called with pib_lock held.
  *       Returns either zero, or negative errno.
+ *
+ * set_cca_ed_level
+ *       Sets the CCA energy detection threshold in dBm. Called with pib_lock
+ *       held.
+ *       Returns either zero, or negative errno.
  */
 struct ieee802154_ops {
        struct module   *owner;
@@ -145,6 +150,8 @@ struct ieee802154_ops {
        int             (*set_txpower)(struct ieee802154_dev *dev, int db);
        int             (*set_lbt)(struct ieee802154_dev *dev, bool on);
        int             (*set_cca_mode)(struct ieee802154_dev *dev, u8 mode);
+       int             (*set_cca_ed_level)(struct ieee802154_dev *dev,
+                                           s32 level);
 };
 
 /* Basic interface to register ieee802154 device */
index 03b59051972d4c19242929e98caed289ee586113..0b570ad5e5fa91d56f5d99cf782be440d36e404c 100644 (file)
@@ -48,6 +48,7 @@ struct wpan_phy {
        u8 cca_mode;
 
        bool lbt;
+       s32 cca_ed_level;
 
        struct device dev;
        int idx;
@@ -59,6 +60,7 @@ struct wpan_phy {
        int (*set_txpower)(struct wpan_phy *phy, int db);
        int (*set_lbt)(struct wpan_phy *phy, bool on);
        int (*set_cca_mode)(struct wpan_phy *phy, u8 cca_mode);
+       int (*set_cca_ed_level)(struct wpan_phy *phy, int level);
 
        char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
 };
index 36f58d633868124c4ce1b1ea8502afbef76fb9fa..0af0d424dee0dae3e4142d6e0cbaaffb7896a27c 100644 (file)
@@ -58,7 +58,8 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
            nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel) ||
            nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power) ||
            nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, phy->lbt) ||
-           nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, phy->cca_mode))
+           nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE, phy->cca_mode) ||
+           nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL, phy->cca_ed_level))
                goto nla_put_failure;
        for (i = 0; i < 32; i++) {
                if (phy->channels_supported[i])
@@ -403,6 +404,20 @@ static int phy_set_cca_mode(struct wpan_phy *phy, struct genl_info *info)
        return 0;
 }
 
+static int phy_set_cca_ed_level(struct wpan_phy *phy, struct genl_info *info)
+{
+       s32 level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]);
+       int rc;
+
+       rc = phy->set_cca_ed_level(phy, level);
+       if (rc < 0)
+               return rc;
+
+       phy->cca_ed_level = level;
+
+       return 0;
+}
+
 int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info)
 {
        struct wpan_phy *phy;
@@ -413,7 +428,8 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info)
 
        if (!info->attrs[IEEE802154_ATTR_PHY_NAME] &&
            !info->attrs[IEEE802154_ATTR_LBT_ENABLED] &&
-           !info->attrs[IEEE802154_ATTR_CCA_MODE])
+           !info->attrs[IEEE802154_ATTR_CCA_MODE] &&
+           !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])
                return -EINVAL;
 
        name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
@@ -426,7 +442,9 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info)
 
        if ((!phy->set_txpower && info->attrs[IEEE802154_ATTR_TXPOWER]) ||
            (!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) ||
-           (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]))
+           (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) ||
+           (!phy->set_cca_ed_level &&
+            info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]))
                goto out;
 
        mutex_lock(&phy->pib_lock);
@@ -449,6 +467,12 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info)
                        goto error;
        }
 
+       if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) {
+               rc = phy_set_cca_ed_level(phy, info);
+               if (rc < 0)
+                       goto error;
+       }
+
        mutex_unlock(&phy->pib_lock);
 
        wpan_phy_put(phy);
index d87c2c90411000d7bd877315b40feaa883ffae66..55b5616295ff9043c43d1d3c8adbca05f8d25d9a 100644 (file)
@@ -56,5 +56,6 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
        [IEEE802154_ATTR_TXPOWER] = { .type = NLA_S8, },
        [IEEE802154_ATTR_LBT_ENABLED] = { .type = NLA_U8, },
        [IEEE802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_CCA_ED_LEVEL] = { .type = NLA_S32, },
 };
 
index 4965e4ce6b5b504b2f636bdda497dbfaa7a9c90e..4707f36546d38c23e74d0951cffb501d8c0fc331 100644 (file)
@@ -195,6 +195,16 @@ static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode)
        return priv->ops->set_cca_mode(&priv->hw, mode);
 }
 
+static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level)
+{
+       struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+       if (!priv->ops->set_cca_ed_level)
+               return -ENOTSUPP;
+
+       return priv->ops->set_cca_ed_level(&priv->hw, level);
+}
+
 struct ieee802154_dev *
 ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
 {
@@ -275,6 +285,7 @@ int ieee802154_register_device(struct ieee802154_dev *dev)
        priv->phy->set_txpower = mac802154_set_txpower;
        priv->phy->set_lbt = mac802154_set_lbt;
        priv->phy->set_cca_mode = mac802154_set_cca_mode;
+       priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level;
 
        rc = wpan_phy_register(priv->phy);
        if (rc < 0)