]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'ieee802154'
authorDavid S. Miller <davem@davemloft.net>
Mon, 17 Feb 2014 21:43:17 +0000 (16:43 -0500)
committerDavid S. Miller <davem@davemloft.net>
Mon, 17 Feb 2014 21:43:17 +0000 (16:43 -0500)
Phoebe Buckheister says:

====================
ieee802154: support rf212 and extended mac features

this patch set adds support for the RF212 radio chip to the existing
at86rf230 driver and adds support for numerous features of the RF212
chips to the ieee802154 stack. These features include CSMA parameter
configuration, transmit power control, CCA parameter configuration, and
automatic retransmission of frames. Netlink APIs are provided for all
new options introduced in this set.

Many features might also work for RF230, but since I have no such chips
at my disposal, most new features are implemented only for RF212.

Changes since v2:
 * Indentation
Changes since v1:
 * CodingStyle compliance. Thanks Sergei Shtylyov
 * Add CSMA parameters to netlink phy list that were forgotten in v1
====================

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/ieee802154.h
net/ieee802154/netlink.c
net/ieee802154/nl-phy.c
net/ieee802154/nl_policy.c
net/ieee802154/wpan-class.c
net/mac802154/ieee802154_dev.c

index ab31544bc25487ac82b1703beef9df6c069e615e..34bf011584fb95e2ecf3cd6d4bdec0665c1644b2 100644 (file)
@@ -37,7 +37,6 @@
 
 struct at86rf230_local {
        struct spi_device *spi;
-       int rstn, slp_tr, dig2;
 
        u8 part;
        u8 vers;
@@ -53,8 +52,16 @@ struct at86rf230_local {
        spinlock_t lock;
        bool irq_busy;
        bool is_tx;
+       bool tx_aret;
+
+       int rssi_base_val;
 };
 
+static inline int is_rf212(struct at86rf230_local *local)
+{
+       return local->part == 7;
+}
+
 #define        RG_TRX_STATUS   (0x01)
 #define        SR_TRX_STATUS           0x01, 0x1f, 0
 #define        SR_RESERVED_01_3        0x01, 0x20, 5
@@ -100,7 +107,10 @@ struct at86rf230_local {
 #define        SR_SFD_VALUE            0x0b, 0xff, 0
 #define        RG_TRX_CTRL_2   (0x0c)
 #define        SR_OQPSK_DATA_RATE      0x0c, 0x03, 0
-#define        SR_RESERVED_0c_2        0x0c, 0x7c, 2
+#define        SR_SUB_MODE             0x0c, 0x04, 2
+#define        SR_BPSK_QPSK            0x0c, 0x08, 3
+#define        SR_OQPSK_SUB1_RC_EN     0x0c, 0x10, 4
+#define        SR_RESERVED_0c_5        0x0c, 0x60, 5
 #define        SR_RX_SAFE_MODE         0x0c, 0x80, 7
 #define        RG_ANT_DIV      (0x0d)
 #define        SR_ANT_CTRL             0x0d, 0x03, 0
@@ -145,7 +155,7 @@ struct at86rf230_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
@@ -243,6 +253,57 @@ struct at86rf230_local {
 #define STATE_BUSY_RX_AACK_NOCLK 0x1E
 #define STATE_TRANSITION_IN_PROGRESS 0x1F
 
+static int
+__at86rf230_detect_device(struct spi_device *spi, u16 *man_id, u8 *part,
+               u8 *version)
+{
+       u8 data[4];
+       u8 *buf = kmalloc(2, GFP_KERNEL);
+       int status;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len    = 2,
+               .tx_buf = buf,
+               .rx_buf = buf,
+       };
+       u8 reg;
+
+       if (!buf)
+               return -ENOMEM;
+
+       for (reg = RG_PART_NUM; reg <= RG_MAN_ID_1; reg++) {
+               buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
+               buf[1] = 0xff;
+               dev_vdbg(&spi->dev, "buf[0] = %02x\n", buf[0]);
+               spi_message_init(&msg);
+               spi_message_add_tail(&xfer, &msg);
+
+               status = spi_sync(spi, &msg);
+               dev_vdbg(&spi->dev, "status = %d\n", status);
+               if (msg.status)
+                       status = msg.status;
+
+               dev_vdbg(&spi->dev, "status = %d\n", status);
+               dev_vdbg(&spi->dev, "buf[0] = %02x\n", buf[0]);
+               dev_vdbg(&spi->dev, "buf[1] = %02x\n", buf[1]);
+
+               if (status == 0)
+                       data[reg - RG_PART_NUM] = buf[1];
+               else
+                       break;
+       }
+
+       if (status == 0) {
+               *part = data[0];
+               *version = data[1];
+               *man_id = (data[3] << 8) | data[2];
+       }
+
+       kfree(buf);
+
+       return status;
+}
+
 static int
 __at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data)
 {
@@ -489,7 +550,9 @@ at86rf230_state(struct ieee802154_dev *dev, int state)
        } while (val == STATE_TRANSITION_IN_PROGRESS);
 
 
-       if (val == desired_status)
+       if (val == desired_status ||
+           (desired_status == STATE_RX_ON && val == STATE_BUSY_RX) ||
+           (desired_status == STATE_RX_AACK_ON && val == STATE_BUSY_RX_AACK))
                return 0;
 
        pr_err("unexpected state change: %d, asked for %d\n", val, state);
@@ -510,7 +573,11 @@ at86rf230_start(struct ieee802154_dev *dev)
        if (rc)
                return rc;
 
-       return at86rf230_state(dev, STATE_RX_ON);
+       rc = at86rf230_state(dev, STATE_FORCE_TX_ON);
+       if (rc)
+               return rc;
+
+       return at86rf230_state(dev, STATE_RX_AACK_ON);
 }
 
 static void
