drivers: ieee802154: introduce new Telink B91 IEEE802154 driver
IEEE802154 driver basic support for Telink B91 platform. Signed-off-by: Yuriy Vynnychek <yura.vynnychek@telink-semi.com>
This commit is contained in:
parent
1d50dd5a38
commit
1c0ed94601
6 changed files with 791 additions and 0 deletions
|
@ -243,6 +243,7 @@
|
|||
/drivers/i2s/i2s_ll_stm32* @avisconti
|
||||
/drivers/i2s/*nrfx* @anangl
|
||||
/drivers/ieee802154/ @rlubos @tbursztyka
|
||||
/drivers/ieee802154/*b91* @yurvyn
|
||||
/drivers/ieee802154/ieee802154_rf2xx* @tbursztyka @nandojve
|
||||
/drivers/ieee802154/ieee802154_cc13xx* @bwitherspoon @cfriedt
|
||||
/drivers/interrupt_controller/ @dcpleung @nashif
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_IEEE802154_TELINK_B91 ieee802154_b91.c)
|
||||
zephyr_sources_ifdef(CONFIG_IEEE802154_CC2520 ieee802154_cc2520.c)
|
||||
zephyr_sources_ifdef(CONFIG_IEEE802154_KW41Z ieee802154_kw41z.c)
|
||||
zephyr_sources_ifdef(CONFIG_IEEE802154_UPIPE ieee802154_uart_pipe.c)
|
||||
|
|
|
@ -57,6 +57,8 @@ config IEEE802154_VENDOR_OUI
|
|||
|
||||
endif # IEEE802154_VENDOR_OUI_ENABLE
|
||||
|
||||
source "drivers/ieee802154/Kconfig.b91"
|
||||
|
||||
source "drivers/ieee802154/Kconfig.cc2520"
|
||||
|
||||
source "drivers/ieee802154/Kconfig.kw41z"
|
||||
|
|
62
drivers/ieee802154/Kconfig.b91
Normal file
62
drivers/ieee802154/Kconfig.b91
Normal file
|
@ -0,0 +1,62 @@
|
|||
# Copyright (c) 2021 Telink Semiconductor
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Telink B91 RF configuration options
|
||||
|
||||
menuconfig IEEE802154_TELINK_B91
|
||||
bool "Telink B91 RF driver"
|
||||
depends on NETWORKING
|
||||
|
||||
if IEEE802154_TELINK_B91
|
||||
|
||||
config IEEE802154_B91_DRV_NAME
|
||||
string "b91 IEEE 802.15.4 Driver's name"
|
||||
default "IEEE802154_b91"
|
||||
help
|
||||
This option sets the driver name
|
||||
|
||||
config IEEE802154_B91_INIT_PRIO
|
||||
int "Telink B91 IEEE 802.15.4 initialization priority"
|
||||
default 80
|
||||
help
|
||||
Set the initialization priority number. Do not mess with it unless
|
||||
you know what you are doing.
|
||||
|
||||
config IEEE802154_B91_RANDOM_MAC
|
||||
bool "Random MAC address"
|
||||
default y
|
||||
help
|
||||
Generate a random MAC address dynamically.
|
||||
|
||||
if ! IEEE802154_B91_RANDOM_MAC
|
||||
|
||||
config IEEE802154_B91_MAC4
|
||||
hex "MAC Address Byte 4"
|
||||
default 0
|
||||
range 0 0xff
|
||||
help
|
||||
This is the byte 4 of the MAC address.
|
||||
|
||||
config IEEE802154_B91_MAC5
|
||||
hex "MAC Address Byte 5"
|
||||
default 0
|
||||
range 0 0xff
|
||||
help
|
||||
This is the byte 5 of the MAC address.
|
||||
|
||||
config IEEE802154_B91_MAC6
|
||||
hex "MAC Address Byte 6"
|
||||
default 0
|
||||
range 0 0xff
|
||||
help
|
||||
This is the byte 6 of the MAC address.
|
||||
|
||||
config IEEE802154_B91_MAC7
|
||||
hex "MAC Address Byte 7"
|
||||
default 0
|
||||
range 0 0xff
|
||||
help
|
||||
This is the byte 7 of the MAC address.
|
||||
|
||||
endif # ! IEEE802154_B91_RANDOM_MAC
|
||||
endif # IEEE802154_TELINK_B91
|
618
drivers/ieee802154/ieee802154_b91.c
Normal file
618
drivers/ieee802154/ieee802154_b91.c
Normal file
|
@ -0,0 +1,618 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Telink Semiconductor
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT telink_b91_zb
|
||||
|
||||
#include "rf.h"
|
||||
#include "stimer.h"
|
||||
|
||||
#define LOG_MODULE_NAME ieee802154_b91
|
||||
#if defined(CONFIG_IEEE802154_DRIVER_LOG_LEVEL)
|
||||
#define LOG_LEVEL CONFIG_IEEE802154_DRIVER_LOG_LEVEL
|
||||
#else
|
||||
#define LOG_LEVEL LOG_LEVEL_NONE
|
||||
#endif
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
||||
|
||||
#include <random/rand32.h>
|
||||
#include <net/ieee802154_radio.h>
|
||||
#if defined(CONFIG_NET_L2_OPENTHREAD)
|
||||
#include <net/openthread.h>
|
||||
#endif
|
||||
|
||||
#include "ieee802154_b91.h"
|
||||
|
||||
|
||||
/* B91 data structure */
|
||||
static struct b91_data data;
|
||||
|
||||
/* Set filter PAN ID */
|
||||
static int b91_set_pan_id(uint16_t pan_id)
|
||||
{
|
||||
uint8_t pan_id_le[B91_PAN_ID_SIZE];
|
||||
|
||||
sys_put_le16(pan_id, pan_id_le);
|
||||
memcpy(data.filter_pan_id, pan_id_le, B91_PAN_ID_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set filter short address */
|
||||
static int b91_set_short_addr(uint16_t short_addr)
|
||||
{
|
||||
uint8_t short_addr_le[B91_SHORT_ADDRESS_SIZE];
|
||||
|
||||
sys_put_le16(short_addr, short_addr_le);
|
||||
memcpy(data.filter_short_addr, short_addr_le, B91_SHORT_ADDRESS_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set filter IEEE address */
|
||||
static int b91_set_ieee_addr(const uint8_t *ieee_addr)
|
||||
{
|
||||
memcpy(data.filter_ieee_addr, ieee_addr, B91_IEEE_ADDRESS_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Filter PAN ID, short address and IEEE address */
|
||||
static bool b91_run_filter(uint8_t *rx_buffer)
|
||||
{
|
||||
/* Check destination PAN Id */
|
||||
if (memcmp(&rx_buffer[B91_PAN_ID_OFFSET], data.filter_pan_id,
|
||||
B91_PAN_ID_SIZE) != 0 &&
|
||||
memcmp(&rx_buffer[B91_PAN_ID_OFFSET], B91_BROADCAST_ADDRESS,
|
||||
B91_PAN_ID_SIZE) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check destination address */
|
||||
switch (rx_buffer[B91_DEST_ADDR_TYPE_OFFSET] & B91_DEST_ADDR_TYPE_MASK) {
|
||||
case B91_DEST_ADDR_TYPE_SHORT:
|
||||
/* First check if the destination is broadcast */
|
||||
/* If not broadcast, check if length and address matches */
|
||||
if (memcmp(&rx_buffer[B91_DEST_ADDR_OFFSET], B91_BROADCAST_ADDRESS,
|
||||
B91_SHORT_ADDRESS_SIZE) != 0 &&
|
||||
memcmp(&rx_buffer[B91_DEST_ADDR_OFFSET], data.filter_short_addr,
|
||||
B91_SHORT_ADDRESS_SIZE) != 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case B91_DEST_ADDR_TYPE_IEEE:
|
||||
/* If not broadcast, check if length and address matches */
|
||||
if ((net_if_get_link_addr(data.iface)->len != B91_IEEE_ADDRESS_SIZE) ||
|
||||
memcmp(&rx_buffer[B91_DEST_ADDR_OFFSET], data.filter_ieee_addr,
|
||||
B91_IEEE_ADDRESS_SIZE) != 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Get MAC address */
|
||||
static inline uint8_t *b91_get_mac(const struct device *dev)
|
||||
{
|
||||
struct b91_data *b91 = dev->data;
|
||||
|
||||
#if defined(CONFIG_IEEE802154_B91_RANDOM_MAC)
|
||||
uint32_t *ptr = (uint32_t *)(b91->mac_addr);
|
||||
|
||||
UNALIGNED_PUT(sys_rand32_get(), ptr);
|
||||
ptr = (uint32_t *)(b91->mac_addr + 4);
|
||||
UNALIGNED_PUT(sys_rand32_get(), ptr);
|
||||
|
||||
/*
|
||||
* Clear bit 0 to ensure it isn't a multicast address and set
|
||||
* bit 1 to indicate address is locally administered and may
|
||||
* not be globally unique.
|
||||
*/
|
||||
b91->mac_addr[0] = (b91->mac_addr[0] & ~0x01) | 0x02;
|
||||
#else
|
||||
/* Vendor Unique Identifier */
|
||||
b91->mac_addr[0] = 0xC4;
|
||||
b91->mac_addr[1] = 0x19;
|
||||
b91->mac_addr[2] = 0xD1;
|
||||
b91->mac_addr[3] = 0x00;
|
||||
|
||||
/* Extended Unique Identifier */
|
||||
b91->mac_addr[4] = CONFIG_IEEE802154_B91_MAC4;
|
||||
b91->mac_addr[5] = CONFIG_IEEE802154_B91_MAC5;
|
||||
b91->mac_addr[6] = CONFIG_IEEE802154_B91_MAC6;
|
||||
b91->mac_addr[7] = CONFIG_IEEE802154_B91_MAC7;
|
||||
#endif
|
||||
|
||||
return b91->mac_addr;
|
||||
}
|
||||
|
||||
/* Convert RSSI to LQI */
|
||||
static uint8_t b91_convert_rssi_to_lqi(int8_t rssi)
|
||||
{
|
||||
uint32_t lqi32 = 0;
|
||||
|
||||
/* check for MIN value */
|
||||
if (rssi < B91_RSSI_TO_LQI_MIN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* convert RSSI to LQI */
|
||||
lqi32 = B91_RSSI_TO_LQI_SCALE * (rssi - B91_RSSI_TO_LQI_MIN);
|
||||
|
||||
/* check for MAX value */
|
||||
if (lqi32 > 0xFF) {
|
||||
lqi32 = 0xFF;
|
||||
}
|
||||
|
||||
return (uint8_t)lqi32;
|
||||
}
|
||||
|
||||
/* Update RSSI and LQI parameters */
|
||||
static void b91_update_rssi_and_lqi(struct net_pkt *pkt)
|
||||
{
|
||||
int8_t rssi;
|
||||
uint8_t lqi;
|
||||
|
||||
rssi = ((signed char)(data.rx_buffer
|
||||
[data.rx_buffer[B91_LENGTH_OFFSET] + B91_RSSI_OFFSET])) - 110;
|
||||
lqi = b91_convert_rssi_to_lqi(rssi);
|
||||
|
||||
net_pkt_set_ieee802154_lqi(pkt, lqi);
|
||||
net_pkt_set_ieee802154_rssi(pkt, rssi);
|
||||
}
|
||||
|
||||
/* Prepare TX buffer */
|
||||
static void b91_set_tx_payload(uint8_t *payload, uint8_t payload_len)
|
||||
{
|
||||
unsigned char rf_data_len;
|
||||
unsigned int rf_tx_dma_len;
|
||||
|
||||
rf_data_len = payload_len + 1;
|
||||
rf_tx_dma_len = rf_tx_packet_dma_len(rf_data_len);
|
||||
data.tx_buffer[0] = rf_tx_dma_len & 0xff;
|
||||
data.tx_buffer[1] = (rf_tx_dma_len >> 8) & 0xff;
|
||||
data.tx_buffer[2] = (rf_tx_dma_len >> 16) & 0xff;
|
||||
data.tx_buffer[3] = (rf_tx_dma_len >> 24) & 0xff;
|
||||
data.tx_buffer[4] = payload_len + 2;
|
||||
memcpy(data.tx_buffer + B91_PAYLOAD_OFFSET, payload, payload_len);
|
||||
}
|
||||
|
||||
/* Enable ack handler */
|
||||
static void b91_handle_ack_en(void)
|
||||
{
|
||||
data.ack_handler_en = true;
|
||||
}
|
||||
|
||||
/* Disable ack handler */
|
||||
static void b91_handle_ack_dis(void)
|
||||
{
|
||||
data.ack_handler_en = false;
|
||||
}
|
||||
|
||||
/* Handle acknowledge packet */
|
||||
static void b91_handle_ack(void)
|
||||
{
|
||||
struct net_pkt *ack_pkt;
|
||||
|
||||
/* allocate ack packet */
|
||||
ack_pkt = net_pkt_alloc_with_buffer(data.iface, B91_ACK_FRAME_LEN,
|
||||
AF_UNSPEC, 0, K_NO_WAIT);
|
||||
if (!ack_pkt) {
|
||||
LOG_ERR("No free packet available.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* update packet data */
|
||||
if (net_pkt_write(ack_pkt, data.rx_buffer + B91_PAYLOAD_OFFSET,
|
||||
B91_ACK_FRAME_LEN) < 0) {
|
||||
LOG_ERR("Failed to write to a packet.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* update RSSI and LQI */
|
||||
b91_update_rssi_and_lqi(ack_pkt);
|
||||
|
||||
/* init net cursor */
|
||||
net_pkt_cursor_init(ack_pkt);
|
||||
|
||||
/* handle ack */
|
||||
if (ieee802154_radio_handle_ack(data.iface, ack_pkt) != NET_OK) {
|
||||
LOG_INF("ACK packet not handled - releasing.");
|
||||
}
|
||||
|
||||
/* release ack_wait semaphore */
|
||||
k_sem_give(&data.ack_wait);
|
||||
|
||||
out:
|
||||
net_pkt_unref(ack_pkt);
|
||||
}
|
||||
|
||||
/* Send acknowledge packet */
|
||||
static void b91_send_ack(uint8_t seq_num)
|
||||
{
|
||||
uint8_t ack_buf[] = { B91_ACK_TYPE, 0, seq_num };
|
||||
|
||||
b91_set_tx_payload(ack_buf, sizeof(ack_buf));
|
||||
rf_set_txmode();
|
||||
delay_us(B91_SET_TRX_MODE_DELAY_US);
|
||||
rf_tx_pkt(data.tx_buffer);
|
||||
}
|
||||
|
||||
/* RX IRQ handler */
|
||||
static void b91_rf_rx_isr(void)
|
||||
{
|
||||
uint8_t status;
|
||||
uint8_t length;
|
||||
uint8_t *payload;
|
||||
struct net_pkt *pkt;
|
||||
|
||||
/* disable DMA and clread IRQ flag */
|
||||
dma_chn_dis(DMA1);
|
||||
rf_clr_irq_status(FLD_RF_IRQ_RX);
|
||||
|
||||
/* check CRC */
|
||||
if (rf_zigbee_packet_crc_ok(data.rx_buffer)) {
|
||||
/* get payload length */
|
||||
if (IS_ENABLED(CONFIG_IEEE802154_RAW_MODE) ||
|
||||
IS_ENABLED(CONFIG_NET_L2_OPENTHREAD)) {
|
||||
length = data.rx_buffer[B91_LENGTH_OFFSET];
|
||||
} else {
|
||||
length = data.rx_buffer[B91_LENGTH_OFFSET] - B91_FCS_LENGTH;
|
||||
}
|
||||
|
||||
/* check length */
|
||||
if ((length < B91_PAYLOAD_MIN) || (length > B91_PAYLOAD_MAX)) {
|
||||
LOG_ERR("Invalid length\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* get payload */
|
||||
payload = (uint8_t *)(data.rx_buffer + B91_PAYLOAD_OFFSET);
|
||||
|
||||
/* handle acknowledge packet if enabled */
|
||||
if ((length == (B91_ACK_FRAME_LEN + B91_FCS_LENGTH)) &&
|
||||
(payload[B91_FRAME_TYPE_OFFSET] == B91_ACK_TYPE)) {
|
||||
if (data.ack_handler_en) {
|
||||
b91_handle_ack();
|
||||
}
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* run filter (check PAN ID and destination address) */
|
||||
if (b91_run_filter(payload) == false) {
|
||||
LOG_DBG("Packet received is not addressed to me");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* send ack if requested */
|
||||
if (payload[B91_FRAME_TYPE_OFFSET] & B91_ACK_REQUEST) {
|
||||
b91_send_ack(payload[B91_DSN_OFFSET]);
|
||||
}
|
||||
|
||||
/* get packet pointer from NET stack */
|
||||
pkt = net_pkt_alloc_with_buffer(data.iface, length, AF_UNSPEC, 0, K_NO_WAIT);
|
||||
if (!pkt) {
|
||||
LOG_ERR("No pkt available");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* update packet data */
|
||||
if (net_pkt_write(pkt, payload, length)) {
|
||||
LOG_ERR("Failed to write to a packet.");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* update RSSI and LQI prameters */
|
||||
b91_update_rssi_and_lqi(pkt);
|
||||
|
||||
/* transfer data to NET stack */
|
||||
status = net_recv_data(data.iface, pkt);
|
||||
if (status < 0) {
|
||||
LOG_ERR("RCV Packet dropped by NET stack: %d", status);
|
||||
net_pkt_unref(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
dma_chn_en(DMA1);
|
||||
}
|
||||
|
||||
/* TX IRQ handler */
|
||||
static void b91_rf_tx_isr(void)
|
||||
{
|
||||
/* clear irq status */
|
||||
rf_clr_irq_status(FLD_RF_IRQ_TX);
|
||||
|
||||
/* release tx semaphore */
|
||||
k_sem_give(&data.tx_wait);
|
||||
|
||||
/* set to rx mode */
|
||||
rf_set_rxmode();
|
||||
}
|
||||
|
||||
/* IRQ handler */
|
||||
static void b91_rf_isr(void)
|
||||
{
|
||||
if (rf_get_irq_status(FLD_RF_IRQ_RX)) {
|
||||
b91_rf_rx_isr();
|
||||
} else if (rf_get_irq_status(FLD_RF_IRQ_TX)) {
|
||||
b91_rf_tx_isr();
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver initialization */
|
||||
static int b91_init(const struct device *dev)
|
||||
{
|
||||
struct b91_data *b91 = dev->data;
|
||||
|
||||
/* init semaphores */
|
||||
k_sem_init(&b91->tx_wait, 0, 1);
|
||||
k_sem_init(&b91->ack_wait, 0, 1);
|
||||
|
||||
/* init rf module */
|
||||
rf_mode_init();
|
||||
rf_set_zigbee_250K_mode();
|
||||
rf_set_tx_dma(2, B91_TRX_LENGTH);
|
||||
rf_set_rx_dma(data.rx_buffer, 3, B91_TRX_LENGTH);
|
||||
rf_set_rxmode();
|
||||
|
||||
/* init IRQs */
|
||||
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), b91_rf_isr, 0, 0);
|
||||
riscv_plic_irq_enable(DT_INST_IRQN(0));
|
||||
riscv_plic_set_priority(DT_INST_IRQN(0), DT_INST_IRQ(0, priority));
|
||||
rf_set_irq_mask(FLD_RF_IRQ_RX | FLD_RF_IRQ_TX);
|
||||
|
||||
/* init data variables */
|
||||
data.is_started = true;
|
||||
data.ack_handler_en = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API implementation: iface_init */
|
||||
static void b91_iface_init(struct net_if *iface)
|
||||
{
|
||||
const struct device *dev = net_if_get_device(iface);
|
||||
struct b91_data *b91 = dev->data;
|
||||
uint8_t *mac = b91_get_mac(dev);
|
||||
|
||||
net_if_set_link_addr(iface, mac, B91_IEEE_ADDRESS_SIZE, NET_LINK_IEEE802154);
|
||||
|
||||
b91->iface = iface;
|
||||
|
||||
ieee802154_init(iface);
|
||||
}
|
||||
|
||||
/* API implementation: get_capabilities */
|
||||
static enum ieee802154_hw_caps b91_get_capabilities(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return IEEE802154_HW_FCS | IEEE802154_HW_2_4_GHZ |
|
||||
IEEE802154_HW_FILTER | IEEE802154_HW_TX_RX_ACK;
|
||||
}
|
||||
|
||||
/* API implementation: cca */
|
||||
static int b91_cca(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
unsigned int t1 = stimer_get_tick();
|
||||
|
||||
while (!clock_time_exceed(t1, B91_CCA_TIME_MAX_US)) {
|
||||
if (rf_get_rssi() < B91_CCA_RSSI_MIN) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* API implementation: set_channel */
|
||||
static int b91_set_channel(const struct device *dev, uint16_t channel)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
if (channel < 11 || channel > 26) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rf_set_chn(B91_LOGIC_CHANNEL_TO_PHYSICAL(channel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API implementation: filter */
|
||||
static int b91_filter(const struct device *dev,
|
||||
bool set,
|
||||
enum ieee802154_filter_type type,
|
||||
const struct ieee802154_filter *filter)
|
||||
{
|
||||
if (!set) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (type == IEEE802154_FILTER_TYPE_IEEE_ADDR) {
|
||||
return b91_set_ieee_addr(filter->ieee_addr);
|
||||
} else if (type == IEEE802154_FILTER_TYPE_SHORT_ADDR) {
|
||||
return b91_set_short_addr(filter->short_addr);
|
||||
} else if (type == IEEE802154_FILTER_TYPE_PAN_ID) {
|
||||
return b91_set_pan_id(filter->pan_id);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* API implementation: set_txpower */
|
||||
static int b91_set_txpower(const struct device *dev, int16_t dbm)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
/* check for supported Min/Max range */
|
||||
if (dbm < B91_TX_POWER_MIN) {
|
||||
dbm = B91_TX_POWER_MIN;
|
||||
} else if (dbm > B91_TX_POWER_MAX) {
|
||||
dbm = B91_TX_POWER_MAX;
|
||||
}
|
||||
|
||||
/* set TX power */
|
||||
rf_set_power_level(b91_tx_pwr_lt[dbm - B91_TX_POWER_MIN]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API implementation: start */
|
||||
static int b91_start(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
/* check if RF is already started */
|
||||
if (!data.is_started) {
|
||||
rf_set_rxmode();
|
||||
delay_us(B91_SET_TRX_MODE_DELAY_US);
|
||||
riscv_plic_irq_enable(DT_INST_IRQN(0));
|
||||
data.is_started = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API implementation: stop */
|
||||
static int b91_stop(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
/* check if RF is already stopped */
|
||||
if (data.is_started) {
|
||||
riscv_plic_irq_disable(DT_INST_IRQN(0));
|
||||
rf_set_tx_rx_off();
|
||||
delay_us(B91_SET_TRX_MODE_DELAY_US);
|
||||
data.is_started = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* API implementation: tx */
|
||||
static int b91_tx(const struct device *dev,
|
||||
enum ieee802154_tx_mode mode,
|
||||
struct net_pkt *pkt,
|
||||
struct net_buf *frag)
|
||||
{
|
||||
ARG_UNUSED(pkt);
|
||||
|
||||
int status;
|
||||
struct b91_data *b91 = dev->data;
|
||||
|
||||
/* check for supported mode */
|
||||
if (mode != IEEE802154_TX_MODE_DIRECT) {
|
||||
LOG_DBG("TX mode %d not supported", mode);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* prepare tx buffer */
|
||||
b91_set_tx_payload(frag->data, frag->len);
|
||||
|
||||
/* reset semaphores */
|
||||
k_sem_reset(&b91->tx_wait);
|
||||
k_sem_reset(&b91->ack_wait);
|
||||
|
||||
/* start transmission */
|
||||
rf_set_txmode();
|
||||
delay_us(B91_SET_TRX_MODE_DELAY_US);
|
||||
rf_tx_pkt(data.tx_buffer);
|
||||
|
||||
/* wait for tx done */
|
||||
status = k_sem_take(&b91->tx_wait, K_MSEC(B91_TX_WAIT_TIME_MS));
|
||||
if (status != 0) {
|
||||
rf_set_rxmode();
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* wait for ACK if requested */
|
||||
if (frag->data[B91_FRAME_TYPE_OFFSET] & B91_ACK_REQUEST) {
|
||||
b91_handle_ack_en();
|
||||
status = k_sem_take(&b91->ack_wait, K_MSEC(B91_ACK_WAIT_TIME_MS));
|
||||
b91_handle_ack_dis();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* API implementation: ed_scan */
|
||||
static int b91_ed_scan(const struct device *dev, uint16_t duration,
|
||||
energy_scan_done_cb_t done_cb)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
ARG_UNUSED(duration);
|
||||
ARG_UNUSED(done_cb);
|
||||
|
||||
/* ed_scan not supported */
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* API implementation: configure */
|
||||
static int b91_configure(const struct device *dev,
|
||||
enum ieee802154_config_type type,
|
||||
const struct ieee802154_config *config)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
ARG_UNUSED(type);
|
||||
ARG_UNUSED(config);
|
||||
|
||||
/* configure not supported */
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* IEEE802154 driver APIs structure */
|
||||
static struct ieee802154_radio_api b91_radio_api = {
|
||||
.iface_api.init = b91_iface_init,
|
||||
.get_capabilities = b91_get_capabilities,
|
||||
.cca = b91_cca,
|
||||
.set_channel = b91_set_channel,
|
||||
.filter = b91_filter,
|
||||
.set_txpower = b91_set_txpower,
|
||||
.start = b91_start,
|
||||
.stop = b91_stop,
|
||||
.tx = b91_tx,
|
||||
.ed_scan = b91_ed_scan,
|
||||
.configure = b91_configure,
|
||||
};
|
||||
|
||||
|
||||
#if defined(CONFIG_NET_L2_IEEE802154)
|
||||
#define L2 IEEE802154_L2
|
||||
#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(IEEE802154_L2)
|
||||
#define MTU 125
|
||||
#elif defined(CONFIG_NET_L2_OPENTHREAD)
|
||||
#define L2 OPENTHREAD_L2
|
||||
#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(OPENTHREAD_L2)
|
||||
#define MTU 1280
|
||||
#endif
|
||||
|
||||
|
||||
/* IEEE802154 driver registration */
|
||||
#if defined(CONFIG_NET_L2_IEEE802154) || defined(CONFIG_NET_L2_OPENTHREAD)
|
||||
NET_DEVICE_INIT(b91_154_radio, CONFIG_IEEE802154_B91_DRV_NAME,
|
||||
b91_init, NULL, &data, NULL,
|
||||
CONFIG_IEEE802154_B91_INIT_PRIO,
|
||||
&b91_radio_api, L2,
|
||||
L2_CTX_TYPE, MTU);
|
||||
#else
|
||||
DEVICE_DEFINE(b91_154_radio, CONFIG_IEEE802154_B91_DRV_NAME,
|
||||
b91_init, NULL, &data, NULL,
|
||||
POST_KERNEL, CONFIG_IEEE802154_B91_INIT_PRIO,
|
||||
&b91_radio_api);
|
||||
#endif
|
107
drivers/ieee802154/ieee802154_b91.h
Normal file
107
drivers/ieee802154/ieee802154_b91.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Telink Semiconductor
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_IEEE802154_IEEE802154_B91_H_
|
||||
#define ZEPHYR_DRIVERS_IEEE802154_IEEE802154_B91_H_
|
||||
|
||||
/* Timeouts */
|
||||
#define B91_TX_WAIT_TIME_MS (10)
|
||||
#define B91_ACK_WAIT_TIME_MS (10)
|
||||
#define B91_SET_TRX_MODE_DELAY_US (100)
|
||||
|
||||
/* Received data parsing */
|
||||
#define B91_PAYLOAD_OFFSET (5)
|
||||
#define B91_PAYLOAD_MIN (5)
|
||||
#define B91_PAYLOAD_MAX (127)
|
||||
#define B91_FRAME_TYPE_OFFSET (0)
|
||||
#define B91_DEST_ADDR_TYPE_OFFSET (1)
|
||||
#define B91_DEST_ADDR_TYPE_MASK (0x0c)
|
||||
#define B91_DEST_ADDR_TYPE_SHORT (8)
|
||||
#define B91_DEST_ADDR_TYPE_IEEE (0x0c)
|
||||
#define B91_PAN_ID_OFFSET (3)
|
||||
#define B91_PAN_ID_SIZE (2)
|
||||
#define B91_DEST_ADDR_OFFSET (5)
|
||||
#define B91_SHORT_ADDRESS_SIZE (2)
|
||||
#define B91_IEEE_ADDRESS_SIZE (8)
|
||||
#define B91_LENGTH_OFFSET (4)
|
||||
#define B91_RSSI_OFFSET (11)
|
||||
#define B91_BROADCAST_ADDRESS ((uint8_t [2]) { 0xff, 0xff })
|
||||
#define B91_ACK_FRAME_LEN (3)
|
||||
#define B91_ACK_TYPE (2)
|
||||
#define B91_ACK_REQUEST (1 << 5)
|
||||
#define B91_DSN_OFFSET (2)
|
||||
#define B91_FCS_LENGTH (2)
|
||||
|
||||
/* Generic */
|
||||
#define B91_TRX_LENGTH (256)
|
||||
#define B91_RSSI_TO_LQI_SCALE (3)
|
||||
#define B91_RSSI_TO_LQI_MIN (-87)
|
||||
#define B91_CCA_RSSI_MIN (-60)
|
||||
#define B91_CCA_TIME_MAX_US (200)
|
||||
#define B91_LOGIC_CHANNEL_TO_PHYSICAL(p) (((p) - 10) * 5)
|
||||
|
||||
/* TX power lookup table */
|
||||
#define B91_TX_POWER_MIN (-30)
|
||||
#define B91_TX_POWER_MAX (9)
|
||||
static const uint8_t b91_tx_pwr_lt[] = {
|
||||
RF_POWER_N30dBm, /* -30.0 dBm: -30 */
|
||||
RF_POWER_N30dBm, /* -30.0 dBm: -29 */
|
||||
RF_POWER_N30dBm, /* -30.0 dBm: -28 */
|
||||
RF_POWER_N30dBm, /* -30.0 dBm: -27 */
|
||||
RF_POWER_N30dBm, /* -30.0 dBm: -26 */
|
||||
RF_POWER_N23p54dBm, /* -23.5 dBm: -25 */
|
||||
RF_POWER_N23p54dBm, /* -23.5 dBm: -24 */
|
||||
RF_POWER_N23p54dBm, /* -23.5 dBm: -23 */
|
||||
RF_POWER_N23p54dBm, /* -23.5 dBm: -22 */
|
||||
RF_POWER_N23p54dBm, /* -23.5 dBm: -21 */
|
||||
RF_POWER_N17p83dBm, /* -17.8 dBm: -20 */
|
||||
RF_POWER_N17p83dBm, /* -17.8 dBm: -19 */
|
||||
RF_POWER_N17p83dBm, /* -17.8 dBm: -18 */
|
||||
RF_POWER_N17p83dBm, /* -17.8 dBm: -17 */
|
||||
RF_POWER_N17p83dBm, /* -17.8 dBm: -16 */
|
||||
RF_POWER_N12p06dBm, /* -12.0 dBm: -15 */
|
||||
RF_POWER_N12p06dBm, /* -12.0 dBm: -14 */
|
||||
RF_POWER_N12p06dBm, /* -12.0 dBm: -13 */
|
||||
RF_POWER_N12p06dBm, /* -12.0 dBm: -12 */
|
||||
RF_POWER_N12p06dBm, /* -12.0 dBm: -11 */
|
||||
RF_POWER_N8p78dBm, /* -8.7 dBm: -10 */
|
||||
RF_POWER_N8p78dBm, /* -8.7 dBm: -9 */
|
||||
RF_POWER_N8p78dBm, /* -8.7 dBm: -8 */
|
||||
RF_POWER_N6p54dBm, /* -6.5 dBm: -7 */
|
||||
RF_POWER_N6p54dBm, /* -6.5 dBm: -6 */
|
||||
RF_POWER_N4p77dBm, /* -4.7 dBm: -5 */
|
||||
RF_POWER_N4p77dBm, /* -4.7 dBm: -4 */
|
||||
RF_POWER_N3p37dBm, /* -3.3 dBm: -3 */
|
||||
RF_POWER_N2p01dBm, /* -2.0 dBm: -2 */
|
||||
RF_POWER_N1p37dBm, /* -1.3 dBm: -1 */
|
||||
RF_POWER_P0p01dBm, /* 0.0 dBm: 0 */
|
||||
RF_POWER_P0p80dBm, /* 0.8 dBm: 1 */
|
||||
RF_POWER_P2p32dBm, /* 2.3 dBm: 2 */
|
||||
RF_POWER_P3p25dBm, /* 3.2 dBm: 3 */
|
||||
RF_POWER_P4p35dBm, /* 4.3 dBm: 4 */
|
||||
RF_POWER_P5p68dBm, /* 5.6 dBm: 5 */
|
||||
RF_POWER_P5p68dBm, /* 5.6 dBm: 6 */
|
||||
RF_POWER_P6p98dBm, /* 6.9 dBm: 7 */
|
||||
RF_POWER_P8p05dBm, /* 8.0 dBm: 8 */
|
||||
RF_POWER_P9p11dBm, /* 9.1 dBm: 9 */
|
||||
};
|
||||
|
||||
/* data structure */
|
||||
struct b91_data {
|
||||
uint8_t mac_addr[B91_IEEE_ADDRESS_SIZE];
|
||||
uint8_t rx_buffer[B91_TRX_LENGTH];
|
||||
uint8_t tx_buffer[B91_TRX_LENGTH];
|
||||
struct net_if *iface;
|
||||
struct k_sem tx_wait;
|
||||
struct k_sem ack_wait;
|
||||
uint8_t filter_pan_id[B91_PAN_ID_SIZE];
|
||||
uint8_t filter_short_addr[B91_SHORT_ADDRESS_SIZE];
|
||||
uint8_t filter_ieee_addr[B91_IEEE_ADDRESS_SIZE];
|
||||
bool is_started;
|
||||
bool ack_handler_en;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue