ieee802154: add support for listen-before-talk in wpan_phy
authorPhoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
Mon, 17 Feb 2014 10:34:10 +0000 (11:34 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 17 Feb 2014 21:42:38 +0000 (16:42 -0500)
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.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 04a995dad593d0d3fcea6447d8a7d62121acdacb..3d40c23502617235e930dac6f23ec62615e4eddd 100644 (file)
@@ -152,7 +152,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
@@ -786,6 +786,14 @@ at86rf212_set_txpower(struct ieee802154_dev *dev, int db)
        return 0;
 }
 
+static int
+at86rf212_set_lbt(struct ieee802154_dev *dev, bool on)
+{
+       struct at86rf230_local *lp = dev->priv;
+
+       return at86rf230_write_subreg(lp, SR_CSMA_LBT_MODE, on);
+}
+
 static struct ieee802154_ops at86rf230_ops = {
        .owner = THIS_MODULE,
        .xmit = at86rf230_xmit,
@@ -805,6 +813,7 @@ static struct ieee802154_ops at86rf212_ops = {
        .stop = at86rf230_stop,
        .set_hw_addr_filt = at86rf230_set_hw_addr_filt,
        .set_txpower = at86rf212_set_txpower,
+       .set_lbt = at86rf212_set_lbt,
 };
 
 static void at86rf230_irqwork(struct work_struct *work)
index 625d19e0a1defdc5b91f2944f272f936c67e223a..326baee227f76757122802e4dc8a3a5e17069a0c 100644 (file)
@@ -71,6 +71,7 @@ enum {
        IEEE802154_ATTR_DEV_TYPE,
 
        IEEE802154_ATTR_TXPOWER,
+       IEEE802154_ATTR_LBT_ENABLED,
 
        __IEEE802154_ATTR_MAX,
 };
index 8bd2785a663c93bd0eeb85727206113d24f5197a..521edcb0e586cb4cb69a4fd9cda8eccad9fabd66 100644 (file)
@@ -117,6 +117,11 @@ struct ieee802154_dev {
  * set_txpower:
  *       Set radio transmit power in dB. Called with pib_lock held.
  *       Returns either zero, or negative errno.
+ *
+ * set_lbt
+ *       Enables or disables listen before talk on the device. Called with
+ *       pib_lock held.
+ *       Returns either zero, or negative errno.
  */
 struct ieee802154_ops {
        struct module   *owner;
@@ -134,6 +139,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, bool on);
 };
 
 /* Basic interface to register ieee802154 device */
index 47fc0c1bc3c7446d70fdf33b39b2a04b52b85d59..804e6c4f5f8a1fce6b2879c20391681dc6fe203d 100644 (file)
@@ -47,6 +47,8 @@ struct wpan_phy {
        s8 transmit_power;
        u8 cca_mode;
 
+       bool lbt;
+
        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 db);
+       int (*set_lbt)(struct wpan_phy *phy, bool on);
 
        char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
 };
index d3ee62fbae99dd3be1c71a147271512c7282c6e7..f029310b066245fd96f42b5cdc48f3187d44c997 100644 (file)
@@ -56,7 +56,8 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
        if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
            nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) ||
            nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel) ||
-           nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power))
+           nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, phy->transmit_power) ||
+           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])
@@ -356,40 +357,71 @@ out_dev:
        return rc;
 }
 
+static int phy_set_txpower(struct wpan_phy *phy, struct genl_info *info)
+{
+       int txpower = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]);
+       int rc;
+
+       rc = phy->set_txpower(phy, txpower);
+       if (rc < 0)
+               return rc;
+
+       phy->transmit_power = txpower;
+
+       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;
-       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_s8(info->attrs[IEEE802154_ATTR_TXPOWER]);
-
        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;
 
        mutex_lock(&phy->pib_lock);
 
-       rc = phy->set_txpower(phy, txpower);
-       if (rc < 0) {
-               mutex_unlock(&phy->pib_lock);
-               goto out;
+       if (info->attrs[IEEE802154_ATTR_TXPOWER]) {
+               rc = phy_set_txpower(phy, info);
+               if (rc < 0)
+                       goto error;
        }
 
-       phy->transmit_power = txpower;
+       if (info->attrs[IEEE802154_ATTR_LBT_ENABLED]) {
+               rc = phy_set_lbt(phy, info);
+               if (rc < 0)
+                       goto error;
+       }
 
        mutex_unlock(&phy->pib_lock);
 
@@ -397,6 +429,8 @@ int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info)
 
        return 0;
 
+error:
+       mutex_unlock(&phy->pib_lock);
 out:
        wpan_phy_put(phy);
        return rc;
index 90b1d0d2c14e46b1420a88897775cf4841407e6c..a09f6423a6e9b8137cac7efab391e7cae79cd88a 100644 (file)
@@ -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_S8, },
+       [IEEE802154_ATTR_LBT_ENABLED] = { .type = NLA_U8, },
 };
 
index 9eb49e0886a4a54e1eae99a7bea08363b5dc6ca7..56338c8cfc3343580156211f58b6fbeebc422ef2 100644 (file)
@@ -175,6 +175,16 @@ static int mac802154_set_txpower(struct wpan_phy *phy, int db)
        return priv->ops->set_txpower(&priv->hw, db);
 }
 
+static int mac802154_set_lbt(struct wpan_phy *phy, bool on)
+{
+       struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+       if (!priv->ops->set_lbt)
+               return -ENOTSUPP;
+
+       return priv->ops->set_lbt(&priv->hw, on);
+}
+
 struct ieee802154_dev *
 ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
 {
@@ -253,6 +263,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)