@@ -519,6 +586,39 @@ at86rf230_stop(struct ieee802154_dev *dev)
        at86rf230_state(dev, STATE_FORCE_TRX_OFF);
 }
 
+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);
+}
+
+static int
+at86rf212_set_channel(struct at86rf230_local *lp, int page, int channel)
+{
+       int rc;
+
+       if (channel == 0)
+               rc = at86rf230_write_subreg(lp, SR_SUB_MODE, 0);
+       else
+               rc = at86rf230_write_subreg(lp, SR_SUB_MODE, 1);
+       if (rc < 0)
+               return rc;
+
+       if (page == 0) {
+               rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 0);
+               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;
+
+       return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
+}
+
 static int
 at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
 {
@@ -527,14 +627,22 @@ at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
 
        might_sleep();
 
-       if (page != 0 || channel < 11 || channel > 26) {
+       if (page < 0 || page > 31 ||
+           !(lp->dev->phy->channels_supported[page] & BIT(channel))) {
                WARN_ON(1);
                return -EINVAL;
        }
 
-       rc = at86rf230_write_subreg(lp, SR_CHANNEL, channel);
+       if (is_rf212(lp))
+               rc = at86rf212_set_channel(lp, page, channel);
+       else
+               rc = at86rf230_set_channel(lp, page, channel);
+       if (rc < 0)
+               return rc;
+
        msleep(1); /* Wait for PLL */
        dev->phy->current_channel = channel;
+       dev->phy->current_page = page;
 
        return 0;
 }
@@ -568,6 +676,12 @@ at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
        if (rc)
                goto err_rx;
 
+       if (lp->tx_aret) {
+               rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TX_ARET_ON);
+               if (rc)
+                       goto err_rx;
+       }
+
        rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_BUSY_TX);
        if (rc)
                goto err_rx;
@@ -668,6 +782,98 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
        return 0;
 }
 
