Listen-before-talk is an alternative to CSMA in uncoordinated networks
and prescribed by european regulations if one wants to have a device
with radio duty cycles above 10% (or less in some bands). Add a phy
property to enable/disable LBT in the phy, including support in the
at86rf230 driver for RF212 chips.

Signed-off-by: Phoebe Buckheister <phoebe.buckheis...@itwm.fraunhofer.de>
---
 drivers/net/ieee802154/at86rf230.c |   17 +++++++++-
 include/linux/nl802154.h           |    1 +
 include/net/mac802154.h            |    7 ++++-
 include/net/wpan-phy.h             |    3 ++
 net/ieee802154/nl-phy.c            |   61 +++++++++++++++++++++++++++---------
 net/ieee802154/nl_policy.c         |    1 +
 net/mac802154/ieee802154_dev.c     |    8 +++++
 7 files changed, 81 insertions(+), 17 deletions(-)

diff --git a/drivers/net/ieee802154/at86rf230.c 
b/drivers/net/ieee802154/at86rf230.c
index 10a4647..319b0e6 100644
--- a/drivers/net/ieee802154/at86rf230.c
+++ b/drivers/net/ieee802154/at86rf230.c
@@ -153,7 +153,7 @@ static inline int is_rf212(struct at86rf230_local *local)
 #define        SR_RESERVED_17_5        0x17, 0x08, 3
 #define        SR_AACK_UPLD_RES_FT     0x17, 0x10, 4
 #define        SR_AACK_FLTR_RES_FT     0x17, 0x20, 5
-#define        SR_RESERVED_17_2        0x17, 0x40, 6
+#define        SR_CSMA_LBT_MODE        0x17, 0x40, 6
 #define        SR_RESERVED_17_1        0x17, 0x80, 7
 #define        RG_FTN_CTRL     (0x18)
 #define        SR_RESERVED_18_2        0x18, 0x7f, 0
@@ -720,6 +720,20 @@ at86rf230_set_txpower(struct ieee802154_dev *dev, int db)
        return 0;
 }
 
+static int
+at86rf230_set_lbt(struct ieee802154_dev *dev, u8 on)
+{
+       struct at86rf230_local *lp = dev->priv;
+       int rc;
+
+       if (!is_rf212(lp))
+               return -ENOTSUPP;
+
+       rc = at86rf230_write_subreg(lp, SR_CSMA_LBT_MODE, on);
+
+       return rc;
+}
+
 static struct ieee802154_ops at86rf230_ops = {
        .owner = THIS_MODULE,
        .xmit = at86rf230_xmit,
@@ -729,6 +743,7 @@ static struct ieee802154_ops at86rf230_ops = {
        .stop = at86rf230_stop,
        .set_hw_addr_filt = at86rf230_set_hw_addr_filt,
        .set_txpower = at86rf230_set_txpower,
+       .set_lbt = at86rf230_set_lbt,
 };
 
 static void at86rf230_irqwork(struct work_struct *work)
diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h
index 625d19e..326baee 100644
--- a/include/linux/nl802154.h
+++ b/include/linux/nl802154.h
@@ -71,6 +71,7 @@ enum {
        IEEE802154_ATTR_DEV_TYPE,
 
        IEEE802154_ATTR_TXPOWER,
+       IEEE802154_ATTR_LBT_ENABLED,
 
        __IEEE802154_ATTR_MAX,
 };
diff --git a/include/net/mac802154.h b/include/net/mac802154.h
index 334950f..c0bfdd4 100644
--- a/include/net/mac802154.h
+++ b/include/net/mac802154.h
@@ -116,7 +116,11 @@ struct ieee802154_dev {
  *
  * set_txpower:
  *       Set radio transmit power in dB.
- *       Returns either zero, er negative errno.
+ *       Returns either zero, or negative errno.
+ *
+ * set_lbt
+ *       Enables or disables listen before talk on the device.
+ *       Returns either zero, or negative errno.
  */
 struct ieee802154_ops {
        struct module   *owner;
@@ -134,6 +138,7 @@ struct ieee802154_ops {
        int             (*ieee_addr)(struct ieee802154_dev *dev,
                                     u8 addr[IEEE802154_ADDR_LEN]);
        int             (*set_txpower)(struct ieee802154_dev *dev, int db);
+       int             (*set_lbt)(struct ieee802154_dev *dev, u8 on);
 };
 
 /* Basic interface to register ieee802154 device */
diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h
index 44e5e49..30cdae6 100644
--- a/include/net/wpan-phy.h
+++ b/include/net/wpan-phy.h
@@ -47,6 +47,8 @@ struct wpan_phy {
        u8 transmit_power;
        u8 cca_mode;
 
+       u8 lbt:1;
+
        struct device dev;
        int idx;
 
@@ -55,6 +57,7 @@ struct wpan_phy {
        void (*del_iface)(struct wpan_phy *phy, struct net_device *dev);
 
        int (*set_txpower)(struct wpan_phy *phy, int txpower);
+       int (*set_lbt)(struct wpan_phy *phy, u8 on);
 
        char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
 };
diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c
index 3ade8c6..cda6e0d 100644
--- a/net/ieee802154/nl-phy.c
+++ b/net/ieee802154/nl-phy.c
@@ -57,7 +57,8 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 
portid,
            nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) ||
            nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel) ||
            nla_put_s32(msg, IEEE802154_ATTR_TXPOWER,
-                               ((signed char) (phy->transmit_power << 2)) >> 
2))
+                   ((signed char) (phy->transmit_power << 2)) >> 2) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, phy->lbt))
                goto nla_put_failure;
        for (i = 0; i < 32; i++) {
                if (phy->channels_supported[i])
@@ -357,41 +358,71 @@ out_dev:
        return rc;
 }
 
