Message ID | 20170910093026.4386-1-starnight@g.ncu.edu.tw (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Hello Pan, From my point of view, it is not a good idea to write sx127x driver for IEEE 802154. Because LoRa is not compatible with IEEE 802.15.4 in PHY and MAC layer. I think it is better to make individual Phy and MAC layers for LoRa in Linux kernel, where a sx127x driver will make sense. Regards, Xue Liu On 10 September 2017 at 11:30, Jian-Hong Pan <starnight@g.ncu.edu.tw> wrote: > LoRa is an implementation of LPWPAN. > Chirp Spread Spectrum (CSS) modulation, which is used by LoRa has been > specified by IEEE 802.15.4a. > This driver implements IEEE 802.15.4 mac over LoRa physical layer not > LoRaWAN. > > Signed-off-by: Jian-Hong Pan <starnight@g.ncu.edu.tw> > --- > .../devicetree/bindings/net/ieee802154/sx1278.txt | 30 + > drivers/net/ieee802154/Kconfig | 12 + > drivers/net/ieee802154/Makefile | 1 + > drivers/net/ieee802154/sx1278.c | 1636 ++++++++++++++++++++ > 4 files changed, 1679 insertions(+) > create mode 100644 Documentation/devicetree/bindings/net/ieee802154/sx1278.txt > create mode 100644 drivers/net/ieee802154/sx1278.c > > diff --git a/Documentation/devicetree/bindings/net/ieee802154/sx1278.txt b/Documentation/devicetree/bindings/net/ieee802154/sx1278.txt > new file mode 100644 > index 000000000000..78ca60feac30 > --- /dev/null > +++ b/Documentation/devicetree/bindings/net/ieee802154/sx1278.txt > @@ -0,0 +1,30 @@ > +* SX1278 IEEE 802.15.4 Compatible Radio * > + > +Required properties: > + - compatible: should be "semtech,sx1276", "semtech,sx1277", > + "semtech,sx1277" or "semtech,sx1279" depends on your > + transceiver board > + - spi-max-frequency: maximal bus speed, should be set something under or > + equal 10000000 Hz > + - reg: the chipselect index > + - clock-frequency: the external crystal oscillator frequency in Hz of the > + transceiver > +Optional properties: > + - center-carrier-frq: the RF center carrier frequency in Hz > + - rf-bandwidth: the RF bandwidth in Hz > + - minimal-RF-channel: the minimal RF channel number and the value must be with > + prefix "/bits/ 8" because of being a byte datatype > + - maximum-RF-channel: the maximum RF channel number and the value must be with > + prefix "/bits/ 8" because of being a byte datatype > + - spreading-factor: the spreading factor of Chirp Spread Spectrum modulation > + > +Example: > + > + sx1278@0 { > + compatible = "semtech,sx1278"; > + spi-max-frequency = <15200>; > + reg = <0>; > + clock-frequency = <32000000>; > + minimal-RF-channel = /bits/ 8 <11>; > + maximum-RF-channel = /bits/ 8 <11>; > + }; > diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig > index 303ba4133920..57ed64e55d13 100644 > --- a/drivers/net/ieee802154/Kconfig > +++ b/drivers/net/ieee802154/Kconfig > @@ -104,3 +104,15 @@ config IEEE802154_CA8210_DEBUGFS > exposes a debugfs node for each CA8210 instance which allows > direct use of the Cascoda API, exposing the 802.15.4 MAC > management entities. > + > +config IEEE802154_SX1278 > + tristate "Semtech SX1276/77/78/79 transceiver driver" > + depends on IEEE802154_DRIVERS && MAC802154 > + depends on SPI > + select REGMAP_SPI > + ---help--- > + Say Y here to enable the SX1278 SPI 802.15.4 wireless > + controller. > + > + This driver can also be built as a module. To do so, say M here. > + the module will be called 'sx1278'. > diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile > index 8374bb44a145..634e8e1bfad6 100644 > --- a/drivers/net/ieee802154/Makefile > +++ b/drivers/net/ieee802154/Makefile > @@ -5,3 +5,4 @@ obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o > obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o > obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o > obj-$(CONFIG_IEEE802154_CA8210) += ca8210.o > +obj-$(CONFIG_IEEE802154_SX1278) += sx1278.o > diff --git a/drivers/net/ieee802154/sx1278.c b/drivers/net/ieee802154/sx1278.c > new file mode 100644 > index 000000000000..2018407a0ab7 > --- /dev/null > +++ b/drivers/net/ieee802154/sx1278.c > @@ -0,0 +1,1636 @@ > +/*- > + * Copyright (c) 2017 Jian-Hong, Pan <starnight@g.ncu.edu.tw> > + * > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer, > + * without modification. > + * 2. Redistributions in binary form must reproduce at minimum a disclaimer > + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any > + * redistribution must be conditioned upon including a substantially > + * similar Disclaimer requirement for further binary redistribution. > + * 3. Neither the names of the above-listed copyright holders nor the names > + * of any contributors may be used to endorse or promote products derived > + * from this software without specific prior written permission. > + * > + * Alternatively, this software may be distributed under the terms of the > + * GNU General Public License ("GPL") version 2 as published by the Free > + * Software Foundation. > + * > + * NO WARRANTY > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY > + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL > + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, > + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF > + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS > + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER > + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) > + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF > + * THE POSSIBILITY OF SUCH DAMAGES. > + * > + */ > + > +#include <linux/module.h> > +#include <linux/timer.h> > +#include <linux/device.h> > +#include <linux/acpi.h> > +#include <linux/of_device.h> > +#include <linux/spinlock.h> > +#include <linux/spi/spi.h> > +#include <linux/regmap.h> > +#include <net/mac802154.h> > + > +/*------------------------------ LoRa Functions ------------------------------*/ > + > +#ifndef F_XOSC > +#define F_XOSC 32000000 > +#endif > +static u32 xosc_frq = F_XOSC; > +module_param(xosc_frq, uint, 0000); > +MODULE_PARM_DESC(xosc_frq, "Crystal oscillator frequency of the LoRa chip"); > + > +#define __POW_2_19 0x80000 > + > +#ifndef SX127X_SPRF > +#define SX127X_SPRF 512 > +#endif > +static u32 sprf = SX127X_SPRF; > +module_param(sprf, uint, 0000); > +MODULE_PARM_DESC(sprf, "Spreading factor of Chirp Spread Spectrum modulation"); > + > +#ifndef SX127X_RX_BYTE_TIMEOUT > +#define SX127X_RX_BYTE_TIMEOUT 1023 > +#endif > +static u32 rx_timeout = SX127X_RX_BYTE_TIMEOUT; > +module_param(rx_timeout, uint, 0000); > +MODULE_PARM_DESC(rx_timeout, "RX time-out value as number of symbols"); > + > +/* SX127X Registers addresses */ > +#define SX127X_REG_FIFO 0x00 > +#define SX127X_REG_OP_MODE 0x01 > +#define SX127X_REG_FRF_MSB 0x06 > +#define SX127X_REG_FRF_MID 0x07 > +#define SX127X_REG_FRF_LSB 0x08 > +#define SX127X_REG_PA_CONFIG 0x09 > +#define SX127X_REG_PA_RAMP 0x0A > +#define SX127X_REG_OCP 0x0B > +#define SX127X_REG_LNA 0x0C > +#define SX127X_REG_FIFO_ADDR_PTR 0x0D > +#define SX127X_REG_FIFO_TX_BASE_ADDR 0x0E > +#define SX127X_REG_FIFO_RX_BASE_ADDR 0x0F > +#define SX127X_REG_FIFO_RX_CURRENT_ADDR 0x10 > +#define SX127X_REG_IRQ_FLAGS_MASK 0x11 > +#define SX127X_REG_IRQ_FLAGS 0x12 > +#define SX127X_REG_RX_NB_BYTES 0x13 > +#define SX127X_REG_RX_HEADER_CNT_VALUE_MSB 0x14 > +#define SX127X_REG_RX_HEADER_CNT_VALUE_LSB 0x15 > +#define SX127X_REG_RX_PACKET_CNT_VALUE_MSB 0x16 > +#define SX127X_REG_RX_PACKET_CNT_VALUE_LSB 0x17 > +#define SX127X_REG_MODEM_STAT 0x18 > +#define SX127X_REG_PKT_SNR_VALUE 0x19 > +#define SX127X_REG_PKT_RSSI_VALUE 0x1A > +#define SX127X_REG_RSSI_VALUE 0x1B > +#define SX127X_REG_HOP_CHANNEL 0x1C > +#define SX127X_REG_MODEM_CONFIG1 0x1D > +#define SX127X_REG_MODEM_CONFIG2 0x1E > +#define SX127X_REG_SYMB_TIMEOUT_LSB 0x1F > +#define SX127X_REG_PREAMBLE_MSB 0x20 > +#define SX127X_REG_PREAMBLE_LSB 0x21 > +#define SX127X_REG_PAYLOAD_LENGTH 0x22 > +#define SX127X_REG_MAX_PAYLOAD_LENGTH 0x23 > +#define SX127X_REG_HOP_PERIOD 0x24 > +#define SX127X_REG_FIFO_RX_BYTE_ADDR 0x25 > +#define SX127X_REG_MODEM_CONFIG3 0x26 > +#define SX127X_REG_FEI_MSB 0x28 > +#define SX127X_REG_FEI_MID 0x29 > +#define SX127X_REG_FEI_LSB 0x2A > +#define SX127X_REG_RSSI_WIDEBAND 0x2C > +#define SX127X_REG_DETECT_OPTIMIZE 0x31 > +#define SX127X_REG_INVERT_IRQ 0x33 > +#define SX127X_REG_DETECTION_THRESHOLD 0x37 > +#define SX127X_REG_SYNC_WORD 0x39 > +#define SX127X_REG_VERSION 0x42 > +#define SX127X_REG_TCXO 0x4B > +#define SX127X_REG_PA_DAC 0x4D > +#define SX127X_REG_FORMER_TEMP 0x5B > +#define SX127X_REG_AGC_REF 0x61 > +#define SX127X_REG_AGC_THRESH1 0x62 > +#define SX127X_REG_AGC_THRESH2 0x63 > +#define SX127X_REG_AGC_THRESH3 0x64 > +#define SX127X_REG_PLL 0x70 > +#define SX127X_MAX_REG SX127X_REG_PLL > + > +/* SX127X's operating states in LoRa mode */ > +#define SX127X_SLEEP_MODE 0x00 > +#define SX127X_STANDBY_MODE 0x01 > +#define SX127X_FSTX_MODE 0x02 > +#define SX127X_TX_MODE 0x03 > +#define SX127X_FSRX_MODE 0x04 > +#define SX127X_RXCONTINUOUS_MODE 0x05 > +#define SX127X_RXSINGLE_MODE 0x06 > +#define SX127X_CAD_MODE 0x07 > + > +/* SX127X's IRQ flags in LoRa mode */ > +#define SX127X_FLAG_RXTIMEOUT 0x80 > +#define SX127X_FLAG_RXDONE 0x40 > +#define SX127X_FLAG_PAYLOADCRCERROR 0x20 > +#define SX127X_FLAG_VALIDHEADER 0x10 > +#define SX127X_FLAG_TXDONE 0x08 > +#define SX127X_FLAG_CADDONE 0x04 > +#define SX127X_FLAG_FHSSCHANGECHANNEL 0x02 > +#define SX127X_FLAG_CADDETECTED 0x01 > + > +/* SX127X's IRQ flags' mask for output pins in LoRa mode */ > +#define SX127X_FLAGMASK_RXTIMEOUT 0x80 > +#define SX127X_FLAGMASK_RXDONE 0x40 > +#define SX127X_FLAGMASK_PAYLOADCRCERROR 0x20 > +#define SX127X_FLAGMASK_VALIDHEADER 0x10 > +#define SX127X_FLAGMASK_TXDONE 0x08 > +#define SX127X_FLAGMASK_CADDONE 0x04 > +#define SX127X_FLAGMASK_FHSSCHANGECHANNEL 0x02 > +#define SX127X_FLAGMASK_CADDETECTED 0x01 > + > +/* SX127X's RX/TX FIFO base address */ > +#define SX127X_FIFO_RX_BASE_ADDRESS 0x00 > +#define SX127X_FIFO_TX_BASE_ADDRESS 0x80 > + > +struct sx1278_phy { > + struct ieee802154_hw *hw; > + struct regmap *map; > + > + bool suspended; > + u8 opmode; > + struct timer_list timer; > + struct work_struct irqwork; > + /* Lock the RX and TX actions. */ > + spinlock_t buf_lock; > + struct sk_buff *tx_buf; > + u8 tx_delay; > + bool one_to_be_sent; > + bool post_tx_done; > + bool is_busy; > +}; > + > +/** > + * sx127X_read_version - Get LoRa device's chip version > + * @map: the device as a regmap to communicate with > + * > + * Return: Positive / negtive values for version code / failed > + * Version code: bits 7-4 full version number, > + * bits 3-0 metal mask revision number > + */ > +int > +sx127X_read_version(struct regmap *map) > +{ > + u8 v; > + int status; > + > + status = regmap_raw_read(map, SX127X_REG_VERSION, &v, 1); > + > + if ((status == 0) && (v > 0) && (v < 0xFF)) > + status = v; > + else > + status = -ENODEV; > + > + return status; > +} > + > +/** > + * sx127X_get_mode - Get LoRa device's mode register > + * @map: the device as a regmap to communicate with > + * > + * Return: LoRa device's register value > + */ > +u8 > +sx127X_get_mode(struct regmap *map) > +{ > + u8 op_mode; > + > + /* Get original OP Mode register. */ > + regmap_raw_read(map, SX127X_REG_OP_MODE, &op_mode, 1); > + > + return op_mode; > +} > + > +/** > + * sx127X_set_state - Set LoRa device's operating state > + * @map: the device as a regmap to communicate with > + * @st: LoRa device's operating state going to be assigned > + */ > +void > +sx127X_set_state(struct regmap *map, u8 st) > +{ > + u8 op_mode; > + > + /* Get original OP Mode register. */ > + op_mode = sx127X_get_mode(map); > + /* Set device to designated state. */ > + op_mode = (op_mode & 0xF8) | (st & 0x07); > + regmap_raw_write(map, SX127X_REG_OP_MODE, &op_mode, 1); > +} > + > +/** > + * sx127X_get_state - Get LoRa device's operating state > + * @map: the device as a regmap to communicate with > + * > + * Return: LoRa device's operating state > + */ > +u8 > +sx127X_get_state(struct regmap *map) > +{ > + u8 op_mode; > + > + op_mode = sx127X_get_mode(map) & 0x07; > + > + return op_mode; > +} > + > +/** > + * sx127X_set_lorafrq - Set RF frequency > + * @map: the device as a regmap to communicate with > + * @fr: RF frequency going to be assigned in Hz > + */ > +void > +sx127X_set_lorafrq(struct regmap *map, u32 fr) > +{ > + u64 frt64; > + u32 frt; > + u8 buf[3]; > + u8 i; > + u32 f_xosc; > + > +#ifdef CONFIG_OF > + /* Set the LoRa module's crystal oscillator's clock if OF is defined. */ > + struct device_node *of_node = (regmap_get_device(map))->of_node; > + > + if (of_property_read_u32(of_node, "clock-frequency", &f_xosc)) > + f_xosc = xosc_frq; > +#else > + f_xosc = xosc_frq; > +#endif > + > + frt64 = (uint64_t)fr * (uint64_t)__POW_2_19; > + do_div(frt64, f_xosc); > + frt = frt64; > + > + for (i = 2; i >= 0; i--) { > + buf[i] = frt % 256; > + frt = frt >> 8; > + } > + > + regmap_raw_write(map, SX127X_REG_FRF_MSB, buf, 3); > +} > + > +/** > + * sx127X_get_lorafrq - Get RF frequency > + * @map: the device as a regmap to communicate with > + * > + * Return: RF frequency in Hz > + */ > +u32 > +sx127X_get_lorafrq(struct regmap *map) > +{ > + u64 frt = 0; > + u8 buf[3]; > + u8 i; > + int status; > + u32 fr; > + u32 f_xosc; > + > +#ifdef CONFIG_OF > + /* Set the LoRa module's crystal oscillator's clock if OF is defined. */ > + struct device_node *of_node = (regmap_get_device(map))->of_node; > + > + if (of_property_read_u32(of_node, "clock-frequency", &f_xosc)) > + f_xosc = xosc_frq; > +#else > + f_xosc = xosc_frq; > +#endif > + > + status = regmap_raw_read(map, SX127X_REG_FRF_MSB, buf, 3); > + if (status < 0) > + return 0.0; > + > + for (i = 0; i <= 2; i++) > + frt = frt * 256 + buf[i]; > + > + fr = frt * f_xosc / __POW_2_19; > + > + return fr; > +} > + > +/** > + * sx127X_set_lorapower - Set RF output power > + * @map: the device as a regmap to communicate with > + * @pout: RF output power going to be assigned in dbm > + */ > +void > +sx127X_set_lorapower(struct regmap *map, s32 pout) > +{ > + u8 pacf; > + u8 boost; > + u8 output_power; > + s32 pmax; > + > + if (pout > 14) { > + /* Pout > 14dbm */ > + boost = 1; > + pmax = 7; > + output_power = pout - 2; > + } else if (pout < 0) { > + /* Pout < 0dbm */ > + boost = 0; > + pmax = 2; > + output_power = 3 + pout; > + } else { > + /* 0dbm <= Pout <= 14dbm */ > + boost = 0; > + pmax = 7; > + output_power = pout; > + } > + > + pacf = (boost << 7) | (pmax << 4) | (output_power); > + regmap_raw_write(map, SX127X_REG_PA_CONFIG, &pacf, 1); > +} > + > +/** > + * sx127X_get_lorapower - Get RF output power > + * @map: the device as a regmap to communicate with > + * > + * Return: RF output power in dbm > + */ > +s32 > +sx127X_get_lorapower(struct regmap *map) > +{ > + u8 pac; > + u8 boost; > + s32 output_power; > + s32 pmax; > + s32 pout; > + > + regmap_raw_read(map, SX127X_REG_PA_CONFIG, &pac, 1); > + boost = (pac & 0x80) >> 7; > + output_power = pac & 0x0F; > + if (boost) { > + pout = 2 + output_power; > + } else { > + /* Power max should be pmax/10. It is 10 times for now. */ > + pmax = (108 + 6 * ((pac & 0x70) >> 4)); > + pout = (pmax - (150 - output_power * 10)) / 10; > + } > + > + return pout; > +} > + > +/** > + * sx127X_dbm2mbm - dbm to mbm unit conversion > + * @dbm: the value in dbm > + * > + * Return: the value in mbm > + */ > +#define sx127X_dbm2mbm(dbm) (dbm * 100) > + > +/** > + * sx127X_mbm2dbm - mbm to dbm unit conversion > + * @mbm: the value in mbm > + * > + * Return: the value in dbm > + */ > +#define sx127X_mbm2dbm(mbm) (mbm / 100) > + > +s8 lna_gain[] = { > + 0, > + -6, > + -12, > + -24, > + -26, > + -48 > +}; > + > +/** > + * sx127X_set_loralna - Set RF LNA gain > + * @map: the device as a regmap to communicate with > + * @db: RF LNA gain going to be assigned in db > + */ > +void > +sx127X_set_loralna(struct regmap *map, s32 db) > +{ > + u8 i, g; > + u8 lnacf; > + > + for (i = 0; i < 5; i++) { > + if (lna_gain[i] <= db) > + break; > + } > + g = i + 1; > + > + regmap_raw_read(map, SX127X_REG_LNA, &lnacf, 1); > + lnacf = (lnacf & 0x1F) | (g << 5); > + regmap_raw_write(map, SX127X_REG_LNA, &lnacf, 1); > +} > + > +/** > + * sx127X_get_loralna - Get RF LNA gain > + * @map: the device as a regmap to communicate with > + * > + * Return: RF LNA gain db > + */ > +s32 > +sx127X_get_loralna(struct regmap *map) > +{ > + s32 db; > + s8 i, g; > + u8 lnacf; > + > + regmap_raw_read(map, SX127X_REG_LNA, &lnacf, 1); > + g = (lnacf >> 5); > + i = g - 1; > + db = lna_gain[i]; > + > + return db; > +} > + > +/** > + * sx127X_set_loralnaagc - Set RF LNA go with auto gain control or manual > + * @map: the device as a regmap to communicate with > + * @yesno: 1 / 0 for auto gain control / manual > + */ > +void > +sx127X_set_loralnaagc(struct regmap *map, s32 yesno) > +{ > + u8 mcf3; > + > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG3, &mcf3, 1); > + mcf3 = (yesno) ? (mcf3 | 0x04) : (mcf3 & (~0x04)); > + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG3, &mcf3, 1); > +} > + > +/** > + * sx127X_get_loraallflag - Get all of the LoRa device IRQ flags' current state > + * @map: the device as a regmap to communicate with > + * > + * Return: All of the LoRa device's IRQ flags' current state in a byte > + */ > +u8 > +sx127X_get_loraallflag(struct regmap *map) > +{ > + u8 flags; > + > + regmap_raw_read(map, SX127X_REG_IRQ_FLAGS, &flags, 1); > + > + return flags; > +} > + > +/** > + * sx127X_get_loraallflag - Get interested LoRa device IRQ flag's current state > + * @map: the device as a regmap to communicate with > + * @f: the interested LoRa device's IRQ flag > + * > + * Return: The interested LoRa device's IRQ flag's current state in a byte > + */ > +#define sx127X_get_loraflag(map, f) (sx127X_get_loraallflag(map) & (f)) > + > +/** > + * sx127X_clear_loraflag - Clear designated LoRa device's IRQ flag > + * @map: the device as a regmap to communicate with > + * @f: flags going to be cleared > + */ > +void > +sx127X_clear_loraflag(struct regmap *map, u8 f) > +{ > + u8 flag; > + > + /* Get oiginal flag. */ > + flag = sx127X_get_loraallflag(map); > + /* Set the designated bits of the flag. */ > + flag |= f; > + regmap_raw_write(map, SX127X_REG_IRQ_FLAGS, &flag, 1); > +} > + > +/** > + * sx127X_clear_loraallflag - Clear designated LoRa device's all IRQ flags > + * @map: the device as a regmap to communicate with > + */ > +#define sx127X_clear_loraallflag(spi) sx127X_clear_loraflag(spi, 0xFF) > + > +/** > + * sx127X_set_lorasprf - Set the RF modulation's spreading factor > + * @map: the device as a regmap to communicate with > + * @c_s: Spreading factor in chips / symbol > + */ > +void > +sx127X_set_lorasprf(struct regmap *map, u32 c_s) > +{ > + u8 sf; > + u8 mcf2; > + > + for (sf = 6; sf < 12; sf++) { > + if (c_s == ((u32)1 << sf)) > + break; > + } > + > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, &mcf2, 1); > + mcf2 = (mcf2 & 0x0F) | (sf << 4); > + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG2, &mcf2, 1); > +} > + > +/** > + * sx127X_get_lorasprf - Get the RF modulation's spreading factor > + * @map: the device as a regmap to communicate with > + * > + * Return: Spreading factor in chips / symbol > + */ > +u32 > +sx127X_get_lorasprf(struct regmap *map) > +{ > + u8 sf; > + u32 c_s; > + > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, &sf, 1); > + sf = sf >> 4; > + c_s = 1 << sf; > + > + return c_s; > +} > + > +const u32 hz[] = { > + 7800, > + 10400, > + 15600, > + 20800, > + 31250, > + 41700, > + 62500, > + 125000, > + 250000, > + 500000 > +}; > + > +/** > + * sx127X_set_lorabw - Set RF bandwidth > + * @map: the device as a regmap to communicate with > + * @bw: RF bandwidth going to be assigned in Hz > + */ > +void > +sx127X_set_lorabw(struct regmap *map, u32 bw) > +{ > + u8 i; > + u8 mcf1; > + > + for (i = 0; i < 9; i++) { > + if (hz[i] >= bw) > + break; > + } > + > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); > + mcf1 = (mcf1 & 0x0F) | (i << 4); > + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); > +} > + > +/** > + * sx127X_get_lorabw - Get RF bandwidth > + * @map: the device as a regmap to communicate with > + * > + * Return: RF bandwidth in Hz > + */ > +u32 > +sx127X_get_lorabw(struct regmap *map) > +{ > + u8 mcf1; > + u8 bw; > + > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); > + bw = mcf1 >> 4; > + > + return hz[bw]; > +} > + > +/** > + * sx127X_set_loracr - Set LoRa package's coding rate > + * @map: the device as a regmap to communicate with > + * @cr: Coding rate going to be assigned in a byte > + * high 4 bits / low 4 bits: numerator / denominator > + */ > +void > +sx127X_set_loracr(struct regmap *map, u8 cr) > +{ > + u8 mcf1; > + > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); > + mcf1 = (mcf1 & 0x0E) | (((cr & 0xF) - 4) << 1); > + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); > +} > + > +/** > + * sx127X_get_loracr - Get LoRa package's coding rate > + * @map: the device as a regmap to communicate with > + * > + * Return: Coding rate in a byte > + * high 4 bits / low 4 bits: numerator / denominator > + */ > +u8 > +sx127X_get_loracr(struct regmap *map) > +{ > + u8 mcf1; > + u8 cr; /* ex: 0x45 represents cr=4/5 */ > + > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); > + cr = 0x40 + ((mcf1 & 0x0E) >> 1) + 4; > + > + return cr; > +} > + > +/** > + * sx127X_set_loraimplicit - Set LoRa packages with Explicit / Implicit Header > + * @map: the device as a regmap to communicate with > + * @yesno: 1 / 0 for Implicit Header Mode / Explicit Header Mode > + */ > +void > +sx127X_set_loraimplicit(struct regmap *map, u8 yesno) > +{ > + u8 mcf1; > + > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); > + mcf1 = (yesno) ? (mcf1 | 0x01) : (mcf1 & 0xFE); > + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); > +} > + > +/** > + * sx127X_set_lorarxbytetimeout - Set RX operation time-out in terms of symbols > + * @map: the device as a regmap to communicate with > + * @n: Time-out in terms of symbols (bytes) going to be assigned > + */ > +void > +sx127X_set_lorarxbytetimeout(struct regmap *map, u32 n) > +{ > + u8 buf[2]; > + u8 mcf2; > + > + if (n < 1) > + n = 1; > + if (n > 1023) > + n = 1023; > + > + /* Read original Modem config 2. */ > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, &mcf2, 1); > + > + /* LSB */ > + buf[1] = n % 256; > + /* MSB */ > + buf[0] = (mcf2 & 0xFC) | (n >> 8); > + > + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG2, buf, 2); > +} > + > +/** > + * sx127X_set_lorarxtimeout - Set RX operation time-out seconds > + * @map: the device as a regmap to communicate with > + * @ms: The RX time-out time in ms > + */ > +void > +sx127X_set_lorarxtimeout(struct regmap *map, u32 ms) > +{ > + u32 n; > + > + n = ms * sx127X_get_lorabw(map) / (sx127X_get_lorasprf(map) * 1000); > + > + sx127X_set_lorarxbytetimeout(map, n); > +} > + > +/** > + * sx127X_get_lorarxbytetimeout - Get RX operation time-out in terms of symbols > + * @map: the device as a regmap to communicate with > + * > + * Return: Time-out in terms of symbols (bytes) > + */ > +u32 > +sx127X_get_lorarxbytetimeout(struct regmap *map) > +{ > + u32 n; > + u8 buf[2]; > + > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, buf, 2); > + > + n = (buf[0] & 0x03) * 256 + buf[1]; > + > + return n; > +} > + > +/** > + * sx127X_get_lorarxtimeout - Get RX operation time-out seconds > + * @map: the device as a regmap to communicate with > + * > + * Return: The RX time-out time in ms > + */ > +u32 > +sx127X_get_lorarxtimeout(struct regmap *map) > +{ > + u32 ms; > + > + ms = 1000 * sx127X_get_lorarxbytetimeout(map) * > + sx127X_get_lorasprf(map) / sx127X_get_lorabw(map); > + > + return ms; > +} > + > +/** > + * sx127X_set_loramaxrxbuff - Maximum payload length in LoRa packet > + * @map: the device as a regmap to communicate with > + * @len: the max payload length going to be assigned in bytes > + */ > +void > +sx127X_set_loramaxrxbuff(struct regmap *map, u8 len) > +{ > + regmap_raw_write(map, SX127X_REG_MAX_PAYLOAD_LENGTH, &len, 1); > +} > + > +/** > + * sx127X_get_loralastpktpayloadlen - Get the RX last packet payload length > + * @map: the device as a regmap to communicate with > + * > + * Return: the actual RX last packet payload length in bytes > + */ > +u8 > +sx127X_get_loralastpktpayloadlen(struct regmap *map) > +{ > + u8 len; > + > + regmap_raw_read(map, SX127X_REG_RX_NB_BYTES, &len, 1); > + > + return len; > +} > + > +/** > + * sx127X_readloradata - Read data from LoRa device (read RX FIFO) > + * @map: the device as a regmap to communicate with > + * @buf: buffer going to be read data into > + * @len: the length of the data going to be read in bytes > + * > + * Return: Positive / negtive values for the actual data length read from > + * the LoRa device in bytes / failed > + */ > +ssize_t > +sx127X_readloradata(struct regmap *map, u8 *buf, size_t len) > +{ > + u8 start_adr; > + int ret; > + > + /* Set chip FIFO pointer to FIFO last packet address. */ > + start_adr = SX127X_FIFO_RX_BASE_ADDRESS; > + regmap_raw_write(map, SX127X_REG_FIFO_ADDR_PTR, &start_adr, 1); > + > + /* Read LoRa packet payload. */ > + len = (len <= IEEE802154_MTU) ? len : IEEE802154_MTU; > + ret = regmap_raw_read(map, SX127X_REG_FIFO, buf, len); > + > + return (ret >= 0) ? len : ret; > +} > + > +/** > + * sx127X_sendloradata - Send data through LoRa device (write TX FIFO) > + * @rm: the device as a regmap to communicate with > + * @buf: buffer going to be send > + * @len: the length of the buffer in bytes > + * > + * Return: the actual length written into the LoRa device in bytes > + */ > +size_t > +sx127X_sendloradata(struct regmap *map, u8 *buf, size_t len) > +{ > + u8 base_adr; > + u8 blen; > + > + /* Set chip FIFO pointer to FIFO TX base. */ > + base_adr = SX127X_FIFO_TX_BASE_ADDRESS; > + regmap_raw_write(map, SX127X_REG_FIFO_ADDR_PTR, &base_adr, 1); > + > + /* Write payload synchronously to fill the FIFO of the chip. */ > + blen = (len <= IEEE802154_MTU) ? len : IEEE802154_MTU; > + regmap_raw_write(map, SX127X_REG_FIFO, buf, blen); > + > + /* Set the FIFO payload length. */ > + regmap_raw_write(map, SX127X_REG_PAYLOAD_LENGTH, &blen, 1); > + > + return blen; > +} > + > +/** > + * sx127X_get_loralastpktsnr - Get last LoRa packet's SNR > + * @map: the device as a regmap to communicate with > + * > + * Return: the last LoRa packet's SNR in db > + */ > +s32 > +sx127X_get_loralastpktsnr(struct regmap *map) > +{ > + s32 db; > + s8 snr; > + > + regmap_raw_read(map, SX127X_REG_PKT_SNR_VALUE, &snr, 1); > + db = snr / 4; > + > + return db; > +} > + > +/** > + * sx127X_get_loralastpktrssi - Get last LoRa packet's SNR > + * @map: the device as a regmap to communicate with > + * > + * Return: the last LoRa packet's RSSI in dbm > + */ > +s32 > +sx127X_get_loralastpktrssi(struct regmap *map) > +{ > + s32 dbm; > + u8 lhf; > + u8 rssi; > + s8 snr; > + > + /* Get LoRa is in high or low frequency mode. */ > + lhf = sx127X_get_mode(map) & 0x08; > + /* Get RSSI value. */ > + regmap_raw_read(map, SX127X_REG_PKT_RSSI_VALUE, &rssi, 1); > + dbm = (lhf) ? -164 + rssi : -157 + rssi; > + > + /* Adjust to correct the last packet RSSI if SNR < 0. */ > + regmap_raw_read(map, SX127X_REG_PKT_SNR_VALUE, &snr, 1); > + if (snr < 0) > + dbm += snr / 4; > + > + return dbm; > +} > + > +/** > + * sx127X_get_lorarssi - Get current RSSI value > + * @map: the device as a regmap to communicate with > + * > + * Return: the current RSSI in dbm > + */ > +s32 > +sx127X_get_lorarssi(struct regmap *map) > +{ > + s32 dbm; > + u8 lhf; > + u8 rssi; > + > + /* Get LoRa is in high or low frequency mode. */ > + lhf = sx127X_get_mode(map) & 0x08; > + /* Get RSSI value. */ > + regmap_raw_read(map, SX127X_REG_RSSI_VALUE, &rssi, 1); > + dbm = (lhf) ? -164 + rssi : -157 + rssi; > + > + return dbm; > +} > + > +/** > + * sx127X_set_lorapreamblelen - Set LoRa preamble length > + * @map: the device as a regmap to communicate with > + * @len: the preamble length going to be assigned > + */ > +void > +sx127X_set_lorapreamblelen(struct regmap *map, u32 len) > +{ > + u8 pl[2]; > + > + pl[1] = len % 256; > + pl[0] = (len >> 8) % 256; > + > + regmap_raw_write(map, SX127X_REG_PREAMBLE_MSB, pl, 2); > +} > + > +/** > + * sx127X_get_lorapreamblelen - Get LoRa preamble length > + * @map: the device as a regmap to communicate with > + * > + * Return: length of the LoRa preamble > + */ > +u32 > +sx127X_get_lorapreamblelen(struct regmap *map) > +{ > + u8 pl[2]; > + u32 len; > + > + regmap_raw_read(map, SX127X_REG_PREAMBLE_MSB, pl, 2); > + len = pl[0] * 256 + pl[1]; > + > + return len; > +} > + > +/** > + * sx127X_set_loracrc - Enable CRC generation and check on received payload > + * @map: the device as a regmap to communicate with > + * @yesno: 1 / 0 for check / not check > + */ > +void > +sx127X_set_loracrc(struct regmap *map, u8 yesno) > +{ > + u8 mcf2; > + > + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, &mcf2, 1); > + mcf2 = (yesno) ? mcf2 | (1 << 2) : mcf2 & (~(1 << 2)); > + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG2, &mcf2, 1); > +} > + > +/** > + * sx127X_set_boost - Set RF power amplifier boost in normal output range > + * @map: the device as a regmap to communicate with > + * @yesno: 1 / 0 for boost / not boost > + */ > +void > +sx127X_set_boost(struct regmap *map, u8 yesno) > +{ > + u8 pacf; > + > + regmap_raw_read(map, SX127X_REG_PA_CONFIG, &pacf, 1); > + pacf = (yesno) ? pacf | (1 << 7) : pacf & (~(1 << 7)); > + regmap_raw_write(map, SX127X_REG_PA_CONFIG, &pacf, 1); > +} > + > +/** > + * sx127X_start_loramode - Start the device and set it in LoRa mode > + * @map: the device as a regmap to communicate with > + */ > +void > +sx127X_start_loramode(struct regmap *map) > +{ > + u8 op_mode; > + u8 base_adr; > +#ifdef CONFIG_OF > + struct device_node *of_node = (regmap_get_device(map))->of_node; > +#endif > + > + /* Get original OP Mode register. */ > + op_mode = sx127X_get_mode(map); > + dev_dbg(regmap_get_device(map), > + "the original OP mode is 0x%X\n", op_mode); > + > + /* Set device to sleep state. */ > + sx127X_set_state(map, SX127X_SLEEP_MODE); > + /* Set device to LoRa mode. */ > + op_mode = sx127X_get_mode(map); > + op_mode = op_mode | 0x80; > + regmap_raw_write(map, SX127X_REG_OP_MODE, &op_mode, 1); > + /* Set device to standby state. */ > + sx127X_set_state(map, SX127X_STANDBY_MODE); > + op_mode = sx127X_get_mode(map); > + dev_dbg(regmap_get_device(map), > + "the current OP mode is 0x%X\n", op_mode); > + > + /* Set LoRa in explicit header mode. */ > + sx127X_set_loraimplicit(map, 0); > + > + /* Set chip FIFO RX base. */ > + base_adr = SX127X_FIFO_RX_BASE_ADDRESS; > + regmap_raw_write(map, SX127X_REG_FIFO_RX_BASE_ADDR, &base_adr, 1); > + /* Set chip FIFO TX base. */ > + base_adr = SX127X_FIFO_TX_BASE_ADDRESS; > + regmap_raw_write(map, SX127X_REG_FIFO_TX_BASE_ADDR, &base_adr, 1); > + > + /* Set the CSS spreading factor. */ > +#ifdef CONFIG_OF > + of_property_read_u32(of_node, "spreading-factor", &sprf); > +#endif > + sx127X_set_lorasprf(map, sprf); > + > + /* Set RX time-out value. */ > + sx127X_set_lorarxbytetimeout(map, rx_timeout); > + > + /* Clear all of the IRQ flags. */ > + sx127X_clear_loraallflag(map); > + /* Set chip to RX state waiting for receiving. */ > + sx127X_set_state(map, SX127X_RXSINGLE_MODE); > +} > + > +/** > + * init_sx127x - Initial the SX127X device > + * @map: the device as a regmap to communicate with > + * > + * Return: 0 / negtive values for success / failed > + */ > +int > +init_sx127x(struct regmap *map) > +{ > + int v; > +#ifdef DEBUG > + u8 fv, mv; > +#endif > + > + dev_dbg(regmap_get_device(map), "init sx127X\n"); > + > + v = sx127X_read_version(map); > + if (v > 0) { > +#ifdef DEBUG > + fv = (v >> 4) & 0xF; > + mv = v & 0xF; > + dev_dbg(regmap_get_device(map), "chip version %d.%d\n", fv, mv); > +#endif > + return 0; > + } else { > + return -ENODEV; > + } > +} > + > +/*---------------------- SX1278 IEEE 802.15.4 Functions ----------------------*/ > + > +/* LoRa device's sensitivity in dbm. */ > +#ifndef SX1278_IEEE_SENSITIVITY > +#define SX1278_IEEE_SENSITIVITY (-148) > +#endif > +static s32 sensitivity = SX1278_IEEE_SENSITIVITY; > +module_param(sensitivity, int, 0000); > +MODULE_PARM_DESC(sensitivity, "RF receiver's sensitivity"); > + > +#define SX1278_IEEE_ENERGY_RANGE (-sensitivity) > + > +static int > +sx1278_ieee_ed(struct ieee802154_hw *hw, u8 *level) > +{ > + struct sx1278_phy *phy = hw->priv; > + s32 rssi; > + s32 range = SX1278_IEEE_ENERGY_RANGE - 10; > + > + dev_dbg(regmap_get_device(phy->map), "%s\n", __func__); > + > + /* ED: IEEE 802.15.4-2011 8.2.5 Recevier ED. */ > + rssi = sx127X_get_lorarssi(phy->map); > + if (rssi < (sensitivity + 10)) > + *level = 0; > + else if (rssi >= 0) > + *level = 255; > + else > + *level = ((s32)255 * (rssi + range) / range) % 255; > + > + return 0; > +} > + > +#ifndef SX1278_IEEE_CHANNEL_MIN > +#define SX1278_IEEE_CHANNEL_MIN 11 > +#endif > +static u8 channel_min = SX1278_IEEE_CHANNEL_MIN; > +module_param(channel_min, byte, 0000); > +MODULE_PARM_DESC(channel_min, "Minimal channel number"); > + > +#ifndef SX1278_IEEE_CHANNEL_MAX > +#define SX1278_IEEE_CHANNEL_MAX 11 > +#endif > +static u8 channel_max = SX1278_IEEE_CHANNEL_MAX; > +module_param(channel_max, byte, 0000); > +MODULE_PARM_DESC(channel_max, "Maximum channel number"); > + > +#ifndef SX1278_IEEE_CENTER_CARRIER_FRQ > +#define SX1278_IEEE_CENTER_CARRIER_FRQ 434000000 > +#endif > +static u32 carrier_frq = SX1278_IEEE_CENTER_CARRIER_FRQ; > +module_param(carrier_frq, uint, 0000); > +MODULE_PARM_DESC(carrier_frq, "Center carrier frequency in Hz"); > + > +#ifndef SX1278_IEEE_BANDWIDTH > +#define SX1278_IEEE_BANDWIDTH 500000 > +#endif > +static u32 bandwidth = SX1278_IEEE_BANDWIDTH; > +module_param(bandwidth, uint, 0000); > +MODULE_PARM_DESC(bandwidth, "Bandwidth in Hz"); > + > +struct rf_frq { > + u32 carrier; > + u32 bw; > + u8 ch_min; > + u8 ch_max; > +}; > + > +void > +sx1278_ieee_get_rf_config(struct ieee802154_hw *hw, struct rf_frq *rf) > +{ > +#ifdef CONFIG_OF > + struct sx1278_phy *phy = hw->priv; > + struct device_node *of_node = (regmap_get_device(phy->map))->of_node; > + > + /* Set the LoRa chip's center carrier frequency. */ > + if (of_property_read_u32(of_node, "center-carrier-frq", &rf->carrier)) > + rf->carrier = carrier_frq; > + > + /* Set the LoRa chip's RF bandwidth. */ > + if (of_property_read_u32(of_node, "rf-bandwidth", &rf->carrier)) > + rf->bw = bandwidth; > + > + /* Set the LoRa chip's min & max RF channel if OF is defined. */ > + if (of_property_read_u8(of_node, "minimal-RF-channel", &rf->ch_min)) > + rf->ch_min = channel_min; > + > + if (of_property_read_u8(of_node, "maximum-RF-channel", &rf->ch_max)) > + rf->ch_max = channel_max; > +#else > + rf->carrier = carrier_frq; > + rf->bw = bandwidth; > + rf->ch_min = channel_min; > + rf->ch_max = channel_max; > +#endif > +} > + > +static int > +sx1278_ieee_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) > +{ > + struct sx1278_phy *phy = hw->priv; > + struct rf_frq rf; > + u32 fr; > + s8 d; > + > + dev_dbg(regmap_get_device(phy->map), > + "%s channel: %u", __func__, channel); > + > + sx1278_ieee_get_rf_config(hw, &rf); > + > + if (channel < rf.ch_min) > + channel = rf.ch_min; > + else if (channel > rf.ch_max) > + channel = rf.ch_max; > + > + d = channel - (rf.ch_min + rf.ch_max) / 2; > + fr = rf.carrier + d * rf.bw; > + > + sx127X_set_lorafrq(phy->map, fr); > + > + return 0; > +} > + > +/* in mbm */ > +s32 sx1278_powers[] = { > + -200, -100, 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, > + 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300}; > + > +static int > +sx1278_ieee_set_txpower(struct ieee802154_hw *hw, s32 mbm) > +{ > + struct sx1278_phy *phy = hw->priv; > + s32 dbm = sx127X_mbm2dbm(mbm); > + > + dev_dbg(regmap_get_device(phy->map), > + "%s TX power: %d mbm", __func__, mbm); > + > + sx127X_set_lorapower(phy->map, dbm); > + > + return 0; > +} > + > +int > +sx1278_ieee_rx(struct ieee802154_hw *hw) > +{ > + struct sx1278_phy *phy = hw->priv; > + bool do_rx; > + > + dev_dbg(regmap_get_device(phy->map), "%s\n", __func__); > + > + spin_lock(&phy->buf_lock); > + if (!phy->is_busy) { > + phy->is_busy = true; > + do_rx = true; > + } else { > + do_rx = false; > + } > + spin_unlock(&phy->buf_lock); > + > + if (do_rx) { > + sx127X_set_state(phy->map, SX127X_RXSINGLE_MODE); > + return 0; > + } else { > + return -EBUSY; > + } > +} > + > +static int > +sx1278_ieee_rx_complete(struct ieee802154_hw *hw) > +{ > + struct sx1278_phy *phy = hw->priv; > + struct sk_buff *skb; > + u8 len; > + u8 lqi; > + s32 rssi; > + s32 range = SX1278_IEEE_ENERGY_RANGE; > + int err; > + > + skb = dev_alloc_skb(IEEE802154_MTU); > + if (!skb) { > + err = -ENOMEM; > + goto sx1278_ieee_rx_err; > + } > + > + len = sx127X_get_loralastpktpayloadlen(phy->map); > + sx127X_readloradata(phy->map, skb_put(skb, len), len); > + > + /* LQI: IEEE 802.15.4-2011 8.2.6 Link quality indicator. */ > + rssi = sx127X_get_loralastpktrssi(phy->map); > + rssi = (rssi > 0) ? 0 : rssi; > + lqi = ((s32)255 * (rssi + range) / range) % 255; > + > + ieee802154_rx_irqsafe(hw, skb, lqi); > + > + spin_lock(&phy->buf_lock); > + phy->is_busy = false; > + spin_unlock(&phy->buf_lock); > + > + dev_dbg(regmap_get_device(phy->map), > + "%s: len=%u LQI=%u\n", __func__, len, lqi); > + > + return 0; > + > +sx1278_ieee_rx_err: > + return err; > +} > + > +int > +sx1278_ieee_tx(struct ieee802154_hw *hw) > +{ > + struct sx1278_phy *phy = hw->priv; > + struct sk_buff *tx_buf = phy->tx_buf; > + bool do_tx = false; > + > + dev_dbg(regmap_get_device(phy->map), > + "%s: len=%u\n", __func__, tx_buf->len); > + > + if (!phy->post_tx_done) { > + sx127X_sendloradata(phy->map, tx_buf->data, tx_buf->len); > + phy->post_tx_done = true; > + } > + > + spin_lock(&phy->buf_lock); > + if (!phy->is_busy) { > + phy->is_busy = true; > + do_tx = true; > + phy->one_to_be_sent = false; > + } > + spin_unlock(&phy->buf_lock); > + > + if (do_tx) { > + /* Set chip as TX state and transfer the data in FIFO. */ > + phy->opmode = (phy->opmode & 0xF8) | SX127X_TX_MODE; > + regmap_write_async(phy->map, SX127X_REG_OP_MODE, phy->opmode); > + return 0; > + } else { > + return -EBUSY; > + } > +} > + > +static int > +sx1278_ieee_tx_complete(struct ieee802154_hw *hw) > +{ > + struct sx1278_phy *phy = hw->priv; > + struct sk_buff *skb = phy->tx_buf; > + > + dev_dbg(regmap_get_device(phy->map), "%s\n", __func__); > + > + ieee802154_xmit_complete(hw, skb, false); > + > + spin_lock(&phy->buf_lock); > + phy->is_busy = false; > + phy->tx_buf = NULL; > + spin_unlock(&phy->buf_lock); > + > + return 0; > +} > + > +static int > +sx1278_ieee_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) > +{ > + struct sx1278_phy *phy = hw->priv; > + int ret; > + > + dev_dbg(regmap_get_device(phy->map), "%s\n", __func__); > + > + WARN_ON(phy->suspended); > + > + spin_lock(&phy->buf_lock); > + if (phy->tx_buf) { > + ret = -EBUSY; > + } else { > + phy->tx_buf = skb; > + phy->one_to_be_sent = true; > + phy->post_tx_done = false; > + ret = 0; > + } > + spin_unlock(&phy->buf_lock); > + > + return ret; > +} > + > +static int > +sx1278_ieee_start(struct ieee802154_hw *hw) > +{ > + struct sx1278_phy *phy = hw->priv; > + > + dev_dbg(regmap_get_device(phy->map), "interface up\n"); > + > + phy->suspended = false; > + sx127X_start_loramode(phy->map); > + phy->opmode = sx127X_get_mode(phy->map); > + add_timer(&phy->timer); > + > + return 0; > +} > + > +static void > +sx1278_ieee_stop(struct ieee802154_hw *hw) > +{ > + struct sx1278_phy *phy = hw->priv; > + > + dev_dbg(regmap_get_device(phy->map), "interface down\n"); > + > + phy->suspended = true; > + del_timer(&phy->timer); > + sx127X_set_state(phy->map, SX127X_SLEEP_MODE); > +} > + > +static int > +sx1278_ieee_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) > +{ > + return 0; > +} > + > +void > +sx1278_ieee_statemachine(struct ieee802154_hw *hw) > +{ > + struct sx1278_phy *phy = hw->priv; > + u8 flags; > + u8 state; > + bool do_next_rx = false; > + > + flags = sx127X_get_loraallflag(phy->map); > + state = sx127X_get_state(phy->map); > + > + if (flags & (SX127X_FLAG_RXTIMEOUT | SX127X_FLAG_PAYLOADCRCERROR)) { > + sx127X_clear_loraflag(phy->map, SX127X_FLAG_RXTIMEOUT > + | SX127X_FLAG_PAYLOADCRCERROR > + | SX127X_FLAG_RXDONE); > + spin_lock(&phy->buf_lock); > + phy->is_busy = false; > + spin_unlock(&phy->buf_lock); > + do_next_rx = true; > + } else if (flags & SX127X_FLAG_RXDONE) { > + sx1278_ieee_rx_complete(phy->hw); > + sx127X_clear_loraflag(phy->map, SX127X_FLAG_RXDONE); > + do_next_rx = true; > + } > + > + if (flags & SX127X_FLAG_TXDONE) { > + sx1278_ieee_tx_complete(phy->hw); > + sx127X_clear_loraflag(phy->map, SX127X_FLAG_TXDONE); > + phy->tx_delay = 10; > + do_next_rx = true; > + } > + > + if (phy->one_to_be_sent && > + (state == SX127X_STANDBY_MODE) && > + (phy->tx_delay == 0)) { > + if (!sx1278_ieee_tx(phy->hw)) > + do_next_rx = false; > + } > + > + if (do_next_rx) > + sx1278_ieee_rx(phy->hw); > + > + if (phy->tx_delay > 0) > + phy->tx_delay -= 1; > + > + if (!phy->suspended) { > + phy->timer.expires = jiffies_64 + 1; > + add_timer(&phy->timer); > + } > +} > + > +/** > + * sx1278_timer_irqwork - The actual work which checks the IRQ flags of the chip > + * @work: the work entry listed in the workqueue > + */ > +static void > +sx1278_timer_irqwork(struct work_struct *work) > +{ > + struct sx1278_phy *phy; > + > + phy = container_of(work, struct sx1278_phy, irqwork); > + sx1278_ieee_statemachine(phy->hw); > +} > + > +/** > + * sx1278_timer_isr - Callback function for the timer interrupt > + * @arg: the general argument for this callback function > + */ > +static void > +sx1278_timer_isr(unsigned long arg) > +{ > + struct sx1278_phy *phy = (struct sx1278_phy *)arg; > + > + schedule_work(&phy->irqwork); > +} > + > +static const struct ieee802154_ops sx1278_ops = { > + .owner = THIS_MODULE, > + .xmit_async = sx1278_ieee_xmit, > + .ed = sx1278_ieee_ed, > + .set_channel = sx1278_ieee_set_channel, > + .set_txpower = sx1278_ieee_set_txpower, > + .start = sx1278_ieee_start, > + .stop = sx1278_ieee_stop, > + .set_promiscuous_mode = sx1278_ieee_set_promiscuous_mode, > +}; > + > +/** > + * sx1278X_ieee_channel_mask - Get the available channels' mask of LoRa device > + * @hw: LoRa IEEE 802.15.4 device > + * > + * Return: The bitwise channel mask in 4 bytes > + */ > +u32 > +sx1278_ieee_channel_mask(struct ieee802154_hw *hw) > +{ > + struct rf_frq rf; > + u32 mask; > + > + sx1278_ieee_get_rf_config(hw, &rf); > + > + mask = ((u32)(1 << (rf.ch_max + 1)) - (u32)(1 << rf.ch_min)); > + > + return mask; > +} > + > +static int > +sx1278_ieee_add_one(struct sx1278_phy *phy) > +{ > + struct ieee802154_hw *hw = phy->hw; > + int err; > + > + /* Define channels could be used. */ > + hw->phy->supported.channels[0] = sx1278_ieee_channel_mask(hw); > + /* SX1278 phy channel 11 as default */ > + hw->phy->current_channel = 11; > + > + /* Define RF power. */ > + hw->phy->supported.tx_powers = sx1278_powers; > + hw->phy->supported.tx_powers_size = ARRAY_SIZE(sx1278_powers); > + hw->phy->transmit_power = sx1278_powers[12]; > + > + ieee802154_random_extended_addr(&hw->phy->perm_extended_addr); > + hw->flags = IEEE802154_HW_TX_OMIT_CKSUM > + | IEEE802154_HW_RX_OMIT_CKSUM > + | IEEE802154_HW_PROMISCUOUS; > + > + err = ieee802154_register_hw(hw); > + if (err) > + goto err_reg; > + > + INIT_WORK(&phy->irqwork, sx1278_timer_irqwork); > + > + init_timer(&phy->timer); > + phy->timer.expires = jiffies_64 + HZ; > + phy->timer.function = sx1278_timer_isr; > + phy->timer.data = (unsigned long)phy; > + > + spin_lock_init(&phy->buf_lock); > + > + err = init_sx127x(phy->map); > + if (err) > + goto err_reg; > + > + return 0; > + > +err_reg: > + dev_err(regmap_get_device(phy->map), > + "register as IEEE 802.15.4 device failed\n"); > + return err; > +} > + > +static void > +sx1278_ieee_del(struct sx1278_phy *phy) > +{ > + if (!phy) > + return; > + > + del_timer(&phy->timer); > + flush_work(&phy->irqwork); > + > + ieee802154_unregister_hw(phy->hw); > + ieee802154_free_hw(phy->hw); > +} > + > +/*--------------------------- SX1278 SPI Functions ---------------------------*/ > + > +/* The compatible chip array. */ > +#ifdef CONFIG_OF > +static const struct of_device_id sx1278_dt_ids[] = { > + { .compatible = "semtech,sx1276" }, > + { .compatible = "semtech,sx1277" }, > + { .compatible = "semtech,sx1278" }, > + { .compatible = "semtech,sx1279" }, > + { .compatible = "sx1278" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, sx1278_dt_ids); > +#endif > + > +/* The compatible ACPI device array. */ > +#ifdef CONFIG_ACPI > +static const struct acpi_device_id sx1278_acpi_ids[] = { > + { .id = "sx1278" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(acpi, sx1278_acpi_ids); > +#endif > + > +/* The compatible SPI device id array. */ > +static const struct spi_device_id sx1278_spi_ids[] = { > + { .name = "sx1278" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(spi, sx1278_spi_ids); > + > +bool sx1278_reg_volatile(struct device *dev, unsigned int reg) > +{ > + return true; > +} > + > +/* The SX1278 regmap config. */ > +struct regmap_config sx1278_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > + .max_register = SX127X_MAX_REG, > + .read_flag_mask = 0x00, > + .write_flag_mask = 0x80, > + .volatile_reg = sx1278_reg_volatile, > +}; > + > +/* The SPI probe callback function. */ > +static int sx1278_spi_probe(struct spi_device *spi) > +{ > + struct ieee802154_hw *hw; > + struct sx1278_phy *phy; > + int err; > + > + hw = ieee802154_alloc_hw(sizeof(*phy), &sx1278_ops); > + if (!hw) { > + dev_err(&spi->dev, "not enough memory\n"); > + return -ENOMEM; > + } > + > + phy = hw->priv; > + phy->hw = hw; > + hw->parent = &spi->dev; > + phy->map = devm_regmap_init_spi(spi, &sx1278_regmap_config); > + > + /* Set the SPI device's driver data for later usage. */ > + spi_set_drvdata(spi, phy); > + > + err = sx1278_ieee_add_one(phy); > + if (err < 0) { > + dev_err(&spi->dev, "no SX1278 compatible device\n"); > + goto sx1278_spi_probe_err; > + } > + > + dev_info(&spi->dev, > + "add an IEEE 802.15.4 over LoRa SX1278 compatible device\n"); > + > + return 0; > + > +sx1278_spi_probe_err: > + sx1278_ieee_del(phy); > + return err; > +} > + > +/* The SPI remove callback function. */ > +static int sx1278_spi_remove(struct spi_device *spi) > +{ > + struct sx1278_phy *phy = spi_get_drvdata(spi); > + > + sx1278_ieee_del(phy); > + > + return 0; > +} > + > +#define __DRIVER_NAME "sx1278" > + > +/* The SPI driver which acts as a protocol driver in this kernel module. */ > +static struct spi_driver sx1278_spi_driver = { > + .driver = { > + .name = __DRIVER_NAME, > + .owner = THIS_MODULE, > +#ifdef CONFIG_OF > + .of_match_table = of_match_ptr(sx1278_dt_ids), > +#endif > +#ifdef CONFIG_ACPI > + .acpi_match_table = ACPI_PTR(sx1278_acpi_ids), > +#endif > + }, > + .probe = sx1278_spi_probe, > + .remove = sx1278_spi_remove, > + .id_table = sx1278_spi_ids, > +}; > + > +/* Register SX1278 kernel module. */ > +module_spi_driver(sx1278_spi_driver); > + > +MODULE_AUTHOR("Jian-Hong Pan, <starnight@g.ncu.edu.tw>"); > +MODULE_DESCRIPTION("LoRa device SX1278 driver with IEEE 802.15.4 interface"); > +MODULE_LICENSE("Dual BSD/GPL"); > -- > 2.14.1 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-wpan" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, On Mon, Sep 11, 2017 at 3:50 AM, Xue Liu <liuxuenetmail@gmail.com> wrote: > Hello Pan, > > From my point of view, it is not a good idea to write sx127x driver > for IEEE 802154. Because LoRa is not compatible with IEEE 802.15.4 in > PHY and MAC layer. > This isn't true. The security layer is exactly the same and copied from IEEE 802.15.4. The rest of the mac layer is pretty simple. Simpler than IEEE 802.15.4... for now. > I think it is better to make individual Phy and MAC layers for LoRa in > Linux kernel, where a sx127x driver will make sense. What about to introduce a new interface type? Then you can do a complete different parsing on this interface type and share the code what you need from 802.15.4 - Alex -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, 2017-09-11 20:47 GMT+08:00 Alexander Aring <aring@mojatatu.com>: > Hi, > > On Mon, Sep 11, 2017 at 3:50 AM, Xue Liu <liuxuenetmail@gmail.com> wrote: >> Hello Pan, >> >> From my point of view, it is not a good idea to write sx127x driver >> for IEEE 802154. Because LoRa is not compatible with IEEE 802.15.4 in >> PHY and MAC layer. >> > > This isn't true. The security layer is exactly the same and copied > from IEEE 802.15.4. > The rest of the mac layer is pretty simple. Simpler than IEEE > 802.15.4... for now. That is the first reason why I write this driver. I also found "Transmission of IPv6 Packets over LoRaWAN" on IETF website https://datatracker.ietf.org/doc/draft-vilajosana-lpwan-lora-hc/ However, it has been expired since 2017-01-09. It is the second reason why I write this driver. >> I think it is better to make individual Phy and MAC layers for LoRa in >> Linux kernel, where a sx127x driver will make sense. > > What about to introduce a new interface type? Then you can do a > complete different parsing on this interface type and share the code > what you need from 802.15.4 So, will the suggestion be a new interface like "6LoWPAN over LoRaWAN"? Thanks, Jian-Hong Pan -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, On Mon, Sep 11, 2017 at 11:17 AM, StarNight <starnight@g.ncu.edu.tw> wrote: > Hi, > > 2017-09-11 20:47 GMT+08:00 Alexander Aring <aring@mojatatu.com>: >> Hi, >> >> On Mon, Sep 11, 2017 at 3:50 AM, Xue Liu <liuxuenetmail@gmail.com> wrote: >>> Hello Pan, >>> >>> From my point of view, it is not a good idea to write sx127x driver >>> for IEEE 802154. Because LoRa is not compatible with IEEE 802.15.4 in >>> PHY and MAC layer. >>> >> >> This isn't true. The security layer is exactly the same and copied >> from IEEE 802.15.4. >> The rest of the mac layer is pretty simple. Simpler than IEEE >> 802.15.4... for now. > > That is the first reason why I write this driver. > > I also found "Transmission of IPv6 Packets over LoRaWAN" on IETF website > https://datatracker.ietf.org/doc/draft-vilajosana-lpwan-lora-hc/ > However, it has been expired since 2017-01-09. > It is the second reason why I write this driver. > >>> I think it is better to make individual Phy and MAC layers for LoRa in >>> Linux kernel, where a sx127x driver will make sense. >> >> What about to introduce a new interface type? Then you can do a >> complete different parsing on this interface type and share the code >> what you need from 802.15.4 > > So, will the suggestion be a new interface like "6LoWPAN over LoRaWAN"? > You mixing L2 and 6LoWPAN here. L2 layer is 802.15.4, there we have the device nodes "node", "monitor" and "coordinator" (not implemented yet). My idea is to add a Lora L2 interface type there and then you can catch frames and make a "lora.c" file in net/mac802154 for lora frame handling. If you need 802.15.4 stuff just use it then. This makes sense, because Lora use some 802.15.4 stuff which is mostly the same. For 6LoWPAN you need to make an implementation for Lora 6LoWPAN adaptation, question is if we want to put that in net/ieee802154 :-/ or do e.g. "net/lora" (There you can put lora socket handling/6lowpan adaptation etc.) Will wait for Stefan's opinion according to that. - Alex -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, 2017-09-12 5:13 GMT+08:00 Alexander Aring <aring@mojatatu.com>: > Hi, > > On Mon, Sep 11, 2017 at 11:17 AM, StarNight <starnight@g.ncu.edu.tw> wrote: >> Hi, >> >> 2017-09-11 20:47 GMT+08:00 Alexander Aring <aring@mojatatu.com>: >>> Hi, >>> >>> On Mon, Sep 11, 2017 at 3:50 AM, Xue Liu <liuxuenetmail@gmail.com> wrote: >>>> Hello Pan, >>>> >>>> From my point of view, it is not a good idea to write sx127x driver >>>> for IEEE 802154. Because LoRa is not compatible with IEEE 802.15.4 in >>>> PHY and MAC layer. >>>> >>> >>> This isn't true. The security layer is exactly the same and copied >>> from IEEE 802.15.4. >>> The rest of the mac layer is pretty simple. Simpler than IEEE >>> 802.15.4... for now. >> >> That is the first reason why I write this driver. >> >> I also found "Transmission of IPv6 Packets over LoRaWAN" on IETF website >> https://datatracker.ietf.org/doc/draft-vilajosana-lpwan-lora-hc/ >> However, it has been expired since 2017-01-09. >> It is the second reason why I write this driver. >> >>>> I think it is better to make individual Phy and MAC layers for LoRa in >>>> Linux kernel, where a sx127x driver will make sense. >>> >>> What about to introduce a new interface type? Then you can do a >>> complete different parsing on this interface type and share the code >>> what you need from 802.15.4 >> >> So, will the suggestion be a new interface like "6LoWPAN over LoRaWAN"? >> > > You mixing L2 and 6LoWPAN here. L2 layer is 802.15.4, there we have > the device nodes "node", "monitor" and "coordinator" (not implemented > yet). > My idea is to add a Lora L2 interface type there and then you can > catch frames and make a "lora.c" file in net/mac802154 for lora frame > handling. If you need 802.15.4 stuff just use it then. This makes > sense, because Lora use some 802.15.4 stuff which is mostly the same. I have found this slide: LoRaWAN specification developments by Nicolas Sornin https://www.ietf.org/mail-archive/web/lp-wan/current/pdfbVG486gGVD.pdf In the page 5: LoRaWAN1.0 Close ALIGNMENT WITH IEEE 802.15.4 in order to preserve higher layers Maybe, I can make a comparison list of IEEE 802.15.4 and LoRaWAN. This will be useful for lora.c > For 6LoWPAN you need to make an implementation for Lora 6LoWPAN > adaptation, question is if we want to put that in net/ieee802154 :-/ > or do e.g. "net/lora" (There you can put lora socket handling/6lowpan > adaptation etc.) > > Will wait for Stefan's opinion according to that. > Jian-Hong Pan -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
2017-09-15 20:26 GMT+08:00 Stefan Schmidt <stefan@osg.samsung.com>: > Hello. > > On 09/10/2017 11:30 AM, Jian-Hong Pan wrote: >> >> LoRa is an implementation of LPWPAN. >> Chirp Spread Spectrum (CSS) modulation, which is used by LoRa has been >> specified by IEEE 802.15.4a. >> This driver implements IEEE 802.15.4 mac over LoRa physical layer not >> LoRaWAN. > > > Please see my architectural comments on how to fit in LoRa transceiver in my > other mail. I have a very specific question on this driver and its use with > the mac802154 subsystem though. > > I had a look at the first datasheet I could find for it > (http://www.semtech.com/images/datasheet/sx1276_77_78_79.pdf) and stumbled > over this sentence: > "Standard GFSK, FSK, OOK, and GMSK modulation is also provided > to allow compatibility with existing systems or standards such > as wireless MBUS and IEEE 802.15.4g." > > Does this mean the transceiver can run in a mode where it would be > compatible with an plain (no LoRA support) IEEE 802.15.4g transceiver on the > same frequency band, subHGZ here? It is the datasheet that I refer to when I write the driver. This series of chips (Semtech SX1276/77/78/79) have two operating modes: LoRa mode and FSK/OOK mode Its registers may be in different meaning and usage with in different operating mode. The overall registers comparing list can be found at "Table 41 Registers Summary" of the datasheet. However, the operating frequency of the chip is an issue. It shuold meet regional regulatory requirements. The meeted frequency could be found in the series of chips. > While we do not have support for the 15.4g extension (MTU size, etc) in our > stack right now this would be a very interesting first test for the driver. > If this would be the case. According to the datasheat, the FIFO buffer size of this series of chips is: - 256 bytes in LoRa mode (Chapter 4.1.2.3. LoRa Mode FIFO Data Buffer) - 64 bytes in FSK/OOK mode (Chapter 4.2.10. FIFO) > How did you test your driver so far? Making sure that the device gets > detected and setup correctly I assume. Did you also do full transmission and > receive tests with this transceiver and mac802154? If yes, was it the same > hardware on both sides or different? I use a Raspberry Pi as the board and Archlinux ARM as the OS. I make two SX1278 transceivers as SPI slaves connect to the Raspberry Pi, because the Raspberry Pi has two SPI chip selector addresses. Then, prepare the Device Tree overlap and install it before modprobe the driver as a module. There wil be two wpan interfaces in the system and show them with "ip addr". I refer to the shell script in the "Setup a 6LoWPAN test network" section of http://wpan.cakelab.org/ to setup the wpan interfaces. iwpan dev wpan${i} set pan_id $panid ip link add link wpan${i} name lowpan${i} type lowpan ip link set wpan${i} up ip link set lowpan${i} up Two lowpan interfaces will be added and brought up. Use "ip addr" again and will see the IPv6 addresses of the lowpan interfaces. I also use an echo server and a client written by myself in Python 3 to test the UDP communication between the two interfaces. console of server side: server.py <listening IPv6 address> <listening port> console of client side: client.py <src IPv6 address> <dest IPv6 address> <dest port> <data string> Client will send the data string to server. Server will capitalize the recieved data string from the client and send back to the client. Client will receive the capitalized data string and print it out. By the way, those could be found in the project on my GitHub: https://github.com/starnight/LoRa Jian-Hong Pan -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hello. On 09/16/2017 09:08 AM, StarNight wrote: > 2017-09-15 20:26 GMT+08:00 Stefan Schmidt <stefan@osg.samsung.com>: >> Hello. >> >> On 09/10/2017 11:30 AM, Jian-Hong Pan wrote: >>> >>> LoRa is an implementation of LPWPAN. >>> Chirp Spread Spectrum (CSS) modulation, which is used by LoRa has been >>> specified by IEEE 802.15.4a. >>> This driver implements IEEE 802.15.4 mac over LoRa physical layer not >>> LoRaWAN. >> >> >> Please see my architectural comments on how to fit in LoRa transceiver in my >> other mail. I have a very specific question on this driver and its use with >> the mac802154 subsystem though. >> >> I had a look at the first datasheet I could find for it >> (http://www.semtech.com/images/datasheet/sx1276_77_78_79.pdf) and stumbled >> over this sentence: >> "Standard GFSK, FSK, OOK, and GMSK modulation is also provided >> to allow compatibility with existing systems or standards such >> as wireless MBUS and IEEE 802.15.4g." >> >> Does this mean the transceiver can run in a mode where it would be >> compatible with an plain (no LoRA support) IEEE 802.15.4g transceiver on the >> same frequency band, subHGZ here? > > It is the datasheet that I refer to when I write the driver. OK > This series of chips (Semtech SX1276/77/78/79) have two operating modes: > LoRa mode and FSK/OOK mode > Its registers may be in different meaning and usage with in different operating > mode. > The overall registers comparing list can be found at "Table 41 Registers > Summary" of the datasheet. > > However, the operating frequency of the chip is an issue. > It shuold meet regional regulatory requirements. > The meeted frequency could be found in the series of chips. Sure, you would need to make sure that the 802.15.4g compliant transceiver on the other side operates actually in the same frequencies (based on regional regulations). What I try to understand is if we can setup this device in a 802.15.4 compliant phy mode. In that case I have no problem with it landing in the ieee802154 subsystem. If this is not possible and we could only run mac802514 over them but they would only work against the same device I do not think it should go into ieee802154. In that case we should rather work out what parts of 802.15.4 can be shared with a full LoRa subsystem and work on that. >> While we do not have support for the 15.4g extension (MTU size, etc) in our >> stack right now this would be a very interesting first test for the driver. >> If this would be the case. > > According to the datasheat, the FIFO buffer size of this series of chips is: > - 256 bytes in LoRa mode (Chapter 4.1.2.3. LoRa Mode FIFO Data Buffer) > - 64 bytes in FSK/OOK mode (Chapter 4.2.10. FIFO) > >> How did you test your driver so far? Making sure that the device gets >> detected and setup correctly I assume. Did you also do full transmission and >> receive tests with this transceiver and mac802154? If yes, was it the same >> hardware on both sides or different? > > I use a Raspberry Pi as the board and Archlinux ARM as the OS. > I make two SX1278 transceivers as SPI slaves connect to the Raspberry Pi, > because the Raspberry Pi has two SPI chip selector addresses. > > Then, prepare the Device Tree overlap and install it before modprobe the driver > as a module. > There wil be two wpan interfaces in the system and show them with "ip addr". > I refer to the shell script in the "Setup a 6LoWPAN test network" section of > http://wpan.cakelab.org/ to setup the wpan interfaces. > > iwpan dev wpan${i} set pan_id $panid > ip link add link wpan${i} name lowpan${i} type lowpan > ip link set wpan${i} up > ip link set lowpan${i} up > > Two lowpan interfaces will be added and brought up. > Use "ip addr" again and will see the IPv6 addresses of the lowpan interfaces. > > I also use an echo server and a client written by myself in Python 3 to test the > UDP communication between the two interfaces. > > console of server side: > server.py <listening IPv6 address> <listening port> > > console of client side: > client.py <src IPv6 address> <dest IPv6 address> <dest port> <data string> > > Client will send the data string to server. > Server will capitalize the recieved data string from the client and send back to > the client. > Client will receive the capitalized data string and print it out. > > By the way, those could be found in the project on my GitHub: > https://github.com/starnight/LoRa Thanks for the explanation. You tested full communication up to UDP over 6LoWPAN adaption, but only between the same devices. Did these devices run in LoRa or in FSK/OOK mode? regards Stefan Schmidt -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, I have written a note which is reordered and extracted from LoRaWAN 1.0.2. https://hackmd.io/s/S1kg6Ymo-# Hope this wiil be easier for readers. LoRaWAN defiened by LoRa Alliance is the MAC layer over LoRa PHY. (The relation between LoRaWAN and LoRa PHY) 2017-09-19 15:10 GMT+08:00 Stefan Schmidt <stefan@osg.samsung.com>: > Hello. > > On 09/16/2017 09:08 AM, StarNight wrote: >> >> 2017-09-15 20:26 GMT+08:00 Stefan Schmidt <stefan@osg.samsung.com>: >>> >>> Hello. >>> >>> On 09/10/2017 11:30 AM, Jian-Hong Pan wrote: >>>> >>>> >>>> LoRa is an implementation of LPWPAN. >>>> Chirp Spread Spectrum (CSS) modulation, which is used by LoRa has been >>>> specified by IEEE 802.15.4a. >>>> This driver implements IEEE 802.15.4 mac over LoRa physical layer not >>>> LoRaWAN. >>> >>> >>> >>> Please see my architectural comments on how to fit in LoRa transceiver in >>> my >>> other mail. I have a very specific question on this driver and its use >>> with >>> the mac802154 subsystem though. >>> >>> I had a look at the first datasheet I could find for it >>> (http://www.semtech.com/images/datasheet/sx1276_77_78_79.pdf) and >>> stumbled >>> over this sentence: >>> "Standard GFSK, FSK, OOK, and GMSK modulation is also provided >>> to allow compatibility with existing systems or standards such >>> as wireless MBUS and IEEE 802.15.4g." >>> >>> Does this mean the transceiver can run in a mode where it would be >>> compatible with an plain (no LoRA support) IEEE 802.15.4g transceiver on >>> the >>> same frequency band, subHGZ here? >> >> >> It is the datasheet that I refer to when I write the driver. > > > OK > >> This series of chips (Semtech SX1276/77/78/79) have two operating modes: >> LoRa mode and FSK/OOK mode >> Its registers may be in different meaning and usage with in different >> operating >> mode. >> The overall registers comparing list can be found at "Table 41 Registers >> Summary" of the datasheet. >> >> However, the operating frequency of the chip is an issue. >> It shuold meet regional regulatory requirements. >> The meeted frequency could be found in the series of chips. > > > Sure, you would need to make sure that the 802.15.4g compliant transceiver > on the other side operates actually in the same frequencies (based on > regional regulations). > > What I try to understand is if we can setup this device in a 802.15.4 > compliant phy mode. In that case I have no problem with it landing in the > ieee802154 subsystem. > > If this is not possible and we could only run mac802514 over them but they > would only work against the same device I do not think it should go into > ieee802154. In that case we should rather work out what parts of 802.15.4 > can be shared with a full LoRa subsystem and work on that. > > >>> While we do not have support for the 15.4g extension (MTU size, etc) in >>> our >>> stack right now this would be a very interesting first test for the >>> driver. >>> If this would be the case. >> >> >> According to the datasheat, the FIFO buffer size of this series of chips >> is: >> - 256 bytes in LoRa mode (Chapter 4.1.2.3. LoRa Mode FIFO Data Buffer) >> - 64 bytes in FSK/OOK mode (Chapter 4.2.10. FIFO) >> >>> How did you test your driver so far? Making sure that the device gets >>> detected and setup correctly I assume. Did you also do full transmission >>> and >>> receive tests with this transceiver and mac802154? If yes, was it the >>> same >>> hardware on both sides or different? >> >> >> I use a Raspberry Pi as the board and Archlinux ARM as the OS. >> I make two SX1278 transceivers as SPI slaves connect to the Raspberry Pi, >> because the Raspberry Pi has two SPI chip selector addresses. >> >> Then, prepare the Device Tree overlap and install it before modprobe the >> driver >> as a module. >> There wil be two wpan interfaces in the system and show them with "ip >> addr". >> I refer to the shell script in the "Setup a 6LoWPAN test network" section >> of >> http://wpan.cakelab.org/ to setup the wpan interfaces. >> >> iwpan dev wpan${i} set pan_id $panid >> ip link add link wpan${i} name lowpan${i} type lowpan >> ip link set wpan${i} up >> ip link set lowpan${i} up >> >> Two lowpan interfaces will be added and brought up. >> Use "ip addr" again and will see the IPv6 addresses of the lowpan >> interfaces. >> >> I also use an echo server and a client written by myself in Python 3 to >> test the >> UDP communication between the two interfaces. >> >> console of server side: >> server.py <listening IPv6 address> <listening port> >> >> console of client side: >> client.py <src IPv6 address> <dest IPv6 address> <dest port> <data string> >> >> Client will send the data string to server. >> Server will capitalize the recieved data string from the client and send >> back to >> the client. >> Client will receive the capitalized data string and print it out. >> >> By the way, those could be found in the project on my GitHub: >> https://github.com/starnight/LoRa > > > Thanks for the explanation. > > You tested full communication up to UDP over 6LoWPAN adaption, but only > between the same devices. Did these devices run in LoRa or in FSK/OOK mode? The driver which I wrote makes the devices in LoRa mode (Make them only as LoRa PHYs). The devices are two SX1278 tranceivers. So, the SX1278 driver is IEEE 802.15.4 MAC over LoRa PHY. Jian-Hong Pan -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hello. [I dropped the device tree list as this is really unrelated to them now] On 09/24/2017 04:53 PM, StarNight wrote: > Hi, > > I have written a note which is reordered and extracted from LoRaWAN 1.0.2. > https://hackmd.io/s/S1kg6Ymo-# > Hope this wiil be easier for readers. > > LoRaWAN defiened by LoRa Alliance is the MAC layer over LoRa PHY. > (The relation between LoRaWAN and LoRa PHY) Thanks a lot for putting thee notes together. Reading the MAC frame format I see not much in common with IEEE 802.15.4. Maybe they really refer to the link layer security parts only. Given that your driver operates in LoRa and thus will not work with any other 802.15.4 device I would not apply it to the ieee802154 subsystem. The driver is a good start for real LoRa support in the Linux kernel thought. I would very much welcome such support and would also be happy to help. (Not before November though as my next weeks will be to busy) Xue (in CC now) also has an interest in LoRa support in the Linux Kernel and already started some work if I remember correctly. If you both are interested to actively contribute to such a subsystem I would be happy to help getting it into the mainline kernel. Please let me both know what you think. I can ask for a linux-lora (or linux-lpwan?) list on vger to get the ball rolling for initial discussions. regards Stefan Schmidt -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, On Tue, Sep 26, 2017 at 6:16 AM, Stefan Schmidt <stefan@osg.samsung.com> wrote: > Hello. > > [I dropped the device tree list as this is really unrelated to them now] > > On 09/24/2017 04:53 PM, StarNight wrote: >> Hi, >> >> I have written a note which is reordered and extracted from LoRaWAN 1.0.2. >> https://hackmd.io/s/S1kg6Ymo-# >> Hope this wiil be easier for readers. >> >> LoRaWAN defiened by LoRa Alliance is the MAC layer over LoRa PHY. >> (The relation between LoRaWAN and LoRa PHY) > > Thanks a lot for putting thee notes together. > > Reading the MAC frame format I see not much in common with IEEE 802.15.4. > Maybe they really refer to the link layer security parts only. > > Given that your driver operates in LoRa and thus will not work with any > other 802.15.4 device I would not apply it to the ieee802154 subsystem. > > The driver is a good start for real LoRa support in the Linux kernel thought. > I would very much welcome such support and would also be happy to help. > (Not before November though as my next weeks will be to busy) > > Xue (in CC now) also has an interest in LoRa support in the Linux Kernel and > already started some work if I remember correctly. If you both are interested > to actively contribute to such a subsystem I would be happy to help getting > it into the mainline kernel. > For security stuff this should be shared between these subsystems then... Don't know how currently - we need to look. The only possibility how to put lora in _softmac_ 802.15.4 is for me to introduce new "lora_register_hw" function who registered a special "lora" interface type (like node in 802.15.4) and use some of ieee802154 infrastructure and of course a lora.c which do some frame parsing on upper layer... Upper layers need to be some "net/lora" then... Just an idea how I can imagine how it _could_ work... to put lora in SoftMAC here. - Alex -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hello. On 09/26/2017 02:34 PM, Alexander Aring wrote: > Hi, > > On Tue, Sep 26, 2017 at 6:16 AM, Stefan Schmidt <stefan@osg.samsung.com> wrote: >> Hello. >> >> [I dropped the device tree list as this is really unrelated to them now] >> >> On 09/24/2017 04:53 PM, StarNight wrote: >>> Hi, >>> >>> I have written a note which is reordered and extracted from LoRaWAN 1.0.2. >>> https://hackmd.io/s/S1kg6Ymo-# >>> Hope this wiil be easier for readers. >>> >>> LoRaWAN defiened by LoRa Alliance is the MAC layer over LoRa PHY. >>> (The relation between LoRaWAN and LoRa PHY) >> >> Thanks a lot for putting thee notes together. >> >> Reading the MAC frame format I see not much in common with IEEE 802.15.4. >> Maybe they really refer to the link layer security parts only. >> >> Given that your driver operates in LoRa and thus will not work with any >> other 802.15.4 device I would not apply it to the ieee802154 subsystem. >> >> The driver is a good start for real LoRa support in the Linux kernel thought. >> I would very much welcome such support and would also be happy to help. >> (Not before November though as my next weeks will be to busy) >> >> Xue (in CC now) also has an interest in LoRa support in the Linux Kernel and >> already started some work if I remember correctly. If you both are interested >> to actively contribute to such a subsystem I would be happy to help getting >> it into the mainline kernel. >> > > For security stuff this should be shared between these subsystems > then... Don't know how currently - we need to look. This will become clearer when being worked on. For now we only need to keep in mind that this needs to be shared. > The only possibility how to put lora in _softmac_ 802.15.4 is for me > to introduce new "lora_register_hw" function who registered a special > "lora" interface type (like node in 802.15.4) and use some of > ieee802154 infrastructure and of course a lora.c which do some frame > parsing on upper layer... I still do not see a good use case for this. LoRa is even more MTU constrained than 15.4 already is. Their own MAC frame format shows that by being even more sparse. Why would one use the 15.4 MAC on LoRA? If there are some good arguments for this I would like to hear them. So far I am not convinced that this is a good idea. Putting the efforts into implementing a real LoRA MAC is imho way better. regards Stefan Schmidt -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, 2017-09-27 16:42 GMT+08:00 Stefan Schmidt <stefan@osg.samsung.com>: > Hello. > > On 09/26/2017 02:34 PM, Alexander Aring wrote: >> Hi, >> >> On Tue, Sep 26, 2017 at 6:16 AM, Stefan Schmidt <stefan@osg.samsung.com> wrote: >>> Hello. >>> >>> [I dropped the device tree list as this is really unrelated to them now] >>> >>> On 09/24/2017 04:53 PM, StarNight wrote: >>>> Hi, >>>> >>>> I have written a note which is reordered and extracted from LoRaWAN 1.0.2. >>>> https://hackmd.io/s/S1kg6Ymo-# >>>> Hope this wiil be easier for readers. >>>> >>>> LoRaWAN defiened by LoRa Alliance is the MAC layer over LoRa PHY. >>>> (The relation between LoRaWAN and LoRa PHY) >>> >>> Thanks a lot for putting thee notes together. >>> >>> Reading the MAC frame format I see not much in common with IEEE 802.15.4. >>> Maybe they really refer to the link layer security parts only. >>> >>> Given that your driver operates in LoRa and thus will not work with any >>> other 802.15.4 device I would not apply it to the ieee802154 subsystem. >>> >>> The driver is a good start for real LoRa support in the Linux kernel thought. >>> I would very much welcome such support and would also be happy to help. >>> (Not before November though as my next weeks will be to busy) >>> >>> Xue (in CC now) also has an interest in LoRa support in the Linux Kernel and >>> already started some work if I remember correctly. If you both are interested >>> to actively contribute to such a subsystem I would be happy to help getting >>> it into the mainline kernel. >>> >> >> For security stuff this should be shared between these subsystems >> then... Don't know how currently - we need to look. > > This will become clearer when being worked on. For now we only need to keep in mind that this needs to be shared. > >> The only possibility how to put lora in _softmac_ 802.15.4 is for me >> to introduce new "lora_register_hw" function who registered a special >> "lora" interface type (like node in 802.15.4) and use some of >> ieee802154 infrastructure and of course a lora.c which do some frame >> parsing on upper layer... > > I still do not see a good use case for this. LoRa is even more MTU constrained than 15.4 already is. Their own MAC frame format shows that > by being even more sparse. Why would one use the 15.4 MAC on LoRA? > > If there are some good arguments for this I would like to hear them. So far I am not convinced that this is a good idea. > > Putting the efforts into implementing a real LoRA MAC is imho way better. I think what Alexander concerns is no more duplex work for the same works. Like the API problem of SPI and I2C, then the regmap comes out. For the MAC of LoRaWAN or LoRa ... We need the functions for device like: (just for example) struct lrw_hw { ... }; struct lrw_ops { ... }; struct lrw_hw * lrw_alloc_hw(size_t priv_data_len, const struct lrw_ops *ops); void lrw_free_hw(struct lrw_hw *hw); int lrw_register_hw(struct lrw_hw *hw); void lrw_unregister_hw(struct lrw_hw *hw); void lrw_rx_irqsafe(struct lrw_hw *hw, struct sk_buff *skb, u8 lqi); void lrw_xmit_complete(struct lrw_hw *hw, struct sk_buff *skb, bool ifs_handling); ... some more functions We also could make it as a type of wpan_phy for upper layer. All of the mentioned are like the template which exists in IEEE 802.15.4 MAC. The difference will be how to parse the MAC frame format. If I misunderstand the words or have wrong idea, just point out. It is okay. Jian-Hong Pan -- To unsubscribe from this list: send the line "unsubscribe linux-wpan" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/Documentation/devicetree/bindings/net/ieee802154/sx1278.txt b/Documentation/devicetree/bindings/net/ieee802154/sx1278.txt new file mode 100644 index 000000000000..78ca60feac30 --- /dev/null +++ b/Documentation/devicetree/bindings/net/ieee802154/sx1278.txt @@ -0,0 +1,30 @@ +* SX1278 IEEE 802.15.4 Compatible Radio * + +Required properties: + - compatible: should be "semtech,sx1276", "semtech,sx1277", + "semtech,sx1277" or "semtech,sx1279" depends on your + transceiver board + - spi-max-frequency: maximal bus speed, should be set something under or + equal 10000000 Hz + - reg: the chipselect index + - clock-frequency: the external crystal oscillator frequency in Hz of the + transceiver +Optional properties: + - center-carrier-frq: the RF center carrier frequency in Hz + - rf-bandwidth: the RF bandwidth in Hz + - minimal-RF-channel: the minimal RF channel number and the value must be with + prefix "/bits/ 8" because of being a byte datatype + - maximum-RF-channel: the maximum RF channel number and the value must be with + prefix "/bits/ 8" because of being a byte datatype + - spreading-factor: the spreading factor of Chirp Spread Spectrum modulation + +Example: + + sx1278@0 { + compatible = "semtech,sx1278"; + spi-max-frequency = <15200>; + reg = <0>; + clock-frequency = <32000000>; + minimal-RF-channel = /bits/ 8 <11>; + maximum-RF-channel = /bits/ 8 <11>; + }; diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index 303ba4133920..57ed64e55d13 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig @@ -104,3 +104,15 @@ config IEEE802154_CA8210_DEBUGFS exposes a debugfs node for each CA8210 instance which allows direct use of the Cascoda API, exposing the 802.15.4 MAC management entities. + +config IEEE802154_SX1278 + tristate "Semtech SX1276/77/78/79 transceiver driver" + depends on IEEE802154_DRIVERS && MAC802154 + depends on SPI + select REGMAP_SPI + ---help--- + Say Y here to enable the SX1278 SPI 802.15.4 wireless + controller. + + This driver can also be built as a module. To do so, say M here. + the module will be called 'sx1278'. diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile index 8374bb44a145..634e8e1bfad6 100644 --- a/drivers/net/ieee802154/Makefile +++ b/drivers/net/ieee802154/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o obj-$(CONFIG_IEEE802154_CA8210) += ca8210.o +obj-$(CONFIG_IEEE802154_SX1278) += sx1278.o diff --git a/drivers/net/ieee802154/sx1278.c b/drivers/net/ieee802154/sx1278.c new file mode 100644 index 000000000000..2018407a0ab7 --- /dev/null +++ b/drivers/net/ieee802154/sx1278.c @@ -0,0 +1,1636 @@ +/*- + * Copyright (c) 2017 Jian-Hong, Pan <starnight@g.ncu.edu.tw> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include <linux/module.h> +#include <linux/timer.h> +#include <linux/device.h> +#include <linux/acpi.h> +#include <linux/of_device.h> +#include <linux/spinlock.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> +#include <net/mac802154.h> + +/*------------------------------ LoRa Functions ------------------------------*/ + +#ifndef F_XOSC +#define F_XOSC 32000000 +#endif +static u32 xosc_frq = F_XOSC; +module_param(xosc_frq, uint, 0000); +MODULE_PARM_DESC(xosc_frq, "Crystal oscillator frequency of the LoRa chip"); + +#define __POW_2_19 0x80000 + +#ifndef SX127X_SPRF +#define SX127X_SPRF 512 +#endif +static u32 sprf = SX127X_SPRF; +module_param(sprf, uint, 0000); +MODULE_PARM_DESC(sprf, "Spreading factor of Chirp Spread Spectrum modulation"); + +#ifndef SX127X_RX_BYTE_TIMEOUT +#define SX127X_RX_BYTE_TIMEOUT 1023 +#endif +static u32 rx_timeout = SX127X_RX_BYTE_TIMEOUT; +module_param(rx_timeout, uint, 0000); +MODULE_PARM_DESC(rx_timeout, "RX time-out value as number of symbols"); + +/* SX127X Registers addresses */ +#define SX127X_REG_FIFO 0x00 +#define SX127X_REG_OP_MODE 0x01 +#define SX127X_REG_FRF_MSB 0x06 +#define SX127X_REG_FRF_MID 0x07 +#define SX127X_REG_FRF_LSB 0x08 +#define SX127X_REG_PA_CONFIG 0x09 +#define SX127X_REG_PA_RAMP 0x0A +#define SX127X_REG_OCP 0x0B +#define SX127X_REG_LNA 0x0C +#define SX127X_REG_FIFO_ADDR_PTR 0x0D +#define SX127X_REG_FIFO_TX_BASE_ADDR 0x0E +#define SX127X_REG_FIFO_RX_BASE_ADDR 0x0F +#define SX127X_REG_FIFO_RX_CURRENT_ADDR 0x10 +#define SX127X_REG_IRQ_FLAGS_MASK 0x11 +#define SX127X_REG_IRQ_FLAGS 0x12 +#define SX127X_REG_RX_NB_BYTES 0x13 +#define SX127X_REG_RX_HEADER_CNT_VALUE_MSB 0x14 +#define SX127X_REG_RX_HEADER_CNT_VALUE_LSB 0x15 +#define SX127X_REG_RX_PACKET_CNT_VALUE_MSB 0x16 +#define SX127X_REG_RX_PACKET_CNT_VALUE_LSB 0x17 +#define SX127X_REG_MODEM_STAT 0x18 +#define SX127X_REG_PKT_SNR_VALUE 0x19 +#define SX127X_REG_PKT_RSSI_VALUE 0x1A +#define SX127X_REG_RSSI_VALUE 0x1B +#define SX127X_REG_HOP_CHANNEL 0x1C +#define SX127X_REG_MODEM_CONFIG1 0x1D +#define SX127X_REG_MODEM_CONFIG2 0x1E +#define SX127X_REG_SYMB_TIMEOUT_LSB 0x1F +#define SX127X_REG_PREAMBLE_MSB 0x20 +#define SX127X_REG_PREAMBLE_LSB 0x21 +#define SX127X_REG_PAYLOAD_LENGTH 0x22 +#define SX127X_REG_MAX_PAYLOAD_LENGTH 0x23 +#define SX127X_REG_HOP_PERIOD 0x24 +#define SX127X_REG_FIFO_RX_BYTE_ADDR 0x25 +#define SX127X_REG_MODEM_CONFIG3 0x26 +#define SX127X_REG_FEI_MSB 0x28 +#define SX127X_REG_FEI_MID 0x29 +#define SX127X_REG_FEI_LSB 0x2A +#define SX127X_REG_RSSI_WIDEBAND 0x2C +#define SX127X_REG_DETECT_OPTIMIZE 0x31 +#define SX127X_REG_INVERT_IRQ 0x33 +#define SX127X_REG_DETECTION_THRESHOLD 0x37 +#define SX127X_REG_SYNC_WORD 0x39 +#define SX127X_REG_VERSION 0x42 +#define SX127X_REG_TCXO 0x4B +#define SX127X_REG_PA_DAC 0x4D +#define SX127X_REG_FORMER_TEMP 0x5B +#define SX127X_REG_AGC_REF 0x61 +#define SX127X_REG_AGC_THRESH1 0x62 +#define SX127X_REG_AGC_THRESH2 0x63 +#define SX127X_REG_AGC_THRESH3 0x64 +#define SX127X_REG_PLL 0x70 +#define SX127X_MAX_REG SX127X_REG_PLL + +/* SX127X's operating states in LoRa mode */ +#define SX127X_SLEEP_MODE 0x00 +#define SX127X_STANDBY_MODE 0x01 +#define SX127X_FSTX_MODE 0x02 +#define SX127X_TX_MODE 0x03 +#define SX127X_FSRX_MODE 0x04 +#define SX127X_RXCONTINUOUS_MODE 0x05 +#define SX127X_RXSINGLE_MODE 0x06 +#define SX127X_CAD_MODE 0x07 + +/* SX127X's IRQ flags in LoRa mode */ +#define SX127X_FLAG_RXTIMEOUT 0x80 +#define SX127X_FLAG_RXDONE 0x40 +#define SX127X_FLAG_PAYLOADCRCERROR 0x20 +#define SX127X_FLAG_VALIDHEADER 0x10 +#define SX127X_FLAG_TXDONE 0x08 +#define SX127X_FLAG_CADDONE 0x04 +#define SX127X_FLAG_FHSSCHANGECHANNEL 0x02 +#define SX127X_FLAG_CADDETECTED 0x01 + +/* SX127X's IRQ flags' mask for output pins in LoRa mode */ +#define SX127X_FLAGMASK_RXTIMEOUT 0x80 +#define SX127X_FLAGMASK_RXDONE 0x40 +#define SX127X_FLAGMASK_PAYLOADCRCERROR 0x20 +#define SX127X_FLAGMASK_VALIDHEADER 0x10 +#define SX127X_FLAGMASK_TXDONE 0x08 +#define SX127X_FLAGMASK_CADDONE 0x04 +#define SX127X_FLAGMASK_FHSSCHANGECHANNEL 0x02 +#define SX127X_FLAGMASK_CADDETECTED 0x01 + +/* SX127X's RX/TX FIFO base address */ +#define SX127X_FIFO_RX_BASE_ADDRESS 0x00 +#define SX127X_FIFO_TX_BASE_ADDRESS 0x80 + +struct sx1278_phy { + struct ieee802154_hw *hw; + struct regmap *map; + + bool suspended; + u8 opmode; + struct timer_list timer; + struct work_struct irqwork; + /* Lock the RX and TX actions. */ + spinlock_t buf_lock; + struct sk_buff *tx_buf; + u8 tx_delay; + bool one_to_be_sent; + bool post_tx_done; + bool is_busy; +}; + +/** + * sx127X_read_version - Get LoRa device's chip version + * @map: the device as a regmap to communicate with + * + * Return: Positive / negtive values for version code / failed + * Version code: bits 7-4 full version number, + * bits 3-0 metal mask revision number + */ +int +sx127X_read_version(struct regmap *map) +{ + u8 v; + int status; + + status = regmap_raw_read(map, SX127X_REG_VERSION, &v, 1); + + if ((status == 0) && (v > 0) && (v < 0xFF)) + status = v; + else + status = -ENODEV; + + return status; +} + +/** + * sx127X_get_mode - Get LoRa device's mode register + * @map: the device as a regmap to communicate with + * + * Return: LoRa device's register value + */ +u8 +sx127X_get_mode(struct regmap *map) +{ + u8 op_mode; + + /* Get original OP Mode register. */ + regmap_raw_read(map, SX127X_REG_OP_MODE, &op_mode, 1); + + return op_mode; +} + +/** + * sx127X_set_state - Set LoRa device's operating state + * @map: the device as a regmap to communicate with + * @st: LoRa device's operating state going to be assigned + */ +void +sx127X_set_state(struct regmap *map, u8 st) +{ + u8 op_mode; + + /* Get original OP Mode register. */ + op_mode = sx127X_get_mode(map); + /* Set device to designated state. */ + op_mode = (op_mode & 0xF8) | (st & 0x07); + regmap_raw_write(map, SX127X_REG_OP_MODE, &op_mode, 1); +} + +/** + * sx127X_get_state - Get LoRa device's operating state + * @map: the device as a regmap to communicate with + * + * Return: LoRa device's operating state + */ +u8 +sx127X_get_state(struct regmap *map) +{ + u8 op_mode; + + op_mode = sx127X_get_mode(map) & 0x07; + + return op_mode; +} + +/** + * sx127X_set_lorafrq - Set RF frequency + * @map: the device as a regmap to communicate with + * @fr: RF frequency going to be assigned in Hz + */ +void +sx127X_set_lorafrq(struct regmap *map, u32 fr) +{ + u64 frt64; + u32 frt; + u8 buf[3]; + u8 i; + u32 f_xosc; + +#ifdef CONFIG_OF + /* Set the LoRa module's crystal oscillator's clock if OF is defined. */ + struct device_node *of_node = (regmap_get_device(map))->of_node; + + if (of_property_read_u32(of_node, "clock-frequency", &f_xosc)) + f_xosc = xosc_frq; +#else + f_xosc = xosc_frq; +#endif + + frt64 = (uint64_t)fr * (uint64_t)__POW_2_19; + do_div(frt64, f_xosc); + frt = frt64; + + for (i = 2; i >= 0; i--) { + buf[i] = frt % 256; + frt = frt >> 8; + } + + regmap_raw_write(map, SX127X_REG_FRF_MSB, buf, 3); +} + +/** + * sx127X_get_lorafrq - Get RF frequency + * @map: the device as a regmap to communicate with + * + * Return: RF frequency in Hz + */ +u32 +sx127X_get_lorafrq(struct regmap *map) +{ + u64 frt = 0; + u8 buf[3]; + u8 i; + int status; + u32 fr; + u32 f_xosc; + +#ifdef CONFIG_OF + /* Set the LoRa module's crystal oscillator's clock if OF is defined. */ + struct device_node *of_node = (regmap_get_device(map))->of_node; + + if (of_property_read_u32(of_node, "clock-frequency", &f_xosc)) + f_xosc = xosc_frq; +#else + f_xosc = xosc_frq; +#endif + + status = regmap_raw_read(map, SX127X_REG_FRF_MSB, buf, 3); + if (status < 0) + return 0.0; + + for (i = 0; i <= 2; i++) + frt = frt * 256 + buf[i]; + + fr = frt * f_xosc / __POW_2_19; + + return fr; +} + +/** + * sx127X_set_lorapower - Set RF output power + * @map: the device as a regmap to communicate with + * @pout: RF output power going to be assigned in dbm + */ +void +sx127X_set_lorapower(struct regmap *map, s32 pout) +{ + u8 pacf; + u8 boost; + u8 output_power; + s32 pmax; + + if (pout > 14) { + /* Pout > 14dbm */ + boost = 1; + pmax = 7; + output_power = pout - 2; + } else if (pout < 0) { + /* Pout < 0dbm */ + boost = 0; + pmax = 2; + output_power = 3 + pout; + } else { + /* 0dbm <= Pout <= 14dbm */ + boost = 0; + pmax = 7; + output_power = pout; + } + + pacf = (boost << 7) | (pmax << 4) | (output_power); + regmap_raw_write(map, SX127X_REG_PA_CONFIG, &pacf, 1); +} + +/** + * sx127X_get_lorapower - Get RF output power + * @map: the device as a regmap to communicate with + * + * Return: RF output power in dbm + */ +s32 +sx127X_get_lorapower(struct regmap *map) +{ + u8 pac; + u8 boost; + s32 output_power; + s32 pmax; + s32 pout; + + regmap_raw_read(map, SX127X_REG_PA_CONFIG, &pac, 1); + boost = (pac & 0x80) >> 7; + output_power = pac & 0x0F; + if (boost) { + pout = 2 + output_power; + } else { + /* Power max should be pmax/10. It is 10 times for now. */ + pmax = (108 + 6 * ((pac & 0x70) >> 4)); + pout = (pmax - (150 - output_power * 10)) / 10; + } + + return pout; +} + +/** + * sx127X_dbm2mbm - dbm to mbm unit conversion + * @dbm: the value in dbm + * + * Return: the value in mbm + */ +#define sx127X_dbm2mbm(dbm) (dbm * 100) + +/** + * sx127X_mbm2dbm - mbm to dbm unit conversion + * @mbm: the value in mbm + * + * Return: the value in dbm + */ +#define sx127X_mbm2dbm(mbm) (mbm / 100) + +s8 lna_gain[] = { + 0, + -6, + -12, + -24, + -26, + -48 +}; + +/** + * sx127X_set_loralna - Set RF LNA gain + * @map: the device as a regmap to communicate with + * @db: RF LNA gain going to be assigned in db + */ +void +sx127X_set_loralna(struct regmap *map, s32 db) +{ + u8 i, g; + u8 lnacf; + + for (i = 0; i < 5; i++) { + if (lna_gain[i] <= db) + break; + } + g = i + 1; + + regmap_raw_read(map, SX127X_REG_LNA, &lnacf, 1); + lnacf = (lnacf & 0x1F) | (g << 5); + regmap_raw_write(map, SX127X_REG_LNA, &lnacf, 1); +} + +/** + * sx127X_get_loralna - Get RF LNA gain + * @map: the device as a regmap to communicate with + * + * Return: RF LNA gain db + */ +s32 +sx127X_get_loralna(struct regmap *map) +{ + s32 db; + s8 i, g; + u8 lnacf; + + regmap_raw_read(map, SX127X_REG_LNA, &lnacf, 1); + g = (lnacf >> 5); + i = g - 1; + db = lna_gain[i]; + + return db; +} + +/** + * sx127X_set_loralnaagc - Set RF LNA go with auto gain control or manual + * @map: the device as a regmap to communicate with + * @yesno: 1 / 0 for auto gain control / manual + */ +void +sx127X_set_loralnaagc(struct regmap *map, s32 yesno) +{ + u8 mcf3; + + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG3, &mcf3, 1); + mcf3 = (yesno) ? (mcf3 | 0x04) : (mcf3 & (~0x04)); + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG3, &mcf3, 1); +} + +/** + * sx127X_get_loraallflag - Get all of the LoRa device IRQ flags' current state + * @map: the device as a regmap to communicate with + * + * Return: All of the LoRa device's IRQ flags' current state in a byte + */ +u8 +sx127X_get_loraallflag(struct regmap *map) +{ + u8 flags; + + regmap_raw_read(map, SX127X_REG_IRQ_FLAGS, &flags, 1); + + return flags; +} + +/** + * sx127X_get_loraallflag - Get interested LoRa device IRQ flag's current state + * @map: the device as a regmap to communicate with + * @f: the interested LoRa device's IRQ flag + * + * Return: The interested LoRa device's IRQ flag's current state in a byte + */ +#define sx127X_get_loraflag(map, f) (sx127X_get_loraallflag(map) & (f)) + +/** + * sx127X_clear_loraflag - Clear designated LoRa device's IRQ flag + * @map: the device as a regmap to communicate with + * @f: flags going to be cleared + */ +void +sx127X_clear_loraflag(struct regmap *map, u8 f) +{ + u8 flag; + + /* Get oiginal flag. */ + flag = sx127X_get_loraallflag(map); + /* Set the designated bits of the flag. */ + flag |= f; + regmap_raw_write(map, SX127X_REG_IRQ_FLAGS, &flag, 1); +} + +/** + * sx127X_clear_loraallflag - Clear designated LoRa device's all IRQ flags + * @map: the device as a regmap to communicate with + */ +#define sx127X_clear_loraallflag(spi) sx127X_clear_loraflag(spi, 0xFF) + +/** + * sx127X_set_lorasprf - Set the RF modulation's spreading factor + * @map: the device as a regmap to communicate with + * @c_s: Spreading factor in chips / symbol + */ +void +sx127X_set_lorasprf(struct regmap *map, u32 c_s) +{ + u8 sf; + u8 mcf2; + + for (sf = 6; sf < 12; sf++) { + if (c_s == ((u32)1 << sf)) + break; + } + + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, &mcf2, 1); + mcf2 = (mcf2 & 0x0F) | (sf << 4); + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG2, &mcf2, 1); +} + +/** + * sx127X_get_lorasprf - Get the RF modulation's spreading factor + * @map: the device as a regmap to communicate with + * + * Return: Spreading factor in chips / symbol + */ +u32 +sx127X_get_lorasprf(struct regmap *map) +{ + u8 sf; + u32 c_s; + + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, &sf, 1); + sf = sf >> 4; + c_s = 1 << sf; + + return c_s; +} + +const u32 hz[] = { + 7800, + 10400, + 15600, + 20800, + 31250, + 41700, + 62500, + 125000, + 250000, + 500000 +}; + +/** + * sx127X_set_lorabw - Set RF bandwidth + * @map: the device as a regmap to communicate with + * @bw: RF bandwidth going to be assigned in Hz + */ +void +sx127X_set_lorabw(struct regmap *map, u32 bw) +{ + u8 i; + u8 mcf1; + + for (i = 0; i < 9; i++) { + if (hz[i] >= bw) + break; + } + + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); + mcf1 = (mcf1 & 0x0F) | (i << 4); + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); +} + +/** + * sx127X_get_lorabw - Get RF bandwidth + * @map: the device as a regmap to communicate with + * + * Return: RF bandwidth in Hz + */ +u32 +sx127X_get_lorabw(struct regmap *map) +{ + u8 mcf1; + u8 bw; + + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); + bw = mcf1 >> 4; + + return hz[bw]; +} + +/** + * sx127X_set_loracr - Set LoRa package's coding rate + * @map: the device as a regmap to communicate with + * @cr: Coding rate going to be assigned in a byte + * high 4 bits / low 4 bits: numerator / denominator + */ +void +sx127X_set_loracr(struct regmap *map, u8 cr) +{ + u8 mcf1; + + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); + mcf1 = (mcf1 & 0x0E) | (((cr & 0xF) - 4) << 1); + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); +} + +/** + * sx127X_get_loracr - Get LoRa package's coding rate + * @map: the device as a regmap to communicate with + * + * Return: Coding rate in a byte + * high 4 bits / low 4 bits: numerator / denominator + */ +u8 +sx127X_get_loracr(struct regmap *map) +{ + u8 mcf1; + u8 cr; /* ex: 0x45 represents cr=4/5 */ + + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); + cr = 0x40 + ((mcf1 & 0x0E) >> 1) + 4; + + return cr; +} + +/** + * sx127X_set_loraimplicit - Set LoRa packages with Explicit / Implicit Header + * @map: the device as a regmap to communicate with + * @yesno: 1 / 0 for Implicit Header Mode / Explicit Header Mode + */ +void +sx127X_set_loraimplicit(struct regmap *map, u8 yesno) +{ + u8 mcf1; + + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); + mcf1 = (yesno) ? (mcf1 | 0x01) : (mcf1 & 0xFE); + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG1, &mcf1, 1); +} + +/** + * sx127X_set_lorarxbytetimeout - Set RX operation time-out in terms of symbols + * @map: the device as a regmap to communicate with + * @n: Time-out in terms of symbols (bytes) going to be assigned + */ +void +sx127X_set_lorarxbytetimeout(struct regmap *map, u32 n) +{ + u8 buf[2]; + u8 mcf2; + + if (n < 1) + n = 1; + if (n > 1023) + n = 1023; + + /* Read original Modem config 2. */ + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, &mcf2, 1); + + /* LSB */ + buf[1] = n % 256; + /* MSB */ + buf[0] = (mcf2 & 0xFC) | (n >> 8); + + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG2, buf, 2); +} + +/** + * sx127X_set_lorarxtimeout - Set RX operation time-out seconds + * @map: the device as a regmap to communicate with + * @ms: The RX time-out time in ms + */ +void +sx127X_set_lorarxtimeout(struct regmap *map, u32 ms) +{ + u32 n; + + n = ms * sx127X_get_lorabw(map) / (sx127X_get_lorasprf(map) * 1000); + + sx127X_set_lorarxbytetimeout(map, n); +} + +/** + * sx127X_get_lorarxbytetimeout - Get RX operation time-out in terms of symbols + * @map: the device as a regmap to communicate with + * + * Return: Time-out in terms of symbols (bytes) + */ +u32 +sx127X_get_lorarxbytetimeout(struct regmap *map) +{ + u32 n; + u8 buf[2]; + + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, buf, 2); + + n = (buf[0] & 0x03) * 256 + buf[1]; + + return n; +} + +/** + * sx127X_get_lorarxtimeout - Get RX operation time-out seconds + * @map: the device as a regmap to communicate with + * + * Return: The RX time-out time in ms + */ +u32 +sx127X_get_lorarxtimeout(struct regmap *map) +{ + u32 ms; + + ms = 1000 * sx127X_get_lorarxbytetimeout(map) * + sx127X_get_lorasprf(map) / sx127X_get_lorabw(map); + + return ms; +} + +/** + * sx127X_set_loramaxrxbuff - Maximum payload length in LoRa packet + * @map: the device as a regmap to communicate with + * @len: the max payload length going to be assigned in bytes + */ +void +sx127X_set_loramaxrxbuff(struct regmap *map, u8 len) +{ + regmap_raw_write(map, SX127X_REG_MAX_PAYLOAD_LENGTH, &len, 1); +} + +/** + * sx127X_get_loralastpktpayloadlen - Get the RX last packet payload length + * @map: the device as a regmap to communicate with + * + * Return: the actual RX last packet payload length in bytes + */ +u8 +sx127X_get_loralastpktpayloadlen(struct regmap *map) +{ + u8 len; + + regmap_raw_read(map, SX127X_REG_RX_NB_BYTES, &len, 1); + + return len; +} + +/** + * sx127X_readloradata - Read data from LoRa device (read RX FIFO) + * @map: the device as a regmap to communicate with + * @buf: buffer going to be read data into + * @len: the length of the data going to be read in bytes + * + * Return: Positive / negtive values for the actual data length read from + * the LoRa device in bytes / failed + */ +ssize_t +sx127X_readloradata(struct regmap *map, u8 *buf, size_t len) +{ + u8 start_adr; + int ret; + + /* Set chip FIFO pointer to FIFO last packet address. */ + start_adr = SX127X_FIFO_RX_BASE_ADDRESS; + regmap_raw_write(map, SX127X_REG_FIFO_ADDR_PTR, &start_adr, 1); + + /* Read LoRa packet payload. */ + len = (len <= IEEE802154_MTU) ? len : IEEE802154_MTU; + ret = regmap_raw_read(map, SX127X_REG_FIFO, buf, len); + + return (ret >= 0) ? len : ret; +} + +/** + * sx127X_sendloradata - Send data through LoRa device (write TX FIFO) + * @rm: the device as a regmap to communicate with + * @buf: buffer going to be send + * @len: the length of the buffer in bytes + * + * Return: the actual length written into the LoRa device in bytes + */ +size_t +sx127X_sendloradata(struct regmap *map, u8 *buf, size_t len) +{ + u8 base_adr; + u8 blen; + + /* Set chip FIFO pointer to FIFO TX base. */ + base_adr = SX127X_FIFO_TX_BASE_ADDRESS; + regmap_raw_write(map, SX127X_REG_FIFO_ADDR_PTR, &base_adr, 1); + + /* Write payload synchronously to fill the FIFO of the chip. */ + blen = (len <= IEEE802154_MTU) ? len : IEEE802154_MTU; + regmap_raw_write(map, SX127X_REG_FIFO, buf, blen); + + /* Set the FIFO payload length. */ + regmap_raw_write(map, SX127X_REG_PAYLOAD_LENGTH, &blen, 1); + + return blen; +} + +/** + * sx127X_get_loralastpktsnr - Get last LoRa packet's SNR + * @map: the device as a regmap to communicate with + * + * Return: the last LoRa packet's SNR in db + */ +s32 +sx127X_get_loralastpktsnr(struct regmap *map) +{ + s32 db; + s8 snr; + + regmap_raw_read(map, SX127X_REG_PKT_SNR_VALUE, &snr, 1); + db = snr / 4; + + return db; +} + +/** + * sx127X_get_loralastpktrssi - Get last LoRa packet's SNR + * @map: the device as a regmap to communicate with + * + * Return: the last LoRa packet's RSSI in dbm + */ +s32 +sx127X_get_loralastpktrssi(struct regmap *map) +{ + s32 dbm; + u8 lhf; + u8 rssi; + s8 snr; + + /* Get LoRa is in high or low frequency mode. */ + lhf = sx127X_get_mode(map) & 0x08; + /* Get RSSI value. */ + regmap_raw_read(map, SX127X_REG_PKT_RSSI_VALUE, &rssi, 1); + dbm = (lhf) ? -164 + rssi : -157 + rssi; + + /* Adjust to correct the last packet RSSI if SNR < 0. */ + regmap_raw_read(map, SX127X_REG_PKT_SNR_VALUE, &snr, 1); + if (snr < 0) + dbm += snr / 4; + + return dbm; +} + +/** + * sx127X_get_lorarssi - Get current RSSI value + * @map: the device as a regmap to communicate with + * + * Return: the current RSSI in dbm + */ +s32 +sx127X_get_lorarssi(struct regmap *map) +{ + s32 dbm; + u8 lhf; + u8 rssi; + + /* Get LoRa is in high or low frequency mode. */ + lhf = sx127X_get_mode(map) & 0x08; + /* Get RSSI value. */ + regmap_raw_read(map, SX127X_REG_RSSI_VALUE, &rssi, 1); + dbm = (lhf) ? -164 + rssi : -157 + rssi; + + return dbm; +} + +/** + * sx127X_set_lorapreamblelen - Set LoRa preamble length + * @map: the device as a regmap to communicate with + * @len: the preamble length going to be assigned + */ +void +sx127X_set_lorapreamblelen(struct regmap *map, u32 len) +{ + u8 pl[2]; + + pl[1] = len % 256; + pl[0] = (len >> 8) % 256; + + regmap_raw_write(map, SX127X_REG_PREAMBLE_MSB, pl, 2); +} + +/** + * sx127X_get_lorapreamblelen - Get LoRa preamble length + * @map: the device as a regmap to communicate with + * + * Return: length of the LoRa preamble + */ +u32 +sx127X_get_lorapreamblelen(struct regmap *map) +{ + u8 pl[2]; + u32 len; + + regmap_raw_read(map, SX127X_REG_PREAMBLE_MSB, pl, 2); + len = pl[0] * 256 + pl[1]; + + return len; +} + +/** + * sx127X_set_loracrc - Enable CRC generation and check on received payload + * @map: the device as a regmap to communicate with + * @yesno: 1 / 0 for check / not check + */ +void +sx127X_set_loracrc(struct regmap *map, u8 yesno) +{ + u8 mcf2; + + regmap_raw_read(map, SX127X_REG_MODEM_CONFIG2, &mcf2, 1); + mcf2 = (yesno) ? mcf2 | (1 << 2) : mcf2 & (~(1 << 2)); + regmap_raw_write(map, SX127X_REG_MODEM_CONFIG2, &mcf2, 1); +} + +/** + * sx127X_set_boost - Set RF power amplifier boost in normal output range + * @map: the device as a regmap to communicate with + * @yesno: 1 / 0 for boost / not boost + */ +void +sx127X_set_boost(struct regmap *map, u8 yesno) +{ + u8 pacf; + + regmap_raw_read(map, SX127X_REG_PA_CONFIG, &pacf, 1); + pacf = (yesno) ? pacf | (1 << 7) : pacf & (~(1 << 7)); + regmap_raw_write(map, SX127X_REG_PA_CONFIG, &pacf, 1); +} + +/** + * sx127X_start_loramode - Start the device and set it in LoRa mode + * @map: the device as a regmap to communicate with + */ +void +sx127X_start_loramode(struct regmap *map) +{ + u8 op_mode; + u8 base_adr; +#ifdef CONFIG_OF + struct device_node *of_node = (regmap_get_device(map))->of_node; +#endif + + /* Get original OP Mode register. */ + op_mode = sx127X_get_mode(map); + dev_dbg(regmap_get_device(map), + "the original OP mode is 0x%X\n", op_mode); + + /* Set device to sleep state. */ + sx127X_set_state(map, SX127X_SLEEP_MODE); + /* Set device to LoRa mode. */ + op_mode = sx127X_get_mode(map); + op_mode = op_mode | 0x80; + regmap_raw_write(map, SX127X_REG_OP_MODE, &op_mode, 1); + /* Set device to standby state. */ + sx127X_set_state(map, SX127X_STANDBY_MODE); + op_mode = sx127X_get_mode(map); + dev_dbg(regmap_get_device(map), + "the current OP mode is 0x%X\n", op_mode); + + /* Set LoRa in explicit header mode. */ + sx127X_set_loraimplicit(map, 0); + + /* Set chip FIFO RX base. */ + base_adr = SX127X_FIFO_RX_BASE_ADDRESS; + regmap_raw_write(map, SX127X_REG_FIFO_RX_BASE_ADDR, &base_adr, 1); + /* Set chip FIFO TX base. */ + base_adr = SX127X_FIFO_TX_BASE_ADDRESS; + regmap_raw_write(map, SX127X_REG_FIFO_TX_BASE_ADDR, &base_adr, 1); + + /* Set the CSS spreading factor. */ +#ifdef CONFIG_OF + of_property_read_u32(of_node, "spreading-factor", &sprf); +#endif + sx127X_set_lorasprf(map, sprf); + + /* Set RX time-out value. */ + sx127X_set_lorarxbytetimeout(map, rx_timeout); + + /* Clear all of the IRQ flags. */ + sx127X_clear_loraallflag(map); + /* Set chip to RX state waiting for receiving. */ + sx127X_set_state(map, SX127X_RXSINGLE_MODE); +} + +/** + * init_sx127x - Initial the SX127X device + * @map: the device as a regmap to communicate with + * + * Return: 0 / negtive values for success / failed + */ +int +init_sx127x(struct regmap *map) +{ + int v; +#ifdef DEBUG + u8 fv, mv; +#endif + + dev_dbg(regmap_get_device(map), "init sx127X\n"); + + v = sx127X_read_version(map); + if (v > 0) { +#ifdef DEBUG + fv = (v >> 4) & 0xF; + mv = v & 0xF; + dev_dbg(regmap_get_device(map), "chip version %d.%d\n", fv, mv); +#endif + return 0; + } else { + return -ENODEV; + } +} + +/*---------------------- SX1278 IEEE 802.15.4 Functions ----------------------*/ + +/* LoRa device's sensitivity in dbm. */ +#ifndef SX1278_IEEE_SENSITIVITY +#define SX1278_IEEE_SENSITIVITY (-148) +#endif +static s32 sensitivity = SX1278_IEEE_SENSITIVITY; +module_param(sensitivity, int, 0000); +MODULE_PARM_DESC(sensitivity, "RF receiver's sensitivity"); + +#define SX1278_IEEE_ENERGY_RANGE (-sensitivity) + +static int +sx1278_ieee_ed(struct ieee802154_hw *hw, u8 *level) +{ + struct sx1278_phy *phy = hw->priv; + s32 rssi; + s32 range = SX1278_IEEE_ENERGY_RANGE - 10; + + dev_dbg(regmap_get_device(phy->map), "%s\n", __func__); + + /* ED: IEEE 802.15.4-2011 8.2.5 Recevier ED. */ + rssi = sx127X_get_lorarssi(phy->map); + if (rssi < (sensitivity + 10)) + *level = 0; + else if (rssi >= 0) + *level = 255; + else + *level = ((s32)255 * (rssi + range) / range) % 255; + + return 0; +} + +#ifndef SX1278_IEEE_CHANNEL_MIN +#define SX1278_IEEE_CHANNEL_MIN 11 +#endif +static u8 channel_min = SX1278_IEEE_CHANNEL_MIN; +module_param(channel_min, byte, 0000); +MODULE_PARM_DESC(channel_min, "Minimal channel number"); + +#ifndef SX1278_IEEE_CHANNEL_MAX +#define SX1278_IEEE_CHANNEL_MAX 11 +#endif +static u8 channel_max = SX1278_IEEE_CHANNEL_MAX; +module_param(channel_max, byte, 0000); +MODULE_PARM_DESC(channel_max, "Maximum channel number"); + +#ifndef SX1278_IEEE_CENTER_CARRIER_FRQ +#define SX1278_IEEE_CENTER_CARRIER_FRQ 434000000 +#endif +static u32 carrier_frq = SX1278_IEEE_CENTER_CARRIER_FRQ; +module_param(carrier_frq, uint, 0000); +MODULE_PARM_DESC(carrier_frq, "Center carrier frequency in Hz"); + +#ifndef SX1278_IEEE_BANDWIDTH +#define SX1278_IEEE_BANDWIDTH 500000 +#endif +static u32 bandwidth = SX1278_IEEE_BANDWIDTH; +module_param(bandwidth, uint, 0000); +MODULE_PARM_DESC(bandwidth, "Bandwidth in Hz"); + +struct rf_frq { + u32 carrier; + u32 bw; + u8 ch_min; + u8 ch_max; +}; + +void +sx1278_ieee_get_rf_config(struct ieee802154_hw *hw, struct rf_frq *rf) +{ +#ifdef CONFIG_OF + struct sx1278_phy *phy = hw->priv; + struct device_node *of_node = (regmap_get_device(phy->map))->of_node; + + /* Set the LoRa chip's center carrier frequency. */ + if (of_property_read_u32(of_node, "center-carrier-frq", &rf->carrier)) + rf->carrier = carrier_frq; + + /* Set the LoRa chip's RF bandwidth. */ + if (of_property_read_u32(of_node, "rf-bandwidth", &rf->carrier)) + rf->bw = bandwidth; + + /* Set the LoRa chip's min & max RF channel if OF is defined. */ + if (of_property_read_u8(of_node, "minimal-RF-channel", &rf->ch_min)) + rf->ch_min = channel_min; + + if (of_property_read_u8(of_node, "maximum-RF-channel", &rf->ch_max)) + rf->ch_max = channel_max; +#else + rf->carrier = carrier_frq; + rf->bw = bandwidth; + rf->ch_min = channel_min; + rf->ch_max = channel_max; +#endif +} + +static int +sx1278_ieee_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) +{ + struct sx1278_phy *phy = hw->priv; + struct rf_frq rf; + u32 fr; + s8 d; + + dev_dbg(regmap_get_device(phy->map), + "%s channel: %u", __func__, channel); + + sx1278_ieee_get_rf_config(hw, &rf); + + if (channel < rf.ch_min) + channel = rf.ch_min; + else if (channel > rf.ch_max) + channel = rf.ch_max; + + d = channel - (rf.ch_min + rf.ch_max) / 2; + fr = rf.carrier + d * rf.bw; + + sx127X_set_lorafrq(phy->map, fr); + + return 0; +} + +/* in mbm */ +s32 sx1278_powers[] = { + -200, -100, 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, + 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300}; + +static int +sx1278_ieee_set_txpower(struct ieee802154_hw *hw, s32 mbm) +{ + struct sx1278_phy *phy = hw->priv; + s32 dbm = sx127X_mbm2dbm(mbm); + + dev_dbg(regmap_get_device(phy->map), + "%s TX power: %d mbm", __func__, mbm); + + sx127X_set_lorapower(phy->map, dbm); + + return 0; +} + +int +sx1278_ieee_rx(struct ieee802154_hw *hw) +{ + struct sx1278_phy *phy = hw->priv; + bool do_rx; + + dev_dbg(regmap_get_device(phy->map), "%s\n", __func__); + + spin_lock(&phy->buf_lock); + if (!phy->is_busy) { + phy->is_busy = true; + do_rx = true; + } else { + do_rx = false; + } + spin_unlock(&phy->buf_lock); + + if (do_rx) { + sx127X_set_state(phy->map, SX127X_RXSINGLE_MODE); + return 0; + } else { + return -EBUSY; + } +} + +static int +sx1278_ieee_rx_complete(struct ieee802154_hw *hw) +{ + struct sx1278_phy *phy = hw->priv; + struct sk_buff *skb; + u8 len; + u8 lqi; + s32 rssi; + s32 range = SX1278_IEEE_ENERGY_RANGE; + int err; + + skb = dev_alloc_skb(IEEE802154_MTU); + if (!skb) { + err = -ENOMEM; + goto sx1278_ieee_rx_err; + } + + len = sx127X_get_loralastpktpayloadlen(phy->map); + sx127X_readloradata(phy->map, skb_put(skb, len), len); + + /* LQI: IEEE 802.15.4-2011 8.2.6 Link quality indicator. */ + rssi = sx127X_get_loralastpktrssi(phy->map); + rssi = (rssi > 0) ? 0 : rssi; + lqi = ((s32)255 * (rssi + range) / range) % 255; + + ieee802154_rx_irqsafe(hw, skb, lqi); + + spin_lock(&phy->buf_lock); + phy->is_busy = false; + spin_unlock(&phy->buf_lock); + + dev_dbg(regmap_get_device(phy->map), + "%s: len=%u LQI=%u\n", __func__, len, lqi); + + return 0; + +sx1278_ieee_rx_err: + return err; +} + +int +sx1278_ieee_tx(struct ieee802154_hw *hw) +{ + struct sx1278_phy *phy = hw->priv; + struct sk_buff *tx_buf = phy->tx_buf; + bool do_tx = false; + + dev_dbg(regmap_get_device(phy->map), + "%s: len=%u\n", __func__, tx_buf->len); + + if (!phy->post_tx_done) { + sx127X_sendloradata(phy->map, tx_buf->data, tx_buf->len); + phy->post_tx_done = true; + } + + spin_lock(&phy->buf_lock); + if (!phy->is_busy) { + phy->is_busy = true; + do_tx = true; + phy->one_to_be_sent = false; + } + spin_unlock(&phy->buf_lock); + + if (do_tx) { + /* Set chip as TX state and transfer the data in FIFO. */ + phy->opmode = (phy->opmode & 0xF8) | SX127X_TX_MODE; + regmap_write_async(phy->map, SX127X_REG_OP_MODE, phy->opmode); + return 0; + } else { + return -EBUSY; + } +} + +static int +sx1278_ieee_tx_complete(struct ieee802154_hw *hw) +{ + struct sx1278_phy *phy = hw->priv; + struct sk_buff *skb = phy->tx_buf; + + dev_dbg(regmap_get_device(phy->map), "%s\n", __func__); + + ieee802154_xmit_complete(hw, skb, false); + + spin_lock(&phy->buf_lock); + phy->is_busy = false; + phy->tx_buf = NULL; + spin_unlock(&phy->buf_lock); + + return 0; +} + +static int +sx1278_ieee_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) +{ + struct sx1278_phy *phy = hw->priv; + int ret; + + dev_dbg(regmap_get_device(phy->map), "%s\n", __func__); + + WARN_ON(phy->suspended); + + spin_lock(&phy->buf_lock); + if (phy->tx_buf) { + ret = -EBUSY; + } else { + phy->tx_buf = skb; + phy->one_to_be_sent = true; + phy->post_tx_done = false; + ret = 0; + } + spin_unlock(&phy->buf_lock); + + return ret; +} + +static int +sx1278_ieee_start(struct ieee802154_hw *hw) +{ + struct sx1278_phy *phy = hw->priv; + + dev_dbg(regmap_get_device(phy->map), "interface up\n"); + + phy->suspended = false; + sx127X_start_loramode(phy->map); + phy->opmode = sx127X_get_mode(phy->map); + add_timer(&phy->timer); + + return 0; +} + +static void +sx1278_ieee_stop(struct ieee802154_hw *hw) +{ + struct sx1278_phy *phy = hw->priv; + + dev_dbg(regmap_get_device(phy->map), "interface down\n"); + + phy->suspended = true; + del_timer(&phy->timer); + sx127X_set_state(phy->map, SX127X_SLEEP_MODE); +} + +static int +sx1278_ieee_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) +{ + return 0; +} + +void +sx1278_ieee_statemachine(struct ieee802154_hw *hw) +{ + struct sx1278_phy *phy = hw->priv; + u8 flags; + u8 state; + bool do_next_rx = false; + + flags = sx127X_get_loraallflag(phy->map); + state = sx127X_get_state(phy->map); + + if (flags & (SX127X_FLAG_RXTIMEOUT | SX127X_FLAG_PAYLOADCRCERROR)) { + sx127X_clear_loraflag(phy->map, SX127X_FLAG_RXTIMEOUT + | SX127X_FLAG_PAYLOADCRCERROR + | SX127X_FLAG_RXDONE); + spin_lock(&phy->buf_lock); + phy->is_busy = false; + spin_unlock(&phy->buf_lock); + do_next_rx = true; + } else if (flags & SX127X_FLAG_RXDONE) { + sx1278_ieee_rx_complete(phy->hw); + sx127X_clear_loraflag(phy->map, SX127X_FLAG_RXDONE); + do_next_rx = true; + } + + if (flags & SX127X_FLAG_TXDONE) { + sx1278_ieee_tx_complete(phy->hw); + sx127X_clear_loraflag(phy->map, SX127X_FLAG_TXDONE); + phy->tx_delay = 10; + do_next_rx = true; + } + + if (phy->one_to_be_sent && + (state == SX127X_STANDBY_MODE) && + (phy->tx_delay == 0)) { + if (!sx1278_ieee_tx(phy->hw)) + do_next_rx = false; + } + + if (do_next_rx) + sx1278_ieee_rx(phy->hw); + + if (phy->tx_delay > 0) + phy->tx_delay -= 1; + + if (!phy->suspended) { + phy->timer.expires = jiffies_64 + 1; + add_timer(&phy->timer); + } +} + +/** + * sx1278_timer_irqwork - The actual work which checks the IRQ flags of the chip + * @work: the work entry listed in the workqueue + */ +static void +sx1278_timer_irqwork(struct work_struct *work) +{ + struct sx1278_phy *phy; + + phy = container_of(work, struct sx1278_phy, irqwork); + sx1278_ieee_statemachine(phy->hw); +} + +/** + * sx1278_timer_isr - Callback function for the timer interrupt + * @arg: the general argument for this callback function + */ +static void +sx1278_timer_isr(unsigned long arg) +{ + struct sx1278_phy *phy = (struct sx1278_phy *)arg; + + schedule_work(&phy->irqwork); +} + +static const struct ieee802154_ops sx1278_ops = { + .owner = THIS_MODULE, + .xmit_async = sx1278_ieee_xmit, + .ed = sx1278_ieee_ed, + .set_channel = sx1278_ieee_set_channel, + .set_txpower = sx1278_ieee_set_txpower, + .start = sx1278_ieee_start, + .stop = sx1278_ieee_stop, + .set_promiscuous_mode = sx1278_ieee_set_promiscuous_mode, +}; + +/** + * sx1278X_ieee_channel_mask - Get the available channels' mask of LoRa device + * @hw: LoRa IEEE 802.15.4 device + * + * Return: The bitwise channel mask in 4 bytes + */ +u32 +sx1278_ieee_channel_mask(struct ieee802154_hw *hw) +{ + struct rf_frq rf; + u32 mask; + + sx1278_ieee_get_rf_config(hw, &rf); + + mask = ((u32)(1 << (rf.ch_max + 1)) - (u32)(1 << rf.ch_min)); + + return mask; +} + +static int +sx1278_ieee_add_one(struct sx1278_phy *phy) +{ + struct ieee802154_hw *hw = phy->hw; + int err; + + /* Define channels could be used. */ + hw->phy->supported.channels[0] = sx1278_ieee_channel_mask(hw); + /* SX1278 phy channel 11 as default */ + hw->phy->current_channel = 11; + + /* Define RF power. */ + hw->phy->supported.tx_powers = sx1278_powers; + hw->phy->supported.tx_powers_size = ARRAY_SIZE(sx1278_powers); + hw->phy->transmit_power = sx1278_powers[12]; + + ieee802154_random_extended_addr(&hw->phy->perm_extended_addr); + hw->flags = IEEE802154_HW_TX_OMIT_CKSUM + | IEEE802154_HW_RX_OMIT_CKSUM + | IEEE802154_HW_PROMISCUOUS; + + err = ieee802154_register_hw(hw); + if (err) + goto err_reg; + + INIT_WORK(&phy->irqwork, sx1278_timer_irqwork); + + init_timer(&phy->timer); + phy->timer.expires = jiffies_64 + HZ; + phy->timer.function = sx1278_timer_isr; + phy->timer.data = (unsigned long)phy; + + spin_lock_init(&phy->buf_lock); + + err = init_sx127x(phy->map); + if (err) + goto err_reg; + + return 0; + +err_reg: + dev_err(regmap_get_device(phy->map), + "register as IEEE 802.15.4 device failed\n"); + return err; +} + +static void +sx1278_ieee_del(struct sx1278_phy *phy) +{ + if (!phy) + return; + + del_timer(&phy->timer); + flush_work(&phy->irqwork); + + ieee802154_unregister_hw(phy->hw); + ieee802154_free_hw(phy->hw); +} + +/*--------------------------- SX1278 SPI Functions ---------------------------*/ + +/* The compatible chip array. */ +#ifdef CONFIG_OF +static const struct of_device_id sx1278_dt_ids[] = { + { .compatible = "semtech,sx1276" }, + { .compatible = "semtech,sx1277" }, + { .compatible = "semtech,sx1278" }, + { .compatible = "semtech,sx1279" }, + { .compatible = "sx1278" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sx1278_dt_ids); +#endif + +/* The compatible ACPI device array. */ +#ifdef CONFIG_ACPI +static const struct acpi_device_id sx1278_acpi_ids[] = { + { .id = "sx1278" }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, sx1278_acpi_ids); +#endif + +/* The compatible SPI device id array. */ +static const struct spi_device_id sx1278_spi_ids[] = { + { .name = "sx1278" }, + {}, +}; +MODULE_DEVICE_TABLE(spi, sx1278_spi_ids); + +bool sx1278_reg_volatile(struct device *dev, unsigned int reg) +{ + return true; +} + +/* The SX1278 regmap config. */ +struct regmap_config sx1278_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = SX127X_MAX_REG, + .read_flag_mask = 0x00, + .write_flag_mask = 0x80, + .volatile_reg = sx1278_reg_volatile, +}; + +/* The SPI probe callback function. */ +static int sx1278_spi_probe(struct spi_device *spi) +{ + struct ieee802154_hw *hw; + struct sx1278_phy *phy; + int err; + + hw = ieee802154_alloc_hw(sizeof(*phy), &sx1278_ops); + if (!hw) { + dev_err(&spi->dev, "not enough memory\n"); + return -ENOMEM; + } + + phy = hw->priv; + phy->hw = hw; + hw->parent = &spi->dev; + phy->map = devm_regmap_init_spi(spi, &sx1278_regmap_config); + + /* Set the SPI device's driver data for later usage. */ + spi_set_drvdata(spi, phy); + + err = sx1278_ieee_add_one(phy); + if (err < 0) { + dev_err(&spi->dev, "no SX1278 compatible device\n"); + goto sx1278_spi_probe_err; + } + + dev_info(&spi->dev, + "add an IEEE 802.15.4 over LoRa SX1278 compatible device\n"); + + return 0; + +sx1278_spi_probe_err: + sx1278_ieee_del(phy); + return err; +} + +/* The SPI remove callback function. */ +static int sx1278_spi_remove(struct spi_device *spi) +{ + struct sx1278_phy *phy = spi_get_drvdata(spi); + + sx1278_ieee_del(phy); + + return 0; +} + +#define __DRIVER_NAME "sx1278" + +/* The SPI driver which acts as a protocol driver in this kernel module. */ +static struct spi_driver sx1278_spi_driver = { + .driver = { + .name = __DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(sx1278_dt_ids), +#endif +#ifdef CONFIG_ACPI + .acpi_match_table = ACPI_PTR(sx1278_acpi_ids), +#endif + }, + .probe = sx1278_spi_probe, + .remove = sx1278_spi_remove, + .id_table = sx1278_spi_ids, +}; + +/* Register SX1278 kernel module. */ +module_spi_driver(sx1278_spi_driver); + +MODULE_AUTHOR("Jian-Hong Pan, <starnight@g.ncu.edu.tw>"); +MODULE_DESCRIPTION("LoRa device SX1278 driver with IEEE 802.15.4 interface"); +MODULE_LICENSE("Dual BSD/GPL");
LoRa is an implementation of LPWPAN. Chirp Spread Spectrum (CSS) modulation, which is used by LoRa has been specified by IEEE 802.15.4a. This driver implements IEEE 802.15.4 mac over LoRa physical layer not LoRaWAN. Signed-off-by: Jian-Hong Pan <starnight@g.ncu.edu.tw> --- .../devicetree/bindings/net/ieee802154/sx1278.txt | 30 + drivers/net/ieee802154/Kconfig | 12 + drivers/net/ieee802154/Makefile | 1 + drivers/net/ieee802154/sx1278.c | 1636 ++++++++++++++++++++ 4 files changed, 1679 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/ieee802154/sx1278.txt create mode 100644 drivers/net/ieee802154/sx1278.c