+static int
+at86rf212_set_txpower(struct ieee802154_dev *dev, int db)
+{
+       struct at86rf230_local *lp = dev->priv;
+       int rc;
+
+       /* typical maximum output is 5dBm with RG_PHY_TX_PWR 0x60, lower five
+        * bits decrease power in 1dB steps. 0x60 represents extra PA gain of
+        * 0dB.
+        * thus, supported values for db range from -26 to 5, for 31dB of
+        * reduction to 0dB of reduction.
+        */
+       if (db > 5 || db < -26)
+               return -EINVAL;
+
+       db = -(db - 5);
+
+       rc = __at86rf230_write(lp, RG_PHY_TX_PWR, 0x60 | db);
+       if (rc)
+               return rc;
+
+       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 int
+at86rf212_set_cca_mode(struct ieee802154_dev *dev, u8 mode)
+{
+       struct at86rf230_local *lp = dev->priv;
+
+       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 int
+at86rf212_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be,
+                         u8 retries)
+{
+       struct at86rf230_local *lp = dev->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;
+
+       rc = at86rf230_write_subreg(lp, SR_MAX_BE, max_be);
+       if (rc)
+               return rc;
+
+       return at86rf230_write_subreg(lp, SR_MAX_CSMA_RETRIES, max_be);
+}
+
+static int
+at86rf212_set_frame_retries(struct ieee802154_dev *dev, s8 retries)
+{
+       struct at86rf230_local *lp = dev->priv;
+       int rc = 0;
+
+       if (retries < -1 || retries > 15)
+               return -EINVAL;
+
+       lp->tx_aret = retries >= 0;
+
+       if (retries >= 0)
+               rc = at86rf230_write_subreg(lp, SR_MAX_FRAME_RETRIES, retries);
+
+       return rc;
+}
+
 static struct ieee802154_ops at86rf230_ops = {
        .owner = THIS_MODULE,
        .xmit = at86rf230_xmit,
@@ -678,6 +884,22 @@ static struct ieee802154_ops at86rf230_ops = {
        .set_hw_addr_filt = at86rf230_set_hw_addr_filt,
 };
 
+static struct ieee802154_ops at86rf212_ops = {
+       .owner = THIS_MODULE,
+       .xmit = at86rf230_xmit,
+       .ed = at86rf230_ed,
+       .set_channel = at86rf230_channel,
+       .start = at86rf230_start,
+       .stop = at86rf230_stop,
+       .set_hw_addr_filt = at86rf230_set_hw_addr_filt,
+       .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,
+       .set_csma_params = at86rf212_set_csma_params,
+       .set_frame_retries = at86rf212_set_frame_retries,
+};
+
 static void at86rf230_irqwork(struct work_struct *work)
 {
        struct at86rf230_local *lp =
@@ -752,22 +974,15 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
        struct at86rf230_platform_data *pdata = lp->spi->dev.platform_data;
        int rc, irq_pol;
        u8 status;
+       u8 csma_seed[2];
 
        rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
        if (rc)
                return rc;
 
-       dev_info(&lp->spi->dev, "Status: %02x\n", status);
-       if (status == STATE_P_ON) {
-               rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TRX_OFF);
-               if (rc)
-                       return rc;
-               msleep(1);
-               rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
-               if (rc)
-                       return rc;
-               dev_info(&lp->spi->dev, "Status: %02x\n", status);
-       }
+       rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_FORCE_TRX_OFF);
+       if (rc)
+               return rc;
 
        /* configure irq polarity, defaults to high active */
        if (pdata->irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
@@ -783,6 +998,14 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
        if (rc)
                return rc;
 
+       get_random_bytes(csma_seed, ARRAY_SIZE(csma_seed));
+       rc = at86rf230_write_subreg(lp, SR_CSMA_SEED_0, csma_seed[0]);
+       if (rc)
+               return rc;
+       rc = at86rf230_write_subreg(lp, SR_CSMA_SEED_1, csma_seed[1]);
+       if (rc)
+               return rc;
+
        /* CLKM changes are applied immediately */
        rc = at86rf230_write_subreg(lp, SR_CLKM_SHA_SEL, 0x00);
        if (rc)
@@ -795,16 +1018,6 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
        /* Wait the next SLEEP cycle */
        msleep(100);
 
-       rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TX_ON);
-       if (rc)
-               return rc;
-       msleep(1);
-
-       rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
-       if (rc)
-               return rc;
-       dev_info(&lp->spi->dev, "Status: %02x\n", status);
-
        rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &status);
        if (rc)
                return rc;
@@ -824,26 +1037,18 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
        return 0;
 }
 