+static int phy_set_txpower(struct wpan_phy *phy, struct genl_info *info)
+{
+       int txpower = nla_get_s32(info->attrs[IEEE802154_ATTR_TXPOWER]);
+       int rc;
+       u8 txpower_wpan;
+
+       if (txpower < -64 || txpower > 63)
+               return -EINVAL; /* txpower code subfield is 6 bits long */
+
+       txpower_wpan = ((unsigned char) (txpower << 2)) >> 2;
+
+       rc = phy->set_txpower(phy, txpower);
+       if (rc < 0)
+               return rc;
+
+       phy->transmit_power = txpower_wpan;
+
+       return 0;
+}
+
+static int phy_set_lbt(struct wpan_phy *phy, struct genl_info *info)
+{
+       u8 on = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]);
+       int rc;
+
+       rc = phy->set_lbt(phy, on);
+       if (rc < 0)
+               return rc;
+
+       phy->lbt = on;
+       
+       return 0;
+}
+
 int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info)
 {
        struct wpan_phy *phy;
        const char *name;
-       int txpower;
-       u8 txpower_wpan;
-       int rc = -EINVAL;
+       int rc = -ENOTSUPP;
 
        pr_debug("%s\n", __func__);
 
-       if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
+       if (!info->attrs[IEEE802154_ATTR_PHY_NAME] &&
+               !info->attrs[IEEE802154_ATTR_LBT_ENABLED])
                return -EINVAL;
 
        name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
        if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
                return -EINVAL; /* phy name should be null-terminated */
 
-       txpower = nla_get_s32(info->attrs[IEEE802154_ATTR_TXPOWER]);
-       if (txpower < -64 || txpower > 63)
-               return -EINVAL; /* txpower code subfield is 6 bits long */
-
-       txpower_wpan = ((unsigned char) (txpower << 2)) >> 2;
-
        phy = wpan_phy_find(name);
        if (!phy)
                return -ENODEV;
 
-       if (!phy->set_txpower)
+       if ((!phy->set_txpower && info->attrs[IEEE802154_ATTR_TXPOWER]) ||
+               (!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]))
                goto out;
 
-       rc = phy->set_txpower(phy, txpower);
-       if (rc < 0)
+       if (info->attrs[IEEE802154_ATTR_TXPOWER] &&
+               (rc = phy_set_txpower(phy, info)) < 0)
                goto out;
 
-       phy->transmit_power = txpower_wpan;
+       if (info->attrs[IEEE802154_ATTR_LBT_ENABLED] &&
+               (rc = phy_set_lbt(phy, info)) < 0)
+               goto out;
 
        wpan_phy_put(phy);
 
diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c
index 4035333..ab88033 100644
--- a/net/ieee802154/nl_policy.c
+++ b/net/ieee802154/nl_policy.c
@@ -54,5 +54,6 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX 
+ 1] = {
        [IEEE802154_ATTR_CHANNEL_PAGE_LIST] = { .len = 32 * 4, },
 
        [IEEE802154_ATTR_TXPOWER] = { .type = NLA_S32, },
+       [IEEE802154_ATTR_LBT_ENABLED] = { .type = NLA_U8, },
 };
 
diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c
index febf2ab..762a452 100644
--- a/net/mac802154/ieee802154_dev.c
+++ b/net/mac802154/ieee802154_dev.c
@@ -172,6 +172,13 @@ static int mac802154_set_txpower(struct wpan_phy *phy, int 
txpower)
        return priv->ops->set_txpower(&priv->hw, txpower);
 }
 
+static int mac802154_set_lbt(struct wpan_phy *phy, u8 on)
+{
+       struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+       return priv->ops->set_lbt(&priv->hw, on);
+}
+
 struct ieee802154_dev *
 ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
 {
@@ -250,6 +257,7 @@ int ieee802154_register_device(struct ieee802154_dev *dev)
        priv->phy->add_iface = mac802154_add_iface;
        priv->phy->del_iface = mac802154_del_iface;
        priv->phy->set_txpower = mac802154_set_txpower;
+       priv->phy->set_lbt = mac802154_set_lbt;
 
        rc = wpan_phy_register(priv->phy);
        if (rc < 0)
-- 
1.7.9.5


------------------------------------------------------------------------------
CenturyLink Cloud: The Leader in Enterprise Cloud Services.
Learn Why More Businesses Are Choosing CenturyLink Cloud For
Critical Workloads, Development Environments & Everything In Between.
Get a Quote or Start a Free Trial Today. 
http://pubads.g.doubleclick.net/gampad/clk?id=119420431&iu=/4140/ostg.clktrk
_______________________________________________
Linux-zigbee-devel mailing list
Linux-zigbee-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-zigbee-devel

Reply via email to