Merge branch 'ieee802154'

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>
This commit is contained in:
David S. Miller 2014-02-17 16:43:17 -05:00
commit e035b8addc
10 changed files with 682 additions and 132 deletions

View 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,20 +1061,85 @@ static int at86rf230_probe(struct spi_device *spi)
return -EINVAL;
}
dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops);
if (!dev)
return -ENOMEM;
rc = gpio_request(pdata->rstn, "rstn");
if (rc)
return rc;
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(pdata->rstn, 1);
if (rc)
goto err_gpio_dir;
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(pdata->rstn, 0);
msleep(1);
gpio_set_value(pdata->rstn, 1);
msleep(1);
rc = __at86rf230_detect_device(spi, &man_id, &part, &version);
if (rc < 0)
goto err_gpio_dir;
if (man_id != 0x001f) {
dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n",
man_id >> 8, man_id & 0xFF);
rc = -EINVAL;
goto err_gpio_dir;
}
switch (part) {
case 2:
chip = "at86rf230";
/* FIXME: should be easy to support; */
break;
case 3:
chip = "at86rf231";
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, 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;
/* We do support only 2.4 Ghz */
dev->phy->channels_supported[0] = 0x7FFF800;
dev->flags = IEEE802154_HW_OMIT_CKSUM;
dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK;
if (pdata->irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
irq_worker = at86rf230_irqwork;
@ -886,86 +1156,22 @@ static int at86rf230_probe(struct spi_device *spi)
spi_set_drvdata(spi, lp);
at86rf230_fill_data(spi);
rc = gpio_request(lp->rstn, "rstn");
if (rc)
goto err_rstn;
if (gpio_is_valid(lp->slp_tr)) {
rc = gpio_request(lp->slp_tr, "slp_tr");
if (rc)
goto err_slp_tr;
}
rc = gpio_direction_output(lp->rstn, 1);
if (rc)
goto err_gpio_dir;
if (gpio_is_valid(lp->slp_tr)) {
rc = gpio_direction_output(lp->slp_tr, 0);
if (rc)
goto err_gpio_dir;
}
/* Reset */
msleep(1);
gpio_set_value(lp->rstn, 0);
msleep(1);
gpio_set_value(lp->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)
goto err_gpio_dir;
if (man_id_1 != 0x00 || man_id_0 != 0x1f) {
dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n",
man_id_1, man_id_0);
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) {
case 2:
chip = "at86rf230";
/* supported = 1; FIXME: should be easy to support; */
break;
case 3:
chip = "at86rf231";
supported = 1;
break;
default:
chip = "UNKNOWN";
break;
}
dev_info(&spi->dev, "Detected %s chip version %d\n", chip, lp->vers);
if (!supported) {
rc = -ENOTSUPP;
goto err_gpio_dir;
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);

View 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,
};

View 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 */

View 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)));
};

View 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,

View 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),

View 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;
}

View 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, },
};

View 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:

View 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)