-static void at86rf230_fill_data(struct spi_device *spi)
-{
-       struct at86rf230_local *lp = spi_get_drvdata(spi);
-       struct at86rf230_platform_data *pdata = spi->dev.platform_data;
-
-       lp->rstn = pdata->rstn;
-       lp->slp_tr = pdata->slp_tr;
-       lp->dig2 = pdata->dig2;
-}
-
 static int at86rf230_probe(struct spi_device *spi)
 {
        struct at86rf230_platform_data *pdata;
        struct ieee802154_dev *dev;
        struct at86rf230_local *lp;
-       u8 man_id_0, man_id_1, status;
+       u16 man_id = 0;
+       u8 part = 0, version = 0, status;
        irq_handler_t irq_handler;
        work_func_t irq_worker;
-       int rc, supported = 0;
+       int rc;
        const char *chip;
+       struct ieee802154_ops *ops = NULL;
 
        if (!spi->irq) {
                dev_err(&spi->dev, "no IRQ specified\n");
@@ -856,116 +1061,117 @@ static int at86rf230_probe(struct spi_device *spi)
                return -EINVAL;
        }
 
-       dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops);
-       if (!dev)
-               return -ENOMEM;
-
-       lp = dev->priv;
-       lp->dev = dev;
-
-       lp->spi = spi;
-
-       dev->parent = &spi->dev;
-       dev->extra_tx_headroom = 0;
-       /* We do support only 2.4 Ghz */
-       dev->phy->channels_supported[0] = 0x7FFF800;
-       dev->flags = IEEE802154_HW_OMIT_CKSUM;
-
-       if (pdata->irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
-               irq_worker = at86rf230_irqwork;
-               irq_handler = at86rf230_isr;
-       } else {
-               irq_worker = at86rf230_irqwork_level;
-               irq_handler = at86rf230_isr_level;
-       }
-
-       mutex_init(&lp->bmux);
-       INIT_WORK(&lp->irqwork, irq_worker);
-       spin_lock_init(&lp->lock);
-       init_completion(&lp->tx_complete);
-
-       spi_set_drvdata(spi, lp);
-
-       at86rf230_fill_data(spi);
-
-       rc = gpio_request(lp->rstn, "rstn");
+       rc = gpio_request(pdata->rstn, "rstn");
        if (rc)
-               goto err_rstn;
+               return rc;
 
-       if (gpio_is_valid(lp->slp_tr)) {
-               rc = gpio_request(lp->slp_tr, "slp_tr");
+       if (gpio_is_valid(pdata->slp_tr)) {
+               rc = gpio_request(pdata->slp_tr, "slp_tr");
                if (rc)
                        goto err_slp_tr;
        }
 
-       rc = gpio_direction_output(lp->rstn, 1);
+       rc = gpio_direction_output(pdata->rstn, 1);
        if (rc)
                goto err_gpio_dir;
 
-       if (gpio_is_valid(lp->slp_tr)) {
-               rc = gpio_direction_output(lp->slp_tr, 0);
+       if (gpio_is_valid(pdata->slp_tr)) {
+               rc = gpio_direction_output(pdata->slp_tr, 0);
                if (rc)
                        goto err_gpio_dir;
        }
 
        /* Reset */
        msleep(1);
-       gpio_set_value(lp->rstn, 0);
+       gpio_set_value(pdata->rstn, 0);
        msleep(1);
-       gpio_set_value(lp->rstn, 1);
+       gpio_set_value(pdata->rstn, 1);
        msleep(1);
 
-       rc = at86rf230_read_subreg(lp, SR_MAN_ID_0, &man_id_0);
-       if (rc)
-               goto err_gpio_dir;
-       rc = at86rf230_read_subreg(lp, SR_MAN_ID_1, &man_id_1);
-       if (rc)
+       rc = __at86rf230_detect_device(spi, &man_id, &part, &version);
+       if (rc < 0)
                goto err_gpio_dir;
 
-       if (man_id_1 != 0x00 || man_id_0 != 0x1f) {
+       if (man_id != 0x001f) {
                dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n",
-                       man_id_1, man_id_0);
+                       man_id >> 8, man_id & 0xFF);
                rc = -EINVAL;
                goto err_gpio_dir;
        }
 
-       rc = at86rf230_read_subreg(lp, SR_PART_NUM, &lp->part);
-       if (rc)
-               goto err_gpio_dir;
-
-       rc = at86rf230_read_subreg(lp, SR_VERSION_NUM, &lp->vers);
-       if (rc)
-               goto err_gpio_dir;
-
-       switch (lp->part) {
+       switch (part) {
        case 2:
                chip = "at86rf230";
-               /* supported = 1;  FIXME: should be easy to support; */
+               /* FIXME: should be easy to support; */
                break;
        case 3:
                chip = "at86rf231";
-               supported = 1;
+               ops = &at86rf230_ops;
+               break;
+       case 7:
+               chip = "at86rf212";
+               if (version == 1)
+                       ops = &at86rf212_ops;
                break;
        default:
                chip = "UNKNOWN";
                break;
        }
 
-       dev_info(&spi->dev, "Detected %s chip version %d\n", chip, lp->vers);
-       if (!supported) {
+       dev_info(&spi->dev, "Detected %s chip version %d\n", chip, version);
+       if (!ops) {
                rc = -ENOTSUPP;
                goto err_gpio_dir;
        }
 
+       dev = ieee802154_alloc_device(sizeof(*lp), ops);
+       if (!dev) {
+               rc = -ENOMEM;
+               goto err_gpio_dir;
+       }
+
+       lp = dev->priv;
+       lp->dev = dev;
+       lp->part = part;
+       lp->vers = version;
+
+       lp->spi = spi;
+
+       dev->parent = &spi->dev;
+       dev->extra_tx_headroom = 0;
+       dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK;
+
+       if (pdata->irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+               irq_worker = at86rf230_irqwork;
+               irq_handler = at86rf230_isr;
+       } else {
+               irq_worker = at86rf230_irqwork_level;
+               irq_handler = at86rf230_isr_level;
+       }
+
+       mutex_init(&lp->bmux);
+       INIT_WORK(&lp->irqwork, irq_worker);
+       spin_lock_init(&lp->lock);
+       init_completion(&lp->tx_complete);
+
+       spi_set_drvdata(spi, lp);
+
+       if (is_rf212(lp)) {
+               dev->phy->channels_supported[0] = 0x00007FF;
+               dev->phy->channels_supported[2] = 0x00007FF;
+       } else {
+               dev->phy->channels_supported[0] = 0x7FFF800;
+       }
+
        rc = at86rf230_hw_init(lp);
        if (rc)
-               goto err_gpio_dir;
+               goto err_hw_init;
 
        rc = request_irq(spi->irq, irq_handler,
                         IRQF_SHARED | pdata->irq_type,
                         dev_name(&spi->dev), lp);
        if (rc)
-               goto err_gpio_dir;
+               goto err_hw_init;
 
        /* Read irq status register to reset irq line */
        rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status);
@@ -980,30 +1186,33 @@ static int at86rf230_probe(struct spi_device *spi)
 
 err_irq:
        free_irq(spi->irq, lp);
+err_hw_init:
        flush_work(&lp->irqwork);
-err_gpio_dir:
-       if (gpio_is_valid(lp->slp_tr))
-               gpio_free(lp->slp_tr);
-err_slp_tr:
-       gpio_free(lp->rstn);
-err_rstn:
+       spi_set_drvdata(spi, NULL);
        mutex_destroy(&lp->bmux);
        ieee802154_free_device(lp->dev);
+
+err_gpio_dir:
+       if (gpio_is_valid(pdata->slp_tr))
+               gpio_free(pdata->slp_tr);
+err_slp_tr:
+       gpio_free(pdata->rstn);
        return rc;
 }
 
 static int at86rf230_remove(struct spi_device *spi)
 {
        struct at86rf230_local *lp = spi_get_drvdata(spi);
+       struct at86rf230_platform_data *pdata = spi->dev.platform_data;
 
        ieee802154_unregister_device(lp->dev);
 
        free_irq(spi->irq, lp);
        flush_work(&lp->irqwork);
 
-       if (gpio_is_valid(lp->slp_tr))
-               gpio_free(lp->slp_tr);
-       gpio_free(lp->rstn);
+       if (gpio_is_valid(pdata->slp_tr))
+               gpio_free(pdata->slp_tr);
+       gpio_free(pdata->rstn);
 
        mutex_destroy(&lp->bmux);
        ieee802154_free_device(lp->dev);
index fd4f2d1cdf6cbfd95a3dd9eba2c10b27eca9d062..e110b8c266f547a884bca0040dd15e3be03f1485 100644 (file)
@@ -70,6 +70,16 @@ enum {
        IEEE802154_ATTR_PHY_NAME,
        IEEE802154_ATTR_DEV_TYPE,
 
+       IEEE802154_ATTR_TXPOWER,
+       IEEE802154_ATTR_LBT_ENABLED,
+       IEEE802154_ATTR_CCA_MODE,
+       IEEE802154_ATTR_CCA_ED_LEVEL,
+       IEEE802154_ATTR_CSMA_RETRIES,
+       IEEE802154_ATTR_CSMA_MIN_BE,
+       IEEE802154_ATTR_CSMA_MAX_BE,
+
+       IEEE802154_ATTR_FRAME_RETRIES,
+
        __IEEE802154_ATTR_MAX,
 };
 
@@ -122,6 +132,8 @@ enum {
        IEEE802154_ADD_IFACE,
        IEEE802154_DEL_IFACE,
 
+       IEEE802154_SET_PHYPARAMS,
+
        __IEEE802154_CMD_MAX,
 };
 
index 807d6b7a943fecab78db1afd6b57ee33b70ded72..8ca3d04e7558266bac7226533415ef540e5b0032 100644 (file)
@@ -113,6 +113,32 @@ struct ieee802154_dev {
  *       Set radio for listening on specific address.
  *       Set the device for listening on specified address.
  *       Returns either zero, or negative errno.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * set_csma_params
+ *       Sets the CSMA parameter set for the PHY. Called with pib_lock held.
+ *       Returns either zero, or negative errno.
+ *
+ * set_frame_retries
+ *       Sets the retransmission attempt limit. Called with pib_lock held.
+ *       Returns either zero, or negative errno.
  */
 struct ieee802154_ops {
        struct module   *owner;
@@ -129,6 +155,15 @@ struct ieee802154_ops {
                                            unsigned long changed);
        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);
+       int             (*set_cca_mode)(struct ieee802154_dev *dev, u8 mode);
+       int             (*set_cca_ed_level)(struct ieee802154_dev *dev,
+                                           s32 level);
+       int             (*set_csma_params)(struct ieee802154_dev *dev,
+                                          u8 min_be, u8 max_be, u8 retries);
+       int             (*set_frame_retries)(struct ieee802154_dev *dev,
+                                            s8 retries);
 };
 
 /* Basic interface to register ieee802154 device */
index b52bda8d13b183a953b88cb90700a1ee8cf08e23..10ab0fc6d4f79a8a608d0ce642aae294636dcecc 100644 (file)
@@ -37,15 +37,22 @@ struct wpan_phy {
        struct mutex pib_lock;
 
        /*
-        * This is a PIB according to 802.15.4-2006.
+        * This is a PIB according to 802.15.4-2011.
         * We do not provide timing-related variables, as they
         * aren't used outside of driver
         */
        u8 current_channel;
        u8 current_page;
        u32 channels_supported[32];
-       u8 transmit_power;
+       s8 transmit_power;
        u8 cca_mode;
+       u8 min_be;
+       u8 max_be;
+       u8 csma_retries;
+       s8 frame_retries;
+
+       bool lbt;
+       s32 cca_ed_level;
 
        struct device dev;
        int idx;
@@ -54,6 +61,14 @@ struct wpan_phy {
                                        const char *name, int type);
        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);
+       int (*set_cca_mode)(struct wpan_phy *phy, u8 cca_mode);
+       int (*set_cca_ed_level)(struct wpan_phy *phy, int level);
+       int (*set_csma_params)(struct wpan_phy *phy, u8 min_be, u8 max_be,
+                              u8 retries);
+       int (*set_frame_retries)(struct wpan_phy *phy, s8 retries);
+
        char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
 };
 
index cee4425b995689702375435f0be7dd85eca3ed73..6cbc8965be9181ce27314973532968dd7caab181 100644 (file)
@@ -53,6 +53,7 @@ int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info);
 int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb);
 int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info);
 int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info);
 
 enum ieee802154_mcgrp_ids {
        IEEE802154_COORD_MCGRP,
index 43f1b2bf469f40938a431582b305c45587049a04..67c151bf4b91b610835ffc60185428a9a84be8d8 100644 (file)
@@ -115,6 +115,7 @@ static const struct genl_ops ieee8021154_ops[] = {
                        ieee802154_dump_phy),
        IEEE802154_OP(IEEE802154_ADD_IFACE, ieee802154_add_iface),
        IEEE802154_OP(IEEE802154_DEL_IFACE, ieee802154_del_iface),
+       IEEE802154_OP(IEEE802154_SET_PHYPARAMS, ieee802154_set_phyparams),
        /* see nl-mac.c */
        IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req),
        IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp),
index 89b265aea151eaf7f301c28f391d064b7d22edfc..c9dfd6f59e346d5fa22eec0d87e18557b6b0c547 100644 (file)
@@ -55,7 +55,15 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
        mutex_lock(&phy->pib_lock);
        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_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_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL, phy->cca_ed_level) ||
+           nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES, phy->csma_retries) ||
+           nla_put_u8(msg, IEEE802154_ATTR_CSMA_MIN_BE, phy->min_be) ||
+           nla_put_u8(msg, IEEE802154_ATTR_CSMA_MAX_BE, phy->max_be) ||
+           nla_put_s8(msg, IEEE802154_ATTR_FRAME_RETRIES, phy->frame_retries))
                goto nla_put_failure;
        for (i = 0; i < 32; i++) {
                if (phy->channels_supported[i])
@@ -354,3 +362,191 @@ 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;
+}
+
+static int phy_set_cca_mode(struct wpan_phy *phy, struct genl_info *info)
+{
+       u8 mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
+       int rc;
+
+       if (mode > 3)
+               return -EINVAL;
+
+       rc = phy->set_cca_mode(phy, mode);
+       if (rc < 0)
+               return rc;
+
+       phy->cca_mode = mode;
+
+       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;
+}
+
+static int phy_set_csma_params(struct wpan_phy *phy, struct genl_info *info)
+{
+       int rc;
+       u8 min_be = phy->min_be;
+       u8 max_be = phy->max_be;
+       u8 retries = phy->csma_retries;
+
+       if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES])
+               retries = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_RETRIES]);
+       if (info->attrs[IEEE802154_ATTR_CSMA_MIN_BE])
+               min_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]);
+       if (info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])
+               max_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]);
+
+       if (retries > 5 || max_be > 8 || min_be > max_be ||
+           retries < -1 || retries > 7)
+               return -EINVAL;
+
+       rc = phy->set_csma_params(phy, min_be, max_be, retries);
+       if (rc < 0)
+               return rc;
+
+       phy->min_be = min_be;
+       phy->max_be = max_be;
+       phy->csma_retries = retries;
+
+       return 0;
+}
+
+static int phy_set_frame_retries(struct wpan_phy *phy, struct genl_info *info)
+{
+       s8 retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
+       int rc;
+
+       rc = phy->set_frame_retries(phy, retries);
+       if (rc < 0)
+               return rc;
+
+       phy->frame_retries = retries;
+
+       return 0;
+}
+
+int ieee802154_set_phyparams(struct sk_buff *skb, struct genl_info *info)
+{
+       struct wpan_phy *phy;
+       const char *name;
+       int rc = -ENOTSUPP;
+
+       pr_debug("%s\n", __func__);
+
+       if (!info->attrs[IEEE802154_ATTR_PHY_NAME] &&
+           !info->attrs[IEEE802154_ATTR_LBT_ENABLED] &&
+           !info->attrs[IEEE802154_ATTR_CCA_MODE] &&
+           !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL] &&
+           !info->attrs[IEEE802154_ATTR_CSMA_RETRIES] &&
+           !info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] &&
+           !info->attrs[IEEE802154_ATTR_CSMA_MAX_BE] &&
+           !info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
+               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 */
+
+       phy = wpan_phy_find(name);
+       if (!phy)
+               return -ENODEV;
+
+       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_ed_level &&
+            info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]))
+               goto out;
+
+       mutex_lock(&phy->pib_lock);
+
+       if (info->attrs[IEEE802154_ATTR_TXPOWER]) {
+               rc = phy_set_txpower(phy, info);
+               if (rc < 0)
+                       goto error;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_LBT_ENABLED]) {
+               rc = phy_set_lbt(phy, info);
+               if (rc < 0)
+                       goto error;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_CCA_MODE]) {
+               rc = phy_set_cca_mode(phy, info);
+               if (rc < 0)
+                       goto error;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) {
+               rc = phy_set_cca_ed_level(phy, info);
+               if (rc < 0)
+                       goto error;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] ||
+           info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] ||
+           info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]) {
+               rc = phy_set_csma_params(phy, info);
+               if (rc < 0)
+                       goto error;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) {
+               rc = phy_set_frame_retries(phy, info);
+               if (rc < 0)
+                       goto error;
+       }
+
+       mutex_unlock(&phy->pib_lock);
+
+       wpan_phy_put(phy);
+
+       return 0;
+
+error:
+       mutex_unlock(&phy->pib_lock);
+out:
+       wpan_phy_put(phy);
+       return rc;
+}
index 6adda4d46f95255d6540f639e383c9985a949138..fd7be5e45cefb99d37df9d173c9bf7c0ab1d7dc3 100644 (file)
@@ -52,5 +52,15 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
        [IEEE802154_ATTR_DURATION] = { .type = NLA_U8, },
        [IEEE802154_ATTR_ED_LIST] = { .len = 27 },
        [IEEE802154_ATTR_CHANNEL_PAGE_LIST] = { .len = 32 * 4, },
+
+       [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, },
+       [IEEE802154_ATTR_CSMA_RETRIES] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_CSMA_MIN_BE] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_CSMA_MAX_BE] = { .type = NLA_U8, },
+
+       [IEEE802154_ATTR_FRAME_RETRIES] = { .type = NLA_S8, },
 };
 
index 4dd37615a749c6f1191d5ce35bec09ca9ac05e81..edd0962d55f9a14a18c8901ffa0fa32432c1fcf6 100644 (file)
@@ -44,9 +44,7 @@ static DEVICE_ATTR_RO(name);
 
 MASTER_SHOW(current_channel, "%d");
 MASTER_SHOW(current_page, "%d");
-MASTER_SHOW_COMPLEX(transmit_power, "%d +- %d dB",
-       ((signed char) (phy->transmit_power << 2)) >> 2,
-       (phy->transmit_power >> 6) ? (phy->transmit_power >> 6) * 3 : 1);
+MASTER_SHOW(transmit_power, "%d +- 1 dB");
 MASTER_SHOW(cca_mode, "%d");
 
 static ssize_t channels_supported_show(struct device *dev,
@@ -171,6 +169,12 @@ struct wpan_phy *wpan_phy_alloc(size_t priv_size)
        phy->current_channel = -1; /* not initialised */
        phy->current_page = 0; /* for compatibility */
 
+       /* defaults per 802.15.4-2011 */
+       phy->min_be = 3;
+       phy->max_be = 5;
+       phy->csma_retries = 4;
+       phy->frame_retries = -1; /* for compatibility, actual default is 3 */
+
        return phy;
 
 out:
index 52ae6646a41140e065920ee80c4df626c6a58594..b75bb01e5c6b0b859effb0a53d54c7fc75f10b20 100644 (file)
@@ -165,6 +165,67 @@ err:
        return ERR_PTR(err);
 }
 
+static int mac802154_set_txpower(struct wpan_phy *phy, int db)
+{
+       struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+       if (!priv->ops->set_txpower)
+               return -ENOTSUPP;
+
+       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);
+}
+
+static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode)
+{
+       struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+       if (!priv->ops->set_cca_mode)
+               return -ENOTSUPP;
+
+       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);
+}
+
+static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be,
+                                    u8 max_be, u8 retries)
+{
+       struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+       if (!priv->ops->set_csma_params)
+               return -ENOTSUPP;
+
+       return priv->ops->set_csma_params(&priv->hw, min_be, max_be, retries);
+}
+
+static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries)
+{
+       struct mac802154_priv *priv = wpan_phy_priv(phy);
+
+       if (!priv->ops->set_frame_retries)
+               return -ENOTSUPP;
+
+       return priv->ops->set_frame_retries(&priv->hw, retries);
+}
+
 struct ieee802154_dev *
 ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
 {
@@ -242,6 +303,12 @@ 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;
+       priv->phy->set_cca_mode = mac802154_set_cca_mode;
+       priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level;
+       priv->phy->set_csma_params = mac802154_set_csma_params;
+       priv->phy->set_frame_retries = mac802154_set_frame_retries;
 
        rc = wpan_phy_register(priv->phy);
        if (rc < 0)