Hello,
LoRa is a long-range, low-power wireless technology by Semtech.
Unlike other LPWAN technologies, users don't need to rely on infrastructure
providers and SIM cards and expensive subscription plans, they can set up
their own gateways. Modules, adapters and evaluation boards are available
from a large number of vendors.
Many vendors also make available Open Source software examples on GitHub.
But when taking a closer look, many of them combine licenses in ways that are
not redistributable. My reports have remained without response or solution.
https://github.com/ernstdevreede/lmic_pi/issues/2
https://github.com/Snootlab/lmic_chisterapi/issues/2
https://github.com/Snootlab/lora_chisterapi/issues/2
Another issue was that most such projects around the Raspberry Pi make use of
spidev to communicate with the Semtech chipsets from userspace. The Linux spi
maintainers have chosen to greet any such users of spidev with a friendly
WARN_ON(), preferring in-kernel spi drivers and white-listing individual
devices only.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/spi/spidev.c?h=v4.17#n722
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/spi/spidev.c?h=v4.17#n667
Also I don't quite see the point in having userspace probe what SPI devices
are connected to a generic spidev driver when we have an easy Device Tree
hardware description on arm/arm64 that could give us that info.
I raised the topic during Q&A of a FOSDEM 2017 talk (cut off at the end
of the video) but unfortunately found no one to collaborate on this.
https://archive.fosdem.org/2017/schedule/event/lorawan/
Instead of porting from wiringPi to a differently licensed GPIO library
and dealing with seemingly unmaintained LoRaWAN code dumps, I started a
spi kernel driver for SX1276 back in 2016. But obviously a kernel driver
isn't too helpful without a userspace API to send and receive packets.
This patchset, updated from 2017 and extended, is implementing kernel drivers
for various LoRa chipsets and modules. As API I'm proposing a PF_LORA socket
implementation. Why? LoRa uses data packets with headers and checksums
and differing MTUs and multiple protocols layered on top of it. Apart from
simple headers for addressing used by RadioHead library and IMST's LoRa P2P
protocol, the main use case (not implemented in this patchset) is expected
to be LoRaWAN. And LoRaWAN has competing proprietary protocols, such as
Link Labs' Symphony Link or GlobalSat M.O.S.T. or RadioShuttle, that might
at some point want to adopt a standard API for their implementations, too.
Ready-made LoRa hardware modules come in three flavors,
a) with SPI access to the underlying Semtech chipsets, needing a software
implementation of e.g. LoRaWAN protocol stack (i.e., a soft MAC),
b) with a custom, often UART based interface and a pre-certified LoRaWAN
protocol stack already integrated (i.e., hard/full MAC), and
c) with a microcontroller that serves not only for the protocol stack but
also as application processor, not offering a ready-made interface.
This patchset focuses on option a). An SX1276 based LoRaWAN stack appeared
to be the project of Jian-Hong Pan and is not included here.
This patchset also includes drivers for b), from text based AT commands to
a binary SLIP based HCI protocol.
Hardware examples for c) are Murata CMWX1ZZABZ-078 and RAK813.
This patchset is clearly not ready for merging, but is being submitted for
discussion, as requested by Jiri, in particular of the design choices:
1) PF_LORA/AF_LORA and associated identifiers are proposed to represent
this technology. While for an SX1276 - case a) above - it might work to
layer LoRaWAN as a protocol option for PF_LORA and add LoRaWAN address
fields to the union in my sockaddr_lora, how would that work for devices
that only support LoRaWAN but not pure LoRa? Do we need both AF_LORA and
AF_LORAWAN, or just a separate ETH_P_LORAWAN or ARPHRD_LORAWAN?
2) PF_LORA is used with SOCK_DGRAM here. The assumption is that RAW mode
would be DGRAM plus preamble plus optional checksum.
3) Only the transmit path is partially implemented already. The assumption
is that the devices should go into receive mode by default and only
interrupt that when asked to transmit.
4) Some hardware settings need to be supplied externally, such as the radio
frequency for some modules, but many others can be runtime-configured,
such as Spreading Factor, Bandwidth, Sync Word, or which antenna to use.
What settings should be implemented as socket option vs. netlink layer
vs. ioctl vs. sysfs? What are the criteria to apply?
5) Many of the modules support multiple modes, such as LoRa, LoRaWAN and FSK.
Lacking a LoRaWAN implementation, I am currently switching them into LoRa
mode at probe time wherever possible. How do we deal with that properly?
a) Is there any precedence from the Wifi world for dynamically selecting
between our own trusted Open Source implementation vs. hardware/firmware
accelerated and/or certified implementations?
b) Would a proof of concept for FSK (non-LoRa) modes be required for
merging any LoRa driver for chipsets that support both? Or is there any
facility or design guidelines that would allow us to focus on LoRa and
LoRaWAN and leave non-LoRa radio modes to later contributors?
As evident by the many questions, this is my first deep dive into the Linux
net subsystem. It's also my first experiments with the new serdev subsystem,
so in particular the receive paths will need some review and optimizations.
This patchset was developed and tested mainly as KMP, originally at
https://github.com/afaerber/lora-modules. It was recently transformed into a
linux-next based tree, still mostly tested on our openSUSE Tumbleweed kernel
with a differing AF_LORA value below current AF_MAX limit.
Some corresponding Device Tree Overlays have been collected here:
https://github.com/afaerber/dt-overlays
Only European models for 868 MHz and 433 MHz could be tested when available.
Thanks to all companies and people that have supported this project so far.
Have a lot of fun!
Cheers,
Andreas
Cc: Jian-Hong Pan <[email protected]>
Cc: Jiri Pirko <[email protected]>
Cc: Marcel Holtmann <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Matthias Brugger <[email protected]>
Cc: Konstantin Böhm <[email protected]>
Cc: Jan Jongboom <[email protected]>
Cc: Janus Piwek <[email protected]>
Cc: Michael Röder <[email protected]>
Cc: Dollar Chen (陳義元) <[email protected]>
Cc: Ken Yu (禹凯) <[email protected]>
Cc: Jon Ortego <[email protected]>
Cc: [email protected]
Cc: Ben Whitten <[email protected]>
Cc: Brian Ray <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: Alexander Graf <[email protected]>
Cc: Michal Kubeček <[email protected]>
Cc: Rob Herring <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: Steve deRosier <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: [email protected]
Andreas Färber (15):
net: Reserve protocol numbers for LoRa
net: lora: Define sockaddr_lora
net: lora: Add protocol numbers
net: Add lora subsystem
HACK: net: lora: Deal with .poll_mask in 4.18-rc2
net: lora: Prepare for device drivers
net: lora: Add Semtech SX1276
net: lora: sx1276: Add debugfs
net: lora: Prepare EUI helpers
net: lora: Add Microchip RN2483
net: lora: Add IMST WiMOD
net: lora: Add USI WM-SG-SM-42
net: lora: Prepare RAK RAK811
net: lora: Prepare Semtech SX1257
net: lora: Add Semtech SX1301
drivers/net/Makefile | 1 +
drivers/net/lora/Kconfig | 72 ++++
drivers/net/lora/Makefile | 32 ++
drivers/net/lora/dev.c | 125 ++++++
drivers/net/lora/rak811.c | 219 +++++++++++
drivers/net/lora/rn2483.c | 344 +++++++++++++++++
drivers/net/lora/rn2483.h | 40 ++
drivers/net/lora/rn2483_cmd.c | 130 +++++++
drivers/net/lora/sx1257.c | 96 +++++
drivers/net/lora/sx1276.c | 740 ++++++++++++++++++++++++++++++++++++
drivers/net/lora/sx1301.c | 446 ++++++++++++++++++++++
drivers/net/lora/usi.c | 411 ++++++++++++++++++++
drivers/net/lora/wimod.c | 597 +++++++++++++++++++++++++++++
include/linux/lora/dev.h | 44 +++
include/linux/lora/skb.h | 29 ++
include/linux/socket.h | 4 +-
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 1 +
include/uapi/linux/lora.h | 24 ++
net/Kconfig | 1 +
net/Makefile | 1 +
net/lora/Kconfig | 15 +
net/lora/Makefile | 8 +
net/lora/af_lora.c | 152 ++++++++
net/lora/af_lora.h | 13 +
net/lora/dgram.c | 297 +++++++++++++++
security/selinux/hooks.c | 4 +-
security/selinux/include/classmap.h | 4 +-
28 files changed, 3848 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/lora/Kconfig
create mode 100644 drivers/net/lora/Makefile
create mode 100644 drivers/net/lora/dev.c
create mode 100644 drivers/net/lora/rak811.c
create mode 100644 drivers/net/lora/rn2483.c
create mode 100644 drivers/net/lora/rn2483.h
create mode 100644 drivers/net/lora/rn2483_cmd.c
create mode 100644 drivers/net/lora/sx1257.c
create mode 100644 drivers/net/lora/sx1276.c
create mode 100644 drivers/net/lora/sx1301.c
create mode 100644 drivers/net/lora/usi.c
create mode 100644 drivers/net/lora/wimod.c
create mode 100644 include/linux/lora/dev.h
create mode 100644 include/linux/lora/skb.h
create mode 100644 include/uapi/linux/lora.h
create mode 100644 net/lora/Kconfig
create mode 100644 net/lora/Makefile
create mode 100644 net/lora/af_lora.c
create mode 100644 net/lora/af_lora.h
create mode 100644 net/lora/dgram.c
--
2.16.4
These will be used by the RN2483 and other LoRaWAN capable modules.
Signed-off-by: Andreas Färber <[email protected]>
---
include/linux/lora/dev.h | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/include/linux/lora/dev.h b/include/linux/lora/dev.h
index 531e68f0c9a6..153f9b2992ca 100644
--- a/include/linux/lora/dev.h
+++ b/include/linux/lora/dev.h
@@ -9,6 +9,27 @@
#include <linux/netdevice.h>
+typedef u8 lora_eui[8];
+
+#define PRIxLORAEUI "%02x%02x%02x%02x%02x%02x%02x%02x"
+#define PRIXLORAEUI "%02X%02X%02X%02X%02X%02X%02X%02X"
+#define LORA_EUI(x) x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]
+
+static inline int lora_strtoeui(const char *str, lora_eui *val)
+{
+ char buf[3];
+ int i, ret;
+
+ for (i = 0; i < 8; i++) {
+ strncpy(buf, str + i * 2, 2);
+ buf[2] = 0;
+ ret = kstrtou8(buf, 16, &(*val)[i]);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
struct net_device *alloc_loradev(int sizeof_priv);
void free_loradev(struct net_device *dev);
int register_loradev(struct net_device *dev);
--
2.16.4
Allow some interactive inspection at runtime via debugfs.
Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/lora/sx1276.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 132 insertions(+)
diff --git a/drivers/net/lora/sx1276.c b/drivers/net/lora/sx1276.c
index d6732111247a..1072019cfcc1 100644
--- a/drivers/net/lora/sx1276.c
+++ b/drivers/net/lora/sx1276.c
@@ -5,6 +5,7 @@
* Copyright (c) 2016-2018 Andreas Färber
*/
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/lora.h>
#include <linux/module.h>
@@ -61,6 +62,8 @@ struct sx1276_priv {
struct workqueue_struct *wq;
struct work_struct tx_work;
+
+ struct dentry *debugfs;
};
static int sx1276_read_single(struct spi_device *spi, u8 reg, u8 *val)
@@ -416,6 +419,128 @@ static const struct net_device_ops sx1276_netdev_ops = {
.ndo_start_xmit = sx1276_loradev_start_xmit,
};
+static ssize_t sx1276_freq_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct net_device *netdev = file->private_data;
+ struct sx1276_priv *priv = netdev_priv(netdev);
+ struct spi_device *spi = priv->spi;
+ ssize_t size;
+ char *buf;
+ int ret;
+ u8 msb, mid, lsb;
+ u32 freq_xosc;
+ unsigned long long frf;
+
+ ret = of_property_read_u32(spi->dev.of_node, "clock-frequency", &freq_xosc);
+ if (ret)
+ return 0;
+
+ mutex_lock(&priv->spi_lock);
+
+ ret = sx1276_read_single(spi, REG_FRF_MSB, &msb);
+ if (!ret)
+ ret = sx1276_read_single(spi, REG_FRF_MID, &mid);
+ if (!ret)
+ ret = sx1276_read_single(spi, REG_FRF_LSB, &lsb);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret)
+ return 0;
+
+ frf = freq_xosc;
+ frf *= ((ulong)msb << 16) | ((ulong)mid << 8) | lsb;
+ frf /= (1 << 19);
+
+ buf = kasprintf(GFP_KERNEL, "%llu\n", frf);
+ if (!buf)
+ return 0;
+
+ size = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+ kfree(buf);
+
+ return size;
+}
+
+static const struct file_operations sx1276_freq_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = sx1276_freq_read,
+};
+
+static ssize_t sx1276_state_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct net_device *netdev = file->private_data;
+ struct sx1276_priv *priv = netdev_priv(netdev);
+ struct spi_device *spi = priv->spi;
+ ssize_t size;
+ char *buf;
+ int len = 0;
+ int ret;
+ u8 val;
+ bool lora_mode = true;
+ const int max_len = 4096;
+
+ buf = kzalloc(max_len, GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ mutex_lock(&priv->spi_lock);
+
+ ret = sx1276_read_single(spi, REG_OPMODE, &val);
+ if (!ret) {
+ len += snprintf(buf + len, max_len - len, "RegOpMode = 0x%02x\n", val);
+ lora_mode = (val & REG_OPMODE_LONG_RANGE_MODE) != 0;
+ }
+
+ ret = sx1276_read_single(spi, REG_FRF_MSB, &val);
+ if (!ret)
+ len += snprintf(buf + len, max_len - len, "RegFrMsb = 0x%02x\n", val);
+ ret = sx1276_read_single(spi, REG_FRF_MID, &val);
+ if (!ret)
+ len += snprintf(buf + len, max_len - len, "RegFrMid = 0x%02x\n", val);
+ ret = sx1276_read_single(spi, REG_FRF_LSB, &val);
+ if (!ret)
+ len += snprintf(buf + len, max_len - len, "RegFrLsb = 0x%02x\n", val);
+
+ ret = sx1276_read_single(spi, REG_PA_CONFIG, &val);
+ if (!ret)
+ len += snprintf(buf + len, max_len - len, "RegPaConfig = 0x%02x\n", val);
+
+ if (lora_mode) {
+ ret = sx1276_read_single(spi, LORA_REG_IRQ_FLAGS_MASK, &val);
+ if (!ret)
+ len += snprintf(buf + len, max_len - len, "RegIrqFlagsMask = 0x%02x\n", val);
+
+ ret = sx1276_read_single(spi, LORA_REG_IRQ_FLAGS, &val);
+ if (!ret)
+ len += snprintf(buf + len, max_len - len, "RegIrqFlags = 0x%02x\n", val);
+
+ ret = sx1276_read_single(spi, LORA_REG_SYNC_WORD, &val);
+ if (!ret)
+ len += snprintf(buf + len, max_len - len, "RegSyncWord = 0x%02x\n", val);
+ }
+
+ ret = sx1276_read_single(spi, REG_PA_DAC, &val);
+ if (!ret)
+ len += snprintf(buf + len, max_len - len, "RegPaDac = 0x%02x\n", val);
+
+ mutex_unlock(&priv->spi_lock);
+
+ size = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+
+ return size;
+}
+
+static const struct file_operations sx1276_state_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = sx1276_state_read,
+};
+
static int sx1276_probe(struct spi_device *spi)
{
struct net_device *netdev;
@@ -566,6 +691,10 @@ static int sx1276_probe(struct spi_device *spi)
return ret;
}
+ priv->debugfs = debugfs_create_dir(dev_name(&spi->dev), NULL);
+ debugfs_create_file("state", S_IRUGO, priv->debugfs, netdev, &sx1276_state_fops);
+ debugfs_create_file("frequency", S_IRUGO, priv->debugfs, netdev, &sx1276_freq_fops);
+
dev_info(&spi->dev, "SX1276 module probed (SX%d)", model);
return 0;
@@ -574,6 +703,9 @@ static int sx1276_probe(struct spi_device *spi)
static int sx1276_remove(struct spi_device *spi)
{
struct net_device *netdev = spi_get_drvdata(spi);
+ struct sx1276_priv *priv = netdev_priv(netdev);
+
+ debugfs_remove_recursive(priv->debugfs);
unregister_loradev(netdev);
free_loradev(netdev);
--
2.16.4
Default protocol implementation will be datagram.
No other protocols are implemented yet.
Q: Would these protocol numbers be the suitable place to define LoRaWAN?
Signed-off-by: Andreas Färber <[email protected]>
---
include/uapi/linux/lora.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/include/uapi/linux/lora.h b/include/uapi/linux/lora.h
index 9368e8a84f3a..4ff00b9c3c20 100644
--- a/include/uapi/linux/lora.h
+++ b/include/uapi/linux/lora.h
@@ -10,6 +10,10 @@
#include <linux/types.h>
#include <linux/socket.h>
+/* particular protocols of the protocol family PF_LORA */
+#define LORA_PROTO_DATAGRAM 0
+#define LORA_NPROTO 1
+
struct sockaddr_lora {
__kernel_sa_family_t lora_family;
int lora_ifindex;
--
2.16.4
The Microchip RN2483 and RN2903 are UART based modules exposing both
LoRaWAN and LoRa. The RN2483 supports switching between 433 and 868 MHz.
Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/lora/Kconfig | 7 +
drivers/net/lora/Makefile | 4 +
drivers/net/lora/rn2483.c | 344 ++++++++++++++++++++++++++++++++++++++++++
drivers/net/lora/rn2483.h | 40 +++++
drivers/net/lora/rn2483_cmd.c | 130 ++++++++++++++++
5 files changed, 525 insertions(+)
create mode 100644 drivers/net/lora/rn2483.c
create mode 100644 drivers/net/lora/rn2483.h
create mode 100644 drivers/net/lora/rn2483_cmd.c
diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
index 0436f6b09a1c..940bd2cbe106 100644
--- a/drivers/net/lora/Kconfig
+++ b/drivers/net/lora/Kconfig
@@ -17,6 +17,13 @@ config LORA_DEV
if LORA_DEV
+config LORA_RN2483
+ tristate "Microchip RN2483/RN2903 driver"
+ default y
+ depends on SERIAL_DEV_BUS
+ help
+ Microchip RN2483/2903
+
config LORA_SX1276
tristate "Semtech SX127x SPI driver"
default y
diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile
index 8845542dba50..07839c3ce9f8 100644
--- a/drivers/net/lora/Makefile
+++ b/drivers/net/lora/Makefile
@@ -9,5 +9,9 @@ lora-dev-y := dev.o
# Alphabetically sorted.
#
+obj-$(CONFIG_LORA_RN2483) += lora-rn2483.o
+lora-rn2483-y := rn2483.o
+lora-rn2483-y += rn2483_cmd.o
+
obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o
lora-sx1276-y := sx1276.o
diff --git a/drivers/net/lora/rn2483.c b/drivers/net/lora/rn2483.c
new file mode 100644
index 000000000000..8b9ec2575ee2
--- /dev/null
+++ b/drivers/net/lora/rn2483.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Microchip RN2483/RN2903
+ *
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/lora.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/serdev.h>
+#include <linux/lora/dev.h>
+
+#include "rn2483.h"
+
+struct rn2483_priv {
+ struct lora_priv lora;
+};
+
+static netdev_tx_t rn2483_loradev_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ if (skb->protocol != htons(ETH_P_LORA)) {
+ kfree_skb(skb);
+ netdev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ netif_stop_queue(netdev);
+
+ /* TODO */
+ return NETDEV_TX_OK;
+}
+
+static int rn2483_loradev_open(struct net_device *netdev)
+{
+ int ret;
+
+ netdev_dbg(netdev, "%s", __func__);
+
+ ret = open_loradev(netdev);
+ if (ret)
+ return ret;
+
+ netif_start_queue(netdev);
+
+ return 0;
+}
+
+static int rn2483_loradev_stop(struct net_device *netdev)
+{
+ netdev_dbg(netdev, "%s", __func__);
+
+ netif_stop_queue(netdev);
+ close_loradev(netdev);
+
+ return 0;
+}
+
+static const struct net_device_ops rn2483_net_device_ops = {
+ .ndo_open = rn2483_loradev_open,
+ .ndo_stop = rn2483_loradev_stop,
+ .ndo_start_xmit = rn2483_loradev_start_xmit,
+};
+
+int rn2483_readline_timeout(struct rn2483_device *rndev, char **line, unsigned long timeout)
+{
+ timeout = wait_for_completion_timeout(&rndev->line_recv_comp, timeout);
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ *line = devm_kstrdup(&rndev->serdev->dev, rndev->buf, GFP_KERNEL);
+ complete(&rndev->line_read_comp);
+ if (!*line)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void rn2483_receive_line(struct rn2483_device *rndev, const char *sz, size_t len)
+{
+ dev_dbg(&rndev->serdev->dev, "Received line '%s' (%d)", sz, (int)len);
+
+ reinit_completion(&rndev->line_read_comp);
+ complete(&rndev->line_recv_comp);
+ wait_for_completion(&rndev->line_read_comp);
+ reinit_completion(&rndev->line_recv_comp);
+}
+
+static int rn2483_receive_buf(struct serdev_device *serdev, const u8 *data, size_t count)
+{
+ struct rn2483_device *rndev = serdev_device_get_drvdata(serdev);
+ size_t i;
+
+ dev_dbg(&serdev->dev, "Receive (%d)", (int)count);
+ if (!rndev->buf) {
+ rndev->buf = devm_kmalloc(&serdev->dev, count, GFP_KERNEL);
+ if (!rndev->buf)
+ return 0;
+ rndev->buflen = 0;
+ } else {
+ void *tmp = devm_kmalloc(&serdev->dev, rndev->buflen + count, GFP_KERNEL);
+ if (!tmp)
+ return 0;
+ memcpy(tmp, rndev->buf, rndev->buflen);
+ devm_kfree(&serdev->dev, rndev->buf);
+ rndev->buf = tmp;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (data[i] == '\r') {
+ rndev->saw_cr = true;
+ } else if (data[i] == '\n' && rndev->saw_cr) {
+ if (i > 1)
+ memcpy(rndev->buf + rndev->buflen, data, i - 1);
+ ((char *)rndev->buf)[rndev->buflen + i - 1] = 0;
+ rn2483_receive_line(rndev, rndev->buf, rndev->buflen + i - 1);
+ rndev->saw_cr = false;
+ devm_kfree(&serdev->dev, rndev->buf);
+ rndev->buf = NULL;
+ rndev->buflen = 0;
+ return i + 1;
+ } else
+ rndev->saw_cr = false;
+ }
+
+ memcpy(rndev->buf + rndev->buflen, data, count);
+ rndev->buflen += count;
+ return count;
+}
+
+static const struct serdev_device_ops rn2483_serdev_client_ops = {
+ .receive_buf = rn2483_receive_buf,
+};
+
+static int rn2483_probe(struct serdev_device *sdev)
+{
+ struct rn2483_device *rndev;
+ char *line, *cmd;
+ char sz[5];
+ u32 status;
+ int ret;
+
+ dev_info(&sdev->dev, "Probing");
+
+ rndev = devm_kzalloc(&sdev->dev, sizeof(struct rn2483_device), GFP_KERNEL);
+ if (!rndev)
+ return -ENOMEM;
+
+ rndev->serdev = sdev;
+ init_completion(&rndev->line_recv_comp);
+ init_completion(&rndev->line_read_comp);
+ mutex_init(&rndev->cmd_lock);
+ serdev_device_set_drvdata(sdev, rndev);
+
+ rndev->reset_gpio = devm_gpiod_get_optional(&sdev->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(rndev->reset_gpio))
+ return PTR_ERR(rndev->reset_gpio);
+
+ ret = serdev_device_open(sdev);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to open (%d)", ret);
+ return ret;
+ }
+
+ serdev_device_set_baudrate(sdev, 57600);
+ serdev_device_set_flow_control(sdev, false);
+
+ gpiod_set_value_cansleep(rndev->reset_gpio, 0);
+ msleep(5);
+ serdev_device_set_client_ops(sdev, &rn2483_serdev_client_ops);
+ gpiod_set_value_cansleep(rndev->reset_gpio, 1);
+ msleep(100);
+
+ ret = rn2483_readline_timeout(rndev, &line, HZ);
+ if (ret) {
+ if (ret != -ENOMEM)
+ dev_err(&sdev->dev, "Timeout waiting for firmware identification");
+ goto err_timeout;
+ }
+
+ if (strlen(line) < strlen("RNxxxx X.Y.Z MMM DD YYYY HH:MM:SS") || line[6] != ' ' ||
+ strncmp(line, "RN", 2) != 0) {
+ dev_err(&sdev->dev, "Unexpected response '%s'", line);
+ devm_kfree(&sdev->dev, line);
+ ret = -EINVAL;
+ goto err_version;
+ }
+ dev_info(&sdev->dev, "Firmware '%s'", line);
+ strncpy(sz, line + 2, 4);
+ sz[4] = 0;
+ devm_kfree(&sdev->dev, line);
+ ret = kstrtouint(sz, 10, &rndev->model);
+ if (ret)
+ goto err_model;
+ if (!(rndev->model == 2483 || rndev->model == 2903)) {
+ dev_err(&sdev->dev, "Unknown model %u", rndev->model);
+ ret = -ENOTSUPP;
+ goto err_model;
+ }
+ dev_info(&sdev->dev, "Detected RN%u", rndev->model);
+
+ ret = rn2483_sys_get_hweui(rndev, &rndev->hweui);
+ if (ret) {
+ if (ret != -ENOMEM)
+ dev_err(&sdev->dev, "Failed to read HWEUI (%d)", ret);
+ goto err_hweui;
+ }
+ dev_info(&sdev->dev, "HWEUI " PRIxLORAEUI, LORA_EUI(rndev->hweui));
+
+ switch (rndev->model) {
+ case 2483:
+ ret = rn2483_mac_get_band(rndev, &rndev->band);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to read band (%d)", ret);
+ goto err_band;
+ }
+ dev_info(&sdev->dev, "Frequency band %u MHz", rndev->band);
+
+ ret = rn2483_mac_reset_band(rndev, 433);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to reset band (%d)", ret);
+ goto err_band;
+ }
+ rndev->band = 433;
+
+ ret = rn2483_mac_get_band(rndev, &rndev->band);
+ if (!ret)
+ dev_info(&sdev->dev, "New frequency band: %u MHz", rndev->band);
+ break;
+ case 2903:
+ /* No "mac get band" command available */
+ rndev->band = 915;
+ break;
+ }
+
+ ret = rn2483_mac_get_status(rndev, &status);
+ if (!ret)
+ dev_info(&sdev->dev, "MAC status %08x", status);
+
+ if (true) {
+ u32 pause;
+ ret = rn2483_mac_pause(rndev, &pause);
+ if (!ret)
+ dev_info(&sdev->dev, "MAC pausing (0x%08x)", pause);
+ ret = rn2483_mac_resume(rndev);
+ if (!ret)
+ dev_info(&sdev->dev, "MAC resuming");
+ }
+
+ cmd = "mac get sync";
+ mutex_lock(&rndev->cmd_lock);
+ ret = rn2483_send_command_timeout(rndev, cmd, &line, HZ);
+ mutex_unlock(&rndev->cmd_lock);
+ if (!ret) {
+ dev_info(&sdev->dev, "%s => '%s'", cmd, line);
+ devm_kfree(&sdev->dev, line);
+ }
+
+ rndev->netdev = alloc_loradev(sizeof(struct rn2483_priv));
+ if (!rndev->netdev) {
+ ret = -ENOMEM;
+ goto err_alloc_netdev;
+ }
+
+ rndev->netdev->netdev_ops = &rn2483_net_device_ops;
+ SET_NETDEV_DEV(rndev->netdev, &sdev->dev);
+
+ ret = register_loradev(rndev->netdev);
+ if (ret)
+ goto err_register_netdev;
+
+ dev_info(&sdev->dev, "Done.");
+
+ return 0;
+
+err_register_netdev:
+ free_loradev(rndev->netdev);
+err_alloc_netdev:
+err_band:
+err_hweui:
+err_model:
+err_version:
+err_timeout:
+ gpiod_set_value_cansleep(rndev->reset_gpio, 0);
+ return ret;
+}
+
+static void rn2483_remove(struct serdev_device *sdev)
+{
+ struct rn2483_device *rndev = serdev_device_get_drvdata(sdev);
+
+ unregister_loradev(rndev->netdev);
+ free_loradev(rndev->netdev);
+
+ gpiod_set_value_cansleep(rndev->reset_gpio, 0);
+
+ complete(&rndev->line_read_comp);
+
+ serdev_device_close(sdev);
+
+ dev_info(&sdev->dev, "Removed");
+}
+
+static const struct of_device_id rn2483_of_match[] = {
+ { .compatible = "microchip,rn2483" },
+ { .compatible = "microchip,rn2903" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rn2483_of_match);
+
+static struct serdev_device_driver rn2483_serdev_driver = {
+ .probe = rn2483_probe,
+ .remove = rn2483_remove,
+ .driver = {
+ .name = "rn2483",
+ .of_match_table = rn2483_of_match,
+ },
+};
+
+static int __init rn2483_init(void)
+{
+ int ret;
+
+ ret = serdev_device_driver_register(&rn2483_serdev_driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void __exit rn2483_exit(void)
+{
+ serdev_device_driver_unregister(&rn2483_serdev_driver);
+}
+
+module_init(rn2483_init);
+module_exit(rn2483_exit);
+
+MODULE_DESCRIPTION("RN2483 serdev driver");
+MODULE_AUTHOR("Andreas Färber <[email protected]>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/lora/rn2483.h b/drivers/net/lora/rn2483.h
new file mode 100644
index 000000000000..f92660286f15
--- /dev/null
+++ b/drivers/net/lora/rn2483.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+#ifndef _RN2483_H
+#define _RN2483_H
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/netdevice.h>
+#include <linux/serdev.h>
+#include <linux/lora/dev.h>
+
+struct rn2483_device {
+ struct serdev_device *serdev;
+ struct gpio_desc *reset_gpio;
+ struct net_device *netdev;
+ unsigned model;
+ lora_eui hweui;
+ unsigned band;
+ bool saw_cr;
+ void *buf;
+ size_t buflen;
+ struct completion line_recv_comp;
+ struct completion line_read_comp;
+ struct mutex cmd_lock;
+};
+
+int rn2483_readline_timeout(struct rn2483_device *rndev, char **line, unsigned long timeout);
+int rn2483_send_command_timeout(struct rn2483_device *rndev,
+ const char *cmd, char **resp, unsigned long timeout);
+
+int rn2483_sys_get_hweui(struct rn2483_device *rndev, lora_eui *val);
+int rn2483_mac_get_band(struct rn2483_device *rndev, uint *val);
+int rn2483_mac_get_status(struct rn2483_device *rndev, u32 *val);
+int rn2483_mac_reset_band(struct rn2483_device *rndev, unsigned band);
+int rn2483_mac_pause(struct rn2483_device *rndev, u32 *max_pause);
+int rn2483_mac_resume(struct rn2483_device *rndev);
+
+#endif
diff --git a/drivers/net/lora/rn2483_cmd.c b/drivers/net/lora/rn2483_cmd.c
new file mode 100644
index 000000000000..6d6fca8fa93c
--- /dev/null
+++ b/drivers/net/lora/rn2483_cmd.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Microchip RN2483/RN2903 - UART commands
+ *
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+#include "rn2483.h"
+
+#define RN2483_CMD_TIMEOUT HZ
+
+int rn2483_send_command_timeout(struct rn2483_device *rndev,
+ const char *cmd, char **resp, unsigned long timeout)
+{
+ int ret;
+
+ ret = serdev_device_write_buf(rndev->serdev, cmd, strlen(cmd));
+ if (ret < 0)
+ return ret;
+
+ ret = serdev_device_write_buf(rndev->serdev, "\r\n", 2);
+ if (ret < 0)
+ return ret;
+
+ return rn2483_readline_timeout(rndev, resp, timeout);
+}
+
+int rn2483_sys_get_hweui(struct rn2483_device *rndev, lora_eui *val)
+{
+ int ret;
+ char *line;
+
+ mutex_lock(&rndev->cmd_lock);
+ ret = rn2483_send_command_timeout(rndev, "sys get hweui", &line, RN2483_CMD_TIMEOUT);
+ mutex_unlock(&rndev->cmd_lock);
+ if (ret)
+ return ret;
+
+ ret = lora_strtoeui(line, val);
+ devm_kfree(&rndev->serdev->dev, line);
+ return ret;
+}
+
+int rn2483_mac_get_band(struct rn2483_device *rndev, uint *val)
+{
+ int ret;
+ char *line;
+
+ mutex_lock(&rndev->cmd_lock);
+ ret = rn2483_send_command_timeout(rndev, "mac get band", &line, RN2483_CMD_TIMEOUT);
+ mutex_unlock(&rndev->cmd_lock);
+ if (ret)
+ return ret;
+
+ ret = kstrtouint(line, 10, val);
+ devm_kfree(&rndev->serdev->dev, line);
+
+ return ret;
+}
+
+int rn2483_mac_get_status(struct rn2483_device *rndev, u32 *val)
+{
+ int ret;
+ char *line;
+
+ mutex_lock(&rndev->cmd_lock);
+ ret = rn2483_send_command_timeout(rndev, "mac get status", &line, RN2483_CMD_TIMEOUT);
+ mutex_unlock(&rndev->cmd_lock);
+ if (ret)
+ return ret;
+
+ ret = kstrtou32(line, 16, val);
+ devm_kfree(&rndev->serdev->dev, line);
+ return ret;
+}
+
+int rn2483_mac_reset_band(struct rn2483_device *rndev, unsigned band)
+{
+ int ret;
+ char *line, *cmd;
+
+ cmd = devm_kasprintf(&rndev->serdev->dev, GFP_KERNEL, "mac reset %u", band);
+ mutex_lock(&rndev->cmd_lock);
+ ret = rn2483_send_command_timeout(rndev, cmd, &line, RN2483_CMD_TIMEOUT);
+ mutex_unlock(&rndev->cmd_lock);
+ devm_kfree(&rndev->serdev->dev, cmd);
+ if (ret)
+ return ret;
+
+ if (strcmp(line, "ok") == 0)
+ ret = 0;
+ else if (strcmp(line, "invalid_param") == 0)
+ ret = -EINVAL;
+ else
+ ret = -EPROTO;
+
+ devm_kfree(&rndev->serdev->dev, line);
+ return ret;
+}
+
+int rn2483_mac_pause(struct rn2483_device *rndev, u32 *max_pause)
+{
+ int ret;
+ char *line;
+
+ mutex_lock(&rndev->cmd_lock);
+ ret = rn2483_send_command_timeout(rndev, "mac pause", &line, RN2483_CMD_TIMEOUT);
+ mutex_unlock(&rndev->cmd_lock);
+ if (ret)
+ return ret;
+
+ ret = kstrtou32(line, 10, max_pause);
+ devm_kfree(&rndev->serdev->dev, line);
+ return ret;
+}
+
+int rn2483_mac_resume(struct rn2483_device *rndev)
+{
+ int ret;
+ char *line;
+
+ mutex_lock(&rndev->cmd_lock);
+ ret = rn2483_send_command_timeout(rndev, "mac resume", &line, RN2483_CMD_TIMEOUT);
+ mutex_unlock(&rndev->cmd_lock);
+ if (ret)
+ return ret;
+
+ ret = (strcmp(line, "ok") == 0) ? 0 : -EPROTO;
+ devm_kfree(&rndev->serdev->dev, line);
+ return ret;
+}
--
2.16.4
The IMST WiMOD uses a SLIP based binary UART protocol. Two separate
firmwares are available. By default it ships with a LoRaWAN firmware.
The alternative firmware is a custom P2P addressing mode based on LoRa.
Cc: Jon Ortego <[email protected]>
Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/lora/Kconfig | 8 +
drivers/net/lora/Makefile | 3 +
drivers/net/lora/wimod.c | 597 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 608 insertions(+)
create mode 100644 drivers/net/lora/wimod.c
diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
index 940bd2cbe106..2e05caef8645 100644
--- a/drivers/net/lora/Kconfig
+++ b/drivers/net/lora/Kconfig
@@ -31,6 +31,14 @@ config LORA_SX1276
help
Semtech SX1272/1276/1278
+config LORA_WIMOD
+ tristate "IMST WiMOD driver"
+ default y
+ depends on SERIAL_DEV_BUS
+ select CRC_CCITT
+ help
+ IMST WiMOD
+
endif
endmenu
diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile
index 07839c3ce9f8..ecb326c859a5 100644
--- a/drivers/net/lora/Makefile
+++ b/drivers/net/lora/Makefile
@@ -15,3 +15,6 @@ lora-rn2483-y += rn2483_cmd.o
obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o
lora-sx1276-y := sx1276.o
+
+obj-$(CONFIG_LORA_WIMOD) += lora-wimod.o
+lora-wimod-y := wimod.o
diff --git a/drivers/net/lora/wimod.c b/drivers/net/lora/wimod.c
new file mode 100644
index 000000000000..a70d5a6dc7c4
--- /dev/null
+++ b/drivers/net/lora/wimod.c
@@ -0,0 +1,597 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IMST WiMOD
+ *
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+
+#include <linux/crc-ccitt.h>
+#include <linux/lora.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/rculist.h>
+#include <linux/serdev.h>
+
+#define WIMOD_HCI_PAYLOAD_MAX 300
+#define WIMOD_HCI_PACKET_MAX (1 + (2 + WIMOD_HCI_PAYLOAD_MAX + 2) * 2 + 1)
+
+struct wimod_device {
+ struct serdev_device *serdev;
+
+ u8 rx_buf[WIMOD_HCI_PACKET_MAX];
+ int rx_len;
+ bool rx_esc;
+ struct list_head packet_dispatchers;
+};
+
+#define SLIP_END 0300
+#define SLIP_ESC 0333
+#define SLIP_ESC_END 0334
+#define SLIP_ESC_ESC 0335
+
+static inline void slip_print_bytes(const u8* buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ printk("%02x ", buf[i]);
+}
+
+static int slip_send_end(struct serdev_device *sdev, unsigned long timeout)
+{
+ u8 val = SLIP_END;
+
+ return serdev_device_write(sdev, &val, 1, timeout);
+}
+
+#if 0
+static int slip_send_data(struct serdev_device *sdev, const u8 *buf, int len,
+ unsigned long timeout)
+{
+ int last_idx = -1;
+ int i;
+ u8 esc[2] = { SLIP_ESC, 0 };
+ int ret;
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] != SLIP_END &&
+ buf[i] != SLIP_ESC)
+ continue;
+
+ slip_print_bytes(&buf[last_idx + 1], i - (last_idx + 1));
+
+ ret = serdev_device_write(sdev,
+ &buf[last_idx + 1], i - (last_idx + 1), timeout);
+ if (ret)
+ return ret;
+
+ switch (buf[i]) {
+ case SLIP_END:
+ esc[1] = SLIP_ESC_END;
+ break;
+ case SLIP_ESC:
+ esc[1] = SLIP_ESC_ESC;
+ break;
+ }
+ slip_print_bytes(esc, 2);
+ ret = serdev_device_write(sdev, esc, 2, timeout);
+ if (ret)
+ return ret;
+
+ last_idx = i;
+ }
+
+ slip_print_bytes(&buf[last_idx + 1], len - (last_idx + 1));
+
+ ret = serdev_device_write(sdev,
+ &buf[last_idx + 1], len - (last_idx + 1), timeout);
+
+ return ret;
+}
+#endif
+
+static int slip_write_data(u8 *buf, int buf_len, const u8 *data, int data_len)
+{
+ int last_idx = -1;
+ int i, n;
+ int count = 0;
+
+ for (i = 0; i < data_len; i++) {
+ if (data[i] != SLIP_END &&
+ data[i] != SLIP_ESC)
+ continue;
+
+ n = i - (last_idx + 1);
+ if (count + n + 2 > buf_len)
+ return -ENOMEM;
+
+ memcpy(buf + count, &data[last_idx + 1], n);
+ count += n;
+
+ buf[count++] = SLIP_ESC;
+ switch (data[i]) {
+ case SLIP_END:
+ buf[count++] = SLIP_ESC_END;
+ break;
+ case SLIP_ESC:
+ buf[count++] = SLIP_ESC_ESC;
+ break;
+ }
+
+ last_idx = i;
+ }
+
+ n = data_len - (last_idx + 1);
+ if (count + n > buf_len)
+ return -ENOMEM;
+
+ memcpy(buf + count, &data[last_idx + 1], n);
+ count += n;
+
+ return count;
+}
+
+#define DEVMGMT_ID 0x01
+
+#define DEVMGMT_MSG_PING_REQ 0x01
+#define DEVMGMT_MSG_PING_RSP 0x02
+#define DEVMGMT_MSG_GET_DEVICE_INFO_REQ 0x03
+#define DEVMGMT_MSG_GET_DEVICE_INFO_RSP 0x04
+#define DEVMGMT_MSG_GET_FW_INFO_REQ 0x05
+#define DEVMGMT_MSG_GET_FW_INFO_RSP 0x06
+
+#define DEVMGMT_STATUS_OK 0x00
+
+struct wimod_hci_packet_dispatcher {
+ struct list_head list;
+ u8 dst_id;
+ u8 msg_id;
+ void (*dispatchee)(const u8*, int, struct wimod_hci_packet_dispatcher *);
+ void *priv;
+};
+
+struct wimod_hci_packet_completion {
+ struct wimod_hci_packet_dispatcher disp;
+ struct completion comp;
+ char *payload;
+ int payload_len;
+};
+
+static void wimod_hci_add_dispatcher(struct wimod_device *wmdev,
+ struct wimod_hci_packet_dispatcher *entry)
+{
+ list_add_tail_rcu(&entry->list, &wmdev->packet_dispatchers);
+}
+
+static void wimod_hci_remove_dispatcher(struct wimod_device *wmdev,
+ struct wimod_hci_packet_dispatcher *entry)
+{
+ list_del_rcu(&entry->list);
+}
+
+static void wimod_hci_packet_dispatch_completion(const u8 *data, int len,
+ struct wimod_hci_packet_dispatcher *d)
+{
+ struct wimod_hci_packet_completion *disp =
+ container_of(d, struct wimod_hci_packet_completion, disp);
+
+ if (completion_done(&disp->comp))
+ return;
+
+ disp->payload_len = len - 2;
+ disp->payload = kzalloc(disp->payload_len, GFP_KERNEL);
+ if (disp->payload)
+ memcpy(disp->payload, data + 2, len - 2);
+
+ complete(&disp->comp);
+}
+
+static int wimod_hci_send(struct serdev_device *sdev,
+ u8 dst_id, u8 msg_id, const u8 *payload, int payload_len,
+ unsigned long timeout)
+{
+ u8 buf[WIMOD_HCI_PACKET_MAX];
+ int buf_len = 0;
+ u16 crc = 0xffff;
+ int ret, i;
+
+ if (payload_len > WIMOD_HCI_PAYLOAD_MAX)
+ return -EINVAL;
+
+ for (i = 0; i < 30; i++) {
+ ret = slip_send_end(sdev, timeout);
+ if (ret) {
+ dev_err(&sdev->dev, "%s: wakeup END %d failed\n", __func__, i);
+ return ret;
+ }
+ }
+
+ crc = crc_ccitt_byte(crc, dst_id);
+ crc = crc_ccitt_byte(crc, msg_id);
+ if (payload_len > 0)
+ crc = crc_ccitt(crc, payload, payload_len);
+ crc = ~crc;
+
+ printk(KERN_INFO "sending: ");
+
+ /*ret = slip_send_end(sdev, timeout);
+ if (ret) {
+ dev_err(&sdev->dev, "%s: initial END failed\n", __func__);
+ return ret;
+ }
+
+ ret = slip_send_data(sdev, &dst_id, 1, timeout);
+ if (ret) {
+ dev_err(&sdev->dev, "%s: dst_id failed\n", __func__);
+ return ret;
+ }
+
+ ret = slip_send_data(sdev, &msg_id, 1, timeout);
+ if (ret) {
+ dev_err(&sdev->dev, "%s: msg_id failed\n", __func__);
+ return ret;
+ }*/
+
+ buf[buf_len++] = SLIP_END;
+
+ ret = slip_write_data(buf + buf_len, sizeof(buf) - buf_len, &dst_id, 1);
+ if (ret < 0)
+ return ret;
+ buf_len += ret;
+
+ ret = slip_write_data(buf + buf_len, sizeof(buf) - buf_len, &msg_id, 1);
+ if (ret < 0)
+ return ret;
+ buf_len += ret;
+
+ if (payload_len > 0) {
+ /*ret = slip_send_data(sdev, payload, payload_len, timeout);
+ if (ret) {
+ dev_err(&sdev->dev, "%s: payload failed\n", __func__);
+ return ret;
+ }*/
+ ret = slip_write_data(buf + buf_len, sizeof(buf) - buf_len, payload, payload_len);
+ if (ret < 0)
+ return ret;
+ buf_len += ret;
+ }
+
+ cpu_to_le16s(crc);
+ /*ret = slip_send_data(sdev, (u8 *)&crc, 2, timeout);
+ if (ret) {
+ dev_err(&sdev->dev, "%s: FCS failed\n", __func__);
+ return ret;
+ }
+
+ ret = slip_send_end(sdev, timeout);
+ if (ret) {
+ dev_err(&sdev->dev, "%s: trailing END failed\n", __func__);
+ return ret;
+ }*/
+
+ ret = slip_write_data(buf + buf_len, sizeof(buf) - buf_len, (u8 *)&crc, 2);
+ if (ret < 0)
+ return ret;
+ buf_len += ret;
+
+ buf[buf_len++] = SLIP_END;
+
+ slip_print_bytes(buf, buf_len);
+
+ return serdev_device_write(sdev, buf, buf_len, timeout);
+
+ //printk("\n");
+
+ //return 0;
+}
+
+static int wimod_hci_devmgmt_status(u8 status)
+{
+ switch (status) {
+ case DEVMGMT_STATUS_OK:
+ return 0;
+ default:
+ pr_info("DEVMGMT status %u\n", (int)status);
+ return -EINVAL;
+ }
+}
+
+static int wimod_hci_devmgmt_send_sync(struct wimod_device *wmdev,
+ u8 req_msg_id, const u8 *req_payload, int req_payload_len,
+ u8 rsp_msg_id, u8 **rsp_payload, int *rsp_payload_len,
+ unsigned long timeout)
+{
+ struct wimod_hci_packet_completion packet = {0};
+ int ret;
+
+ if (rsp_payload && !rsp_payload_len)
+ return -EINVAL;
+
+ packet.disp.dst_id = DEVMGMT_ID;
+ packet.disp.msg_id = rsp_msg_id;
+ packet.disp.dispatchee = wimod_hci_packet_dispatch_completion;
+ init_completion(&packet.comp);
+
+ wimod_hci_add_dispatcher(wmdev, &packet.disp);
+
+ ret = wimod_hci_send(wmdev->serdev, DEVMGMT_ID, req_msg_id, req_payload, req_payload_len, timeout);
+ if (ret) {
+ wimod_hci_remove_dispatcher(wmdev, &(packet.disp));
+ return ret;
+ }
+
+ timeout = wait_for_completion_timeout(&packet.comp, timeout);
+ wimod_hci_remove_dispatcher(wmdev, &packet.disp);
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ if (packet.payload_len < 1) {
+ kfree(packet.payload);
+ return -EINVAL;
+ }
+
+ ret = wimod_hci_devmgmt_status(packet.payload[0]);
+ if (ret || !rsp_payload)
+ kfree(packet.payload);
+ else if (rsp_payload) {
+ *rsp_payload = packet.payload;
+ *rsp_payload_len = packet.payload_len;
+ }
+ return ret;
+}
+
+static int wimod_hci_ping(struct wimod_device *wmdev, unsigned long timeout)
+{
+ return wimod_hci_devmgmt_send_sync(wmdev,
+ DEVMGMT_MSG_PING_REQ, NULL, 0,
+ DEVMGMT_MSG_PING_RSP, NULL, NULL,
+ timeout);
+}
+
+static int wimod_hci_get_device_info(struct wimod_device *wmdev, u8 *buf, unsigned long timeout)
+{
+ u8 *payload;
+ int payload_len;
+ int ret;
+
+ ret = wimod_hci_devmgmt_send_sync(wmdev,
+ DEVMGMT_MSG_GET_DEVICE_INFO_REQ, NULL, 0,
+ DEVMGMT_MSG_GET_DEVICE_INFO_RSP, &payload, &payload_len,
+ timeout);
+ if (ret)
+ return ret;
+
+ if (payload_len < 10) {
+ dev_err(&wmdev->serdev->dev, "get_device_info: payload length (10)\n");
+ kfree(payload);
+ return -EINVAL;
+ }
+
+ if (buf)
+ memcpy(buf, payload + 1, min(payload_len - 1, 9));
+
+ kfree(payload);
+ return 0;
+}
+
+static int wimod_hci_get_fw_info(struct wimod_device *wmdev, u8 **info, int *info_len, unsigned long timeout)
+{
+ u8 *payload;
+ int payload_len;
+ int ret;
+
+ if (info && !info_len)
+ return -EINVAL;
+
+ ret = wimod_hci_devmgmt_send_sync(wmdev,
+ DEVMGMT_MSG_GET_FW_INFO_REQ, NULL, 0,
+ DEVMGMT_MSG_GET_FW_INFO_RSP, &payload, &payload_len,
+ timeout);
+ if (ret)
+ return ret;
+
+ if (info) {
+ *info = payload + 1;
+ *info_len = payload_len - 1;
+ } else
+ kfree(payload);
+
+ return 0;
+}
+
+static void wimod_hci_get_fw_info_free(u8* info)
+{
+ u8 *payload = info - 1;
+
+ kfree(payload);
+}
+
+static void wimod_process_packet(struct serdev_device *sdev, const u8 *data, int len)
+{
+ struct wimod_device *wmdev = serdev_device_get_drvdata(sdev);
+ struct wimod_hci_packet_dispatcher *e;
+ u16 crc;
+
+ dev_info(&sdev->dev, "Processing incoming packet (%d)\n", len);
+
+ if (len < 4) {
+ dev_dbg(&sdev->dev, "Discarding packet of length %d\n", len);
+ return;
+ }
+
+ crc = ~crc_ccitt(0xffff, data, len);
+ if (crc != 0x0f47) {
+ dev_dbg(&sdev->dev, "Discarding packet with wrong checksum\n");
+ return;
+ }
+
+ list_for_each_entry(e, &wmdev->packet_dispatchers, list) {
+ if (e->dst_id == data[0] && e->msg_id == data[1]) {
+ e->dispatchee(data, len - 2, e);
+ break;
+ }
+ }
+}
+
+static int wimod_receive_buf(struct serdev_device *sdev, const u8 *data, size_t count)
+{
+ struct wimod_device *wmdev = serdev_device_get_drvdata(sdev);
+ size_t i = 0;
+ int len = 0;
+
+ dev_dbg(&sdev->dev, "Receive (%d)\n", (int)count);
+
+ while (i < min(count, sizeof(wmdev->rx_buf) - wmdev->rx_len)) {
+ if (wmdev->rx_esc) {
+ wmdev->rx_esc = false;
+ switch (data[i]) {
+ case SLIP_ESC_END:
+ wmdev->rx_buf[wmdev->rx_len++] = SLIP_END;
+ break;
+ case SLIP_ESC_ESC:
+ wmdev->rx_buf[wmdev->rx_len++] = SLIP_ESC;
+ break;
+ default:
+ dev_warn(&sdev->dev, "Ignoring unknown escape sequence 0300 0%o\n", data[i]);
+ break;
+ }
+ len += i + 1;
+ data += i + 1;
+ count -= i + 1;
+ i = 0;
+ continue;
+ }
+ if (data[i] != SLIP_END &&
+ data[i] != SLIP_ESC) {
+ i++;
+ continue;
+ }
+ if (i > 0) {
+ memcpy(&wmdev->rx_buf[wmdev->rx_len], data, i);
+ wmdev->rx_len += i;
+ }
+ if (data[i] == SLIP_END && wmdev->rx_len > 0) {
+ wimod_process_packet(sdev, wmdev->rx_buf, wmdev->rx_len);
+ wmdev->rx_len = 0;
+ } else if (data[i] == SLIP_ESC) {
+ wmdev->rx_esc = true;
+ }
+ len += i + 1;
+ data += i + 1;
+ count -= i + 1;
+ i = 0;
+ }
+
+ dev_dbg(&sdev->dev, "Receive: processed %d\n", len);
+
+ return len;
+}
+
+static const struct serdev_device_ops wimod_serdev_client_ops = {
+ .receive_buf = wimod_receive_buf,
+ .write_wakeup = serdev_device_write_wakeup,
+};
+
+static int wimod_probe(struct serdev_device *sdev)
+{
+ struct wimod_device *wmdev;
+ u8 buf[9];
+ u8 *data;
+ int data_len;
+ int ret;
+
+ dev_info(&sdev->dev, "Probing");
+
+ wmdev = devm_kzalloc(&sdev->dev, sizeof(struct wimod_device), GFP_KERNEL);
+ if (!wmdev)
+ return -ENOMEM;
+
+ wmdev->serdev = sdev;
+ INIT_LIST_HEAD(&wmdev->packet_dispatchers);
+ serdev_device_set_drvdata(sdev, wmdev);
+
+ ret = serdev_device_open(sdev);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to open (%d)\n", ret);
+ return ret;
+ }
+
+ serdev_device_set_baudrate(sdev, 115200);
+ serdev_device_set_flow_control(sdev, false);
+ serdev_device_set_client_ops(sdev, &wimod_serdev_client_ops);
+
+ ret = wimod_hci_ping(wmdev, HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "Ping failed (%d)\n", ret);
+ goto err;
+ }
+
+ ret = wimod_hci_get_device_info(wmdev, buf, HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to obtain device info (%d)\n", ret);
+ goto err;
+ }
+ dev_info(&sdev->dev, "Module type: 0x%02x\n", (int)buf[0]);
+
+ ret = wimod_hci_get_fw_info(wmdev, &data, &data_len, HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to obtain firmware info (%d)\n", ret);
+ goto err;
+ }
+ dev_info(&sdev->dev, "Firmware: %u.%u build %u '%s'\n",
+ data[1], data[0], ((u16)data[3] << 8) | data[2], data + 4);
+ wimod_hci_get_fw_info_free(data);
+
+ dev_info(&sdev->dev, "Done.\n");
+
+ return 0;
+err:
+ serdev_device_close(sdev);
+ return ret;
+}
+
+static void wimod_remove(struct serdev_device *sdev)
+{
+ serdev_device_close(sdev);
+
+ dev_info(&sdev->dev, "Removed\n");
+}
+
+static const struct of_device_id wimod_of_match[] = {
+ { .compatible = "imst,wimod-hci" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, wimod_of_match);
+
+static struct serdev_device_driver wimod_serdev_driver = {
+ .probe = wimod_probe,
+ .remove = wimod_remove,
+ .driver = {
+ .name = "wimod",
+ .of_match_table = wimod_of_match,
+ },
+};
+
+static int __init wimod_init(void)
+{
+ int ret;
+
+ ret = serdev_device_driver_register(&wimod_serdev_driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void __exit wimod_exit(void)
+{
+ serdev_device_driver_unregister(&wimod_serdev_driver);
+}
+
+module_init(wimod_init);
+module_exit(wimod_exit);
+
+MODULE_DESCRIPTION("WiMOD serdev driver");
+MODULE_AUTHOR("Andreas Färber <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.16.4
The Semtech SX1301 was the first multi-channel LoRa "concentrator".
It uses a SPI interface to the host as well as a dual SPI interface to
its radios. These two have been implemented as spi_controller, so that
the Device Tree can specify whether the respective module uses two
SX1257, two SX1255 or a combination of these or some unforeseen chipset.
This implementation is the most recent - initialization is not yet
complete, it will need to load firmware into the two on-chip MCUs.
Unfortunately there is no full datasheet with register descriptions,
only a BSD-licensed userspace HAL implementation using spidev devices.
Therefore some register names are unknown.
Cc: Ben Whitten <[email protected]>
Cc: Steve deRosier <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Michael Röder <[email protected]>
Cc: Ken Yu (禹凯) <[email protected]>
Cc: [email protected]
Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/lora/Kconfig | 7 +
drivers/net/lora/Makefile | 3 +
drivers/net/lora/sx1301.c | 446 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 456 insertions(+)
create mode 100644 drivers/net/lora/sx1301.c
diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
index 68c7480d7812..950450e353b4 100644
--- a/drivers/net/lora/Kconfig
+++ b/drivers/net/lora/Kconfig
@@ -45,6 +45,13 @@ config LORA_SX1276
help
Semtech SX1272/1276/1278
+config LORA_SX1301
+ tristate "Semtech SX1301 SPI driver"
+ default y
+ depends on SPI
+ help
+ Semtech SX1301
+
config LORA_USI
tristate "USI WM-SG-SM-42 driver"
default y
diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile
index 44c578bde7d5..1cc1e3aa189b 100644
--- a/drivers/net/lora/Makefile
+++ b/drivers/net/lora/Makefile
@@ -22,6 +22,9 @@ lora-sx1257-y := sx1257.o
obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o
lora-sx1276-y := sx1276.o
+obj-$(CONFIG_LORA_SX1301) += lora-sx1301.o
+lora-sx1301-y := sx1301.o
+
obj-$(CONFIG_LORA_USI) += lora-usi.o
lora-usi-y := usi.o
diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
new file mode 100644
index 000000000000..5c936c1116d1
--- /dev/null
+++ b/drivers/net/lora/sx1301.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Semtech SX1301 LoRa concentrator
+ *
+ * Copyright (c) 2018 Andreas Färber
+ *
+ * Based on SX1301 HAL code:
+ * Copyright (c) 2013 Semtech-Cycleo
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/lora.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/lora/dev.h>
+#include <linux/spi/spi.h>
+
+#define REG_PAGE_RESET 0
+#define REG_VERSION 1
+#define REG_2_SPI_RADIO_A_DATA 33
+#define REG_2_SPI_RADIO_A_DATA_READBACK 34
+#define REG_2_SPI_RADIO_A_ADDR 35
+#define REG_2_SPI_RADIO_A_CS 37
+#define REG_2_SPI_RADIO_B_DATA 38
+#define REG_2_SPI_RADIO_B_DATA_READBACK 39
+#define REG_2_SPI_RADIO_B_ADDR 40
+#define REG_2_SPI_RADIO_B_CS 42
+
+#define REG_PAGE_RESET_SOFT_RESET BIT(7)
+
+#define REG_16_GLOBAL_EN BIT(3)
+
+#define REG_17_CLK32M_EN BIT(0)
+
+#define REG_2_43_RADIO_A_EN BIT(0)
+#define REG_2_43_RADIO_B_EN BIT(1)
+#define REG_2_43_RADIO_RST BIT(2)
+
+struct spi_sx1301 {
+ struct spi_device *parent;
+ u8 page;
+ u8 regs;
+};
+
+struct sx1301_priv {
+ struct lora_priv lora;
+ struct gpio_desc *rst_gpio;
+ u8 cur_page;
+ struct spi_controller *radio_a_ctrl, *radio_b_ctrl;
+};
+
+static int sx1301_read(struct spi_device *spi, u8 reg, u8 *val)
+{
+ u8 addr = reg & 0x7f;
+ return spi_write_then_read(spi, &addr, 1, val, 1);
+}
+
+static int sx1301_write(struct spi_device *spi, u8 reg, u8 val)
+{
+ u8 buf[2];
+
+ buf[0] = reg | BIT(7);
+ buf[1] = val;
+ return spi_write(spi, buf, 2);
+}
+
+static int sx1301_page_switch(struct spi_device *spi, u8 page)
+{
+ struct sx1301_priv *priv = spi_get_drvdata(spi);
+ int ret;
+
+ if (priv->cur_page == page)
+ return 0;
+
+ dev_dbg(&spi->dev, "switching to page %u\n", (unsigned)page);
+ ret = sx1301_write(spi, REG_PAGE_RESET, page & 0x3);
+ if (ret) {
+ dev_err(&spi->dev, "switching to page %u failed\n", (unsigned)page);
+ return ret;
+ }
+
+ priv->cur_page = page;
+
+ return 0;
+}
+
+static int sx1301_soft_reset(struct spi_device *spi)
+{
+ return sx1301_write(spi, REG_PAGE_RESET, REG_PAGE_RESET_SOFT_RESET);
+}
+
+#define REG_RADIO_X_DATA 0
+#define REG_RADIO_X_DATA_READBACK 1
+#define REG_RADIO_X_ADDR 2
+#define REG_RADIO_X_CS 4
+
+static int sx1301_radio_set_cs(struct spi_controller *ctrl, bool enable)
+{
+ struct spi_sx1301 *ssx = spi_controller_get_devdata(ctrl);
+ u8 cs;
+ int ret;
+
+ dev_dbg(&ctrl->dev, "setting CS to %s\n", enable ? "1" : "0");
+
+ ret = sx1301_page_switch(ssx->parent, ssx->page);
+ if (ret) {
+ dev_warn(&ctrl->dev, "failed to switch page for CS (%d)\n", ret);
+ return ret;
+ }
+
+ ret = sx1301_read(ssx->parent, ssx->regs + REG_RADIO_X_CS, &cs);
+ if (ret) {
+ dev_warn(&ctrl->dev, "failed to read CS (%d)\n", ret);
+ cs = 0;
+ }
+
+ if (enable)
+ cs |= BIT(0);
+ else
+ cs &= ~BIT(0);
+
+ ret = sx1301_write(ssx->parent, ssx->regs + REG_RADIO_X_CS, cs);
+ if (ret)
+ dev_warn(&ctrl->dev, "failed to write CS (%d)\n", ret);
+
+ return 0;
+}
+
+static void sx1301_radio_spi_set_cs(struct spi_device *spi, bool enable)
+{
+ int ret;
+
+ dev_dbg(&spi->dev, "setting SPI CS to %s\n", enable ? "1" : "0");
+
+ if (enable)
+ return;
+
+ ret = sx1301_radio_set_cs(spi->controller, enable);
+ if (ret)
+ dev_warn(&spi->dev, "failed to write CS (%d)\n", ret);
+}
+
+static int sx1301_radio_spi_transfer_one(struct spi_controller *ctrl,
+ struct spi_device *spi, struct spi_transfer *xfr)
+{
+ struct spi_sx1301 *ssx = spi_controller_get_devdata(ctrl);
+ const u8 *tx_buf = xfr->tx_buf;
+ u8 *rx_buf = xfr->rx_buf;
+ int ret;
+
+ if (xfr->len == 0 || xfr->len > 3)
+ return -EINVAL;
+
+ dev_dbg(&spi->dev, "transferring one (%u)\n", xfr->len);
+
+ ret = sx1301_page_switch(ssx->parent, ssx->page);
+ if (ret) {
+ dev_err(&spi->dev, "failed to switch page for transfer (%d)\n", ret);
+ return ret;
+ }
+
+ if (tx_buf) {
+ ret = sx1301_write(ssx->parent, ssx->regs + REG_RADIO_X_ADDR, tx_buf ? tx_buf[0] : 0);
+ if (ret) {
+ dev_err(&spi->dev, "SPI radio address write failed\n");
+ return ret;
+ }
+
+ ret = sx1301_write(ssx->parent, ssx->regs + REG_RADIO_X_DATA, (tx_buf && xfr->len >= 2) ? tx_buf[1] : 0);
+ if (ret) {
+ dev_err(&spi->dev, "SPI radio data write failed\n");
+ return ret;
+ }
+
+ ret = sx1301_radio_set_cs(ctrl, true);
+ if (ret) {
+ dev_err(&spi->dev, "SPI radio CS set failed\n");
+ return ret;
+ }
+
+ ret = sx1301_radio_set_cs(ctrl, false);
+ if (ret) {
+ dev_err(&spi->dev, "SPI radio CS unset failed\n");
+ return ret;
+ }
+ }
+
+ if (rx_buf) {
+ ret = sx1301_read(ssx->parent, ssx->regs + REG_RADIO_X_DATA_READBACK, &rx_buf[xfr->len - 1]);
+ if (ret) {
+ dev_err(&spi->dev, "SPI radio data read failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void sx1301_radio_setup(struct spi_controller *ctrl)
+{
+ ctrl->mode_bits = SPI_CS_HIGH | SPI_NO_CS;
+ ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
+ ctrl->num_chipselect = 1;
+ ctrl->set_cs = sx1301_radio_spi_set_cs;
+ ctrl->transfer_one = sx1301_radio_spi_transfer_one;
+}
+
+static int sx1301_probe(struct spi_device *spi)
+{
+ struct net_device *netdev;
+ struct sx1301_priv *priv;
+ struct spi_sx1301 *radio;
+ struct gpio_desc *rst;
+ int ret;
+ u8 val;
+
+ rst = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(rst))
+ return PTR_ERR(rst);
+
+ gpiod_set_value_cansleep(rst, 1);
+ msleep(100);
+ gpiod_set_value_cansleep(rst, 0);
+ msleep(100);
+
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ ret = sx1301_read(spi, REG_VERSION, &val);
+ if (ret) {
+ dev_err(&spi->dev, "version read failed\n");
+ goto err_version;
+ }
+
+ if (val != 103) {
+ dev_err(&spi->dev, "unexpected version: %u\n", val);
+ ret = -ENXIO;
+ goto err_version;
+ }
+
+ netdev = alloc_loradev(sizeof(*priv));
+ if (!netdev) {
+ ret = -ENOMEM;
+ goto err_alloc_loradev;
+ }
+
+ priv = netdev_priv(netdev);
+ priv->rst_gpio = rst;
+ priv->cur_page = 0xff;
+
+ spi_set_drvdata(spi, netdev);
+ SET_NETDEV_DEV(netdev, &spi->dev);
+
+ ret = sx1301_write(spi, REG_PAGE_RESET, 0);
+ if (ret) {
+ dev_err(&spi->dev, "page/reset write failed\n");
+ return ret;
+ }
+
+ ret = sx1301_soft_reset(spi);
+ if (ret) {
+ dev_err(&spi->dev, "soft reset failed\n");
+ return ret;
+ }
+
+ ret = sx1301_read(spi, 16, &val);
+ if (ret) {
+ dev_err(&spi->dev, "16 read failed\n");
+ return ret;
+ }
+
+ val &= ~REG_16_GLOBAL_EN;
+
+ ret = sx1301_write(spi, 16, val);
+ if (ret) {
+ dev_err(&spi->dev, "16 write failed\n");
+ return ret;
+ }
+
+ ret = sx1301_read(spi, 17, &val);
+ if (ret) {
+ dev_err(&spi->dev, "17 read failed\n");
+ return ret;
+ }
+
+ val &= ~REG_17_CLK32M_EN;
+
+ ret = sx1301_write(spi, 17, val);
+ if (ret) {
+ dev_err(&spi->dev, "17 write failed\n");
+ return ret;
+ }
+
+ ret = sx1301_page_switch(spi, 2);
+ if (ret) {
+ dev_err(&spi->dev, "page 2 switch failed\n");
+ return ret;
+ }
+
+ ret = sx1301_read(spi, 43, &val);
+ if (ret) {
+ dev_err(&spi->dev, "2|43 read failed\n");
+ return ret;
+ }
+
+ val |= REG_2_43_RADIO_B_EN | REG_2_43_RADIO_A_EN;
+
+ ret = sx1301_write(spi, 43, val);
+ if (ret) {
+ dev_err(&spi->dev, "2|43 write failed\n");
+ return ret;
+ }
+
+ msleep(500);
+
+ ret = sx1301_read(spi, 43, &val);
+ if (ret) {
+ dev_err(&spi->dev, "2|43 read failed\n");
+ return ret;
+ }
+
+ val |= REG_2_43_RADIO_RST;
+
+ ret = sx1301_write(spi, 43, val);
+ if (ret) {
+ dev_err(&spi->dev, "2|43 write failed\n");
+ return ret;
+ }
+
+ msleep(5);
+
+ ret = sx1301_read(spi, 43, &val);
+ if (ret) {
+ dev_err(&spi->dev, "2|43 read failed\n");
+ return ret;
+ }
+
+ val &= ~REG_2_43_RADIO_RST;
+
+ ret = sx1301_write(spi, 43, val);
+ if (ret) {
+ dev_err(&spi->dev, "2|43 write failed\n");
+ return ret;
+ }
+
+ /* radio A */
+
+ priv->radio_a_ctrl = spi_alloc_master(&spi->dev, sizeof(*radio));
+ if (!priv->radio_a_ctrl) {
+ ret = -ENOMEM;
+ goto err_radio_a_alloc;
+ }
+
+ sx1301_radio_setup(priv->radio_a_ctrl);
+ priv->radio_a_ctrl->dev.of_node = of_get_child_by_name(spi->dev.of_node, "radio-a");
+
+ radio = spi_controller_get_devdata(priv->radio_a_ctrl);
+ radio->page = 2;
+ radio->regs = REG_2_SPI_RADIO_A_DATA;
+ radio->parent = spi;
+
+ dev_info(&spi->dev, "registering radio A SPI\n");
+
+ ret = devm_spi_register_controller(&spi->dev, priv->radio_a_ctrl);
+ if (ret) {
+ dev_err(&spi->dev, "radio A SPI register failed\n");
+ goto err_radio_a_register;
+ }
+
+ /* radio B */
+
+ priv->radio_b_ctrl = spi_alloc_master(&spi->dev, sizeof(*radio));
+ if (!priv->radio_b_ctrl) {
+ ret = -ENOMEM;
+ goto err_radio_b_alloc;
+ }
+
+ sx1301_radio_setup(priv->radio_b_ctrl);
+ priv->radio_b_ctrl->dev.of_node = of_get_child_by_name(spi->dev.of_node, "radio-b");
+
+ radio = spi_controller_get_devdata(priv->radio_b_ctrl);
+ radio->page = 2;
+ radio->regs = REG_2_SPI_RADIO_B_DATA;
+ radio->parent = spi;
+
+ dev_info(&spi->dev, "registering radio B SPI\n");
+
+ ret = devm_spi_register_controller(&spi->dev, priv->radio_b_ctrl);
+ if (ret) {
+ dev_err(&spi->dev, "radio B SPI register failed\n");
+ goto err_radio_b_register;
+ }
+
+ dev_info(&spi->dev, "SX1301 module probed\n");
+
+ return 0;
+err_radio_b_register:
+ spi_controller_put(priv->radio_b_ctrl);
+err_radio_b_alloc:
+err_radio_a_register:
+ spi_controller_put(priv->radio_a_ctrl);
+err_radio_a_alloc:
+ free_loradev(netdev);
+err_alloc_loradev:
+err_version:
+ return ret;
+}
+
+static int sx1301_remove(struct spi_device *spi)
+{
+ struct net_device *netdev = spi_get_drvdata(spi);
+
+ //unregister_loradev(netdev);
+ free_loradev(netdev);
+
+ dev_info(&spi->dev, "SX1301 module removed\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sx1301_dt_ids[] = {
+ { .compatible = "semtech,sx1301" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sx1301_dt_ids);
+#endif
+
+static struct spi_driver sx1301_spi_driver = {
+ .driver = {
+ .name = "sx1301",
+ .of_match_table = of_match_ptr(sx1301_dt_ids),
+ },
+ .probe = sx1301_probe,
+ .remove = sx1301_remove,
+};
+
+module_spi_driver(sx1301_spi_driver);
+
+MODULE_DESCRIPTION("SX1301 SPI driver");
+MODULE_AUTHOR("Andreas Färber <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.16.4
The RAK811 and RAK812 offer a UART based AT command interface.
It allows both LoRaWAN and LoRa modes.
Cc: Ken Yu (禹凯) <[email protected]>
Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/lora/Kconfig | 7 ++
drivers/net/lora/Makefile | 3 +
drivers/net/lora/rak811.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 229 insertions(+)
create mode 100644 drivers/net/lora/rak811.c
diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
index 72a9d2a0f2be..3e384493cbdd 100644
--- a/drivers/net/lora/Kconfig
+++ b/drivers/net/lora/Kconfig
@@ -17,6 +17,13 @@ config LORA_DEV
if LORA_DEV
+config LORA_RAK811
+ tristate "RAK RAK811 driver"
+ default y
+ depends on SERIAL_DEV_BUS
+ help
+ RAK RAK811/RAK812
+
config LORA_RN2483
tristate "Microchip RN2483/RN2903 driver"
default y
diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile
index dfa9a43dcfb3..6b6870ffbfd8 100644
--- a/drivers/net/lora/Makefile
+++ b/drivers/net/lora/Makefile
@@ -9,6 +9,9 @@ lora-dev-y := dev.o
# Alphabetically sorted.
#
+obj-$(CONFIG_LORA_RAK811) += lora-rak811.o
+lora-rak811-y := rak811.o
+
obj-$(CONFIG_LORA_RN2483) += lora-rn2483.o
lora-rn2483-y := rn2483.o
lora-rn2483-y += rn2483_cmd.o
diff --git a/drivers/net/lora/rak811.c b/drivers/net/lora/rak811.c
new file mode 100644
index 000000000000..ad3d0980c489
--- /dev/null
+++ b/drivers/net/lora/rak811.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RAK RAK811
+ *
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+
+#include <linux/delay.h>
+#include <linux/lora.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/serdev.h>
+#include <linux/lora/dev.h>
+
+struct rak811_device {
+ struct serdev_device *serdev;
+
+ char rx_buf[4096];
+ int rx_len;
+
+ struct completion line_recv_comp;
+};
+
+static int rak811_send_command(struct rak811_device *rakdev, const char *cmd, char **data, unsigned long timeout)
+{
+ struct serdev_device *sdev = rakdev->serdev;
+ const char *crlf = "\r\n";
+ char *resp;
+
+ serdev_device_write_buf(sdev, cmd, strlen(cmd));
+ serdev_device_write_buf(sdev, crlf, 2);
+
+ timeout = wait_for_completion_timeout(&rakdev->line_recv_comp, timeout);
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ resp = rakdev->rx_buf;
+ dev_dbg(&sdev->dev, "Received: '%s'\n", resp);
+ if (data)
+ *data = kstrdup(resp, GFP_KERNEL);
+
+ rakdev->rx_len = 0;
+ reinit_completion(&rakdev->line_recv_comp);
+
+ return 0;
+}
+
+static int rak811_simple_cmd(struct rak811_device *rakdev, const char *cmd, unsigned long timeout)
+{
+ char *resp;
+ int ret;
+
+ ret = rak811_send_command(rakdev, cmd, &resp, timeout);
+ if (ret)
+ return ret;
+
+ if (strncmp(resp, "OK", 2) == 0) {
+ kfree(resp);
+ return 0;
+ }
+
+ kfree(resp);
+
+ return -EINVAL;
+}
+
+static int rak811_get_version(struct rak811_device *rakdev, char **version, unsigned long timeout)
+{
+ char *resp;
+ int ret;
+
+ ret = rak811_send_command(rakdev, "at+version", &resp, timeout);
+ if (ret)
+ return ret;
+
+ if (strncmp(resp, "OK", 2) == 0) {
+ *version = kstrdup(resp + 2, GFP_KERNEL);
+ kfree(resp);
+ return 0;
+ }
+
+ kfree(resp);
+
+ return -EINVAL;
+}
+
+static int rak811_receive_buf(struct serdev_device *sdev, const u8 *data, size_t count)
+{
+ struct rak811_device *rakdev = serdev_device_get_drvdata(sdev);
+ size_t i = 0;
+ int len = 0;
+
+ dev_dbg(&sdev->dev, "Receive (%d)\n", (int)count);
+
+ for (i = 0; i < count; i++) {
+ dev_dbg(&sdev->dev, "Receive: 0x%02x\n", (int)data[i]);
+ }
+
+ if (completion_done(&rakdev->line_recv_comp)) {
+ dev_info(&sdev->dev, "RX waiting on completion\n");
+ return 0;
+ }
+ if (rakdev->rx_len == sizeof(rakdev->rx_buf) - 1) {
+ dev_warn(&sdev->dev, "RX buffer full\n");
+ return 0;
+ }
+
+ i = min(count, sizeof(rakdev->rx_buf) - 1 - rakdev->rx_len);
+ if (i > 0) {
+ memcpy(&rakdev->rx_buf[rakdev->rx_len], data, i);
+ rakdev->rx_len += i;
+ len += i;
+ }
+ if (rakdev->rx_len >= 2 && strncmp(&rakdev->rx_buf[rakdev->rx_len - 2], "\r\n", 2) == 0) {
+ rakdev->rx_len -= 2;
+ rakdev->rx_buf[rakdev->rx_len] = '\0';
+ complete(&rakdev->line_recv_comp);
+ }
+
+ return len;
+}
+
+static const struct serdev_device_ops rak811_serdev_client_ops = {
+ .receive_buf = rak811_receive_buf,
+};
+
+static int rak811_probe(struct serdev_device *sdev)
+{
+ struct rak811_device *rakdev;
+ char *sz;
+ int ret;
+
+ dev_info(&sdev->dev, "Probing\n");
+
+ rakdev = devm_kzalloc(&sdev->dev, sizeof(struct rak811_device), GFP_KERNEL);
+ if (!rakdev)
+ return -ENOMEM;
+
+ rakdev->serdev = sdev;
+ init_completion(&rakdev->line_recv_comp);
+ serdev_device_set_drvdata(sdev, rakdev);
+
+ ret = serdev_device_open(sdev);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to open (%d)\n", ret);
+ return ret;
+ }
+
+ serdev_device_set_baudrate(sdev, 115200);
+ serdev_device_set_flow_control(sdev, false);
+ serdev_device_set_client_ops(sdev, &rak811_serdev_client_ops);
+
+ ret = rak811_get_version(rakdev, &sz, HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to get version (%d)\n", ret);
+ serdev_device_close(sdev);
+ return ret;
+ }
+
+ dev_info(&sdev->dev, "firmware version: %s\n", sz);
+ kfree(sz);
+
+ ret = rak811_simple_cmd(rakdev, "at+mode=1", 2 * HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to set mode to P2P (%d)\n", ret);
+ serdev_device_close(sdev);
+ return ret;
+ }
+
+ dev_info(&sdev->dev, "Done.\n");
+
+ return 0;
+}
+
+static void rak811_remove(struct serdev_device *sdev)
+{
+ serdev_device_close(sdev);
+
+ dev_info(&sdev->dev, "Removed\n");
+}
+
+static const struct of_device_id rak811_of_match[] = {
+ { .compatible = "rakwireless,rak811" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rak811_of_match);
+
+static struct serdev_device_driver rak811_serdev_driver = {
+ .probe = rak811_probe,
+ .remove = rak811_remove,
+ .driver = {
+ .name = "rak811",
+ .of_match_table = rak811_of_match,
+ },
+};
+
+static int __init rak811_init(void)
+{
+ int ret;
+
+ ret = serdev_device_driver_register(&rak811_serdev_driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void __exit rak811_exit(void)
+{
+ serdev_device_driver_unregister(&rak811_serdev_driver);
+}
+
+module_init(rak811_init);
+module_exit(rak811_exit);
+
+MODULE_DESCRIPTION("RAK811 serdev driver");
+MODULE_AUTHOR("Andreas Färber <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.16.4
The Semtech SX1257 and Sx1255 are usually used for radios A/B of SX1301.
This will not implement a netdev itself, but rather needs to interface
with SX130x somehow.
Cc: Ben Whitten <[email protected]>
Cc: Steve deRosier <[email protected]>
Cc: Mark Brown <[email protected]>
Cc: Michael Röder <[email protected]>
Cc: Ken Yu (禹凯) <[email protected]>
Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/lora/Kconfig | 7 ++++
drivers/net/lora/Makefile | 3 ++
drivers/net/lora/sx1257.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+)
create mode 100644 drivers/net/lora/sx1257.c
diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
index 3e384493cbdd..68c7480d7812 100644
--- a/drivers/net/lora/Kconfig
+++ b/drivers/net/lora/Kconfig
@@ -31,6 +31,13 @@ config LORA_RN2483
help
Microchip RN2483/2903
+config LORA_SX1257
+ tristate "Semtech SX125x SPI driver"
+ default y
+ depends on SPI
+ help
+ Semtech SX1255/1257
+
config LORA_SX1276
tristate "Semtech SX127x SPI driver"
default y
diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile
index 6b6870ffbfd8..44c578bde7d5 100644
--- a/drivers/net/lora/Makefile
+++ b/drivers/net/lora/Makefile
@@ -16,6 +16,9 @@ obj-$(CONFIG_LORA_RN2483) += lora-rn2483.o
lora-rn2483-y := rn2483.o
lora-rn2483-y += rn2483_cmd.o
+obj-$(CONFIG_LORA_SX1257) += lora-sx1257.o
+lora-sx1257-y := sx1257.o
+
obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o
lora-sx1276-y := sx1276.o
diff --git a/drivers/net/lora/sx1257.c b/drivers/net/lora/sx1257.c
new file mode 100644
index 000000000000..c4e04ee3ec4b
--- /dev/null
+++ b/drivers/net/lora/sx1257.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Semtech SX1255/SX1257 LoRa transceiver
+ *
+ * Copyright (c) 2018 Andreas Färber
+ *
+ * Based on SX1301 HAL code:
+ * Copyright (c) 2013 Semtech-Cycleo
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+
+static int sx1257_write(struct spi_device *spi, u8 reg, u8 val)
+{
+ u8 buf[2];
+
+ buf[0] = reg | BIT(7);
+ buf[1] = val;
+ return spi_write(spi, buf, 2);
+}
+
+static int sx1257_read(struct spi_device *spi, u8 reg, u8 *val)
+{
+ u8 addr = reg & 0x7f;
+ return spi_write_then_read(spi, &addr, 1, val, 1);
+}
+
+static int sx1257_probe(struct spi_device *spi)
+{
+ u8 val;
+ int ret;
+
+ if (true) {
+ ret = sx1257_read(spi, 0x07, &val);
+ if (ret) {
+ dev_err(&spi->dev, "version read failed\n");
+ return ret;
+ }
+
+ dev_info(&spi->dev, "SX125x version: %02x\n", (unsigned)val);
+ }
+
+ ret = sx1257_write(spi, 0x10, 1 /* + 2 */);
+ if (ret) {
+ dev_err(&spi->dev, "clk write failed\n");
+ return ret;
+ }
+
+ dev_info(&spi->dev, "clk written\n");
+
+ if (true) {
+ ret = sx1257_write(spi, 0x26, 13 + 2 * 16);
+ if (ret) {
+ dev_err(&spi->dev, "xosc write failed\n");
+ return ret;
+ }
+ }
+
+ dev_info(&spi->dev, "SX1257 module probed\n");
+
+ return 0;
+}
+
+static int sx1257_remove(struct spi_device *spi)
+{
+ dev_info(&spi->dev, "SX1257 module removed\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sx1257_dt_ids[] = {
+ { .compatible = "semtech,sx1255" },
+ { .compatible = "semtech,sx1257" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sx1257_dt_ids);
+#endif
+
+static struct spi_driver sx1257_spi_driver = {
+ .driver = {
+ .name = "sx1257",
+ .of_match_table = of_match_ptr(sx1257_dt_ids),
+ },
+ .probe = sx1257_probe,
+ .remove = sx1257_remove,
+};
+
+module_spi_driver(sx1257_spi_driver);
+
+MODULE_DESCRIPTION("SX1257 SPI driver");
+MODULE_AUTHOR("Andreas Färber <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.16.4
Implement helper functions for use by LoRa device drivers.
Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/Makefile | 1 +
drivers/net/lora/Kconfig | 18 +++++++
drivers/net/lora/Makefile | 10 ++++
drivers/net/lora/dev.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/lora/dev.h | 23 +++++++++
net/lora/Kconfig | 6 +++
6 files changed, 183 insertions(+)
create mode 100644 drivers/net/lora/Kconfig
create mode 100644 drivers/net/lora/Makefile
create mode 100644 drivers/net/lora/dev.c
create mode 100644 include/linux/lora/dev.h
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 21cde7e78621..9819bf28633d 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_ETHERNET) += ethernet/
obj-$(CONFIG_FDDI) += fddi/
obj-$(CONFIG_HIPPI) += hippi/
obj-$(CONFIG_HAMRADIO) += hamradio/
+obj-$(CONFIG_LORA) += lora/
obj-$(CONFIG_PLIP) += plip/
obj-$(CONFIG_PPP) += ppp/
obj-$(CONFIG_PPP_ASYNC) += ppp/
diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
new file mode 100644
index 000000000000..40969b148a50
--- /dev/null
+++ b/drivers/net/lora/Kconfig
@@ -0,0 +1,18 @@
+#
+# LoRa
+#
+
+menu "LoRa Device Drivers"
+
+config LORA_DEV
+ tristate "LoRa drivers"
+ default y
+ help
+ LoRa ...
+ If unsure, say Y.
+
+#
+# Alphabetically sorted.
+#
+
+endmenu
diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile
new file mode 100644
index 000000000000..8f9d25ea4e70
--- /dev/null
+++ b/drivers/net/lora/Makefile
@@ -0,0 +1,10 @@
+#
+# LoRa
+#
+
+obj-$(CONFIG_LORA_DEV) += lora-dev.o
+lora-dev-y := dev.o
+
+#
+# Alphabetically sorted.
+#
diff --git a/drivers/net/lora/dev.c b/drivers/net/lora/dev.c
new file mode 100644
index 000000000000..8c01106008be
--- /dev/null
+++ b/drivers/net/lora/dev.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+
+#include <linux/if_arp.h>
+#include <linux/lora.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/lora/dev.h>
+#include <linux/lora/skb.h>
+#include <net/rtnetlink.h>
+
+#define LORA_MTU 256 /* XXX */
+
+struct sk_buff *alloc_lora_skb(struct net_device *dev, u8 **data)
+{
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, sizeof(struct lora_skb_priv) + LORA_MTU);
+ if (unlikely(!skb))
+ return NULL;
+
+ skb->protocol = htons(ETH_P_LORA);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
+
+ lora_skb_reserve(skb);
+ lora_skb_prv(skb)->ifindex = dev->ifindex;
+
+ return skb;
+}
+EXPORT_SYMBOL_GPL(alloc_lora_skb);
+
+int open_loradev(struct net_device *dev)
+{
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(open_loradev);
+
+void close_loradev(struct net_device *dev)
+{
+}
+EXPORT_SYMBOL_GPL(close_loradev);
+
+static void lora_setup(struct net_device *dev)
+{
+ dev->type = ARPHRD_LORA;
+ dev->mtu = LORA_MTU;
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->flags = IFF_NOARP;
+ dev->features = 0;
+}
+
+struct net_device *alloc_loradev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct lora_priv *priv;
+
+ dev = alloc_netdev(sizeof_priv, "lora%d", NET_NAME_UNKNOWN, lora_setup);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+ priv->dev = dev;
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_loradev);
+
+void free_loradev(struct net_device *dev)
+{
+ free_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(free_loradev);
+
+static struct rtnl_link_ops lora_link_ops __read_mostly = {
+ .kind = "lora",
+ .setup = lora_setup,
+};
+
+int register_loradev(struct net_device *dev)
+{
+ dev->rtnl_link_ops = &lora_link_ops;
+ return register_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(register_loradev);
+
+void unregister_loradev(struct net_device *dev)
+{
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_loradev);
+
+static int __init lora_dev_init(void)
+{
+ printk("lora-dev: init\n");
+
+ return rtnl_link_register(&lora_link_ops);
+}
+
+static void __exit lora_dev_exit(void)
+{
+ printk("lora-dev: exit\n");
+
+ rtnl_link_unregister(&lora_link_ops);
+}
+
+module_init(lora_dev_init);
+module_exit(lora_dev_exit);
+
+MODULE_DESCRIPTION("LoRa device driver interface");
+MODULE_ALIAS_RTNL_LINK("lora");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andreas Färber");
diff --git a/include/linux/lora/dev.h b/include/linux/lora/dev.h
new file mode 100644
index 000000000000..531e68f0c9a6
--- /dev/null
+++ b/include/linux/lora/dev.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * linux/lora/dev.h
+ *
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+#ifndef _LORA_DEV_H
+#define _LORA_DEV_H
+
+#include <linux/netdevice.h>
+
+struct net_device *alloc_loradev(int sizeof_priv);
+void free_loradev(struct net_device *dev);
+int register_loradev(struct net_device *dev);
+void unregister_loradev(struct net_device *dev);
+int open_loradev(struct net_device *dev);
+void close_loradev(struct net_device *dev);
+
+struct lora_priv {
+ struct net_device *dev;
+};
+
+#endif /* _LORA_DEV_H */
diff --git a/net/lora/Kconfig b/net/lora/Kconfig
index 44972ea8769f..20658fea3c7c 100644
--- a/net/lora/Kconfig
+++ b/net/lora/Kconfig
@@ -7,3 +7,9 @@ menuconfig LORA
tristate "LoRa subsystem support"
help
LoRa ...
+
+if LORA
+
+source "drivers/net/lora/Kconfig"
+
+endif
--
2.16.4
The USI WM-SG-SM-42 offers a UART based AT command interface.
Cc: Dollar Chen (陳義元) <[email protected]>
Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/lora/Kconfig | 7 +
drivers/net/lora/Makefile | 3 +
drivers/net/lora/usi.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 421 insertions(+)
create mode 100644 drivers/net/lora/usi.c
diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
index 2e05caef8645..72a9d2a0f2be 100644
--- a/drivers/net/lora/Kconfig
+++ b/drivers/net/lora/Kconfig
@@ -31,6 +31,13 @@ config LORA_SX1276
help
Semtech SX1272/1276/1278
+config LORA_USI
+ tristate "USI WM-SG-SM-42 driver"
+ default y
+ depends on SERIAL_DEV_BUS
+ help
+ USI WM-SG-SM-42
+
config LORA_WIMOD
tristate "IMST WiMOD driver"
default y
diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile
index ecb326c859a5..dfa9a43dcfb3 100644
--- a/drivers/net/lora/Makefile
+++ b/drivers/net/lora/Makefile
@@ -16,5 +16,8 @@ lora-rn2483-y += rn2483_cmd.o
obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o
lora-sx1276-y := sx1276.o
+obj-$(CONFIG_LORA_USI) += lora-usi.o
+lora-usi-y := usi.o
+
obj-$(CONFIG_LORA_WIMOD) += lora-wimod.o
lora-wimod-y := wimod.o
diff --git a/drivers/net/lora/usi.c b/drivers/net/lora/usi.c
new file mode 100644
index 000000000000..f0c697e2cde2
--- /dev/null
+++ b/drivers/net/lora/usi.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * USI WM-SG-SM-42
+ *
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+
+#include <linux/lora.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/serdev.h>
+#include <linux/lora/dev.h>
+
+struct usi_device {
+ struct serdev_device *serdev;
+
+ int mode;
+
+ char rx_buf[4096];
+ int rx_len;
+
+ struct completion prompt_recv_comp;
+ struct completion tx_event_recv_comp;
+};
+
+static bool usi_cmd_ok(const char *resp)
+{
+ int len = strlen(resp);
+
+ return (len == 4 && !strcmp(resp, "OK\r\n")) ||
+ (len >= 6 && !strcmp(resp + len - 6, "\r\nOK\r\n"));
+}
+
+static int usi_send_command(struct usi_device *usidev, const char *cmd, char **data, unsigned long timeout)
+{
+ struct serdev_device *sdev = usidev->serdev;
+ const char cr = '\r';
+ int cmd_len, resp_len;
+ char *resp;
+
+ cmd_len = strlen(cmd);
+ serdev_device_write_buf(sdev, cmd, cmd_len);
+ serdev_device_write_buf(sdev, &cr, 1);
+
+ timeout = wait_for_completion_timeout(&usidev->prompt_recv_comp, timeout);
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ resp = usidev->rx_buf;
+ resp_len = usidev->rx_len;
+ if (!strncmp(resp, cmd, cmd_len) && resp[cmd_len] == '\r') {
+ dev_dbg(&sdev->dev, "Skipping echo\n");
+ resp += cmd_len + 1;
+ resp_len -= cmd_len + 1;
+ }
+ dev_dbg(&sdev->dev, "Received: '%s'\n", resp);
+ if (data)
+ *data = kstrdup(resp, GFP_KERNEL);
+
+ usidev->rx_len = 0;
+ reinit_completion(&usidev->prompt_recv_comp);
+
+ return 0;
+}
+
+static int usi_simple_cmd(struct usi_device *usidev, const char *cmd, unsigned long timeout)
+{
+ char *resp;
+ int ret;
+
+ ret = usi_send_command(usidev, cmd, &resp, timeout);
+ if (ret)
+ return ret;
+
+ if (strcmp(resp, "OK\r\n") == 0) {
+ kfree(resp);
+ return 0;
+ }
+
+ kfree(resp);
+
+ return -EINVAL;
+}
+
+static int usi_cmd_reset(struct usi_device *usidev)
+{
+ int ret;
+
+ ret = usi_send_command(usidev, "ATZ", NULL, HZ);
+ if (ret)
+ return ret;
+
+ mdelay(1000);
+
+ return 0;
+}
+
+static int usi_cmd_read_reg(struct usi_device *usidev, u8 addr, u8 *val)
+{
+ char *cmd;
+ char *resp;
+ char *sz;
+ int ret;
+
+ cmd = kasprintf(GFP_KERNEL, "AT+RREG=0x%02x", (int)addr);
+ if (!cmd)
+ return -ENOMEM;
+
+ ret = usi_send_command(usidev, cmd, &resp, HZ);
+ if (ret) {
+ kfree(cmd);
+ return ret;
+ }
+ if (!usi_cmd_ok(resp)) {
+ kfree(resp);
+ kfree(cmd);
+ return -EINVAL;
+ }
+ resp[strlen(resp) - 6] = '\0';
+ sz = resp;
+ if (strstarts(sz, "+Reg="))
+ sz += 5;
+ if (strncasecmp(sz, cmd + 8, 4) == 0 && strstarts(sz + 4, ", "))
+ sz += 6;
+
+ kfree(cmd);
+
+ dev_dbg(&usidev->serdev->dev, "Parsing '%s'\n", sz);
+ ret = kstrtou8(sz, 0, val);
+ if (ret) {
+ kfree(resp);
+ return ret;
+ }
+
+ kfree(resp);
+
+ return 0;
+}
+
+static int usi_receive_buf(struct serdev_device *sdev, const u8 *data, size_t count)
+{
+ struct usi_device *usidev = serdev_device_get_drvdata(sdev);
+ size_t i = 0;
+ int len = 0;
+
+ dev_dbg(&sdev->dev, "Receive (%d)\n", (int)count);
+
+ for (i = 0; i < count; i++) {
+ dev_dbg(&sdev->dev, "Receive: 0x%02x\n", (int)data[i]);
+ }
+ i = 0;
+
+ if (completion_done(&usidev->prompt_recv_comp) ||
+ completion_done(&usidev->tx_event_recv_comp)) {
+ dev_info(&sdev->dev, "RX waiting on completion\n");
+ return 0;
+ }
+ if (usidev->rx_len == sizeof(usidev->rx_buf) - 1) {
+ dev_warn(&sdev->dev, "RX buffer full\n");
+ return 0;
+ }
+
+ i = min(count, sizeof(usidev->rx_buf) - 1 - usidev->rx_len);
+ if (i > 0) {
+ memcpy(&usidev->rx_buf[usidev->rx_len], data, i);
+ usidev->rx_len += i;
+ len += i;
+ }
+ if (usidev->rx_len >= 3 && strncmp(&usidev->rx_buf[usidev->rx_len - 3], "\r# ", 3) == 0) {
+ usidev->rx_len -= 3;
+ usidev->rx_buf[usidev->rx_len] = '\0';
+ complete(&usidev->prompt_recv_comp);
+ } else if (usidev->rx_len > 7 && strstarts(usidev->rx_buf, "+RCV") &&
+ strncmp(&usidev->rx_buf[usidev->rx_len - 2], "\r\n", 2) == 0) {
+ usidev->rx_buf[usidev->rx_len - 2] = '\0';
+ dev_info(&sdev->dev, "RCV event: '%s'\n", usidev->rx_buf + 4);
+ usidev->rx_len = 0;
+ } else if (usidev->rx_len > 6 && strstarts(usidev->rx_buf, "+TX: ") &&
+ strncmp(&usidev->rx_buf[usidev->rx_len - 2], "\r\n", 2) == 0) {
+ usidev->rx_buf[usidev->rx_len - 2] = '\0';
+ dev_info(&sdev->dev, "TX event: '%s'\n", usidev->rx_buf + 5);
+ complete(&usidev->tx_event_recv_comp);
+ }
+
+ return len;
+}
+
+static const struct serdev_device_ops usi_serdev_client_ops = {
+ .receive_buf = usi_receive_buf,
+};
+
+static int usi_probe(struct serdev_device *sdev)
+{
+ struct usi_device *usidev;
+ //unsigned long timeout;
+ char *resp;
+ u8 val;
+ int ret;
+
+ dev_info(&sdev->dev, "Probing");
+
+ usidev = devm_kzalloc(&sdev->dev, sizeof(struct usi_device), GFP_KERNEL);
+ if (!usidev)
+ return -ENOMEM;
+
+ usidev->serdev = sdev;
+ usidev->mode = -1;
+ init_completion(&usidev->prompt_recv_comp);
+ init_completion(&usidev->tx_event_recv_comp);
+ serdev_device_set_drvdata(sdev, usidev);
+
+ ret = serdev_device_open(sdev);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to open (%d)", ret);
+ return ret;
+ }
+
+ serdev_device_set_baudrate(sdev, 115200);
+ serdev_device_set_flow_control(sdev, false);
+ serdev_device_set_client_ops(sdev, &usi_serdev_client_ops);
+
+ ret = usi_cmd_reset(usidev);
+ if (ret)
+ dev_warn(&sdev->dev, "Reset failed\n");
+
+ ret = usi_send_command(usidev, "ATE=0", NULL, HZ);
+ if (ret)
+ dev_warn(&sdev->dev, "ATE failed\n");
+
+ /* Dropped in firmware 2.8 */
+ ret = usi_send_command(usidev, "ATI", &resp, HZ);
+ if (!ret) {
+ if (usi_cmd_ok(resp)) {
+ resp[strlen(resp) - 6] = '\0';
+ dev_info(&sdev->dev, "Firmware '%s'\n", resp);
+ }
+ kfree(resp);
+ }
+
+ ret = usi_send_command(usidev, "AT+DEFMODE", &resp, HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "Checking DEFMODE failed (%d)\n", ret);
+ serdev_device_close(sdev);
+ return ret;
+ }
+ if (usi_cmd_ok(resp)) {
+ resp[strlen(resp) - 6] = '\0';
+ dev_info(&sdev->dev, "Default mode '%s'\n", resp);
+ if (!strcmp(resp, "MFG_WAN_MODE"))
+ usidev->mode = 6;
+ else if (!strcmp(resp, "MFG_TEST_IDLE"))
+ usidev->mode = 0;
+ else if (!strcmp(resp, "MFG_TX_TONE"))
+ usidev->mode = 1;
+ else if (!strcmp(resp, "MFG_TX_PACKET"))
+ usidev->mode = 2;
+ else if (!strcmp(resp, "MFG_ERROR_LESS_ARGUMENETS"))
+ usidev->mode = 3;
+ else if (!strcmp(resp, "MFG_TX_TEXT"))
+ usidev->mode = 4;
+ else if (!strcmp(resp, "MFG_TEST_STOP"))
+ usidev->mode = 5;
+ }
+ kfree(resp);
+
+ if (usidev->mode != 3) {
+ ret = usi_simple_cmd(usidev, "AT+DEFMODE=3", HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "Setting DEFMODE failed (%d)\n", ret);
+ serdev_device_close(sdev);
+ return ret;
+ }
+
+#if 1
+ ret = usi_simple_cmd(usidev, "AT+WDCT", 5 * HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "Writing DCT failed (%d)\n", ret);
+ serdev_device_close(sdev);
+ return ret;
+ }
+
+ ret = usi_cmd_reset(usidev);
+ if (ret) {
+ dev_err(&sdev->dev, "Reset failed\n");
+ serdev_device_close(sdev);
+ return ret;
+ }
+
+ ret = usi_send_command(usidev, "ATE=0", NULL, HZ);
+ if (ret)
+ dev_warn(&sdev->dev, "ATE failed\n");
+#endif
+
+ usidev->mode = -1;
+ ret = usi_send_command(usidev, "AT+DEFMODE", &resp, HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "Checking DEFMODE failed (%d)\n", ret);
+ serdev_device_close(sdev);
+ return ret;
+ }
+ if (usi_cmd_ok(resp)) {
+ resp[strlen(resp) - 6] = '\0';
+ dev_info(&sdev->dev, "Default mode '%s'\n", resp);
+ if (!strcmp(resp, "MFG_WAN_MODE")) {
+ usidev->mode = 6;
+ }
+ }
+ kfree(resp);
+ }
+
+ ret = usi_send_command(usidev, "AT+VER", &resp, HZ);
+ if (!ret) {
+ if (usi_cmd_ok(resp)) {
+ resp[strlen(resp) - 6] = '\0';
+ dev_info(&sdev->dev, "LoRaWAN version '%s'\n",
+ (strstarts(resp, "+VER=")) ? (resp + 5) : resp);
+ }
+ kfree(resp);
+ }
+
+ ret = usi_simple_cmd(usidev, "AT+RF=20,868000000,7,0,1,0,8,0,0,0", HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "AT+RF failed (%d)\n", ret);
+ serdev_device_close(sdev);
+ return ret;
+ }
+
+ /*ret = usi_simple_cmd(usidev, "AT+TXT=1,deadbeef", 2 * HZ);
+ if (ret) {
+ dev_err(&sdev->dev, "TX failed (%d)\n", ret);
+ serdev_device_close(sdev);
+ return ret;
+ }
+
+ timeout = wait_for_completion_timeout(&usidev->tx_event_recv_comp, 5 * HZ);
+ if (!timeout) {
+ serdev_device_close(sdev);
+ return -ETIMEDOUT;
+ }
+ usidev->rx_len = 0;
+ reinit_completion(&usidev->tx_event_recv_comp);*/
+
+ ret = usi_cmd_read_reg(usidev, 0x42, &val);
+ if (!ret) {
+ dev_info(&sdev->dev, "SX1272 VERSION 0x%02x\n", (int)val);
+ }
+
+ ret = usi_cmd_read_reg(usidev, 0x39, &val);
+ if (!ret) {
+ dev_info(&sdev->dev, "SX1272 SyncWord 0x%02x\n", (int)val);
+ }
+
+ ret = usi_cmd_read_reg(usidev, 0x01, &val);
+ if (!ret) {
+ dev_info(&sdev->dev, "SX1272 OpMode 0x%02x\n", (int)val);
+ }
+
+ dev_info(&sdev->dev, "Done.");
+
+ return 0;
+}
+
+static void usi_remove(struct serdev_device *sdev)
+{
+ struct usi_device *usidev = serdev_device_get_drvdata(sdev);
+
+ usi_send_command(usidev, "ATE=1\r", NULL, HZ);
+
+ serdev_device_close(sdev);
+
+ dev_info(&sdev->dev, "Removed\n");
+}
+
+static const struct of_device_id usi_of_match[] = {
+ { .compatible = "usi,wm-sg-sm-42" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, usi_of_match);
+
+static struct serdev_device_driver usi_serdev_driver = {
+ .probe = usi_probe,
+ .remove = usi_remove,
+ .driver = {
+ .name = "usi",
+ .of_match_table = usi_of_match,
+ },
+};
+
+static int __init usi_init(void)
+{
+ int ret;
+
+ ret = serdev_device_driver_register(&usi_serdev_driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void __exit usi_exit(void)
+{
+ serdev_device_driver_unregister(&usi_serdev_driver);
+}
+
+module_init(usi_init);
+module_exit(usi_exit);
+
+MODULE_DESCRIPTION("USI WM-SG-SM-42 serdev driver");
+MODULE_AUTHOR("Andreas Färber <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.16.4
Semtech SX1276/77/78/79 and SX1272/73 are LoRa transceivers with a SPI
interface. They also offer a non-LoRa mode (not exposed here).
Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/lora/Kconfig | 11 +
drivers/net/lora/Makefile | 3 +
drivers/net/lora/sx1276.c | 608 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 622 insertions(+)
create mode 100644 drivers/net/lora/sx1276.c
diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
index 40969b148a50..0436f6b09a1c 100644
--- a/drivers/net/lora/Kconfig
+++ b/drivers/net/lora/Kconfig
@@ -15,4 +15,15 @@ config LORA_DEV
# Alphabetically sorted.
#
+if LORA_DEV
+
+config LORA_SX1276
+ tristate "Semtech SX127x SPI driver"
+ default y
+ depends on SPI
+ help
+ Semtech SX1272/1276/1278
+
+endif
+
endmenu
diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile
index 8f9d25ea4e70..8845542dba50 100644
--- a/drivers/net/lora/Makefile
+++ b/drivers/net/lora/Makefile
@@ -8,3 +8,6 @@ lora-dev-y := dev.o
#
# Alphabetically sorted.
#
+
+obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o
+lora-sx1276-y := sx1276.o
diff --git a/drivers/net/lora/sx1276.c b/drivers/net/lora/sx1276.c
new file mode 100644
index 000000000000..d6732111247a
--- /dev/null
+++ b/drivers/net/lora/sx1276.c
@@ -0,0 +1,608 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Semtech SX1272/SX1276 LoRa transceiver
+ *
+ * Copyright (c) 2016-2018 Andreas Färber
+ */
+
+#include <linux/delay.h>
+#include <linux/lora.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/lora/dev.h>
+#include <linux/spi/spi.h>
+
+#define REG_FIFO 0x00
+#define REG_OPMODE 0x01
+#define REG_FRF_MSB 0x06
+#define REG_FRF_MID 0x07
+#define REG_FRF_LSB 0x08
+#define REG_PA_CONFIG 0x09
+#define LORA_REG_FIFO_ADDR_PTR 0x0d
+#define LORA_REG_FIFO_TX_BASE_ADDR 0x0e
+#define LORA_REG_IRQ_FLAGS_MASK 0x11
+#define LORA_REG_IRQ_FLAGS 0x12
+#define LORA_REG_PAYLOAD_LENGTH 0x22
+#define LORA_REG_SYNC_WORD 0x39
+#define REG_DIO_MAPPING1 0x40
+#define REG_DIO_MAPPING2 0x41
+#define REG_VERSION 0x42
+#define REG_PA_DAC 0x4d
+
+#define REG_OPMODE_LONG_RANGE_MODE BIT(7)
+#define REG_OPMODE_LOW_FREQUENCY_MODE_ON BIT(3)
+#define REG_OPMODE_MODE_MASK GENMASK(2, 0)
+#define REG_OPMODE_MODE_SLEEP (0x0 << 0)
+#define REG_OPMODE_MODE_STDBY (0x1 << 0)
+#define REG_OPMODE_MODE_TX (0x3 << 0)
+#define REG_OPMODE_MODE_RXCONTINUOUS (0x5 << 0)
+#define REG_OPMODE_MODE_RXSINGLE (0x6 << 0)
+
+#define REG_PA_CONFIG_PA_SELECT BIT(7)
+
+#define LORA_REG_IRQ_FLAGS_TX_DONE BIT(3)
+
+#define REG_DIO_MAPPING1_DIO0_MASK GENMASK(7, 6)
+
+struct sx1276_priv {
+ struct lora_priv lora;
+ struct spi_device *spi;
+
+ size_t fifosize;
+ int dio_gpio[6];
+
+ struct mutex spi_lock;
+
+ struct sk_buff *tx_skb;
+ int tx_len;
+
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+};
+
+static int sx1276_read_single(struct spi_device *spi, u8 reg, u8 *val)
+{
+ u8 addr = reg & 0x7f;
+ return spi_write_then_read(spi, &addr, 1, val, 1);
+}
+
+static int sx1276_write_single(struct spi_device *spi, u8 reg, u8 val)
+{
+ u8 buf[2];
+
+ buf[0] = reg | BIT(7);
+ buf[1] = val;
+ return spi_write(spi, buf, 2);
+}
+
+static int sx1276_write_burst(struct spi_device *spi, u8 reg, size_t len, void *val)
+{
+ u8 buf = reg | BIT(7);
+ struct spi_transfer xfers[2] = {
+ [0] = {
+ .tx_buf = &buf,
+ .len = 1,
+ },
+ [1] = {
+ .tx_buf = val,
+ .len = len,
+ },
+ };
+
+ return spi_sync_transfer(spi, xfers, 2);
+}
+
+static int sx1276_write_fifo(struct spi_device *spi, size_t len, void *val)
+{
+ return sx1276_write_burst(spi, REG_FIFO, len, val);
+}
+
+static netdev_tx_t sx1276_loradev_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct sx1276_priv *priv = netdev_priv(netdev);
+
+ netdev_dbg(netdev, "%s\n", __func__);
+
+ if (priv->tx_skb || priv->tx_len) {
+ netdev_warn(netdev, "TX busy\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (skb->protocol != htons(ETH_P_LORA)) {
+ kfree_skb(skb);
+ netdev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ netif_stop_queue(netdev);
+ priv->tx_skb = skb;
+ queue_work(priv->wq, &priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int sx1276_tx(struct spi_device *spi, void *data, int data_len)
+{
+ u8 addr, val;
+ int ret;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ ret = sx1276_read_single(spi, REG_OPMODE, &val);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to read RegOpMode (%d)\n", ret);
+ return ret;
+ }
+ dev_dbg(&spi->dev, "RegOpMode = 0x%02x\n", val);
+ if (!(val & REG_OPMODE_LONG_RANGE_MODE))
+ dev_err(&spi->dev, "LongRange Mode not active!\n");
+ if ((val & REG_OPMODE_MODE_MASK) == REG_OPMODE_MODE_SLEEP)
+ dev_err(&spi->dev, "Cannot access FIFO in Sleep Mode!\n");
+
+ ret = sx1276_read_single(spi, LORA_REG_FIFO_TX_BASE_ADDR, &addr);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to read RegFifoTxBaseAddr (%d)\n", ret);
+ return ret;
+ }
+ dev_dbg(&spi->dev, "RegFifoTxBaseAddr = 0x%02x\n", addr);
+
+ ret = sx1276_write_single(spi, LORA_REG_FIFO_ADDR_PTR, addr);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to write RegFifoAddrPtr (%d)\n", ret);
+ return ret;
+ }
+
+ ret = sx1276_write_single(spi, LORA_REG_PAYLOAD_LENGTH, data_len);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to write RegPayloadLength (%d)\n", ret);
+ return ret;
+ }
+
+ ret = sx1276_write_fifo(spi, data_len, data);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to write into FIFO (%d)\n", ret);
+ return ret;
+ }
+
+ ret = sx1276_read_single(spi, LORA_REG_IRQ_FLAGS, &val);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to read RegIrqFlags (%d)\n", ret);
+ return ret;
+ }
+ dev_dbg(&spi->dev, "RegIrqFlags = 0x%02x\n", val);
+
+ ret = sx1276_write_single(spi, LORA_REG_IRQ_FLAGS, LORA_REG_IRQ_FLAGS_TX_DONE);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to write RegIrqFlags (%d)\n", ret);
+ return ret;
+ }
+
+ ret = sx1276_read_single(spi, LORA_REG_IRQ_FLAGS_MASK, &val);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to read RegIrqFlagsMask (%d)\n", ret);
+ return ret;
+ }
+ dev_dbg(&spi->dev, "RegIrqFlagsMask = 0x%02x\n", val);
+
+ val &= ~LORA_REG_IRQ_FLAGS_TX_DONE;
+ ret = sx1276_write_single(spi, LORA_REG_IRQ_FLAGS_MASK, val);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to write RegIrqFlagsMask (%d)\n", ret);
+ return ret;
+ }
+
+ ret = sx1276_read_single(spi, REG_DIO_MAPPING1, &val);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to read RegDioMapping1 (%d)\n", ret);
+ return ret;
+ }
+
+ val &= ~REG_DIO_MAPPING1_DIO0_MASK;
+ val |= 0x1 << 6;
+ ret = sx1276_write_single(spi, REG_DIO_MAPPING1, val);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to write RegDioMapping1 (%d)\n", ret);
+ return ret;
+ }
+
+ ret = sx1276_read_single(spi, REG_OPMODE, &val);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to read RegOpMode (%d)\n", ret);
+ return ret;
+ }
+
+ val &= ~REG_OPMODE_MODE_MASK;
+ val |= REG_OPMODE_MODE_TX;
+ ret = sx1276_write_single(spi, REG_OPMODE, val);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to write RegOpMode (%d)\n", ret);
+ return ret;
+ }
+
+ dev_dbg(&spi->dev, "%s: done\n", __func__);
+
+ return 0;
+}
+
+static void sx1276_tx_work_handler(struct work_struct *ws)
+{
+ struct sx1276_priv *priv = container_of(ws, struct sx1276_priv, tx_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *netdev = spi_get_drvdata(spi);
+
+ netdev_dbg(netdev, "%s\n", __func__);
+
+ mutex_lock(&priv->spi_lock);
+
+ if (priv->tx_skb) {
+ sx1276_tx(spi, priv->tx_skb->data, priv->tx_skb->data_len);
+ priv->tx_len = 1 + priv->tx_skb->data_len;
+ if (!(netdev->flags & IFF_ECHO) ||
+ priv->tx_skb->pkt_type != PACKET_LOOPBACK ||
+ priv->tx_skb->protocol != htons(ETH_P_LORA))
+ kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+
+ mutex_unlock(&priv->spi_lock);
+}
+
+static irqreturn_t sx1276_dio_interrupt(int irq, void *dev_id)
+{
+ struct net_device *netdev = dev_id;
+ struct sx1276_priv *priv = netdev_priv(netdev);
+ struct spi_device *spi = priv->spi;
+ u8 val;
+ int ret;
+
+ netdev_dbg(netdev, "%s\n", __func__);
+
+ mutex_lock(&priv->spi_lock);
+
+ ret = sx1276_read_single(spi, LORA_REG_IRQ_FLAGS, &val);
+ if (ret) {
+ netdev_warn(netdev, "Failed to read RegIrqFlags (%d)\n", ret);
+ val = 0;
+ }
+
+ if (val & LORA_REG_IRQ_FLAGS_TX_DONE) {
+ netdev_info(netdev, "TX done.\n");
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes += priv->tx_len - 1;
+ priv->tx_len = 0;
+ netif_wake_queue(netdev);
+
+ ret = sx1276_write_single(spi, LORA_REG_IRQ_FLAGS, LORA_REG_IRQ_FLAGS_TX_DONE);
+ if (ret)
+ netdev_warn(netdev, "Failed to write RegIrqFlags (%d)\n", ret);
+ }
+
+ mutex_unlock(&priv->spi_lock);
+
+ return IRQ_HANDLED;
+}
+
+static int sx1276_loradev_open(struct net_device *netdev)
+{
+ struct sx1276_priv *priv = netdev_priv(netdev);
+ struct spi_device *spi = to_spi_device(netdev->dev.parent);
+ u8 val;
+ int ret, irq;
+
+ netdev_dbg(netdev, "%s\n", __func__);
+
+ ret = open_loradev(netdev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ ret = sx1276_read_single(spi, REG_OPMODE, &val);
+ if (ret) {
+ netdev_err(netdev, "Failed to read RegOpMode (%d)\n", ret);
+ goto err_opmode;
+ }
+
+ val &= ~REG_OPMODE_MODE_MASK;
+ val |= REG_OPMODE_MODE_STDBY;
+ ret = sx1276_write_single(spi, REG_OPMODE, val);
+ if (ret) {
+ netdev_err(netdev, "Failed to write RegOpMode (%d)\n", ret);
+ goto err_opmode;
+ }
+
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+
+ priv->wq = alloc_workqueue("sx1276_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, 0);
+ INIT_WORK(&priv->tx_work, sx1276_tx_work_handler);
+
+ if (gpio_is_valid(priv->dio_gpio[0])) {
+ irq = gpio_to_irq(priv->dio_gpio[0]);
+ if (irq <= 0)
+ netdev_warn(netdev, "Failed to obtain interrupt for DIO0 (%d)\n", irq);
+ else {
+ netdev_info(netdev, "Succeeded in obtaining interrupt for DIO0: %d\n", irq);
+ ret = request_threaded_irq(irq, NULL, sx1276_dio_interrupt, IRQF_ONESHOT | IRQF_TRIGGER_RISING, netdev->name, netdev);
+ if (ret) {
+ netdev_err(netdev, "Failed to request interrupt for DIO0 (%d)\n", ret);
+ goto err_irq;
+ }
+ }
+ }
+
+ netif_wake_queue(netdev);
+
+ mutex_unlock(&priv->spi_lock);
+
+ return 0;
+
+err_irq:
+ destroy_workqueue(priv->wq);
+ priv->wq = NULL;
+err_opmode:
+ close_loradev(netdev);
+ mutex_unlock(&priv->spi_lock);
+ return ret;
+}
+
+static int sx1276_loradev_stop(struct net_device *netdev)
+{
+ struct sx1276_priv *priv = netdev_priv(netdev);
+ struct spi_device *spi = to_spi_device(netdev->dev.parent);
+ u8 val;
+ int ret, irq;
+
+ netdev_dbg(netdev, "%s\n", __func__);
+
+ close_loradev(netdev);
+
+ mutex_lock(&priv->spi_lock);
+
+ ret = sx1276_write_single(spi, LORA_REG_IRQ_FLAGS_MASK, 0xff);
+ if (ret) {
+ netdev_err(netdev, "Failed to write RegIrqFlagsMask (%d)\n", ret);
+ goto err_irqmask;
+ }
+
+ ret = sx1276_read_single(spi, REG_OPMODE, &val);
+ if (ret) {
+ netdev_err(netdev, "Failed to read RegOpMode (%d)\n", ret);
+ goto err_opmode;
+ }
+
+ val &= ~REG_OPMODE_MODE_MASK;
+ val |= REG_OPMODE_MODE_SLEEP;
+ ret = sx1276_write_single(spi, REG_OPMODE, val);
+ if (ret) {
+ netdev_err(netdev, "Failed to write RegOpMode (%d)\n", ret);
+ goto err_opmode;
+ }
+
+ if (gpio_is_valid(priv->dio_gpio[0])) {
+ irq = gpio_to_irq(priv->dio_gpio[0]);
+ if (irq > 0) {
+ netdev_dbg(netdev, "Freeing IRQ %d\n", irq);
+ free_irq(irq, netdev);
+ }
+ }
+
+ destroy_workqueue(priv->wq);
+ priv->wq = NULL;
+
+ if (priv->tx_skb || priv->tx_len)
+ netdev->stats.tx_errors++;
+ if (priv->tx_skb)
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+
+ mutex_unlock(&priv->spi_lock);
+
+ return 0;
+
+err_opmode:
+err_irqmask:
+ mutex_unlock(&priv->spi_lock);
+ return ret;
+}
+
+static const struct net_device_ops sx1276_netdev_ops = {
+ .ndo_open = sx1276_loradev_open,
+ .ndo_stop = sx1276_loradev_stop,
+ .ndo_start_xmit = sx1276_loradev_start_xmit,
+};
+
+static int sx1276_probe(struct spi_device *spi)
+{
+ struct net_device *netdev;
+ struct sx1276_priv *priv;
+ int rst, dio[6], ret, model, i;
+ u32 freq_xosc, freq_band;
+ unsigned long long freq_rf;
+ u8 val;
+
+ rst = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0);
+ if (rst == -ENOENT)
+ dev_warn(&spi->dev, "no reset GPIO available, ignoring");
+
+ for (i = 0; i < 6; i++) {
+ dio[i] = of_get_named_gpio(spi->dev.of_node, "dio-gpios", i);
+ if (dio[i] == -ENOENT)
+ dev_dbg(&spi->dev, "DIO%d not available, ignoring", i);
+ else {
+ ret = gpio_direction_input(dio[i]);
+ if (ret)
+ dev_err(&spi->dev, "couldn't set DIO%d to input", i);
+ }
+ }
+
+ if (gpio_is_valid(rst)) {
+ gpio_set_value(rst, 1);
+ udelay(100);
+ gpio_set_value(rst, 0);
+ msleep(5);
+ }
+
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ ret = sx1276_read_single(spi, REG_VERSION, &val);
+ if (ret) {
+ dev_err(&spi->dev, "version read failed");
+ return ret;
+ }
+
+ if (val == 0x22)
+ model = 1272;
+ else {
+ if (gpio_is_valid(rst)) {
+ gpio_set_value(rst, 0);
+ udelay(100);
+ gpio_set_value(rst, 1);
+ msleep(5);
+ }
+
+ ret = sx1276_read_single(spi, REG_VERSION, &val);
+ if (ret) {
+ dev_err(&spi->dev, "version read failed");
+ return ret;
+ }
+
+ if (val == 0x12)
+ model = 1276;
+ else {
+ dev_err(&spi->dev, "transceiver not recognized (RegVersion = 0x%02x)", (unsigned)val);
+ return -EINVAL;
+ }
+ }
+
+ ret = of_property_read_u32(spi->dev.of_node, "clock-frequency", &freq_xosc);
+ if (ret) {
+ dev_err(&spi->dev, "failed reading clock-frequency");
+ return ret;
+ }
+
+ ret = of_property_read_u32(spi->dev.of_node, "radio-frequency", &freq_band);
+ if (ret) {
+ dev_err(&spi->dev, "failed reading radio-frequency");
+ return ret;
+ }
+
+ val = REG_OPMODE_LONG_RANGE_MODE | REG_OPMODE_MODE_SLEEP;
+ if (freq_band < 525000000)
+ val |= REG_OPMODE_LOW_FREQUENCY_MODE_ON;
+ ret = sx1276_write_single(spi, REG_OPMODE, val);
+ if (ret) {
+ dev_err(&spi->dev, "failed writing opmode");
+ return ret;
+ }
+
+ freq_rf = freq_band;
+ freq_rf *= (1 << 19);
+ freq_rf /= freq_xosc;
+ dev_dbg(&spi->dev, "Frf = %llu", freq_rf);
+
+ ret = sx1276_write_single(spi, REG_FRF_MSB, freq_rf >> 16);
+ if (!ret)
+ ret = sx1276_write_single(spi, REG_FRF_MID, freq_rf >> 8);
+ if (!ret)
+ ret = sx1276_write_single(spi, REG_FRF_LSB, freq_rf);
+ if (ret) {
+ dev_err(&spi->dev, "failed writing frequency (%d)", ret);
+ return ret;
+ }
+
+ ret = sx1276_read_single(spi, REG_PA_CONFIG, &val);
+ if (ret) {
+ dev_err(&spi->dev, "failed reading RegPaConfig\n");
+ return ret;
+ }
+ if (true)
+ val |= REG_PA_CONFIG_PA_SELECT;
+ val &= ~GENMASK(3, 0);
+ val |= (23 - 3) - 5;
+ ret = sx1276_write_single(spi, REG_PA_CONFIG, val);
+ if (ret) {
+ dev_err(&spi->dev, "failed writing RegPaConfig\n");
+ return ret;
+ }
+
+ ret = sx1276_read_single(spi, REG_PA_DAC, &val);
+ if (ret) {
+ dev_err(&spi->dev, "failed reading RegPaDac\n");
+ return ret;
+ }
+ val &= ~GENMASK(2, 0);
+ val |= 0x7;
+ ret = sx1276_write_single(spi, REG_PA_DAC, val);
+ if (ret) {
+ dev_err(&spi->dev, "failed writing RegPaDac\n");
+ return ret;
+ }
+
+ netdev = alloc_loradev(sizeof(struct sx1276_priv));
+ if (!netdev)
+ return -ENOMEM;
+
+ netdev->netdev_ops = &sx1276_netdev_ops;
+ netdev->flags |= IFF_ECHO;
+
+ priv = netdev_priv(netdev);
+ priv->spi = spi;
+ mutex_init(&priv->spi_lock);
+ for (i = 0; i < 6; i++)
+ priv->dio_gpio[i] = dio[i];
+
+ spi_set_drvdata(spi, netdev);
+ SET_NETDEV_DEV(netdev, &spi->dev);
+
+ ret = register_loradev(netdev);
+ if (ret) {
+ free_loradev(netdev);
+ return ret;
+ }
+
+ dev_info(&spi->dev, "SX1276 module probed (SX%d)", model);
+
+ return 0;
+}
+
+static int sx1276_remove(struct spi_device *spi)
+{
+ struct net_device *netdev = spi_get_drvdata(spi);
+
+ unregister_loradev(netdev);
+ free_loradev(netdev);
+
+ dev_info(&spi->dev, "SX1276 module removed");
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sx1276_dt_ids[] = {
+ { .compatible = "semtech,sx1272" },
+ { .compatible = "semtech,sx1276" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sx1276_dt_ids);
+#endif
+
+static struct spi_driver sx1276_spi_driver = {
+ .driver = {
+ .name = "sx1276",
+ .of_match_table = of_match_ptr(sx1276_dt_ids),
+ },
+ .probe = sx1276_probe,
+ .remove = sx1276_remove,
+};
+
+module_spi_driver(sx1276_spi_driver);
+
+MODULE_DESCRIPTION("SX1276 SPI driver");
+MODULE_AUTHOR("Andreas Färber <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.16.4
Implement or stub out PF_LORA net_proto_family and datagram proto_ops.
Signed-off-by: Andreas Färber <[email protected]>
---
include/linux/lora/skb.h | 29 +++++
net/Kconfig | 1 +
net/Makefile | 1 +
net/lora/Kconfig | 9 ++
net/lora/Makefile | 8 ++
net/lora/af_lora.c | 152 ++++++++++++++++++++++++
net/lora/af_lora.h | 13 +++
net/lora/dgram.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 506 insertions(+)
create mode 100644 include/linux/lora/skb.h
create mode 100644 net/lora/Kconfig
create mode 100644 net/lora/Makefile
create mode 100644 net/lora/af_lora.c
create mode 100644 net/lora/af_lora.h
create mode 100644 net/lora/dgram.c
diff --git a/include/linux/lora/skb.h b/include/linux/lora/skb.h
new file mode 100644
index 000000000000..8806741464d0
--- /dev/null
+++ b/include/linux/lora/skb.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * linux/lora/skb.h
+ *
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+#ifndef _LORA_SKB_H
+#define _LORA_SKB_H
+
+#include <linux/types.h>
+#include <linux/skbuff.h>
+
+struct lora_skb_priv {
+ int ifindex;
+};
+
+static inline struct lora_skb_priv *lora_skb_prv(struct sk_buff *skb)
+{
+ return (struct lora_skb_priv *)(skb->head);
+}
+
+static inline void lora_skb_reserve(struct sk_buff *skb)
+{
+ skb_reserve(skb, sizeof(struct lora_skb_priv));
+}
+
+struct sk_buff *alloc_lora_skb(struct net_device *dev, u8 **data);
+
+#endif /* _LORA_SKB_H */
diff --git a/net/Kconfig b/net/Kconfig
index f738a6f27665..d294ecc50a3b 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -223,6 +223,7 @@ source "net/phonet/Kconfig"
source "net/6lowpan/Kconfig"
source "net/ieee802154/Kconfig"
source "net/mac802154/Kconfig"
+source "net/lora/Kconfig"
source "net/sched/Kconfig"
source "net/dcb/Kconfig"
source "net/dns_resolver/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index bdaf53925acd..e80b84313851 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -62,6 +62,7 @@ endif
obj-$(CONFIG_6LOWPAN) += 6lowpan/
obj-$(CONFIG_IEEE802154) += ieee802154/
obj-$(CONFIG_MAC802154) += mac802154/
+obj-$(CONFIG_LORA) += lora/
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
diff --git a/net/lora/Kconfig b/net/lora/Kconfig
new file mode 100644
index 000000000000..44972ea8769f
--- /dev/null
+++ b/net/lora/Kconfig
@@ -0,0 +1,9 @@
+#
+# LoRa
+#
+
+menuconfig LORA
+ depends on NET
+ tristate "LoRa subsystem support"
+ help
+ LoRa ...
diff --git a/net/lora/Makefile b/net/lora/Makefile
new file mode 100644
index 000000000000..b58675ef7846
--- /dev/null
+++ b/net/lora/Makefile
@@ -0,0 +1,8 @@
+#
+# LoRa
+#
+
+obj-$(CONFIG_LORA) += lora.o
+
+lora-y := af_lora.o
+lora-y += dgram.o
diff --git a/net/lora/af_lora.c b/net/lora/af_lora.c
new file mode 100644
index 000000000000..662482fe4c9b
--- /dev/null
+++ b/net/lora/af_lora.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+
+#include <linux/if_arp.h>
+#include <linux/lora.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <net/sock.h>
+
+#include "af_lora.h"
+
+int lora_send(struct sk_buff *skb)
+{
+ int ret;
+
+ pr_debug("lora: %s\n", __func__);
+
+ skb->protocol = htons(ETH_P_LORA);
+
+ if (unlikely(skb->len > skb->dev->mtu)) {
+ ret = -EMSGSIZE;
+ goto err_skb;
+ }
+
+ if (unlikely(skb->dev->type != ARPHRD_LORA)) {
+ ret = -EPERM;
+ goto err_skb;
+ }
+
+ if (unlikely(!(skb->dev->flags & IFF_UP))) {
+ ret = -ENETDOWN;
+ goto err_skb;
+ }
+
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
+
+ if (false) {
+ skb->pkt_type = PACKET_LOOPBACK;
+ } else
+ skb->pkt_type = PACKET_HOST;
+
+ ret = dev_queue_xmit(skb);
+ if (ret > 0)
+ ret = net_xmit_errno(ret);
+ if (ret)
+ return ret;
+
+ return 0;
+
+err_skb:
+ kfree_skb(skb);
+ return ret;
+}
+EXPORT_SYMBOL(lora_send);
+
+static void lora_sock_destruct(struct sock *sk)
+{
+ pr_debug("lora: %s\n", __func__);
+
+ skb_queue_purge(&sk->sk_receive_queue);
+}
+
+static int lora_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
+{
+ struct sock *sk;
+
+ pr_debug("lora: %s\n", __func__);
+
+ sock->state = SS_UNCONNECTED;
+
+ if (protocol < 0 || protocol > LORA_NPROTO)
+ return -EINVAL;
+
+ /*if (!net_eq(net, &init_net))
+ return -EAFNOSUPPORT;*/
+
+ if (sock->type != SOCK_DGRAM)
+ return -ESOCKTNOSUPPORT;
+
+ sock->ops = &dgram_proto_ops;
+
+ sk = sk_alloc(net, PF_LORA, GFP_KERNEL, &dgram_proto, kern);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sk->sk_family = PF_LORA;
+ sk->sk_destruct = lora_sock_destruct;
+
+ if (sk->sk_prot->init) {
+ int ret = sk->sk_prot->init(sk);
+ if (ret) {
+ sock_orphan(sk);
+ sock_put(sk);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct net_proto_family lora_net_proto_family = {
+ .family = PF_LORA,
+ .owner = THIS_MODULE,
+ .create = lora_create,
+};
+
+static __init int lora_init(void)
+{
+ int ret;
+
+ pr_debug("lora: init\n");
+
+ ret = proto_register(&dgram_proto, 1);
+ if (ret)
+ goto err_dgram;
+
+ ret = sock_register(&lora_net_proto_family);
+ if (ret)
+ goto err_sock;
+
+ return 0;
+
+err_sock:
+ proto_unregister(&dgram_proto);
+err_dgram:
+ return ret;
+}
+
+static __exit void lora_exit(void)
+{
+ pr_debug("lora: exit\n");
+
+ sock_unregister(PF_LORA);
+ proto_unregister(&dgram_proto);
+}
+
+module_init(lora_init);
+module_exit(lora_exit);
+
+MODULE_DESCRIPTION("LoRa PF_LORA core");
+MODULE_AUTHOR("Andreas Färber <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_LORA);
diff --git a/net/lora/af_lora.h b/net/lora/af_lora.h
new file mode 100644
index 000000000000..47dd863531b2
--- /dev/null
+++ b/net/lora/af_lora.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+#ifndef AF_LORA_H
+#define AF_LORA_H
+
+extern struct proto dgram_proto;
+extern const struct proto_ops dgram_proto_ops;
+
+int lora_send(struct sk_buff *skb);
+
+#endif /* AF_LORA_H */
diff --git a/net/lora/dgram.c b/net/lora/dgram.c
new file mode 100644
index 000000000000..4d931fd3778a
--- /dev/null
+++ b/net/lora/dgram.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/lora.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/version.h>
+#include <linux/lora/skb.h>
+#include <net/sock.h>
+
+#include "af_lora.h"
+
+struct dgram_sock {
+ struct sock sk;
+ int ifindex;
+ bool bound;
+ struct notifier_block notifier;
+};
+
+static inline struct dgram_sock *dgram_sk(const struct sock *sk)
+{
+ return (struct dgram_sock *)sk;
+}
+
+static int dgram_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_lora *addr = (struct sockaddr_lora *)uaddr;
+ struct sock *sk = sock->sk;
+ struct dgram_sock *dgram = dgram_sk(sk);
+ int ifindex;
+ int ret = 0;
+ bool notify_enetdown = false;
+
+ pr_debug("lora: %s\n", __func__);
+
+ if (len < sizeof(*addr))
+ return -EINVAL;
+
+ lock_sock(sk);
+
+ if (dgram->bound && addr->lora_ifindex == dgram->ifindex)
+ goto out;
+
+ if (addr->lora_ifindex) {
+ struct net_device *netdev;
+
+ netdev = dev_get_by_index(sock_net(sk), addr->lora_ifindex);
+ if (!netdev) {
+ ret = -ENODEV;
+ goto out;
+ }
+ if (netdev->type != ARPHRD_LORA) {
+ dev_put(netdev);
+ ret = -ENODEV;
+ goto out;
+ }
+ if (!(netdev->flags & IFF_UP))
+ notify_enetdown = true;
+
+ ifindex = netdev->ifindex;
+
+ dev_put(netdev);
+ } else
+ ifindex = 0;
+
+ dgram->ifindex = ifindex;
+ dgram->bound = true;
+
+out:
+ release_sock(sk);
+
+ if (notify_enetdown) {
+ sk->sk_err = ENETDOWN;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ }
+
+ return ret;
+}
+
+static int dgram_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct dgram_sock *dgram = dgram_sk(sk);
+ struct sk_buff *skb;
+ struct net_device *netdev;
+ int ifindex;
+ int ret;
+
+ pr_debug("lora: %s\n", __func__);
+
+ if (msg->msg_name) {
+ DECLARE_SOCKADDR(struct sockaddr_lora *, addr, msg->msg_name);
+
+ if (msg->msg_namelen < sizeof(*addr))
+ return -EINVAL;
+
+ if (addr->lora_family != AF_LORA)
+ return -EINVAL;
+
+ ifindex = addr->lora_ifindex;
+ } else
+ ifindex = dgram->ifindex;
+
+ netdev = dev_get_by_index(sock_net(sk), ifindex);
+ if (!netdev)
+ return -ENXIO;
+
+ skb = sock_alloc_send_skb(sk, size + sizeof(struct lora_skb_priv),
+ msg->msg_flags & MSG_DONTWAIT, &ret);
+ if (!skb)
+ goto err_sock_alloc_send_skb;
+
+ lora_skb_reserve(skb);
+ lora_skb_prv(skb)->ifindex = netdev->ifindex;
+
+ ret = memcpy_from_msg(skb_put(skb, size), msg, size);
+ if (ret < 0)
+ goto err_memcpy_from_msg;
+
+ sock_tx_timestamp(sk,
+ sk->sk_tsflags,
+ &skb_shinfo(skb)->tx_flags);
+
+ skb->dev = netdev;
+ skb->sk = sk;
+ skb->priority = sk->sk_priority;
+
+ ret = lora_send(skb);
+
+ dev_put(netdev);
+
+ if (ret)
+ return ret;
+
+ return size;
+
+err_memcpy_from_msg:
+ kfree_skb(skb);
+err_sock_alloc_send_skb:
+ dev_put(netdev);
+ return ret;
+}
+
+static int dgram_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ pr_debug("lora: %s\n", __func__);
+
+ return -ENOIOCTLCMD;
+}
+
+static int dgram_getname(struct socket *sock, struct sockaddr *uaddr,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
+ int *len,
+#endif
+ int peer)
+{
+ struct sockaddr_lora *addr = (struct sockaddr_lora *)uaddr;
+ struct sock *sk = sock->sk;
+ struct dgram_sock *dgram = dgram_sk(sk);
+
+ pr_debug("lora: %s\n", __func__);
+
+ if (peer)
+ return -EOPNOTSUPP;
+
+ memset(addr, 0, sizeof(*addr));
+ addr->lora_family = AF_LORA;
+ addr->lora_ifindex = dgram->ifindex;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
+ return sizeof(*addr);
+#else
+ *len = sizeof(*addr);
+ return 0;
+#endif
+}
+
+static int dgram_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct dgram_sock *dgram;
+
+ pr_debug("lora: %s\n", __func__);
+
+ if (!sk)
+ return 0;
+
+ dgram = dgram_sk(sk);
+
+ unregister_netdevice_notifier(&dgram->notifier);
+
+ lock_sock(sk);
+
+ dgram->ifindex = 0;
+ dgram->bound = false;
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+const struct proto_ops dgram_proto_ops = {
+ .family = PF_LORA,
+ .release = dgram_release,
+ .bind = dgram_bind,
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = dgram_getname,
+ .poll = datagram_poll,
+ .ioctl = dgram_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = dgram_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static int dgram_notifier(struct notifier_block *nb, unsigned long msg, void *ptr)
+{
+ struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+ struct dgram_sock *dgram = container_of(nb, struct dgram_sock, notifier);
+ struct sock *sk = &dgram->sk;
+
+ pr_debug("lora: %s\n", __func__);
+
+ if (!net_eq(dev_net(netdev), sock_net(sk)))
+ return NOTIFY_DONE;
+
+ if (netdev->type != ARPHRD_LORA)
+ return NOTIFY_DONE;
+
+ if (dgram->ifindex != netdev->ifindex)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+ case NETDEV_UNREGISTER:
+ lock_sock(sk);
+
+ dgram->ifindex = 0;
+ dgram->bound = false;
+
+ release_sock(sk);
+
+ sk->sk_err = ENODEV;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ break;
+
+ case NETDEV_DOWN:
+ sk->sk_err = ENETDOWN;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_error_report(sk);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int dgram_init(struct sock *sk)
+{
+ struct dgram_sock *dgram = dgram_sk(sk);
+
+ pr_debug("lora: %s\n", __func__);
+
+ dgram->bound = false;
+ dgram->ifindex = 0;
+
+ dgram->notifier.notifier_call = dgram_notifier;
+ register_netdevice_notifier(&dgram->notifier);
+
+ return 0;
+}
+
+struct proto dgram_proto __read_mostly = {
+ .name = "LoRa",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct dgram_sock),
+ .init = dgram_init,
+};
--
2.16.4
LoRa is a long-range, low-power wireless network technology by Semtech.
It serves as base for LoRaWAN as well as multiple proprietary protocols.
AF_LORA
PF_LORA
ARPHRD_LORA
ETH_P_LORA
Signed-off-by: Andreas Färber <[email protected]>
---
include/linux/socket.h | 4 +++-
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 1 +
security/selinux/hooks.c | 4 +++-
security/selinux/include/classmap.h | 4 +++-
5 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 7ed4713d5337..aa1e288b1659 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -208,8 +208,9 @@ struct ucred {
* reuses AF_INET address family
*/
#define AF_XDP 44 /* XDP sockets */
+#define AF_LORA 45 /* LoRa sockets */
-#define AF_MAX 45 /* For now.. */
+#define AF_MAX 46 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -259,6 +260,7 @@ struct ucred {
#define PF_QIPCRTR AF_QIPCRTR
#define PF_SMC AF_SMC
#define PF_XDP AF_XDP
+#define PF_LORA AF_LORA
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
index 4605527ca41b..1ed7cb3f2129 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -98,6 +98,7 @@
#define ARPHRD_NETLINK 824 /* Netlink header */
#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */
#define ARPHRD_VSOCKMON 826 /* Vsock monitor header */
+#define ARPHRD_LORA 827 /* LoRa */
#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */
#define ARPHRD_NONE 0xFFFE /* zero header length */
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index 3a45b4ad71a3..45644dcf5b39 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -147,6 +147,7 @@
#define ETH_P_MAP 0x00F9 /* Qualcomm multiplexing and
* aggregation protocol
*/
+#define ETH_P_LORA 0x00FA /* LoRa */
/*
* This is an Ethernet frame header.
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 10a5d2ce3870..b62bb0389d70 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1473,7 +1473,9 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
return SECCLASS_SMC_SOCKET;
case PF_XDP:
return SECCLASS_XDP_SOCKET;
-#if PF_MAX > 45
+ case PF_LORA:
+ return SECCLASS_LORA_SOCKET;
+#if PF_MAX > 46
#error New address family defined, please update this function.
#endif
}
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index bd5fe0d3204a..060d4bf8385e 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -242,9 +242,11 @@ struct security_class_mapping secclass_map[] = {
{"map_create", "map_read", "map_write", "prog_load", "prog_run"} },
{ "xdp_socket",
{ COMMON_SOCK_PERMS, NULL } },
+ { "lora_socket",
+ { COMMON_SOCK_PERMS, NULL } },
{ NULL }
};
-#if PF_MAX > 45
+#if PF_MAX > 46
#error New address family defined, please update secclass_map.
#endif
--
2.16.4
Prepare a uapi linux/lora.h header defining sockaddr_lora for AF_LORA.
Signed-off-by: Andreas Färber <[email protected]>
---
include/uapi/linux/lora.h | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 include/uapi/linux/lora.h
diff --git a/include/uapi/linux/lora.h b/include/uapi/linux/lora.h
new file mode 100644
index 000000000000..9368e8a84f3a
--- /dev/null
+++ b/include/uapi/linux/lora.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later WITH Linux-syscall-note) */
+/*
+ * linux/lora.h
+ *
+ * Copyright (c) 2017-2018 Andreas Färber
+ */
+#ifndef _UAPI_LINUX_LORA_H
+#define _UAPI_LINUX_LORA_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+struct sockaddr_lora {
+ __kernel_sa_family_t lora_family;
+ int lora_ifindex;
+ union {
+ } lora_addr;
+};
+
+#endif /* _UAPI_LINUX_LORA_H */
--
2.16.4
linux-next and 4.18-rc2 both identify as LINUX_VERSION(4,18,0), but
commit a11e1d432b51f63ba698d044441284a661f01144 (Revert changes to
convert to ->poll_mask() and aio IOCB_CMD_POLL) reverted .poll_mask
to .poll again.
Signed-off-by: Andreas Färber <[email protected]>
---
net/lora/dgram.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/lora/dgram.c b/net/lora/dgram.c
index 4d931fd3778a..ef56fd90e762 100644
--- a/net/lora/dgram.c
+++ b/net/lora/dgram.c
@@ -217,7 +217,11 @@ const struct proto_ops dgram_proto_ops = {
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = dgram_getname,
+#if 0 /* LINUX_VERSION_CODE >= LINUX_VERSION(4, 18, 0) */
+ .poll_mask = datagram_poll_mask,
+#else
.poll = datagram_poll,
+#endif
.ioctl = dgram_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
--
2.16.4
Am 01.07.2018 um 13:07 schrieb Andreas Färber:
> diff --git a/drivers/net/lora/sx1276.c b/drivers/net/lora/sx1276.c
> new file mode 100644
> index 000000000000..d6732111247a
> --- /dev/null
> +++ b/drivers/net/lora/sx1276.c
[...]
> +static int sx1276_probe(struct spi_device *spi)
> +{
> + struct net_device *netdev;
> + struct sx1276_priv *priv;
> + int rst, dio[6], ret, model, i;
> + u32 freq_xosc, freq_band;
> + unsigned long long freq_rf;
> + u8 val;
> +
> + rst = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0);
> + if (rst == -ENOENT)
> + dev_warn(&spi->dev, "no reset GPIO available, ignoring");
> +
> + for (i = 0; i < 6; i++) {
> + dio[i] = of_get_named_gpio(spi->dev.of_node, "dio-gpios", i);
> + if (dio[i] == -ENOENT)
> + dev_dbg(&spi->dev, "DIO%d not available, ignoring", i);
> + else {
> + ret = gpio_direction_input(dio[i]);
> + if (ret)
> + dev_err(&spi->dev, "couldn't set DIO%d to input", i);
> + }
> + }
> +
> + if (gpio_is_valid(rst)) {
> + gpio_set_value(rst, 1);
> + udelay(100);
> + gpio_set_value(rst, 0);
> + msleep(5);
> + }
> +
> + spi->bits_per_word = 8;
> + spi_setup(spi);
> +
> + ret = sx1276_read_single(spi, REG_VERSION, &val);
> + if (ret) {
> + dev_err(&spi->dev, "version read failed");
> + return ret;
> + }
> +
> + if (val == 0x22)
> + model = 1272;
> + else {
> + if (gpio_is_valid(rst)) {
> + gpio_set_value(rst, 0);
> + udelay(100);
> + gpio_set_value(rst, 1);
> + msleep(5);
> + }
> +
> + ret = sx1276_read_single(spi, REG_VERSION, &val);
> + if (ret) {
> + dev_err(&spi->dev, "version read failed");
> + return ret;
> + }
> +
> + if (val == 0x12)
> + model = 1276;
> + else {
> + dev_err(&spi->dev, "transceiver not recognized (RegVersion = 0x%02x)", (unsigned)val);
> + return -EINVAL;
> + }
> + }
[snip]
To counter my own point, this file of course still has leftover model
detection heuristics; should check for the of_device_id match instead!
SX1272 vs. SX1276 have the reset pin inverted, so - knowing which model
it's supposed to be - we can do the right reset from the start and, if
it doesn't work, report an error to the user.
Also I should update that code to use gpiod, as seen in later patches.
Cheers,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Hi Andreas,
Excellent work on doing this I have also been working on and off
this personally for some time.
Have a look at my repository [1] for sx1301 and sx1257 drivers,
I use regmaps capability of switching pages which should simplify
your driver considerably, I also have a full register map and bit field.
I have also been trying to use the clk framework to capture the various
routing that the cards have.
I will dig into this series this evening.
[1] https://github.com/BWhitten/linux-stable/tree/971aadc8fdfe842020d912449bdd71b33d576fe3/drivers/net/lora
> Subject: [RFC net-next 15/15] net: lora: Add Semtech SX1301
>
> The Semtech SX1301 was the first multi-channel LoRa "concentrator".
> It uses a SPI interface to the host as well as a dual SPI interface to
> its radios. These two have been implemented as spi_controller, so that
> the Device Tree can specify whether the respective module uses two
> SX1257, two SX1255 or a combination of these or some unforeseen chipset.
>
> This implementation is the most recent - initialization is not yet
> complete, it will need to load firmware into the two on-chip MCUs.
>
> Unfortunately there is no full datasheet with register descriptions,
> only a BSD-licensed userspace HAL implementation using spidev devices.
> Therefore some register names are unknown.
>
> Cc: Ben Whitten <[email protected]>
> Cc: Steve deRosier <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: Michael Röder <[email protected]>
> Cc: Ken Yu (禹凯) <[email protected]>
> Cc: [email protected]
> Signed-off-by: Andreas Färber <[email protected]>
> ---
> drivers/net/lora/Kconfig | 7 +
> drivers/net/lora/Makefile | 3 +
> drivers/net/lora/sx1301.c | 446
> ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 456 insertions(+)
> create mode 100644 drivers/net/lora/sx1301.c
>
> diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
> index 68c7480d7812..950450e353b4 100644
> --- a/drivers/net/lora/Kconfig
> +++ b/drivers/net/lora/Kconfig
> @@ -45,6 +45,13 @@ config LORA_SX1276
> help
> Semtech SX1272/1276/1278
>
> +config LORA_SX1301
> + tristate "Semtech SX1301 SPI driver"
> + default y
> + depends on SPI
> + help
> + Semtech SX1301
> +
> config LORA_USI
> tristate "USI WM-SG-SM-42 driver"
> default y
> diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile
> index 44c578bde7d5..1cc1e3aa189b 100644
> --- a/drivers/net/lora/Makefile
> +++ b/drivers/net/lora/Makefile
> @@ -22,6 +22,9 @@ lora-sx1257-y := sx1257.o
> obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o
> lora-sx1276-y := sx1276.o
>
> +obj-$(CONFIG_LORA_SX1301) += lora-sx1301.o
> +lora-sx1301-y := sx1301.o
> +
> obj-$(CONFIG_LORA_USI) += lora-usi.o
> lora-usi-y := usi.o
>
> diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
> new file mode 100644
> index 000000000000..5c936c1116d1
> --- /dev/null
> +++ b/drivers/net/lora/sx1301.c
> @@ -0,0 +1,446 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Semtech SX1301 LoRa concentrator
> + *
> + * Copyright (c) 2018 Andreas Färber
> + *
> + * Based on SX1301 HAL code:
> + * Copyright (c) 2013 Semtech-Cycleo
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/lora.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_gpio.h>
> +#include <linux/lora/dev.h>
> +#include <linux/spi/spi.h>
> +
> +#define REG_PAGE_RESET 0
> +#define REG_VERSION 1
> +#define REG_2_SPI_RADIO_A_DATA 33
> +#define REG_2_SPI_RADIO_A_DATA_READBACK 34
> +#define REG_2_SPI_RADIO_A_ADDR 35
> +#define REG_2_SPI_RADIO_A_CS 37
> +#define REG_2_SPI_RADIO_B_DATA 38
> +#define REG_2_SPI_RADIO_B_DATA_READBACK 39
> +#define REG_2_SPI_RADIO_B_ADDR 40
> +#define REG_2_SPI_RADIO_B_CS 42
> +
> +#define REG_PAGE_RESET_SOFT_RESET BIT(7)
> +
> +#define REG_16_GLOBAL_EN BIT(3)
> +
> +#define REG_17_CLK32M_EN BIT(0)
> +
> +#define REG_2_43_RADIO_A_EN BIT(0)
> +#define REG_2_43_RADIO_B_EN BIT(1)
> +#define REG_2_43_RADIO_RST BIT(2)
> +
> +struct spi_sx1301 {
> + struct spi_device *parent;
> + u8 page;
> + u8 regs;
> +};
> +
> +struct sx1301_priv {
> + struct lora_priv lora;
> + struct gpio_desc *rst_gpio;
> + u8 cur_page;
> + struct spi_controller *radio_a_ctrl, *radio_b_ctrl;
> +};
> +
> +static int sx1301_read(struct spi_device *spi, u8 reg, u8 *val)
> +{
> + u8 addr = reg & 0x7f;
> + return spi_write_then_read(spi, &addr, 1, val, 1);
> +}
> +
> +static int sx1301_write(struct spi_device *spi, u8 reg, u8 val)
> +{
> + u8 buf[2];
> +
> + buf[0] = reg | BIT(7);
> + buf[1] = val;
> + return spi_write(spi, buf, 2);
> +}
> +
> +static int sx1301_page_switch(struct spi_device *spi, u8 page)
> +{
> + struct sx1301_priv *priv = spi_get_drvdata(spi);
> + int ret;
> +
> + if (priv->cur_page == page)
> + return 0;
> +
> + dev_dbg(&spi->dev, "switching to page %u\n", (unsigned)page);
> + ret = sx1301_write(spi, REG_PAGE_RESET, page & 0x3);
> + if (ret) {
> + dev_err(&spi->dev, "switching to page %u failed\n",
> (unsigned)page);
> + return ret;
> + }
> +
> + priv->cur_page = page;
> +
> + return 0;
> +}
> +
> +static int sx1301_soft_reset(struct spi_device *spi)
> +{
> + return sx1301_write(spi, REG_PAGE_RESET,
> REG_PAGE_RESET_SOFT_RESET);
> +}
> +
> +#define REG_RADIO_X_DATA 0
> +#define REG_RADIO_X_DATA_READBACK 1
> +#define REG_RADIO_X_ADDR 2
> +#define REG_RADIO_X_CS 4
> +
> +static int sx1301_radio_set_cs(struct spi_controller *ctrl, bool enable)
> +{
> + struct spi_sx1301 *ssx = spi_controller_get_devdata(ctrl);
> + u8 cs;
> + int ret;
> +
> + dev_dbg(&ctrl->dev, "setting CS to %s\n", enable ? "1" : "0");
> +
> + ret = sx1301_page_switch(ssx->parent, ssx->page);
> + if (ret) {
> + dev_warn(&ctrl->dev, "failed to switch page for CS (%d)\n",
> ret);
> + return ret;
> + }
> +
> + ret = sx1301_read(ssx->parent, ssx->regs + REG_RADIO_X_CS, &cs);
> + if (ret) {
> + dev_warn(&ctrl->dev, "failed to read CS (%d)\n", ret);
> + cs = 0;
> + }
> +
> + if (enable)
> + cs |= BIT(0);
> + else
> + cs &= ~BIT(0);
> +
> + ret = sx1301_write(ssx->parent, ssx->regs + REG_RADIO_X_CS, cs);
> + if (ret)
> + dev_warn(&ctrl->dev, "failed to write CS (%d)\n", ret);
> +
> + return 0;
> +}
> +
> +static void sx1301_radio_spi_set_cs(struct spi_device *spi, bool enable)
> +{
> + int ret;
> +
> + dev_dbg(&spi->dev, "setting SPI CS to %s\n", enable ? "1" : "0");
> +
> + if (enable)
> + return;
> +
> + ret = sx1301_radio_set_cs(spi->controller, enable);
> + if (ret)
> + dev_warn(&spi->dev, "failed to write CS (%d)\n", ret);
> +}
> +
> +static int sx1301_radio_spi_transfer_one(struct spi_controller *ctrl,
> + struct spi_device *spi, struct spi_transfer *xfr)
> +{
> + struct spi_sx1301 *ssx = spi_controller_get_devdata(ctrl);
> + const u8 *tx_buf = xfr->tx_buf;
> + u8 *rx_buf = xfr->rx_buf;
> + int ret;
> +
> + if (xfr->len == 0 || xfr->len > 3)
> + return -EINVAL;
> +
> + dev_dbg(&spi->dev, "transferring one (%u)\n", xfr->len);
> +
> + ret = sx1301_page_switch(ssx->parent, ssx->page);
> + if (ret) {
> + dev_err(&spi->dev, "failed to switch page for transfer
> (%d)\n", ret);
> + return ret;
> + }
> +
> + if (tx_buf) {
> + ret = sx1301_write(ssx->parent, ssx->regs +
> REG_RADIO_X_ADDR, tx_buf ? tx_buf[0] : 0);
> + if (ret) {
> + dev_err(&spi->dev, "SPI radio address write
> failed\n");
> + return ret;
> + }
> +
> + ret = sx1301_write(ssx->parent, ssx->regs +
> REG_RADIO_X_DATA, (tx_buf && xfr->len >= 2) ? tx_buf[1] : 0);
> + if (ret) {
> + dev_err(&spi->dev, "SPI radio data write failed\n");
> + return ret;
> + }
> +
> + ret = sx1301_radio_set_cs(ctrl, true);
> + if (ret) {
> + dev_err(&spi->dev, "SPI radio CS set failed\n");
> + return ret;
> + }
> +
> + ret = sx1301_radio_set_cs(ctrl, false);
> + if (ret) {
> + dev_err(&spi->dev, "SPI radio CS unset failed\n");
> + return ret;
> + }
> + }
> +
> + if (rx_buf) {
> + ret = sx1301_read(ssx->parent, ssx->regs +
> REG_RADIO_X_DATA_READBACK, &rx_buf[xfr->len - 1]);
> + if (ret) {
> + dev_err(&spi->dev, "SPI radio data read failed\n");
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void sx1301_radio_setup(struct spi_controller *ctrl)
> +{
> + ctrl->mode_bits = SPI_CS_HIGH | SPI_NO_CS;
> + ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
> + ctrl->num_chipselect = 1;
> + ctrl->set_cs = sx1301_radio_spi_set_cs;
> + ctrl->transfer_one = sx1301_radio_spi_transfer_one;
> +}
> +
> +static int sx1301_probe(struct spi_device *spi)
> +{
> + struct net_device *netdev;
> + struct sx1301_priv *priv;
> + struct spi_sx1301 *radio;
> + struct gpio_desc *rst;
> + int ret;
> + u8 val;
> +
> + rst = devm_gpiod_get_optional(&spi->dev, "reset",
> GPIOD_OUT_LOW);
> + if (IS_ERR(rst))
> + return PTR_ERR(rst);
> +
> + gpiod_set_value_cansleep(rst, 1);
> + msleep(100);
> + gpiod_set_value_cansleep(rst, 0);
> + msleep(100);
> +
> + spi->bits_per_word = 8;
> + spi_setup(spi);
> +
> + ret = sx1301_read(spi, REG_VERSION, &val);
> + if (ret) {
> + dev_err(&spi->dev, "version read failed\n");
> + goto err_version;
> + }
> +
> + if (val != 103) {
> + dev_err(&spi->dev, "unexpected version: %u\n", val);
> + ret = -ENXIO;
> + goto err_version;
> + }
> +
> + netdev = alloc_loradev(sizeof(*priv));
> + if (!netdev) {
> + ret = -ENOMEM;
> + goto err_alloc_loradev;
> + }
> +
> + priv = netdev_priv(netdev);
> + priv->rst_gpio = rst;
> + priv->cur_page = 0xff;
> +
> + spi_set_drvdata(spi, netdev);
> + SET_NETDEV_DEV(netdev, &spi->dev);
> +
> + ret = sx1301_write(spi, REG_PAGE_RESET, 0);
> + if (ret) {
> + dev_err(&spi->dev, "page/reset write failed\n");
> + return ret;
> + }
> +
> + ret = sx1301_soft_reset(spi);
> + if (ret) {
> + dev_err(&spi->dev, "soft reset failed\n");
> + return ret;
> + }
> +
> + ret = sx1301_read(spi, 16, &val);
> + if (ret) {
> + dev_err(&spi->dev, "16 read failed\n");
> + return ret;
> + }
> +
> + val &= ~REG_16_GLOBAL_EN;
> +
> + ret = sx1301_write(spi, 16, val);
> + if (ret) {
> + dev_err(&spi->dev, "16 write failed\n");
> + return ret;
> + }
> +
> + ret = sx1301_read(spi, 17, &val);
> + if (ret) {
> + dev_err(&spi->dev, "17 read failed\n");
> + return ret;
> + }
> +
> + val &= ~REG_17_CLK32M_EN;
> +
> + ret = sx1301_write(spi, 17, val);
> + if (ret) {
> + dev_err(&spi->dev, "17 write failed\n");
> + return ret;
> + }
> +
> + ret = sx1301_page_switch(spi, 2);
> + if (ret) {
> + dev_err(&spi->dev, "page 2 switch failed\n");
> + return ret;
> + }
> +
> + ret = sx1301_read(spi, 43, &val);
> + if (ret) {
> + dev_err(&spi->dev, "2|43 read failed\n");
> + return ret;
> + }
> +
> + val |= REG_2_43_RADIO_B_EN | REG_2_43_RADIO_A_EN;
> +
> + ret = sx1301_write(spi, 43, val);
> + if (ret) {
> + dev_err(&spi->dev, "2|43 write failed\n");
> + return ret;
> + }
> +
> + msleep(500);
> +
> + ret = sx1301_read(spi, 43, &val);
> + if (ret) {
> + dev_err(&spi->dev, "2|43 read failed\n");
> + return ret;
> + }
> +
> + val |= REG_2_43_RADIO_RST;
> +
> + ret = sx1301_write(spi, 43, val);
> + if (ret) {
> + dev_err(&spi->dev, "2|43 write failed\n");
> + return ret;
> + }
> +
> + msleep(5);
> +
> + ret = sx1301_read(spi, 43, &val);
> + if (ret) {
> + dev_err(&spi->dev, "2|43 read failed\n");
> + return ret;
> + }
> +
> + val &= ~REG_2_43_RADIO_RST;
> +
> + ret = sx1301_write(spi, 43, val);
> + if (ret) {
> + dev_err(&spi->dev, "2|43 write failed\n");
> + return ret;
> + }
> +
> + /* radio A */
> +
> + priv->radio_a_ctrl = spi_alloc_master(&spi->dev, sizeof(*radio));
> + if (!priv->radio_a_ctrl) {
> + ret = -ENOMEM;
> + goto err_radio_a_alloc;
> + }
> +
> + sx1301_radio_setup(priv->radio_a_ctrl);
> + priv->radio_a_ctrl->dev.of_node = of_get_child_by_name(spi-
> >dev.of_node, "radio-a");
> +
> + radio = spi_controller_get_devdata(priv->radio_a_ctrl);
> + radio->page = 2;
> + radio->regs = REG_2_SPI_RADIO_A_DATA;
> + radio->parent = spi;
> +
> + dev_info(&spi->dev, "registering radio A SPI\n");
> +
> + ret = devm_spi_register_controller(&spi->dev, priv->radio_a_ctrl);
> + if (ret) {
> + dev_err(&spi->dev, "radio A SPI register failed\n");
> + goto err_radio_a_register;
> + }
> +
> + /* radio B */
> +
> + priv->radio_b_ctrl = spi_alloc_master(&spi->dev, sizeof(*radio));
> + if (!priv->radio_b_ctrl) {
> + ret = -ENOMEM;
> + goto err_radio_b_alloc;
> + }
> +
> + sx1301_radio_setup(priv->radio_b_ctrl);
> + priv->radio_b_ctrl->dev.of_node = of_get_child_by_name(spi-
> >dev.of_node, "radio-b");
> +
> + radio = spi_controller_get_devdata(priv->radio_b_ctrl);
> + radio->page = 2;
> + radio->regs = REG_2_SPI_RADIO_B_DATA;
> + radio->parent = spi;
> +
> + dev_info(&spi->dev, "registering radio B SPI\n");
> +
> + ret = devm_spi_register_controller(&spi->dev, priv->radio_b_ctrl);
> + if (ret) {
> + dev_err(&spi->dev, "radio B SPI register failed\n");
> + goto err_radio_b_register;
> + }
> +
> + dev_info(&spi->dev, "SX1301 module probed\n");
> +
> + return 0;
> +err_radio_b_register:
> + spi_controller_put(priv->radio_b_ctrl);
> +err_radio_b_alloc:
> +err_radio_a_register:
> + spi_controller_put(priv->radio_a_ctrl);
> +err_radio_a_alloc:
> + free_loradev(netdev);
> +err_alloc_loradev:
> +err_version:
> + return ret;
> +}
> +
> +static int sx1301_remove(struct spi_device *spi)
> +{
> + struct net_device *netdev = spi_get_drvdata(spi);
> +
> + //unregister_loradev(netdev);
> + free_loradev(netdev);
> +
> + dev_info(&spi->dev, "SX1301 module removed\n");
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id sx1301_dt_ids[] = {
> + { .compatible = "semtech,sx1301" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, sx1301_dt_ids);
> +#endif
> +
> +static struct spi_driver sx1301_spi_driver = {
> + .driver = {
> + .name = "sx1301",
> + .of_match_table = of_match_ptr(sx1301_dt_ids),
> + },
> + .probe = sx1301_probe,
> + .remove = sx1301_remove,
> +};
> +
> +module_spi_driver(sx1301_spi_driver);
> +
> +MODULE_DESCRIPTION("SX1301 SPI driver");
> +MODULE_AUTHOR("Andreas Färber <[email protected]>");
> +MODULE_LICENSE("GPL");
> --
> 2.16.4
On Sun, Jul 01, 2018 at 01:08:04PM +0200, Andreas F?rber wrote:
> +static void sx1301_radio_spi_set_cs(struct spi_device *spi, bool enable)
> +{
> + int ret;
> +
> + dev_dbg(&spi->dev, "setting SPI CS to %s\n", enable ? "1" : "0");
> +
> + if (enable)
> + return;
> +
> + ret = sx1301_radio_set_cs(spi->controller, enable);
> + if (ret)
> + dev_warn(&spi->dev, "failed to write CS (%d)\n", ret);
> +}
So we never disable chip select?
> + if (tx_buf) {
> + ret = sx1301_write(ssx->parent, ssx->regs + REG_RADIO_X_ADDR, tx_buf ? tx_buf[0] : 0);
This looks confused. We're in an if (tx_buf) block but there's a use of
the ternery operator that appears to be checking if we have a tx_buf?
> + if (ret) {
> + dev_err(&spi->dev, "SPI radio address write failed\n");
> + return ret;
> + }
> +
> + ret = sx1301_write(ssx->parent, ssx->regs + REG_RADIO_X_DATA, (tx_buf && xfr->len >= 2) ? tx_buf[1] : 0);
> + if (ret) {
> + dev_err(&spi->dev, "SPI radio data write failed\n");
> + return ret;
> + }
This looks awfully like you're coming in at the wrong abstraction layer
and the hardware actually implements a register abstraction rather than
a SPI one so you should be using regmap as the abstraction.
> + if (rx_buf) {
> + ret = sx1301_read(ssx->parent, ssx->regs + REG_RADIO_X_DATA_READBACK, &rx_buf[xfr->len - 1]);
> + if (ret) {
> + dev_err(&spi->dev, "SPI radio data read failed\n");
> + return ret;
> + }
> + }
For a read we never set an address?
> +static void sx1301_radio_setup(struct spi_controller *ctrl)
> +{
> + ctrl->mode_bits = SPI_CS_HIGH | SPI_NO_CS;
This controller has no chip select but we provided a set_cs operation?
Sun, Jul 01, 2018 at 01:07:54PM CEST, [email protected] wrote:
>linux-next and 4.18-rc2 both identify as LINUX_VERSION(4,18,0), but
>commit a11e1d432b51f63ba698d044441284a661f01144 (Revert changes to
>convert to ->poll_mask() and aio IOCB_CMD_POLL) reverted .poll_mask
>to .poll again.
>
>Signed-off-by: Andreas F?rber <[email protected]>
>---
> net/lora/dgram.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
>diff --git a/net/lora/dgram.c b/net/lora/dgram.c
>index 4d931fd3778a..ef56fd90e762 100644
>--- a/net/lora/dgram.c
>+++ b/net/lora/dgram.c
>@@ -217,7 +217,11 @@ const struct proto_ops dgram_proto_ops = {
> .socketpair = sock_no_socketpair,
> .accept = sock_no_accept,
> .getname = dgram_getname,
>+#if 0 /* LINUX_VERSION_CODE >= LINUX_VERSION(4, 18, 0) */
>+ .poll_mask = datagram_poll_mask,
>+#else
I guess that you sent this patch by mistake...
> .poll = datagram_poll,
>+#endif
> .ioctl = dgram_ioctl,
> .listen = sock_no_listen,
> .shutdown = sock_no_shutdown,
>--
>2.16.4
>
Sun, Jul 01, 2018 at 01:07:57PM CEST, [email protected] wrote:
>Allow some interactive inspection at runtime via debugfs.
>
>Signed-off-by: Andreas F?rber <[email protected]>
>---
> drivers/net/lora/sx1276.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 132 insertions(+)
>
>diff --git a/drivers/net/lora/sx1276.c b/drivers/net/lora/sx1276.c
>index d6732111247a..1072019cfcc1 100644
>--- a/drivers/net/lora/sx1276.c
>+++ b/drivers/net/lora/sx1276.c
>@@ -5,6 +5,7 @@
> * Copyright (c) 2016-2018 Andreas F?rber
> */
>
>+#include <linux/debugfs.h>
> #include <linux/delay.h>
> #include <linux/lora.h>
> #include <linux/module.h>
>@@ -61,6 +62,8 @@ struct sx1276_priv {
>
> struct workqueue_struct *wq;
> struct work_struct tx_work;
>+
>+ struct dentry *debugfs;
> };
>
> static int sx1276_read_single(struct spi_device *spi, u8 reg, u8 *val)
>@@ -416,6 +419,128 @@ static const struct net_device_ops sx1276_netdev_ops = {
> .ndo_start_xmit = sx1276_loradev_start_xmit,
> };
>
>+static ssize_t sx1276_freq_read(struct file *file, char __user *user_buf,
>+ size_t count, loff_t *ppos)
>+{
>+ struct net_device *netdev = file->private_data;
>+ struct sx1276_priv *priv = netdev_priv(netdev);
>+ struct spi_device *spi = priv->spi;
>+ ssize_t size;
>+ char *buf;
>+ int ret;
>+ u8 msb, mid, lsb;
>+ u32 freq_xosc;
>+ unsigned long long frf;
>+
>+ ret = of_property_read_u32(spi->dev.of_node, "clock-frequency", &freq_xosc);
>+ if (ret)
>+ return 0;
>+
>+ mutex_lock(&priv->spi_lock);
>+
>+ ret = sx1276_read_single(spi, REG_FRF_MSB, &msb);
>+ if (!ret)
>+ ret = sx1276_read_single(spi, REG_FRF_MID, &mid);
>+ if (!ret)
>+ ret = sx1276_read_single(spi, REG_FRF_LSB, &lsb);
>+
>+ mutex_unlock(&priv->spi_lock);
>+
>+ if (ret)
>+ return 0;
>+
>+ frf = freq_xosc;
>+ frf *= ((ulong)msb << 16) | ((ulong)mid << 8) | lsb;
>+ frf /= (1 << 19);
>+
>+ buf = kasprintf(GFP_KERNEL, "%llu\n", frf);
>+ if (!buf)
>+ return 0;
>+
>+ size = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
>+ kfree(buf);
>+
>+ return size;
>+}
>+
>+static const struct file_operations sx1276_freq_fops = {
>+ .owner = THIS_MODULE,
>+ .open = simple_open,
>+ .read = sx1276_freq_read,
>+};
>+
>+static ssize_t sx1276_state_read(struct file *file, char __user *user_buf,
>+ size_t count, loff_t *ppos)
>+{
>+ struct net_device *netdev = file->private_data;
>+ struct sx1276_priv *priv = netdev_priv(netdev);
>+ struct spi_device *spi = priv->spi;
>+ ssize_t size;
>+ char *buf;
>+ int len = 0;
>+ int ret;
>+ u8 val;
>+ bool lora_mode = true;
>+ const int max_len = 4096;
>+
>+ buf = kzalloc(max_len, GFP_KERNEL);
>+ if (!buf)
>+ return 0;
>+
>+ mutex_lock(&priv->spi_lock);
>+
>+ ret = sx1276_read_single(spi, REG_OPMODE, &val);
>+ if (!ret) {
>+ len += snprintf(buf + len, max_len - len, "RegOpMode = 0x%02x\n", val);
>+ lora_mode = (val & REG_OPMODE_LONG_RANGE_MODE) != 0;
>+ }
>+
>+ ret = sx1276_read_single(spi, REG_FRF_MSB, &val);
>+ if (!ret)
>+ len += snprintf(buf + len, max_len - len, "RegFrMsb = 0x%02x\n", val);
>+ ret = sx1276_read_single(spi, REG_FRF_MID, &val);
>+ if (!ret)
>+ len += snprintf(buf + len, max_len - len, "RegFrMid = 0x%02x\n", val);
>+ ret = sx1276_read_single(spi, REG_FRF_LSB, &val);
>+ if (!ret)
>+ len += snprintf(buf + len, max_len - len, "RegFrLsb = 0x%02x\n", val);
>+
>+ ret = sx1276_read_single(spi, REG_PA_CONFIG, &val);
>+ if (!ret)
>+ len += snprintf(buf + len, max_len - len, "RegPaConfig = 0x%02x\n", val);
>+
>+ if (lora_mode) {
>+ ret = sx1276_read_single(spi, LORA_REG_IRQ_FLAGS_MASK, &val);
>+ if (!ret)
>+ len += snprintf(buf + len, max_len - len, "RegIrqFlagsMask = 0x%02x\n", val);
>+
>+ ret = sx1276_read_single(spi, LORA_REG_IRQ_FLAGS, &val);
>+ if (!ret)
>+ len += snprintf(buf + len, max_len - len, "RegIrqFlags = 0x%02x\n", val);
>+
>+ ret = sx1276_read_single(spi, LORA_REG_SYNC_WORD, &val);
>+ if (!ret)
>+ len += snprintf(buf + len, max_len - len, "RegSyncWord = 0x%02x\n", val);
>+ }
>+
>+ ret = sx1276_read_single(spi, REG_PA_DAC, &val);
>+ if (!ret)
>+ len += snprintf(buf + len, max_len - len, "RegPaDac = 0x%02x\n", val);
>+
>+ mutex_unlock(&priv->spi_lock);
>+
>+ size = simple_read_from_buffer(user_buf, count, ppos, buf, len);
>+ kfree(buf);
>+
>+ return size;
>+}
>+
>+static const struct file_operations sx1276_state_fops = {
>+ .owner = THIS_MODULE,
>+ .open = simple_open,
>+ .read = sx1276_state_read,
>+};
>+
> static int sx1276_probe(struct spi_device *spi)
> {
> struct net_device *netdev;
>@@ -566,6 +691,10 @@ static int sx1276_probe(struct spi_device *spi)
> return ret;
> }
>
>+ priv->debugfs = debugfs_create_dir(dev_name(&spi->dev), NULL);
>+ debugfs_create_file("state", S_IRUGO, priv->debugfs, netdev, &sx1276_state_fops);
>+ debugfs_create_file("frequency", S_IRUGO, priv->debugfs, netdev, &sx1276_freq_fops);
Hmm. These look like useful information not only for debugging. I think
it would be worth to expose these via some standard uapi. Like generic
netlink, similar to nl80211
>+
> dev_info(&spi->dev, "SX1276 module probed (SX%d)", model);
>
> return 0;
>@@ -574,6 +703,9 @@ static int sx1276_probe(struct spi_device *spi)
> static int sx1276_remove(struct spi_device *spi)
> {
> struct net_device *netdev = spi_get_drvdata(spi);
>+ struct sx1276_priv *priv = netdev_priv(netdev);
>+
>+ debugfs_remove_recursive(priv->debugfs);
>
> unregister_loradev(netdev);
> free_loradev(netdev);
>--
>2.16.4
>
Am 02.07.2018 um 18:22 schrieb Jiri Pirko:
> Sun, Jul 01, 2018 at 01:07:54PM CEST, [email protected] wrote:
>> linux-next and 4.18-rc2 both identify as LINUX_VERSION(4,18,0), but
>> commit a11e1d432b51f63ba698d044441284a661f01144 (Revert changes to
>> convert to ->poll_mask() and aio IOCB_CMD_POLL) reverted .poll_mask
>> to .poll again.
>>
>> Signed-off-by: Andreas Färber <[email protected]>
>> ---
>> net/lora/dgram.c | 4 ++++
>> 1 file changed, 4 insertions(+)
>>
>> diff --git a/net/lora/dgram.c b/net/lora/dgram.c
>> index 4d931fd3778a..ef56fd90e762 100644
>> --- a/net/lora/dgram.c
>> +++ b/net/lora/dgram.c
>> @@ -217,7 +217,11 @@ const struct proto_ops dgram_proto_ops = {
>> .socketpair = sock_no_socketpair,
>> .accept = sock_no_accept,
>> .getname = dgram_getname,
>> +#if 0 /* LINUX_VERSION_CODE >= LINUX_VERSION(4, 18, 0) */
>> + .poll_mask = datagram_poll_mask,
>> +#else
>
> I guess that you sent this patch by mistake...
No mistake, it's clearly marked as "HACK", and the cover letter states
this is not for merging but for the design discussion. You wanted to see
patches for discussing the topic, so I sent you my current draft.
On my development machine with -rc2 I still use .poll_mask. I will be
able to drop the patch once we have -rc3 built, unless .poll_mask gets
reintroduced later in the cycle. If you know of some macro to check
whether we're -rc2 or -rc3 of 4.18 I'm all ears, otherwise grep'ing in
the Makefile and defining some custom macro would be an alternative.
Some other places in this series have similar, not-commented-out
compatibility code to allow me to test individual modules quickly
without having to build and boot a full kernel for every change. Like
some sk function got/lost a parameter at some point. Originally I kept
compatibility with openSUSE Leap 42.3's 4.4 kernel, now 15.0's 4.12.
https://github.com/afaerber/lora-modules has a Makefile and load.sh
script for that purpose, plus a small C example to send a packet.
Cheers,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Hi Mark,
This driver is still evolving, there's newer code on my lora-next branch
already: https://github.com/afaerber/linux/commits/lora-next
The reason you're in CC on this RFC is two-fold:
1) You applied Ben's patch to associate "semtech,sx1301" with spidev,
whereas I am now preparing a new driver for the same compatible.
2) This SPI device is in turn exposing the two SPI masters that you
already found below, and I didn't see a sane way to split that code out
into drivers/spi/, so it's in drivers/net/lora/ here - has there been
any precedence either way?
More inline ...
Am 02.07.2018 um 18:12 schrieb Mark Brown:
> On Sun, Jul 01, 2018 at 01:08:04PM +0200, Andreas F?rber wrote:
>
>> +static void sx1301_radio_spi_set_cs(struct spi_device *spi, bool enable)
>> +{
>> + int ret;
>> +
>> + dev_dbg(&spi->dev, "setting SPI CS to %s\n", enable ? "1" : "0");
>> +
>> + if (enable)
>> + return;
>> +
>> + ret = sx1301_radio_set_cs(spi->controller, enable);
>> + if (ret)
>> + dev_warn(&spi->dev, "failed to write CS (%d)\n", ret);
>> +}
>
> So we never disable chip select?
Not here, I instead did that in transfer_one below.
Unfortunately there seems to be no documentation, only reference code:
https://github.com/Lora-net/lora_gateway/blob/master/libloragw/src/loragw_radio.c#L121
https://github.com/Lora-net/lora_gateway/blob/master/libloragw/src/loragw_radio.c#L165
It sets CS to 0 before writing to address and data registers, then
immediately sets CS to 1 and back to 0 before reading or ending the
write transaction. I've tried to force the same behavior in this driver.
My guess was that CS is high-active during the short 1-0 cycle, because
if it's low-active during the register writes then why the heck is it
set to 0 again in the end instead of keeping at 1... confusing.
Maybe the Semtech folks CC'ed can comment how these registers work?
>> + if (tx_buf) {
>> + ret = sx1301_write(ssx->parent, ssx->regs + REG_RADIO_X_ADDR, tx_buf ? tx_buf[0] : 0);
>
> This looks confused. We're in an if (tx_buf) block but there's a use of
> the ternery operator that appears to be checking if we have a tx_buf?
Yeah, as mentioned this RFC is not ready for merging - checkpatch.pl
will complain about lines too long, and TODOs are sprinkled all over or
not even mentioned. It's a Proof of Concept that a net_device could work
for a wide range of spi and serdev based drivers, and on top this device
has more than one channel, which may influence network-level design
discussions.
That said, I'll happily drop the second check. Thanks for spotting!
>> + if (ret) {
>> + dev_err(&spi->dev, "SPI radio address write failed\n");
>> + return ret;
>> + }
>> +
>> + ret = sx1301_write(ssx->parent, ssx->regs + REG_RADIO_X_DATA, (tx_buf && xfr->len >= 2) ? tx_buf[1] : 0);
>> + if (ret) {
>> + dev_err(&spi->dev, "SPI radio data write failed\n");
>> + return ret;
>> + }
>
> This looks awfully like you're coming in at the wrong abstraction layer
> and the hardware actually implements a register abstraction rather than
> a SPI one so you should be using regmap as the abstraction.
I don't understand. Ben has suggested using regmap for the SPI _device_
that we're talking to, which may be a good idea. But this SX1301 device
in turn has two SPI _masters_ talking to an SX125x slave each. I don't
see how using regmap instead of my wrappers avoids this spi_controller?
The whole point of this spi_controller is to abstract and separate the
SX1255 vs. SX1257 vs. whatever-radio-attached into a separate driver,
instead of mixing it into the SX1301 driver - to me that looks cleaner
and more extensible. It also has the side-effect that we could configure
the two radios via DT (frequencies, clk output, etc.).
You will find a datasheet with some diagrams mentioning "SPI" at:
https://www.semtech.com/products/wireless-rf/lora-gateways/SX1301
>> + if (rx_buf) {
>> + ret = sx1301_read(ssx->parent, ssx->regs + REG_RADIO_X_DATA_READBACK, &rx_buf[xfr->len - 1]);
>> + if (ret) {
>> + dev_err(&spi->dev, "SPI radio data read failed\n");
>> + return ret;
>> + }
>> + }
>
> For a read we never set an address?
To read, you first write the address via tx_buf, then either in the same
transfer in the third byte or in a subsequent one-byte transfer as first
byte you get the data.
If you have better ideas how to structure this, do let me know.
>> +static void sx1301_radio_setup(struct spi_controller *ctrl)
>> +{
>> + ctrl->mode_bits = SPI_CS_HIGH | SPI_NO_CS;
>
> This controller has no chip select but we provided a set_cs operation?
Oops, I played around with those two options and was hoping SPI_NO_CS
would avoid the undesired set_cs invocations, but it didn't work as
expected and so I added the "if (enabled)" check above.
Thanks for your review,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Graham Norton
HRB 21284 (AG N?rnberg)
Am 02.07.2018 um 18:26 schrieb Jiri Pirko:
> Sun, Jul 01, 2018 at 01:07:57PM CEST, [email protected] wrote:
>> Allow some interactive inspection at runtime via debugfs.
>>
>> Signed-off-by: Andreas Färber <[email protected]>
[...]
>> @@ -566,6 +691,10 @@ static int sx1276_probe(struct spi_device *spi)
>> return ret;
>> }
>>
>> + priv->debugfs = debugfs_create_dir(dev_name(&spi->dev), NULL);
>> + debugfs_create_file("state", S_IRUGO, priv->debugfs, netdev, &sx1276_state_fops);
>> + debugfs_create_file("frequency", S_IRUGO, priv->debugfs, netdev, &sx1276_freq_fops);
>
> Hmm. These look like useful information not only for debugging. I think
> it would be worth to expose these via some standard uapi. Like generic
> netlink, similar to nl80211
Which API to use for reading/writing such config data was question 4) in
my cover letter. :)
"frequency" was added first and helped me debug a calculation overflow.
Netlink might indeed be an option here, haven't worked on it before,
I'll look into nl80211. Thanks.
"state" was just a partial dump of SPI registers, to monitor status
changes after the initial probe or other callbacks with printks, while
debugging GPIO interrupts iirc. So I'd think merging that to mainline
would be unnecessary, but it could remain useful during development.
Cheers,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
> Subject: Re: [RFC net-next 15/15] net: lora: Add Semtech SX1301
>
> Hi Mark,
>
> This driver is still evolving, there's newer code on my lora-next branch
> already: https://github.com/afaerber/linux/commits/lora-next
>
> The reason you're in CC on this RFC is two-fold:
>
> 1) You applied Ben's patch to associate "semtech,sx1301" with spidev,
> whereas I am now preparing a new driver for the same compatible.
>
> 2) This SPI device is in turn exposing the two SPI masters that you
> already found below, and I didn't see a sane way to split that code out
> into drivers/spi/, so it's in drivers/net/lora/ here - has there been
> any precedence either way?
In my work in progress driver I just register one controller for the sx1301 with two chip selects and use the chip select information to choose the correct radio to send to, this is based on the DT reg information. No need to register two separate masters.
> More inline ...
>
> Am 02.07.2018 um 18:12 schrieb Mark Brown:
> > On Sun, Jul 01, 2018 at 01:08:04PM +0200, Andreas F?rber wrote:
> >
> >> +static void sx1301_radio_spi_set_cs(struct spi_device *spi, bool enable)
> >> +{
> >> + int ret;
> >> +
> >> + dev_dbg(&spi->dev, "setting SPI CS to %s\n", enable ? "1" : "0");
> >> +
> >> + if (enable)
> >> + return;
> >> +
> >> + ret = sx1301_radio_set_cs(spi->controller, enable);
> >> + if (ret)
> >> + dev_warn(&spi->dev, "failed to write CS (%d)\n", ret);
> >> +}
> >
> > So we never disable chip select?
>
> Not here, I instead did that in transfer_one below.
>
> Unfortunately there seems to be no documentation, only reference code:
>
> https://github.com/Lora-
> net/lora_gateway/blob/master/libloragw/src/loragw_radio.c#L121
> https://github.com/Lora-
> net/lora_gateway/blob/master/libloragw/src/loragw_radio.c#L165
>
> It sets CS to 0 before writing to address and data registers, then
> immediately sets CS to 1 and back to 0 before reading or ending the
> write transaction. I've tried to force the same behavior in this driver.
> My guess was that CS is high-active during the short 1-0 cycle, because
> if it's low-active during the register writes then why the heck is it
> set to 0 again in the end instead of keeping at 1... confusing.
>
> Maybe the Semtech folks CC'ed can comment how these registers work?
>
> >> + if (tx_buf) {
> >> + ret = sx1301_write(ssx->parent, ssx->regs +
> REG_RADIO_X_ADDR, tx_buf ? tx_buf[0] : 0);
> >
> > This looks confused. We're in an if (tx_buf) block but there's a use of
> > the ternery operator that appears to be checking if we have a tx_buf?
>
> Yeah, as mentioned this RFC is not ready for merging - checkpatch.pl
> will complain about lines too long, and TODOs are sprinkled all over or
> not even mentioned. It's a Proof of Concept that a net_device could work
> for a wide range of spi and serdev based drivers, and on top this device
> has more than one channel, which may influence network-level design
> discussions.
>
> That said, I'll happily drop the second check. Thanks for spotting!
>
> >> + if (ret) {
> >> + dev_err(&spi->dev, "SPI radio address write
> failed\n");
> >> + return ret;
> >> + }
> >> +
> >> + ret = sx1301_write(ssx->parent, ssx->regs +
> REG_RADIO_X_DATA, (tx_buf && xfr->len >= 2) ? tx_buf[1] : 0);
> >> + if (ret) {
> >> + dev_err(&spi->dev, "SPI radio data write failed\n");
> >> + return ret;
> >> + }
> >
> > This looks awfully like you're coming in at the wrong abstraction layer
> > and the hardware actually implements a register abstraction rather than
> > a SPI one so you should be using regmap as the abstraction.
>
> I don't understand. Ben has suggested using regmap for the SPI _device_
> that we're talking to, which may be a good idea. But this SX1301 device
> in turn has two SPI _masters_ talking to an SX125x slave each. I don't
> see how using regmap instead of my wrappers avoids this spi_controller?
> The whole point of this spi_controller is to abstract and separate the
> SX1255 vs. SX1257 vs. whatever-radio-attached into a separate driver,
> instead of mixing it into the SX1301 driver - to me that looks cleaner
> and more extensible. It also has the side-effect that we could configure
> the two radios via DT (frequencies, clk output, etc.).
You want an SPI controller in the SX1301 as the down stream radios are SPI and could be attached directly to a host SPI bus, makes sense to have one radio driver and talk through the SX1301.
But you should use the regmap to access the SX1301 master controller registers.
Example I use with one SPI master and some clock info:
eg:
sx1301: sx1301@0 {
compatible = "semtech,sx1301";
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
spi-max-frequency = <8000000>;
gpios-reset = <&pioA 26 GPIO_ACTIVE_HIGH>;
clocks = <&radio1 0>, <&clkhs 0>;
clock-names = "clk32m", "clkhs";
radio0: sx1257@0 {
compatible = "semtech,sx125x";
reg = <0>;
spi-max-frequency = <8000000>;
tx;
clocks = <&tcxo 0>;
clock-names = "tcxo";
};
radio1: sx1257@1 {
compatible = "semtech,sx125x";
reg = <1>;
spi-max-frequency = <8000000>;
#clock-cells = <0>;
clocks = <&tcxo 0>;
clock-names = "tcxo";
clock-output-names = "clk32m";
};
};
> You will find a datasheet with some diagrams mentioning "SPI" at:
> https://www.semtech.com/products/wireless-rf/lora-gateways/SX1301
>
> >> + if (rx_buf) {
> >> + ret = sx1301_read(ssx->parent, ssx->regs +
> REG_RADIO_X_DATA_READBACK, &rx_buf[xfr->len - 1]);
> >> + if (ret) {
> >> + dev_err(&spi->dev, "SPI radio data read failed\n");
> >> + return ret;
> >> + }
> >> + }
> >
> > For a read we never set an address?
>
> To read, you first write the address via tx_buf, then either in the same
> transfer in the third byte or in a subsequent one-byte transfer as first
> byte you get the data.
>
> If you have better ideas how to structure this, do let me know.
>
> >> +static void sx1301_radio_setup(struct spi_controller *ctrl)
> >> +{
> >> + ctrl->mode_bits = SPI_CS_HIGH | SPI_NO_CS;
> >
> > This controller has no chip select but we provided a set_cs operation?
>
> Oops, I played around with those two options and was hoping SPI_NO_CS
> would avoid the undesired set_cs invocations, but it didn't work as
> expected and so I added the "if (enabled)" check above.
>
> Thanks for your review,
>
> Andreas
>
> --
> SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
> GF: Felix Imend?rffer, Jane Smithard, Graham Norton
> HRB 21284 (AG N?rnberg)
Hi Ben,
Am 02.07.2018 um 13:51 schrieb Ben Whitten:
> Excellent work on doing this I have also been working on and off
> this personally for some time.
Thanks. Colliding work is always unfortunate, I can relate...
> Have a look at my repository [1] for sx1301 and sx1257 drivers,
> I use regmaps capability of switching pages which should simplify
> your driver considerably, I also have a full register map and bit field.
Please note that my lora-next branch already has bug fixes and cleanups
over this patch. The probe error handling was broken, and I implemented
wrappers for paged reads and writes as well as burst modes, plus the
firmware loading.
https://github.com/afaerber/linux/commits/lora-next
I took a quick look at your sx1257 and noticed you licensed it as GPLv2.
Is there any particular reason for that? Since I wrote my driver without
copying from GPLv2 code, I prefer the less restrictive GPLv2+.
So far a work day has passed with no maintainer objecting to or
commenting on the underlying PF_LORA network layer design. Meanwhile
there's already three of us with code and more people have inquired
about testing and contributing, so I'm thinking about setting up a
staging tree on kernel.org to collaborate on...
Would you be willing to contribute your regmap ideas to my driver as a
patch to squash? Needs a Signed-off-by of course, which your GitHub
commits are lacking, so I can't merge them on my own.
> I have also been trying to use the clk framework to capture the various
> routing that the cards have.
I thought about clk too, but won't that cause name conflicts when
probing multiple concentrators? Would be nice to use that for
configuring the SX1257 clock output instead of my current hack.
Another thought I haven't investigated yet is whether we could use
remoteproc for ARB and AGC. I would at least prefer to have the firmware
as a binary loaded via the usual request_firmware(), not as byte array.
But then again the AGC gets firmware loaded twice, so maybe too complex
for remoteproc.
BTW do you have any insights on what MCU is in there? Would be nice to
understand in form of source code what the firmware is doing, to avoid
the hard dependency on a specific firmware version (imagine user
updating kernel-firmware - containing versions X,Y,Z - and kernel and
booting two different kernel versions, the older one stops working).
https://www.thethingsnetwork.org/forum/t/secret-price-of-a-lora-gateway/5730/74
Regards,
Andreas
> I will dig into this series this evening.
>
> [1] https://github.com/BWhitten/linux-stable/tree/971aadc8fdfe842020d912449bdd71b33d576fe3/drivers/net/lora
[...]
>> diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
>> new file mode 100644
>> index 000000000000..5c936c1116d1
>> --- /dev/null
>> +++ b/drivers/net/lora/sx1301.c
>> @@ -0,0 +1,446 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
[snip]
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Hi Ben,
Am 02.07.2018 um 22:43 schrieb Ben Whitten:
>> 2) This SPI device is in turn exposing the two SPI masters that you
>> already found below, and I didn't see a sane way to split that code out
>> into drivers/spi/, so it's in drivers/net/lora/ here - has there been
>> any precedence either way?
>
> In my work in progress driver I just register one controller for the sx1301 with two chip selects and use the chip select information to choose the correct radio to send to, this is based on the DT reg information. No need to register two separate masters.
I had considered that and discarded it. The SX1301 has not just two CS
registers though but also two pairs of addr, data registers. That speaks
for two masters with a single chip-select each, unless I'm
misunderstanding the meaning of the registers.
>> Am 02.07.2018 um 18:12 schrieb Mark Brown:
>>> On Sun, Jul 01, 2018 at 01:08:04PM +0200, Andreas Färber wrote:
>>>
>>>> +static void sx1301_radio_spi_set_cs(struct spi_device *spi, bool enable)
>>>> +{
>>>> + int ret;
>>>> +
>>>> + dev_dbg(&spi->dev, "setting SPI CS to %s\n", enable ? "1" : "0");
>>>> +
>>>> + if (enable)
>>>> + return;
>>>> +
>>>> + ret = sx1301_radio_set_cs(spi->controller, enable);
>>>> + if (ret)
>>>> + dev_warn(&spi->dev, "failed to write CS (%d)\n", ret);
>>>> +}
>>>
>>> So we never disable chip select?
>>
>> Not here, I instead did that in transfer_one below.
>>
>> Unfortunately there seems to be no documentation, only reference code:
>>
>> https://github.com/Lora-
>> net/lora_gateway/blob/master/libloragw/src/loragw_radio.c#L121
>> https://github.com/Lora-
>> net/lora_gateway/blob/master/libloragw/src/loragw_radio.c#L165
>>
>> It sets CS to 0 before writing to address and data registers, then
>> immediately sets CS to 1 and back to 0 before reading or ending the
>> write transaction. I've tried to force the same behavior in this driver.
>> My guess was that CS is high-active during the short 1-0 cycle, because
>> if it's low-active during the register writes then why the heck is it
>> set to 0 again in the end instead of keeping at 1... confusing.
>>
>> Maybe the Semtech folks CC'ed can comment how these registers work?
>>
>>>> + if (tx_buf) {
>>>> + ret = sx1301_write(ssx->parent, ssx->regs +
>> REG_RADIO_X_ADDR, tx_buf ? tx_buf[0] : 0);
>>>
>>> This looks confused. We're in an if (tx_buf) block but there's a use of
>>> the ternery operator that appears to be checking if we have a tx_buf?
>>
>> Yeah, as mentioned this RFC is not ready for merging - checkpatch.pl
>> will complain about lines too long, and TODOs are sprinkled all over or
>> not even mentioned. It's a Proof of Concept that a net_device could work
>> for a wide range of spi and serdev based drivers, and on top this device
>> has more than one channel, which may influence network-level design
>> discussions.
>>
>> That said, I'll happily drop the second check. Thanks for spotting!
>>
>>>> + if (ret) {
>>>> + dev_err(&spi->dev, "SPI radio address write
>> failed\n");
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = sx1301_write(ssx->parent, ssx->regs +
>> REG_RADIO_X_DATA, (tx_buf && xfr->len >= 2) ? tx_buf[1] : 0);
>>>> + if (ret) {
>>>> + dev_err(&spi->dev, "SPI radio data write failed\n");
>>>> + return ret;
>>>> + }
>>>
>>> This looks awfully like you're coming in at the wrong abstraction layer
>>> and the hardware actually implements a register abstraction rather than
>>> a SPI one so you should be using regmap as the abstraction.
>>
>> I don't understand. Ben has suggested using regmap for the SPI _device_
>> that we're talking to, which may be a good idea. But this SX1301 device
>> in turn has two SPI _masters_ talking to an SX125x slave each. I don't
>> see how using regmap instead of my wrappers avoids this spi_controller?
>> The whole point of this spi_controller is to abstract and separate the
>> SX1255 vs. SX1257 vs. whatever-radio-attached into a separate driver,
>> instead of mixing it into the SX1301 driver - to me that looks cleaner
>> and more extensible. It also has the side-effect that we could configure
>> the two radios via DT (frequencies, clk output, etc.).
>
> You want an SPI controller in the SX1301 as the down stream radios are SPI and could be attached directly to a host SPI bus, makes sense to have one radio driver and talk through the SX1301.
> But you should use the regmap to access the SX1301 master controller registers.
> Example I use with one SPI master and some clock info:
> eg:
> sx1301: sx1301@0 {
Node names should not repeat the chipset, that goes into compatible.
lora-concentrator@0?
> compatible = "semtech,sx1301";
> reg = <0>;
> #address-cells = <1>;
> #size-cells = <0>;
I would still find it cleaner to have (a) sub-node(s) for the radios.
> spi-max-frequency = <8000000>;
Datasheet says 10 MHz, why 8 MHz?
> gpios-reset = <&pioA 26 GPIO_ACTIVE_HIGH>;
reset-gpios?
> clocks = <&radio1 0>, <&clkhs 0>;
> clock-names = "clk32m", "clkhs";
>
> radio0: sx1257@0 {
lora@0?
> compatible = "semtech,sx125x";
No wildcards in bindings please, use concrete "semtech,sx1257".
> reg = <0>;
> spi-max-frequency = <8000000>;
Datasheet says 10 ns - I reported to Semtech that it should probably say
10 MHz, too.
> tx;
Might we configure that on the sx1301 instead?
> clocks = <&tcxo 0>;
> clock-names = "tcxo";
> };
>
> radio1: sx1257@1 {
> compatible = "semtech,sx125x";
> reg = <1>;
> spi-max-frequency = <8000000>;
> #clock-cells = <0>;
> clocks = <&tcxo 0>;
> clock-names = "tcxo";
> clock-output-names = "clk32m";
> };
> };
[snip]
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
On Mon, Jul 02, 2018 at 07:34:21PM +0200, Andreas F?rber wrote:
> Hi Mark,
>
> This driver is still evolving, there's newer code on my lora-next branch
> already: https://github.com/afaerber/linux/commits/lora-next
Please don't top post, reply in line with needed context. This allows
readers to readily follow the flow of conversation and understand what
you are talking about and also helps ensure that everything in the
discussion is being addressed.
> 2) This SPI device is in turn exposing the two SPI masters that you
> already found below, and I didn't see a sane way to split that code out
> into drivers/spi/, so it's in drivers/net/lora/ here - has there been
> any precedence either way?
A MFD?
> Am 02.07.2018 um 18:12 schrieb Mark Brown:
> > On Sun, Jul 01, 2018 at 01:08:04PM +0200, Andreas F?rber wrote:
> >> +static void sx1301_radio_spi_set_cs(struct spi_device *spi, bool enable)
> >> +{
> >> + int ret;
> >> +
> >> + dev_dbg(&spi->dev, "setting SPI CS to %s\n", enable ? "1" : "0");
> >> +
> >> + if (enable)
> >> + return;
> > So we never disable chip select?
> Not here, I instead did that in transfer_one below.
That's obviously at best going to be fragile, you're implementing half
the operation here and half somewhere else which is most likely going to
break at some point when the framework changes.
> >> + if (ret) {
> >> + dev_err(&spi->dev, "SPI radio address write failed\n");
> >> + return ret;
> >> + }
> >> +
> >> + ret = sx1301_write(ssx->parent, ssx->regs + REG_RADIO_X_DATA, (tx_buf && xfr->len >= 2) ? tx_buf[1] : 0);
> >> + if (ret) {
> >> + dev_err(&spi->dev, "SPI radio data write failed\n");
> >> + return ret;
> >> + }
> > This looks awfully like you're coming in at the wrong abstraction layer
> > and the hardware actually implements a register abstraction rather than
> > a SPI one so you should be using regmap as the abstraction.
> I don't understand. Ben has suggested using regmap for the SPI _device_
> that we're talking to, which may be a good idea. But this SX1301 device
> in turn has two SPI _masters_ talking to an SX125x slave each. I don't
> see how using regmap instead of my wrappers avoids this spi_controller?
It seems obvious from the code that this isn't actually interacting with
a SPI controller, you're writing an address and a value to a register
map rather than dealing with a byte stream. There may be a SPI bus
somewhere behind some other hardware but you don't seem to be
interacting with it as such.
> The whole point of this spi_controller is to abstract and separate the
> SX1255 vs. SX1257 vs. whatever-radio-attached into a separate driver,
> instead of mixing it into the SX1301 driver - to me that looks cleaner
> and more extensible. It also has the side-effect that we could configure
> the two radios via DT (frequencies, clk output, etc.).
A register map would work just as well here, we already have plenty of
devices that abstract at this level (most obviously the I2C/SPI devices
that use it to offer both interfaces with a single core driver).
Hi Andreas,
First, thanks for your great jobs. LoRaWAN module needs the
compatible devices drivers.
I will reply the message under the LoRaWAN specification.
2018-07-01 19:07 GMT+08:00 Andreas Färber <[email protected]>:
> Hello,
>
> LoRa is a long-range, low-power wireless technology by Semtech.
> Unlike other LPWAN technologies, users don't need to rely on infrastructure
> providers and SIM cards and expensive subscription plans, they can set up
> their own gateways. Modules, adapters and evaluation boards are available
> from a large number of vendors.
>
> Many vendors also make available Open Source software examples on GitHub.
> But when taking a closer look, many of them combine licenses in ways that are
> not redistributable. My reports have remained without response or solution.
>
> https://github.com/ernstdevreede/lmic_pi/issues/2
> https://github.com/Snootlab/lmic_chisterapi/issues/2
> https://github.com/Snootlab/lora_chisterapi/issues/2
>
> Another issue was that most such projects around the Raspberry Pi make use of
> spidev to communicate with the Semtech chipsets from userspace. The Linux spi
> maintainers have chosen to greet any such users of spidev with a friendly
> WARN_ON(), preferring in-kernel spi drivers and white-listing individual
> devices only.
>
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/spi/spidev.c?h=v4.17#n722
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/spi/spidev.c?h=v4.17#n667
>
> Also I don't quite see the point in having userspace probe what SPI devices
> are connected to a generic spidev driver when we have an easy Device Tree
> hardware description on arm/arm64 that could give us that info.
>
> I raised the topic during Q&A of a FOSDEM 2017 talk (cut off at the end
> of the video) but unfortunately found no one to collaborate on this.
>
> https://archive.fosdem.org/2017/schedule/event/lorawan/
>
> Instead of porting from wiringPi to a differently licensed GPIO library
> and dealing with seemingly unmaintained LoRaWAN code dumps, I started a
> spi kernel driver for SX1276 back in 2016. But obviously a kernel driver
> isn't too helpful without a userspace API to send and receive packets.
>
> This patchset, updated from 2017 and extended, is implementing kernel drivers
> for various LoRa chipsets and modules. As API I'm proposing a PF_LORA socket
> implementation. Why? LoRa uses data packets with headers and checksums
> and differing MTUs and multiple protocols layered on top of it. Apart from
> simple headers for addressing used by RadioHead library and IMST's LoRa P2P
> protocol, the main use case (not implemented in this patchset) is expected
> to be LoRaWAN. And LoRaWAN has competing proprietary protocols, such as
> Link Labs' Symphony Link or GlobalSat M.O.S.T. or RadioShuttle, that might
> at some point want to adopt a standard API for their implementations, too.
>
> Ready-made LoRa hardware modules come in three flavors,
> a) with SPI access to the underlying Semtech chipsets, needing a software
> implementation of e.g. LoRaWAN protocol stack (i.e., a soft MAC),
> b) with a custom, often UART based interface and a pre-certified LoRaWAN
> protocol stack already integrated (i.e., hard/full MAC), and
> c) with a microcontroller that serves not only for the protocol stack but
> also as application processor, not offering a ready-made interface.
>
> This patchset focuses on option a). An SX1276 based LoRaWAN stack appeared
> to be the project of Jian-Hong Pan and is not included here.
Thanks, LoRaWAN module needs the compatible device drivers.
> This patchset also includes drivers for b), from text based AT commands to
> a binary SLIP based HCI protocol.
> Hardware examples for c) are Murata CMWX1ZZABZ-078 and RAK813.
>
> This patchset is clearly not ready for merging, but is being submitted for
> discussion, as requested by Jiri, in particular of the design choices:
>
> 1) PF_LORA/AF_LORA and associated identifiers are proposed to represent
> this technology. While for an SX1276 - case a) above - it might work to
> layer LoRaWAN as a protocol option for PF_LORA and add LoRaWAN address
> fields to the union in my sockaddr_lora, how would that work for devices
> that only support LoRaWAN but not pure LoRa? Do we need both AF_LORA and
> AF_LORAWAN, or just a separate ETH_P_LORAWAN or ARPHRD_LORAWAN?
The LoRaWAN module also register the LoRa device as a network device.
Will use the macros AF_LORAWAN, PF_LORAWAN, ARPHRD_LORAWAN and ETH_P_LORAWAN.
> 2) PF_LORA is used with SOCK_DGRAM here. The assumption is that RAW mode
> would be DGRAM plus preamble plus optional checksum.
Is the preamble added by the hardware itself here or software here?
> 3) Only the transmit path is partially implemented already. The assumption
> is that the devices should go into receive mode by default and only
> interrupt that when asked to transmit.
If it goes with LoRaWAN spec., end devices should be in idle (or
called standby mode) by default. Unless the device is asked to be in
transmitting or receiving timing slot.
> 4) Some hardware settings need to be supplied externally, such as the radio
> frequency for some modules, but many others can be runtime-configured,
> such as Spreading Factor, Bandwidth, Sync Word, or which antenna to use.
> What settings should be implemented as socket option vs. netlink layer
> vs. ioctl vs. sysfs? What are the criteria to apply?
- We can have a pre-defined table according to LoRaWAN Regional Parameters.
- Device driver declares the hardware's capability, for example
frequency, TX power. And then registers as a LoRaWAN compatible
device.
- LoRaWAN module handle the requests from upper layer or MAC commands
from a gateway (netlink layer), than uses the pre-defined interface
functions to set the parameters.
LoRaWAN module will export the operation functions interface.
> 5) Many of the modules support multiple modes, such as LoRa, LoRaWAN and FSK.
> Lacking a LoRaWAN implementation, I am currently switching them into LoRa
> mode at probe time wherever possible. How do we deal with that properly?
- There are data rate tables defined in LoRaWAN Regional Parameters.
Those contain which data rate uses LoRa mode or FSK mode.
- LoRaWAN should handle the data rate changing requests and then calls
the corresponding operation functions. Of course, the hardware's
capability registered before should also be under consideration at the
same time.
> a) Is there any precedence from the Wifi world for dynamically selecting
> between our own trusted Open Source implementation vs. hardware/firmware
> accelerated and/or certified implementations?
>
> b) Would a proof of concept for FSK (non-LoRa) modes be required for
> merging any LoRa driver for chipsets that support both? Or is there any
> facility or design guidelines that would allow us to focus on LoRa and
> LoRaWAN and leave non-LoRa radio modes to later contributors?
>
> As evident by the many questions, this is my first deep dive into the Linux
> net subsystem. It's also my first experiments with the new serdev subsystem,
> so in particular the receive paths will need some review and optimizations.
>
> This patchset was developed and tested mainly as KMP, originally at
> https://github.com/afaerber/lora-modules. It was recently transformed into a
> linux-next based tree, still mostly tested on our openSUSE Tumbleweed kernel
> with a differing AF_LORA value below current AF_MAX limit.
>
> Some corresponding Device Tree Overlays have been collected here:
> https://github.com/afaerber/dt-overlays
>
> Only European models for 868 MHz and 433 MHz could be tested when available.
I can test 922-928 MHz in Taiwan.
But I need a workable LoRaWAN gateway first! XD
Cheers,
Jian-Hong Pan
> Thanks to all companies and people that have supported this project so far.
>
> Have a lot of fun!
>
> Cheers,
> Andreas
>
> Cc: Jian-Hong Pan <[email protected]>
> Cc: Jiri Pirko <[email protected]>
> Cc: Marcel Holtmann <[email protected]>
> Cc: "David S. Miller" <[email protected]>
> Cc: Matthias Brugger <[email protected]>
> Cc: Konstantin Böhm <[email protected]>
> Cc: Jan Jongboom <[email protected]>
> Cc: Janus Piwek <[email protected]>
> Cc: Michael Röder <[email protected]>
> Cc: Dollar Chen (陳義元) <[email protected]>
> Cc: Ken Yu (禹凯) <[email protected]>
> Cc: Jon Ortego <[email protected]>
> Cc: [email protected]
> Cc: Ben Whitten <[email protected]>
> Cc: Brian Ray <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: Alexander Graf <[email protected]>
> Cc: Michal Kubeček <[email protected]>
> Cc: Rob Herring <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: Steve deRosier <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: [email protected]
>
> Andreas Färber (15):
> net: Reserve protocol numbers for LoRa
> net: lora: Define sockaddr_lora
> net: lora: Add protocol numbers
> net: Add lora subsystem
> HACK: net: lora: Deal with .poll_mask in 4.18-rc2
> net: lora: Prepare for device drivers
> net: lora: Add Semtech SX1276
> net: lora: sx1276: Add debugfs
> net: lora: Prepare EUI helpers
> net: lora: Add Microchip RN2483
> net: lora: Add IMST WiMOD
> net: lora: Add USI WM-SG-SM-42
> net: lora: Prepare RAK RAK811
> net: lora: Prepare Semtech SX1257
> net: lora: Add Semtech SX1301
>
> drivers/net/Makefile | 1 +
> drivers/net/lora/Kconfig | 72 ++++
> drivers/net/lora/Makefile | 32 ++
> drivers/net/lora/dev.c | 125 ++++++
> drivers/net/lora/rak811.c | 219 +++++++++++
> drivers/net/lora/rn2483.c | 344 +++++++++++++++++
> drivers/net/lora/rn2483.h | 40 ++
> drivers/net/lora/rn2483_cmd.c | 130 +++++++
> drivers/net/lora/sx1257.c | 96 +++++
> drivers/net/lora/sx1276.c | 740 ++++++++++++++++++++++++++++++++++++
> drivers/net/lora/sx1301.c | 446 ++++++++++++++++++++++
> drivers/net/lora/usi.c | 411 ++++++++++++++++++++
> drivers/net/lora/wimod.c | 597 +++++++++++++++++++++++++++++
> include/linux/lora/dev.h | 44 +++
> include/linux/lora/skb.h | 29 ++
> include/linux/socket.h | 4 +-
> include/uapi/linux/if_arp.h | 1 +
> include/uapi/linux/if_ether.h | 1 +
> include/uapi/linux/lora.h | 24 ++
> net/Kconfig | 1 +
> net/Makefile | 1 +
> net/lora/Kconfig | 15 +
> net/lora/Makefile | 8 +
> net/lora/af_lora.c | 152 ++++++++
> net/lora/af_lora.h | 13 +
> net/lora/dgram.c | 297 +++++++++++++++
> security/selinux/hooks.c | 4 +-
> security/selinux/include/classmap.h | 4 +-
> 28 files changed, 3848 insertions(+), 3 deletions(-)
> create mode 100644 drivers/net/lora/Kconfig
> create mode 100644 drivers/net/lora/Makefile
> create mode 100644 drivers/net/lora/dev.c
> create mode 100644 drivers/net/lora/rak811.c
> create mode 100644 drivers/net/lora/rn2483.c
> create mode 100644 drivers/net/lora/rn2483.h
> create mode 100644 drivers/net/lora/rn2483_cmd.c
> create mode 100644 drivers/net/lora/sx1257.c
> create mode 100644 drivers/net/lora/sx1276.c
> create mode 100644 drivers/net/lora/sx1301.c
> create mode 100644 drivers/net/lora/usi.c
> create mode 100644 drivers/net/lora/wimod.c
> create mode 100644 include/linux/lora/dev.h
> create mode 100644 include/linux/lora/skb.h
> create mode 100644 include/uapi/linux/lora.h
> create mode 100644 net/lora/Kconfig
> create mode 100644 net/lora/Makefile
> create mode 100644 net/lora/af_lora.c
> create mode 100644 net/lora/af_lora.h
> create mode 100644 net/lora/dgram.c
>
> --
> 2.16.4
>
Am 03.07.2018 um 16:50 schrieb Mark Brown:
> On Mon, Jul 02, 2018 at 07:34:21PM +0200, Andreas F?rber wrote:
>> Hi Mark,
>>
>> This driver is still evolving, there's newer code on my lora-next branch
>> already: https://github.com/afaerber/linux/commits/lora-next
>
> Please don't top post, reply in line with needed context. This allows
> readers to readily follow the flow of conversation and understand what
> you are talking about and also helps ensure that everything in the
> discussion is being addressed.
I did reply inline, if you cared to read on. ;)
>> 2) This SPI device is in turn exposing the two SPI masters that you
>> already found below, and I didn't see a sane way to split that code out
>> into drivers/spi/, so it's in drivers/net/lora/ here - has there been
>> any precedence either way?
>
> A MFD?
I know of mfd, but how would the the the net vs. spi pieces interact
then? Some functions would need to be exported then or is there an
easier way without needing to set a cross-module API in stone?
>> Am 02.07.2018 um 18:12 schrieb Mark Brown:
>>> On Sun, Jul 01, 2018 at 01:08:04PM +0200, Andreas F?rber wrote:
>>>> +static void sx1301_radio_spi_set_cs(struct spi_device *spi, bool enable)
>>>> +{
>>>> + int ret;
>>>> +
>>>> + dev_dbg(&spi->dev, "setting SPI CS to %s\n", enable ? "1" : "0");
>>>> +
>>>> + if (enable)
>>>> + return;
>
>>> So we never disable chip select?
>
>> Not here, I instead did that in transfer_one below.
>
> That's obviously at best going to be fragile, you're implementing half
> the operation here and half somewhere else which is most likely going to
> break at some point when the framework changes.
>
>>>> + if (ret) {
>>>> + dev_err(&spi->dev, "SPI radio address write failed\n");
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = sx1301_write(ssx->parent, ssx->regs + REG_RADIO_X_DATA, (tx_buf && xfr->len >= 2) ? tx_buf[1] : 0);
>>>> + if (ret) {
>>>> + dev_err(&spi->dev, "SPI radio data write failed\n");
>>>> + return ret;
>>>> + }
>
>>> This looks awfully like you're coming in at the wrong abstraction layer
>>> and the hardware actually implements a register abstraction rather than
>>> a SPI one so you should be using regmap as the abstraction.
>
>> I don't understand. Ben has suggested using regmap for the SPI _device_
>> that we're talking to, which may be a good idea. But this SX1301 device
>> in turn has two SPI _masters_ talking to an SX125x slave each. I don't
>> see how using regmap instead of my wrappers avoids this spi_controller?
>
> It seems obvious from the code that this isn't actually interacting with
> a SPI controller, you're writing an address and a value to a register
> map rather than dealing with a byte stream. There may be a SPI bus
> somewhere behind some other hardware but you don't seem to be
> interacting with it as such.
>
>> The whole point of this spi_controller is to abstract and separate the
>> SX1255 vs. SX1257 vs. whatever-radio-attached into a separate driver,
>> instead of mixing it into the SX1301 driver - to me that looks cleaner
>> and more extensible. It also has the side-effect that we could configure
>> the two radios via DT (frequencies, clk output, etc.).
>
> A register map would work just as well here, we already have plenty of
> devices that abstract at this level (most obviously the I2C/SPI devices
> that use it to offer both interfaces with a single core driver).
The address and data registers together form a two-byte SPI message!
It is transmitted by writing to the CS register.
The received data is afterwards available in another register.
HTE,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Graham Norton
HRB 21284 (AG N?rnberg)
On Tue, Jul 03, 2018 at 05:09:38PM +0200, Andreas F?rber wrote:
> Am 03.07.2018 um 16:50 schrieb Mark Brown:
> >> 2) This SPI device is in turn exposing the two SPI masters that you
> >> already found below, and I didn't see a sane way to split that code out
> >> into drivers/spi/, so it's in drivers/net/lora/ here - has there been
> >> any precedence either way?
> > A MFD?
> I know of mfd, but how would the the the net vs. spi pieces interact
> then? Some functions would need to be exported then or is there an
> easier way without needing to set a cross-module API in stone?
It's an in-kernel ABI it's not exactly set in stone but yeah, you'll
need some interface. A lot of devices work by having the children know
that they're part of a MFD and fish things out of the parent device,
either the pdata or (in the common case where the MFD bit mostly just
instantiates subdevices and holds a regmap) with dev_get_regmap().
> > A register map would work just as well here, we already have plenty of
> > devices that abstract at this level (most obviously the I2C/SPI devices
> > that use it to offer both interfaces with a single core driver).
> The address and data registers together form a two-byte SPI message!
> It is transmitted by writing to the CS register.
> The received data is afterwards available in another register.
Right, but it seems from the code that the hardware understands that
it's formatting register I/O and not just shifting in and out a byte
stream which is what a SPI controller does. I'd not be surprised to
learn that the register you're calling a chip select register is a
strobe that initiates the transfer (and that this may be some of the
difficulty you're having with handling it in the way the framework
expects), the pattern with writing 1 followed immediately by 0 is a bit
of a flag here.
I've seen such before hardware where I know it was intentionally
designed that way so it wouldn't be totally surprising.
Am 03.07.2018 um 17:31 schrieb Mark Brown:
> On Tue, Jul 03, 2018 at 05:09:38PM +0200, Andreas F?rber wrote:
>> Am 03.07.2018 um 16:50 schrieb Mark Brown:
>>> A register map would work just as well here, we already have plenty of
>>> devices that abstract at this level (most obviously the I2C/SPI devices
>>> that use it to offer both interfaces with a single core driver).
>
>> The address and data registers together form a two-byte SPI message!
>
>> It is transmitted by writing to the CS register.
>
>> The received data is afterwards available in another register.
>
> Right, but it seems from the code that the hardware understands that
> it's formatting register I/O and not just shifting in and out a byte
> stream which is what a SPI controller does. I'd not be surprised to
> learn that the register you're calling a chip select register is a
> strobe that initiates the transfer (and that this may be some of the
> difficulty you're having with handling it in the way the framework
> expects), the pattern with writing 1 followed immediately by 0 is a bit
> of a flag here.
Yeah, the current implementation assumes exactly that. :)
> I've seen such before hardware where I know it was intentionally
> designed that way so it wouldn't be totally surprising.
If we don't implement a spi_controller here, then IIUC we can't have
multiple spi_device implementations for the devices on the receiving
end, as they rely on a spi_controller for their APIs.
Do you have an alternative solution for abstraction? A regmap would seem
to require putting everything into a monolithic SX1301 driver despite
those connected chipsets actually being regular, external SPI chips that
could also be attached to non-SX1301 SPI masters.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Graham Norton
HRB 21284 (AG N?rnberg)
On Tue, Jul 03, 2018 at 06:40:41PM +0200, Andreas F?rber wrote:
> Do you have an alternative solution for abstraction? A regmap would seem
> to require putting everything into a monolithic SX1301 driver despite
> those connected chipsets actually being regular, external SPI chips that
> could also be attached to non-SX1301 SPI masters.
I'm suggesting a regmap for those external SPI chips. It doesn't matter
what the host uses.
> Subject: Re: [RFC net-next 15/15] net: lora: Add Semtech SX1301
>
> On Tue, Jul 03, 2018 at 06:40:41PM +0200, Andreas F?rber wrote:
>
> > Do you have an alternative solution for abstraction? A regmap would seem
> > to require putting everything into a monolithic SX1301 driver despite
> > those connected chipsets actually being regular, external SPI chips that
> > could also be attached to non-SX1301 SPI masters.
>
> I'm suggesting a regmap for those external SPI chips. It doesn't matter
> what the host uses.
Currently in my testing drivers I have used regmap for both the SX1301 and the downstream SX1257.
In my SX1257 driver currently the regmap is backed via SPI with devm_regmap_init_spi, please correct me if I am wrong but if I understand correctly this could be split out to regmap backed by SPI in the case of a direct host connection, and a regmap backed by the SX1301's regmapped strobing register interface, regmap_bus?
Is there a precedent to this I can examine?
On Wed, Jul 04, 2018 at 01:41:42PM +0000, Ben Whitten wrote:
Please fix your mail client to word wrap within paragraphs at something
substantially less than 80 columns. Doing this makes your messages much
easier to read and reply to.
> In my SX1257 driver currently the regmap is backed via SPI with
> devm_regmap_init_spi, please correct me if I am wrong but if I
> understand correctly this could be split out to regmap backed by SPI
> in the case of a direct host connection, and a regmap backed by the
> SX1301's regmapped strobing register interface, regmap_bus?
Yes.
> Is there a precedent to this I can examine?
There's lots of devices that provide both SPI and I2C, you'd just be
using a regmap_bus instead of the I2C one. I can't recall an example of
the specific combination you're looking for though.
Hello.
On 01.07.2018 13:07, Andreas Färber wrote:
>
> This patchset, updated from 2017 and extended, is implementing kernel drivers
> for various LoRa chipsets and modules.
I am very happy to see that we now have three people (you, Jian-Hong and
Ben) working towards a lora subsystem for protocol stack and drivers.
Will allocate some time to look over this and provide some review and
feedback how we handled things in the ieee802154 and 6lowpan subsystems.
We are also having a IoT protocols workshop during NetDev Conf next week
in Montreal (short notice if you are not already planning to attend). If
you (or Ben) has soem topics wanted to bring up their please let me know
by pm. Jian-Hong will be there presenting on his lora work and I would
love to get more input from you and Ben on your views on this.
https://netdevconf.org/0x12/session.html?workshop-on-iot-related-mac-layers-header-compression-and-routing-protocols
regards
Stefan Schmidt
> Subject: Re: [RFC net-next 15/15] net: lora: Add Semtech SX1301
>
> Hi Ben,
>
> Am 02.07.2018 um 22:43 schrieb Ben Whitten:
> >> 2) This SPI device is in turn exposing the two SPI masters that you
> >> already found below, and I didn't see a sane way to split that code out
> >> into drivers/spi/, so it's in drivers/net/lora/ here - has there been
> >> any precedence either way?
> >
> > In my work in progress driver I just register one controller for the sx1301
> with two chip selects and use the chip select information to choose the
> correct radio to send to, this is based on the DT reg information. No need to
> register two separate masters.
>
> I had considered that and discarded it. The SX1301 has not just two CS
> registers though but also two pairs of addr, data registers. That speaks
> for two masters with a single chip-select each, unless I'm
> misunderstanding the meaning of the registers.
Based on Marks suggestion I am experimenting with using the SX1301 to expose a regmap_bus that the underlying SX1257 can attach to, so the radio has a core component, a SPI component for direct connection and this concentrator connection component.
> >> Am 02.07.2018 um 18:12 schrieb Mark Brown:
> >>> On Sun, Jul 01, 2018 at 01:08:04PM +0200, Andreas Färber wrote:
> >>>
> >>>> +static void sx1301_radio_spi_set_cs(struct spi_device *spi, bool
> enable)
> >>>> +{
> >>>> + int ret;
> >>>> +
> >>>> + dev_dbg(&spi->dev, "setting SPI CS to %s\n", enable ? "1" : "0");
> >>>> +
> >>>> + if (enable)
> >>>> + return;
> >>>> +
> >>>> + ret = sx1301_radio_set_cs(spi->controller, enable);
> >>>> + if (ret)
> >>>> + dev_warn(&spi->dev, "failed to write CS (%d)\n", ret);
> >>>> +}
> >>>
> >>> So we never disable chip select?
> >>
> >> Not here, I instead did that in transfer_one below.
> >>
> >> Unfortunately there seems to be no documentation, only reference
> code:
> >>
> >> https://github.com/Lora-
> >> net/lora_gateway/blob/master/libloragw/src/loragw_radio.c#L121
> >> https://github.com/Lora-
> >> net/lora_gateway/blob/master/libloragw/src/loragw_radio.c#L165
> >>
> >> It sets CS to 0 before writing to address and data registers, then
> >> immediately sets CS to 1 and back to 0 before reading or ending the
> >> write transaction. I've tried to force the same behavior in this driver.
> >> My guess was that CS is high-active during the short 1-0 cycle, because
> >> if it's low-active during the register writes then why the heck is it
> >> set to 0 again in the end instead of keeping at 1... confusing.
> >>
> >> Maybe the Semtech folks CC'ed can comment how these registers work?
> >>
> >>>> + if (tx_buf) {
> >>>> + ret = sx1301_write(ssx->parent, ssx->regs +
> >> REG_RADIO_X_ADDR, tx_buf ? tx_buf[0] : 0);
> >>>
> >>> This looks confused. We're in an if (tx_buf) block but there's a use of
> >>> the ternery operator that appears to be checking if we have a tx_buf?
> >>
> >> Yeah, as mentioned this RFC is not ready for merging - checkpatch.pl
> >> will complain about lines too long, and TODOs are sprinkled all over or
> >> not even mentioned. It's a Proof of Concept that a net_device could work
> >> for a wide range of spi and serdev based drivers, and on top this device
> >> has more than one channel, which may influence network-level design
> >> discussions.
> >>
> >> That said, I'll happily drop the second check. Thanks for spotting!
> >>
> >>>> + if (ret) {
> >>>> + dev_err(&spi->dev, "SPI radio address write
> >> failed\n");
> >>>> + return ret;
> >>>> + }
> >>>> +
> >>>> + ret = sx1301_write(ssx->parent, ssx->regs +
> >> REG_RADIO_X_DATA, (tx_buf && xfr->len >= 2) ? tx_buf[1] : 0);
> >>>> + if (ret) {
> >>>> + dev_err(&spi->dev, "SPI radio data write failed\n");
> >>>> + return ret;
> >>>> + }
> >>>
> >>> This looks awfully like you're coming in at the wrong abstraction layer
> >>> and the hardware actually implements a register abstraction rather than
> >>> a SPI one so you should be using regmap as the abstraction.
> >>
> >> I don't understand. Ben has suggested using regmap for the SPI _device_
> >> that we're talking to, which may be a good idea. But this SX1301 device
> >> in turn has two SPI _masters_ talking to an SX125x slave each. I don't
> >> see how using regmap instead of my wrappers avoids this spi_controller?
> >> The whole point of this spi_controller is to abstract and separate the
> >> SX1255 vs. SX1257 vs. whatever-radio-attached into a separate driver,
> >> instead of mixing it into the SX1301 driver - to me that looks cleaner
> >> and more extensible. It also has the side-effect that we could configure
> >> the two radios via DT (frequencies, clk output, etc.).
> >
> > You want an SPI controller in the SX1301 as the down stream radios are SPI
> and could be attached directly to a host SPI bus, makes sense to have one
> radio driver and talk through the SX1301.
> > But you should use the regmap to access the SX1301 master controller
> registers.
> > Example I use with one SPI master and some clock info:
> > eg:
> > sx1301: sx1301@0 {
>
> Node names should not repeat the chipset, that goes into compatible.
>
> lora-concentrator@0?
>
Sure
> > compatible = "semtech,sx1301";
> > reg = <0>;
> > #address-cells = <1>;
> > #size-cells = <0>;
>
> I would still find it cleaner to have (a) sub-node(s) for the radios.
How do you mean?
> > spi-max-frequency = <8000000>;
>
> Datasheet says 10 MHz, why 8 MHz?
>
> > gpios-reset = <&pioA 26 GPIO_ACTIVE_HIGH>;
>
> reset-gpios?
Agreed, this seems more common.
> > clocks = <&radio1 0>, <&clkhs 0>;
> > clock-names = "clk32m", "clkhs";
> >
> > radio0: sx1257@0 {
>
> lora@0?
>
> > compatible = "semtech,sx125x";
>
> No wildcards in bindings please, use concrete "semtech,sx1257".
Sure
> > reg = <0>;
> > spi-max-frequency = <8000000>;
>
> Datasheet says 10 ns - I reported to Semtech that it should probably say
> 10 MHz, too.
>
> > tx;
>
> Might we configure that on the sx1301 instead?
Well the ability for a radio to TX is a radio property really. It depends on the board which chain has the PAs on. I don’t think that its appropriate to configure this at the concentrator, it can instead discover this from the radios.
> > clocks = <&tcxo 0>;
> > clock-names = "tcxo";
> > };
> >
> > radio1: sx1257@1 {
> > compatible = "semtech,sx125x";
> > reg = <1>;
> > spi-max-frequency = <8000000>;
> > #clock-cells = <0>;
> > clocks = <&tcxo 0>;
> > clock-names = "tcxo";
> > clock-output-names = "clk32m";
> > };
> > };
> [snip]
>
> Regards,
> Andreas
>
> --
> SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Felix Imendörffer, Jane Smithard, Graham Norton
> HRB 21284 (AG Nürnberg)
> Subject: Re: [RFC net-next 15/15] net: lora: Add Semtech SX1301
>
> Hi Ben,
>
> Am 02.07.2018 um 13:51 schrieb Ben Whitten:
> > Excellent work on doing this I have also been working on and off
> > this personally for some time.
>
> Thanks. Colliding work is always unfortunate, I can relate...
>
> > Have a look at my repository [1] for sx1301 and sx1257 drivers,
> > I use regmaps capability of switching pages which should simplify
> > your driver considerably, I also have a full register map and bit field.
>
> Please note that my lora-next branch already has bug fixes and cleanups
> over this patch. The probe error handling was broken, and I implemented
> wrappers for paged reads and writes as well as burst modes, plus the
> firmware loading.
>
> https://github.com/afaerber/linux/commits/lora-next
>
> I took a quick look at your sx1257 and noticed you licensed it as GPLv2.
> Is there any particular reason for that? Since I wrote my driver without
> copying from GPLv2 code, I prefer the less restrictive GPLv2+.
As I was learning about regmap usage and how SPI devices connect to the bus I adopted the most common licence employed in these kernel files, seemed appropriate.
Ultimately I don’t want a licence choice to hamper inclusion to mainline, not a lawyer I don’t find that fun.
>
> So far a work day has passed with no maintainer objecting to or
> commenting on the underlying PF_LORA network layer design. Meanwhile
> there's already three of us with code and more people have inquired
> about testing and contributing, so I'm thinking about setting up a
> staging tree on kernel.org to collaborate on...
>
> Would you be willing to contribute your regmap ideas to my driver as a
> patch to squash? Needs a Signed-off-by of course, which your GitHub
> commits are lacking, so I can't merge them on my own.
I'm sure whichever way we merge it, yours to mine / mine to yours, we can work together on it.
As I already have regmap running and mostly split out SX1257 drivers to Marks suggestions avoiding the extra SPI layer it may by easier to port the various functions you have to mine and share authorship.
> > I have also been trying to use the clk framework to capture the various
> > routing that the cards have.
>
> I thought about clk too, but won't that cause name conflicts when
> probing multiple concentrators? Would be nice to use that for
> configuring the SX1257 clock output instead of my current hack.
Potentially, it maybe that we need to augment the names of the clk with the CS of the concentrator in use.
Or maybe the radio only searches the clk's of its parent device...
I've not explored this area to far yet but there will be a solution I'm sure.
> Another thought I haven't investigated yet is whether we could use
> remoteproc for ARB and AGC. I would at least prefer to have the firmware
> as a binary loaded via the usual request_firmware(), not as byte array.
> But then again the AGC gets firmware loaded twice, so maybe too complex
> for remoteproc.
>
> BTW do you have any insights on what MCU is in there? Would be nice to
> understand in form of source code what the firmware is doing, to avoid
> the hard dependency on a specific firmware version (imagine user
> updating kernel-firmware - containing versions X,Y,Z - and kernel and
> booting two different kernel versions, the older one stops working).
I'm afraid no insight into the internals of this chip, some good guesswork but I think a firmware blob is something we are going to have to live with for the time being.
> https://www.thethingsnetwork.org/forum/t/secret-price-of-a-lora-
> gateway/5730/74
>
> Regards,
> Andreas
>
> > I will dig into this series this evening.
> >
> > [1] https://github.com/BWhitten/linux-
> stable/tree/971aadc8fdfe842020d912449bdd71b33d576fe3/drivers/net/lora
> [...]
> >> diff --git a/drivers/net/lora/sx1301.c b/drivers/net/lora/sx1301.c
> >> new file mode 100644
> >> index 000000000000..5c936c1116d1
> >> --- /dev/null
> >> +++ b/drivers/net/lora/sx1301.c
> >> @@ -0,0 +1,446 @@
> >> +// SPDX-License-Identifier: GPL-2.0-or-later
> [snip]
Ben
Dear Andreas,
I put the kernel support for the SX1276 LoRa chip in question. I don’t
think that this kind of device should be in the Linux kernel.
Here are a few facts to consider:
- A LoRa transaction is very slow (e.g. SF7 needs about 210 ms, for
SF12 6280 ms) for 12 bytes user data with acknowledge.
- There are many different implementations for the antenna switch,
switching between receiving/sending, PA-BOOST, 433, 868/915 MHz. (I know
SX1276 Heltec ESP32, SX1276 Murata, RFM95-(1276), SX1276 Heltec
STM32-L4) they are all different regarding this.
- The LoRa chip device ID is only 8-bit which is not sufficient for
larger networks, e.g. our RadioShuttle protocol uses compressed 32-bit
device IDs.
- The chip can do MTU sizes up to 2048 bytes, most protocols use less
than 250 bytes.
- Applications do on-the-fly channel and spreading factor switching
which makes it more difficult for the configuration.
- The chip supports Lora modulation as well as FSK, GFSK, MSK, GMSK,
and OOK modulation, for some use cases the other modulations are of
interest, e.g. to communicated with other devices.
- Power saving modes, like sleep, suspend may be required for battery
operation.
- Cad detection is very flexible and can be differently used.
- LoRa preamble may be different depending on the protocol used.
- The new Lora chip SX1262 / 68 (successor of the SX1276) has different
hardware and all different registers, it is driver incompatible with the
SX1276. It needs an entire new driver.
- The device is not multi-process capable (only a single process can
communicate with it).
- There are SX1276 LoRa modules with a build-in protocol (LoRaWAN and
RAW) via a serial connection only, again complete different API compared
the SX1276 chip. Software updates for this devices are difficult.
- I am even convinced that the LoRaWAN protocol with the concentrator
concept is not good, the peer to peer communication and a standard MQTT
gateway (what we do) is way more flexible.
For all this reasons, I feel a user level driver task implementation is
way more flexible. I did a lot of work/enhancements on the SX1276 link
level driver from Semtech, it is available and maintained on mbed.org
and supports mbed-os, Arduino and is prepared for Linux.
https://os.mbed.com/users/Helmut64/code/SX1276GenericLib/
Protocols e.g. our RadioShuttle LoRa peer to peer protocol or the
LoRaWAN protocol can run on top of the SX1276GenericLib. We may should
focus on such a driver library getting supported for multiple OS's (Win,
Linux, mbed, Ardino, etc.)
Again I feel a Linux kernel device driver for the SX1276 chip make
little sense for me.
Regards
Helmut Tschemernjak (http://www.radioshuttle.de, http://www.helios.de)
Dear Helmut,
Am 05.07.2018 um 12:43 schrieb Helmut Tschemernjak:
> I put the kernel support for the SX1276 LoRa chip in question. I don’t
> think that this kind of device should be in the Linux kernel.
Thanks for sharing your opinion.
> Here are a few facts to consider:
> - A LoRa transaction is very slow (e.g. SF7 needs about 210 ms, for
> SF12 6280 ms) for 12 bytes user data with acknowledge.
Where do you see a problem? If you look at my SX1276 patch, you will
find that it queues a work item for the transmission (asynchronously)
and has an interrupt handler to get notified of the TX interrupt. It
surely won't get quicker in userspace - and as you point out, we're not
speaking about DPDK performance here, so no need for polling.
> - There are many different implementations for the antenna switch,
> switching between receiving/sending, PA-BOOST, 433, 868/915 MHz. (I know
> SX1276 Heltec ESP32, SX1276 Murata, RFM95-(1276), SX1276 Heltec
> STM32-L4) they are all different regarding this.
Yes, and as demonstrated with this patch series, the kernel is well able
to handle this variety of interfaces. I would say better than userspace!
As noted, I've tested both 868 MHz and 433 MHz modules. My SX1276 driver
uses the Device Tree (radio-frequency) to configure the driver when this
was a property of the module. RN2483 by contrast supports two, so a
netlink interface was suggested to configure this at runtime.
> - The LoRa chip device ID is only 8-bit which is not sufficient for
> larger networks, e.g. our RadioShuttle protocol uses compressed 32-bit
> device IDs.
What does that have to do with anything? The whole reason that you're
CC'ed on this patchset is to make sure that you or someone else can
implement your custom protocol on top of the APIs being proposed. So
my-protocol-is-better-than-theirs is no argument against this project.
The way I understand LoRa and thus designed the struct sockaddr_lora is
that there is no ID involved for LoRa at all - it is essentially a
broadcast. The 8-byte (not 8-bit) EUIs only come into play for LoRaWAN
AFAIU. Am I missing anything?
LoRaWAN, RadioShuttle, RadioHead and LR Base all have their own ways of
addressing - the question here is how to model that in Linux, whether as
one PF_LORA with different protocol options and a growing union
lora_addr inside sockaddr_lora covering all of them, or a protocol
family (requiring global number allocation) for each one of them.
> - The chip can do MTU sizes up to 2048 bytes, most protocols use less
> than 250 bytes.
How would 2048 bytes work? The FIFO buffer fits a maximum of 256 bytes.
IIRC the recommended MTU is also restricted by the airtime, i.e. SF,
bandwidth, preamble and other factors...
Some UART based modules allow a maximum of 64 bytes.
> - Applications do on-the-fly channel and spreading factor switching
> which makes it more difficult for the configuration.
That's exactly the question of how to implement individual options -
Device Tree would be fixed for the system's hardware, netlink would be
user-configurable for the whole network device, whereas socket options
would be per socket/application, and ioctls might be yet another
implementation layer.
> - The chip supports Lora modulation as well as FSK, GFSK, MSK, GMSK,
> and OOK modulation, for some use cases the other modulations are of
> interest, e.g. to communicated with other devices.
Yes, therefore I raised it in the cover letter as open point 5).
> - Power saving modes, like sleep, suspend may be required for battery
> operation.
The Linux kernel can deal with that much better than userspace. There's
pm hooks that one could implement. And SX1276 returns to standby mode
whenever an operation such as TX is completed. In sleep mode not all
settings get preserved, so they would need to be saved in the private
struct and restored on resume.
> - Cad detection is very flexible and can be differently used.
>
> - LoRa preamble may be different depending on the protocol used.
>
> - The new Lora chip SX1262 / 68 (successor of the SX1276) has different
> hardware and all different registers, it is driver incompatible with the
> SX1276. It needs an entire new driver.
Not unexpected, same as SX1301 being different. As this series shows,
multiple drivers can easily be implemented as necessary.
> - The device is not multi-process capable (only a single process can
> communicate with it).
How is that different from other half-duplex network interfaces? The
driver needs to take care of appropriate locking of its operations, and
the socket subsystem should make sure packets get queued accordingly.
> - There are SX1276 LoRa modules with a build-in protocol (LoRaWAN and
> RAW) via a serial connection only, again complete different API compared
> the SX1276 chip. Software updates for this devices are difficult.
Drivers for such modules are already demonstrated in this series.
> - I am even convinced that the LoRaWAN protocol with the concentrator
> concept is not good, the peer to peer communication and a standard MQTT
> gateway (what we do) is way more flexible.
Your opinion in all honors, LoRaWAN is an open specification, whereas
yours is closed. So whatever benefits yours may have, that way you're
unlikely to make LoRaWAN go away. People are rolling out LoRaWAN
gateways, and therefore the question is not which protocol is more
flexible but rather how all of them can be enabled equally for those
that want to use them.
> For all this reasons, I feel a user level driver task implementation is
> way more flexible. I did a lot of work/enhancements on the SX1276 link
> level driver from Semtech, it is available and maintained on mbed.org
> and supports mbed-os, Arduino and is prepared for Linux.
> https://os.mbed.com/users/Helmut64/code/SX1276GenericLib/
>
> Protocols e.g. our RadioShuttle LoRa peer to peer protocol or the
> LoRaWAN protocol can run on top of the SX1276GenericLib. We may should
> focus on such a driver library getting supported for multiple OS's (Win,
> Linux, mbed, Ardino, etc.)
>
> Again I feel a Linux kernel device driver for the SX1276 chip make
> little sense for me.
Well, feel free to use spidev if you prefer. As I pointed out, the Linux
spi maintainers don't seem to share your view - instead of white-listing
chipsets for concrete kernel drivers (as you appear to suggest here)
they rather black-list chipsets to be "allowed" to use spidev if all
else fails.
Having already done part of the implementation work, for me the question
is not whether to do a kernel driver but how to do it properly, keeping
all corner cases in mind, such as non-standard protocols like yours.
It's great that you're writing an Open Source library for mbed, but its
name indicates that it offers no abstraction like proposed here.
I'll drop you from v2 then, to not bother you about this Linux kernel
implementation anymore.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Dear Andreas,
here are a few comments in response to your answer.
The SX1276 can do MTU sizes up to 2048 bytes, there is a FIFO and
corresponding interrupts to handle this, details are in datasheet.
Regarding the LoRaWAN concept, when companies like Semtech and IBM
designing such a thing, and other companies (Stackforce) implementing
the code for it. This does not mean automatically that it is good for
most use cases. Specifically the idea of the concentrator has major
limits. Check out our article and picture: "Protocols: LoRaWANTM vs
RadioShuttle" at http://www.radioshuttle.de/en/radioshuttle-2/protocol/
Again my opinion is that that such a specific driver is not needed in
the kernel.
I am happy to provide further feedback, when you have questions about
the SX1276Generic driver which I maintain, I am available to discuss
this. And again the open source driver works on mbed, arduino and can be
easily adopted to Linux user space and other environments.
Regards
Helmut
On 11/07/18 04:07, Andreas Färber wrote:
> Dear Helmut,
>
> Am 05.07.2018 um 12:43 schrieb Helmut Tschemernjak:
>> I put the kernel support for the SX1276 LoRa chip in question. I don’t
>> think that this kind of device should be in the Linux kernel.
> Thanks for sharing your opinion.
>
>> Here are a few facts to consider:
>> - A LoRa transaction is very slow (e.g. SF7 needs about 210 ms, for
>> SF12 6280 ms) for 12 bytes user data with acknowledge.
> Where do you see a problem? If you look at my SX1276 patch, you will
> find that it queues a work item for the transmission (asynchronously)
> and has an interrupt handler to get notified of the TX interrupt. It
> surely won't get quicker in userspace - and as you point out, we're not
> speaking about DPDK performance here, so no need for polling.
>
>> - There are many different implementations for the antenna switch,
>> switching between receiving/sending, PA-BOOST, 433, 868/915 MHz. (I know
>> SX1276 Heltec ESP32, SX1276 Murata, RFM95-(1276), SX1276 Heltec
>> STM32-L4) they are all different regarding this.
> Yes, and as demonstrated with this patch series, the kernel is well able
> to handle this variety of interfaces. I would say better than userspace!
>
> As noted, I've tested both 868 MHz and 433 MHz modules. My SX1276 driver
> uses the Device Tree (radio-frequency) to configure the driver when this
> was a property of the module. RN2483 by contrast supports two, so a
> netlink interface was suggested to configure this at runtime.
>
>> - The LoRa chip device ID is only 8-bit which is not sufficient for
>> larger networks, e.g. our RadioShuttle protocol uses compressed 32-bit
>> device IDs.
> What does that have to do with anything? The whole reason that you're
> CC'ed on this patchset is to make sure that you or someone else can
> implement your custom protocol on top of the APIs being proposed. So
> my-protocol-is-better-than-theirs is no argument against this project.
>
> The way I understand LoRa and thus designed the struct sockaddr_lora is
> that there is no ID involved for LoRa at all - it is essentially a
> broadcast. The 8-byte (not 8-bit) EUIs only come into play for LoRaWAN
> AFAIU. Am I missing anything?
>
> LoRaWAN, RadioShuttle, RadioHead and LR Base all have their own ways of
> addressing - the question here is how to model that in Linux, whether as
> one PF_LORA with different protocol options and a growing union
> lora_addr inside sockaddr_lora covering all of them, or a protocol
> family (requiring global number allocation) for each one of them.
>
>> - The chip can do MTU sizes up to 2048 bytes, most protocols use less
>> than 250 bytes.
> How would 2048 bytes work? The FIFO buffer fits a maximum of 256 bytes.
>
> IIRC the recommended MTU is also restricted by the airtime, i.e. SF,
> bandwidth, preamble and other factors...
>
> Some UART based modules allow a maximum of 64 bytes.
>
>> - Applications do on-the-fly channel and spreading factor switching
>> which makes it more difficult for the configuration.
> That's exactly the question of how to implement individual options -
> Device Tree would be fixed for the system's hardware, netlink would be
> user-configurable for the whole network device, whereas socket options
> would be per socket/application, and ioctls might be yet another
> implementation layer.
>
>> - The chip supports Lora modulation as well as FSK, GFSK, MSK, GMSK,
>> and OOK modulation, for some use cases the other modulations are of
>> interest, e.g. to communicated with other devices.
> Yes, therefore I raised it in the cover letter as open point 5).
>
>> - Power saving modes, like sleep, suspend may be required for battery
>> operation.
> The Linux kernel can deal with that much better than userspace. There's
> pm hooks that one could implement. And SX1276 returns to standby mode
> whenever an operation such as TX is completed. In sleep mode not all
> settings get preserved, so they would need to be saved in the private
> struct and restored on resume.
>
>> - Cad detection is very flexible and can be differently used.
>>
>> - LoRa preamble may be different depending on the protocol used.
>>
>> - The new Lora chip SX1262 / 68 (successor of the SX1276) has different
>> hardware and all different registers, it is driver incompatible with the
>> SX1276. It needs an entire new driver.
> Not unexpected, same as SX1301 being different. As this series shows,
> multiple drivers can easily be implemented as necessary.
>
>> - The device is not multi-process capable (only a single process can
>> communicate with it).
> How is that different from other half-duplex network interfaces? The
> driver needs to take care of appropriate locking of its operations, and
> the socket subsystem should make sure packets get queued accordingly.
>
>> - There are SX1276 LoRa modules with a build-in protocol (LoRaWAN and
>> RAW) via a serial connection only, again complete different API compared
>> the SX1276 chip. Software updates for this devices are difficult.
> Drivers for such modules are already demonstrated in this series.
>
>> - I am even convinced that the LoRaWAN protocol with the concentrator
>> concept is not good, the peer to peer communication and a standard MQTT
>> gateway (what we do) is way more flexible.
> Your opinion in all honors, LoRaWAN is an open specification, whereas
> yours is closed. So whatever benefits yours may have, that way you're
> unlikely to make LoRaWAN go away. People are rolling out LoRaWAN
> gateways, and therefore the question is not which protocol is more
> flexible but rather how all of them can be enabled equally for those
> that want to use them.
>
>> For all this reasons, I feel a user level driver task implementation is
>> way more flexible. I did a lot of work/enhancements on the SX1276 link
>> level driver from Semtech, it is available and maintained on mbed.org
>> and supports mbed-os, Arduino and is prepared for Linux.
>> https://os.mbed.com/users/Helmut64/code/SX1276GenericLib/
>>
>> Protocols e.g. our RadioShuttle LoRa peer to peer protocol or the
>> LoRaWAN protocol can run on top of the SX1276GenericLib. We may should
>> focus on such a driver library getting supported for multiple OS's (Win,
>> Linux, mbed, Ardino, etc.)
>>
>> Again I feel a Linux kernel device driver for the SX1276 chip make
>> little sense for me.
> Well, feel free to use spidev if you prefer. As I pointed out, the Linux
> spi maintainers don't seem to share your view - instead of white-listing
> chipsets for concrete kernel drivers (as you appear to suggest here)
> they rather black-list chipsets to be "allowed" to use spidev if all
> else fails.
>
> Having already done part of the implementation work, for me the question
> is not whether to do a kernel driver but how to do it properly, keeping
> all corner cases in mind, such as non-standard protocols like yours.
> It's great that you're writing an Open Source library for mbed, but its
> name indicates that it offers no abstraction like proposed here.
>
> I'll drop you from v2 then, to not bother you about this Linux kernel
> implementation anymore.
>
> Regards,
> Andreas
>
--
best regards / mit freundlichen Gruessen,
Helmut Tschemernjak
HELIOS Software GmbH
Steinriede 3
30827 Garbsen (Hannover)
Phone: +49-5131-709320
Fax: +49-5131-709325
Internet Mail: [email protected]
Web: http://www.helios.de
Google+: http://helios.de/helmut
> Subject: [RFC net-next 00/15] net: A socket API for LoRa
>
> Hello,
>
> LoRa is a long-range, low-power wireless technology by
> Semtech.
> Unlike other LPWAN technologies, users don't need to rely
> on infrastructure
> providers and SIM cards and expensive subscription plans,
> they can set up
> their own gateways. Modules, adapters and evaluation
> boards are available
> from a large number of vendors.
>
> Many vendors also make available Open Source software
> examples on GitHub.
> But when taking a closer look, many of them combine
> licenses in ways that are
> not redistributable. My reports have remained without
> response or solution.
>
> https://github.com/ernstdevreede/lmic_pi/issues/2
> https://github.com/Snootlab/lmic_chisterapi/issues/2
> https://github.com/Snootlab/lora_chisterapi/issues/2
>
> Another issue was that most such projects around the
> Raspberry Pi make use of
> spidev to communicate with the Semtech chipsets from
> userspace. The Linux spi
> maintainers have chosen to greet any such users of spidev
> with a friendly
> WARN_ON(), preferring in-kernel spi drivers and white-
> listing individual
> devices only.
>
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin
> ux.git/tree/drivers/spi/spidev.c?h=v4.17#n722
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin
> ux.git/tree/drivers/spi/spidev.c?h=v4.17#n667
>
> Also I don't quite see the point in having userspace probe
> what SPI devices
> are connected to a generic spidev driver when we have an
> easy Device Tree
> hardware description on arm/arm64 that could give us that
> info.
>
> I raised the topic during Q&A of a FOSDEM 2017 talk (cut off
> at the end
> of the video) but unfortunately found no one to collaborate
> on this.
>
> https://archive.fosdem.org/2017/schedule/event/lorawan/
>
> Instead of porting from wiringPi to a differently licensed
> GPIO library
> and dealing with seemingly unmaintained LoRaWAN code
> dumps, I started a
> spi kernel driver for SX1276 back in 2016. But obviously a
> kernel driver
> isn't too helpful without a userspace API to send and receive
> packets.
>
> This patchset, updated from 2017 and extended, is
> implementing kernel drivers
> for various LoRa chipsets and modules. As API I'm proposing
> a PF_LORA socket
> implementation. Why? LoRa uses data packets with headers
> and checksums
> and differing MTUs and multiple protocols layered on top of
> it. Apart from
> simple headers for addressing used by RadioHead library
> and IMST's LoRa P2P
> protocol, the main use case (not implemented in this
> patchset) is expected
> to be LoRaWAN. And LoRaWAN has competing proprietary
> protocols, such as
> Link Labs' Symphony Link or GlobalSat M.O.S.T. or
> RadioShuttle, that might
> at some point want to adopt a standard API for their
> implementations, too.
>
> Ready-made LoRa hardware modules come in three flavors,
> a) with SPI access to the underlying Semtech chipsets,
> needing a software
> implementation of e.g. LoRaWAN protocol stack (i.e., a
> soft MAC),
> b) with a custom, often UART based interface and a pre-
> certified LoRaWAN
> protocol stack already integrated (i.e., hard/full MAC), and
> c) with a microcontroller that serves not only for the
> protocol stack but
> also as application processor, not offering a ready-made
> interface.
>
> This patchset focuses on option a). An SX1276 based
> LoRaWAN stack appeared
> to be the project of Jian-Hong Pan and is not included here.
> This patchset also includes drivers for b), from text based AT
> commands to
> a binary SLIP based HCI protocol.
> Hardware examples for c) are Murata CMWX1ZZABZ-078
> and RAK813.
>
> This patchset is clearly not ready for merging, but is being
> submitted for
> discussion, as requested by Jiri, in particular of the design
> choices:
>
> 1) PF_LORA/AF_LORA and associated identifiers are
> proposed to represent
> this technology. While for an SX1276 - case a) above - it
> might work to
> layer LoRaWAN as a protocol option for PF_LORA and add
> LoRaWAN address
> fields to the union in my sockaddr_lora, how would that
> work for devices
> that only support LoRaWAN but not pure LoRa? Do we
> need both AF_LORA and
> AF_LORAWAN, or just a separate ETH_P_LORAWAN or
> ARPHRD_LORAWAN?
>
> 2) PF_LORA is used with SOCK_DGRAM here. The
> assumption is that RAW mode
> would be DGRAM plus preamble plus optional checksum.
>
> 3) Only the transmit path is partially implemented already.
> The assumption
> is that the devices should go into receive mode by default
> and only
> interrupt that when asked to transmit.
>
> 4) Some hardware settings need to be supplied externally,
> such as the radio
> frequency for some modules, but many others can be
> runtime-configured,
> such as Spreading Factor, Bandwidth, Sync Word, or which
> antenna to use.
> What settings should be implemented as socket option vs.
> netlink layer
> vs. ioctl vs. sysfs? What are the criteria to apply?
>
> 5) Many of the modules support multiple modes, such as
> LoRa, LoRaWAN and FSK.
> Lacking a LoRaWAN implementation, I am currently
> switching them into LoRa
> mode at probe time wherever possible. How do we deal
> with that properly?
>
> a) Is there any precedence from the Wifi world for
> dynamically selecting
> between our own trusted Open Source implementation
> vs. hardware/firmware
> accelerated and/or certified implementations?
>
> b) Would a proof of concept for FSK (non-LoRa) modes be
> required for
> merging any LoRa driver for chipsets that support both?
> Or is there any
> facility or design guidelines that would allow us to focus
> on LoRa and
> LoRaWAN and leave non-LoRa radio modes to later
> contributors?
Down the line I think we should also plan for a CRDA style regdb somewhere in the path for raw LoRa transceivers operating as softMAC, much like with WiFi.
LoRa radios used in Gateway devices are typically relatively high power (capable of 27dBm) and operate in bands with certain restrictions, eg the EU has keep out areas within 868MHz for alarms and SRD devices must abide by certain duty cycle restrictions, there are also maximum powers to consider for sub-bands. (ETSI EN 300 220-2 V3.2.1, Bands K, L, M, N, P, Q)
The certified AT style modules will (should) already have this regulatory data baked in so it only applied to situations where we drive the transceivers directly, but it wouldn't hurt to check that the frequency being asked to transmit on doesn't spill into a restricted band.
> As evident by the many questions, this is my first deep dive
> into the Linux
> net subsystem. It's also my first experiments with the new
> serdev subsystem,
> so in particular the receive paths will need some review and
> optimizations.
>
> This patchset was developed and tested mainly as KMP,
> originally at
> https://github.com/afaerber/lora-modules. It was recently
> transformed into a
> linux-next based tree, still mostly tested on our openSUSE
> Tumbleweed kernel
> with a differing AF_LORA value below current AF_MAX limit.
>
> Some corresponding Device Tree Overlays have been
> collected here:
> https://github.com/afaerber/dt-overlays
>
> Only European models for 868 MHz and 433 MHz could be
> tested when available.
> Thanks to all companies and people that have supported
> this project so far.
>
> Have a lot of fun!
>
> Cheers,
> Andreas
>
> Cc: Jian-Hong Pan <[email protected]>
> Cc: Jiri Pirko <[email protected]>
> Cc: Marcel Holtmann <[email protected]>
> Cc: "David S. Miller" <[email protected]>
> Cc: Matthias Brugger <[email protected]>
> Cc: Konstantin Böhm <[email protected]>
> Cc: Jan Jongboom <[email protected]>
> Cc: Janus Piwek <[email protected]>
> Cc: Michael Röder <[email protected]>
> Cc: Dollar Chen (陳義元) <[email protected]>
> Cc: Ken Yu (禹凯) <[email protected]>
> Cc: Jon Ortego <[email protected]>
> Cc: [email protected]
> Cc: Ben Whitten <[email protected]>
> Cc: Brian Ray <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: Alexander Graf <[email protected]>
> Cc: Michal Kubeček <[email protected]>
> Cc: Rob Herring <[email protected]>
> Cc: [email protected]
> Cc: [email protected]
> Cc: Steve deRosier <[email protected]>
> Cc: Mark Brown <[email protected]>
> Cc: [email protected]
>
> Andreas Färber (15):
> net: Reserve protocol numbers for LoRa
> net: lora: Define sockaddr_lora
> net: lora: Add protocol numbers
> net: Add lora subsystem
> HACK: net: lora: Deal with .poll_mask in 4.18-rc2
> net: lora: Prepare for device drivers
> net: lora: Add Semtech SX1276
> net: lora: sx1276: Add debugfs
> net: lora: Prepare EUI helpers
> net: lora: Add Microchip RN2483
> net: lora: Add IMST WiMOD
> net: lora: Add USI WM-SG-SM-42
> net: lora: Prepare RAK RAK811
> net: lora: Prepare Semtech SX1257
> net: lora: Add Semtech SX1301
>
> drivers/net/Makefile | 1 +
> drivers/net/lora/Kconfig | 72 ++++
> drivers/net/lora/Makefile | 32 ++
> drivers/net/lora/dev.c | 125 ++++++
> drivers/net/lora/rak811.c | 219 +++++++++++
> drivers/net/lora/rn2483.c | 344 +++++++++++++++++
> drivers/net/lora/rn2483.h | 40 ++
> drivers/net/lora/rn2483_cmd.c | 130 +++++++
> drivers/net/lora/sx1257.c | 96 +++++
> drivers/net/lora/sx1276.c | 740
> ++++++++++++++++++++++++++++++++++++
> drivers/net/lora/sx1301.c | 446
> ++++++++++++++++++++++
> drivers/net/lora/usi.c | 411
> ++++++++++++++++++++
> drivers/net/lora/wimod.c | 597
> +++++++++++++++++++++++++++++
> include/linux/lora/dev.h | 44 +++
> include/linux/lora/skb.h | 29 ++
> include/linux/socket.h | 4 +-
> include/uapi/linux/if_arp.h | 1 +
> include/uapi/linux/if_ether.h | 1 +
> include/uapi/linux/lora.h | 24 ++
> net/Kconfig | 1 +
> net/Makefile | 1 +
> net/lora/Kconfig | 15 +
> net/lora/Makefile | 8 +
> net/lora/af_lora.c | 152 ++++++++
> net/lora/af_lora.h | 13 +
> net/lora/dgram.c | 297 +++++++++++++++
> security/selinux/hooks.c | 4 +-
> security/selinux/include/classmap.h | 4 +-
> 28 files changed, 3848 insertions(+), 3 deletions(-)
> create mode 100644 drivers/net/lora/Kconfig
> create mode 100644 drivers/net/lora/Makefile
> create mode 100644 drivers/net/lora/dev.c
> create mode 100644 drivers/net/lora/rak811.c
> create mode 100644 drivers/net/lora/rn2483.c
> create mode 100644 drivers/net/lora/rn2483.h
> create mode 100644 drivers/net/lora/rn2483_cmd.c
> create mode 100644 drivers/net/lora/sx1257.c
> create mode 100644 drivers/net/lora/sx1276.c
> create mode 100644 drivers/net/lora/sx1301.c
> create mode 100644 drivers/net/lora/usi.c
> create mode 100644 drivers/net/lora/wimod.c
> create mode 100644 include/linux/lora/dev.h
> create mode 100644 include/linux/lora/skb.h
> create mode 100644 include/uapi/linux/lora.h
> create mode 100644 net/lora/Kconfig
> create mode 100644 net/lora/Makefile
> create mode 100644 net/lora/af_lora.c
> create mode 100644 net/lora/af_lora.h
> create mode 100644 net/lora/dgram.c
>
> --
> 2.16.4
+ linux-wireless + Stefan + Seth
Am 11.07.2018 um 17:21 schrieb Ben Whitten:
>> This patchset is clearly not ready for merging, but is being
>> submitted for
>> discussion, as requested by Jiri, in particular of the design
>> choices:
>>
>> 1) PF_LORA/AF_LORA and associated identifiers are
>> proposed to represent
>> this technology. While for an SX1276 - case a) above - it
>> might work to
>> layer LoRaWAN as a protocol option for PF_LORA and add
>> LoRaWAN address
>> fields to the union in my sockaddr_lora, how would that
>> work for devices
>> that only support LoRaWAN but not pure LoRa? Do we
>> need both AF_LORA and
>> AF_LORAWAN, or just a separate ETH_P_LORAWAN or
>> ARPHRD_LORAWAN?
>>
>> 2) PF_LORA is used with SOCK_DGRAM here. The
>> assumption is that RAW mode
>> would be DGRAM plus preamble plus optional checksum.
>>
>> 3) Only the transmit path is partially implemented already.
>> The assumption
>> is that the devices should go into receive mode by default
>> and only
>> interrupt that when asked to transmit.
>>
>> 4) Some hardware settings need to be supplied externally,
>> such as the radio
>> frequency for some modules, but many others can be
>> runtime-configured,
>> such as Spreading Factor, Bandwidth, Sync Word, or which
>> antenna to use.
>> What settings should be implemented as socket option vs.
>> netlink layer
>> vs. ioctl vs. sysfs? What are the criteria to apply?
>>
>> 5) Many of the modules support multiple modes, such as
>> LoRa, LoRaWAN and FSK.
>> Lacking a LoRaWAN implementation, I am currently
>> switching them into LoRa
>> mode at probe time wherever possible. How do we deal
>> with that properly?
>>
>> a) Is there any precedence from the Wifi world for
>> dynamically selecting
>> between our own trusted Open Source implementation
>> vs. hardware/firmware
>> accelerated and/or certified implementations?
>>
>> b) Would a proof of concept for FSK (non-LoRa) modes be
>> required for
>> merging any LoRa driver for chipsets that support both?
>> Or is there any
>> facility or design guidelines that would allow us to focus
>> on LoRa and
>> LoRaWAN and leave non-LoRa radio modes to later
>> contributors?
>
> Down the line I think we should also plan for a CRDA style regdb somewhere in the path for raw LoRa transceivers operating as softMAC, much like with WiFi.
Yes, I had raised the topic of wireless-regdb for Stefan's conference -
currently it seems to only cover 2.4 GHz, 5 GHz and 60 GHz. Not sure if
we can easily extend that to cover 433 MHz, 868 MHz, 915 MHz and 923 MHz
bands or whether we'd just need something similar... Is 802.15.4 able to
share this database with Wifi?
An argument to share with Wifi might be that Semtech's SX1280 and SX1281
2.4 GHz transceivers claim to support LoRa modulation, too. Having two
different regulatory DBs interact with LoRa drivers seems a bad idea,
and duplicating 2.4 GHz into a new DB doesn't sound appealing either.
https://www.semtech.com/products/wireless-rf/24-ghz-transceivers
Meanwhile my attempt to play with netlink during SUSE Hackweek has been
going slow and I could use some guidance or a volunteer to contribute: I
have a bare skeleton of registration, commands, attributes and multicast
groups, but no plan yet how to connect that to the actual drivers to
query or apply the settings...
https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-lora.git/tree/net/lora/netlink.c?h=lora-next
> LoRa radios used in Gateway devices are typically relatively high power (capable of 27dBm) and operate in bands with certain restrictions, eg the EU has keep out areas within 868MHz for alarms and SRD devices must abide by certain duty cycle restrictions, there are also maximum powers to consider for sub-bands. (ETSI EN 300 220-2 V3.2.1, Bands K, L, M, N, P, Q)
> The certified AT style modules will (should) already have this regulatory data baked in so it only applied to situations where we drive the transceivers directly, but it wouldn't hurt to check that the frequency being asked to transmit on doesn't spill into a restricted band.
Some do have configuration options that will need to be set or checked.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
> Subject: Re: [RFC net-next 00/15] net: A socket API for LoRa
>
> + linux-wireless + Stefan + Seth
>
> Am 11.07.2018 um 17:21 schrieb Ben Whitten:
> >> This patchset is clearly not ready for merging, but is being
> >> submitted for
> >> discussion, as requested by Jiri, in particular of the design
> >> choices:
> >>
> >> 1) PF_LORA/AF_LORA and associated identifiers are
> >> proposed to represent
> >> this technology. While for an SX1276 - case a) above - it
> >> might work to
> >> layer LoRaWAN as a protocol option for PF_LORA and
> add
> >> LoRaWAN address
> >> fields to the union in my sockaddr_lora, how would that
> >> work for devices
> >> that only support LoRaWAN but not pure LoRa? Do we
> >> need both AF_LORA and
> >> AF_LORAWAN, or just a separate ETH_P_LORAWAN or
> >> ARPHRD_LORAWAN?
> >>
> >> 2) PF_LORA is used with SOCK_DGRAM here. The
> >> assumption is that RAW mode
> >> would be DGRAM plus preamble plus optional
> checksum.
> >>
> >> 3) Only the transmit path is partially implemented
> already.
> >> The assumption
> >> is that the devices should go into receive mode by
> default
> >> and only
> >> interrupt that when asked to transmit.
> >>
> >> 4) Some hardware settings need to be supplied
> externally,
> >> such as the radio
> >> frequency for some modules, but many others can be
> >> runtime-configured,
> >> such as Spreading Factor, Bandwidth, Sync Word, or
> which
> >> antenna to use.
> >> What settings should be implemented as socket option
> vs.
> >> netlink layer
> >> vs. ioctl vs. sysfs? What are the criteria to apply?
> >>
> >> 5) Many of the modules support multiple modes, such as
> >> LoRa, LoRaWAN and FSK.
> >> Lacking a LoRaWAN implementation, I am currently
> >> switching them into LoRa
> >> mode at probe time wherever possible. How do we
> deal
> >> with that properly?
> >>
> >> a) Is there any precedence from the Wifi world for
> >> dynamically selecting
> >> between our own trusted Open Source
> implementation
> >> vs. hardware/firmware
> >> accelerated and/or certified implementations?
> >>
> >> b) Would a proof of concept for FSK (non-LoRa) modes
> be
> >> required for
> >> merging any LoRa driver for chipsets that support
> both?
> >> Or is there any
> >> facility or design guidelines that would allow us to
> focus
> >> on LoRa and
> >> LoRaWAN and leave non-LoRa radio modes to later
> >> contributors?
> >
> > Down the line I think we should also plan for a CRDA style
> regdb somewhere in the path for raw LoRa transceivers
> operating as softMAC, much like with WiFi.
>
> Yes, I had raised the topic of wireless-regdb for Stefan's
> conference -
> currently it seems to only cover 2.4 GHz, 5 GHz and 60 GHz.
> Not sure if
> we can easily extend that to cover 433 MHz, 868 MHz, 915
> MHz and 923 MHz
> bands or whether we'd just need something similar... Is
> 802.15.4 able to
> share this database with Wifi?
Well the README in the wireless-regdb doesn't bind itself to 80211, there are references to the other ETSI EN specs so this would be the place rather than duplicating.
There would need a bit of additional information to capture duty-cycle requirements, however the SRD spec states the maximum bandwidths can be 'The whole band', so with a flag set we could hijack this band information for duty-cycle.
I am unsure if 802.15.4 uses this database, most of the naming seems geared towards 80211, nl80211, cfg80211 so perhaps we need our own versions of these with a common component, I hope the maintainers can give some guidance here.
> An argument to share with Wifi might be that Semtech's
> SX1280 and SX1281
> 2.4 GHz transceivers claim to support LoRa modulation, too.
> Having two
> different regulatory DBs interact with LoRa drivers seems a
> bad idea,
> and duplicating 2.4 GHz into a new DB doesn't sound
> appealing either.
Well I'm not sure if the modulation affects regulatory information, just bands, power, and techniques like DFS.
As these chips are 2.4GHz only I expect they are bound by the existing regulatory information we would just need a path to access it.
> https://www.semtech.com/products/wireless-rf/24-ghz-
> transceivers
>
> Meanwhile my attempt to play with netlink during SUSE
> Hackweek has been
> going slow and I could use some guidance or a volunteer to
> contribute: I
> have a bare skeleton of registration, commands, attributes
> and multicast
> groups, but no plan yet how to connect that to the actual
> drivers to
> query or apply the settings...
Happy to help, I will be starting from zero on netlink but I can contribute my existing work incorporating Marks comments for sx1301 etal.
> https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/li
> nux-lora.git/tree/net/lora/netlink.c?h=lora-next
>
> > LoRa radios used in Gateway devices are typically relatively
> high power (capable of 27dBm) and operate in bands with
> certain restrictions, eg the EU has keep out areas within
> 868MHz for alarms and SRD devices must abide by certain
> duty cycle restrictions, there are also maximum powers to
> consider for sub-bands. (ETSI EN 300 220-2 V3.2.1, Bands K,
> L, M, N, P, Q)
>
> > The certified AT style modules will (should) already have
> this regulatory data baked in so it only applied to situations
> where we drive the transceivers directly, but it wouldn't
> hurt to check that the frequency being asked to transmit on
> doesn't spill into a restricted band.
>
> Some do have configuration options that will need to be set
> or checked.
>
> Regards,
> Andreas
>
> --
> SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Felix Imendörffer, Jane Smithard, Graham Norton
> HRB 21284 (AG Nürnberg)
2018-07-18 19:28 GMT+08:00 Ben Whitten <[email protected]>:
>> Subject: Re: [RFC net-next 00/15] net: A socket API for LoRa
>>
>> + linux-wireless + Stefan + Seth
>>
>> Am 11.07.2018 um 17:21 schrieb Ben Whitten:
>> >> This patchset is clearly not ready for merging, but is being
>> >> submitted for
>> >> discussion, as requested by Jiri, in particular of the design
>> >> choices:
>> >>
>> >> 1) PF_LORA/AF_LORA and associated identifiers are
>> >> proposed to represent
>> >> this technology. While for an SX1276 - case a) above - it
>> >> might work to
>> >> layer LoRaWAN as a protocol option for PF_LORA and
>> add
>> >> LoRaWAN address
>> >> fields to the union in my sockaddr_lora, how would that
>> >> work for devices
>> >> that only support LoRaWAN but not pure LoRa? Do we
>> >> need both AF_LORA and
>> >> AF_LORAWAN, or just a separate ETH_P_LORAWAN or
>> >> ARPHRD_LORAWAN?
>> >>
>> >> 2) PF_LORA is used with SOCK_DGRAM here. The
>> >> assumption is that RAW mode
>> >> would be DGRAM plus preamble plus optional
>> checksum.
>> >>
>> >> 3) Only the transmit path is partially implemented
>> already.
>> >> The assumption
>> >> is that the devices should go into receive mode by
>> default
>> >> and only
>> >> interrupt that when asked to transmit.
>> >>
>> >> 4) Some hardware settings need to be supplied
>> externally,
>> >> such as the radio
>> >> frequency for some modules, but many others can be
>> >> runtime-configured,
>> >> such as Spreading Factor, Bandwidth, Sync Word, or
>> which
>> >> antenna to use.
>> >> What settings should be implemented as socket option
>> vs.
>> >> netlink layer
>> >> vs. ioctl vs. sysfs? What are the criteria to apply?
>> >>
>> >> 5) Many of the modules support multiple modes, such as
>> >> LoRa, LoRaWAN and FSK.
>> >> Lacking a LoRaWAN implementation, I am currently
>> >> switching them into LoRa
>> >> mode at probe time wherever possible. How do we
>> deal
>> >> with that properly?
>> >>
>> >> a) Is there any precedence from the Wifi world for
>> >> dynamically selecting
>> >> between our own trusted Open Source
>> implementation
>> >> vs. hardware/firmware
>> >> accelerated and/or certified implementations?
>> >>
>> >> b) Would a proof of concept for FSK (non-LoRa) modes
>> be
>> >> required for
>> >> merging any LoRa driver for chipsets that support
>> both?
>> >> Or is there any
>> >> facility or design guidelines that would allow us to
>> focus
>> >> on LoRa and
>> >> LoRaWAN and leave non-LoRa radio modes to later
>> >> contributors?
>> >
>> > Down the line I think we should also plan for a CRDA style
>> regdb somewhere in the path for raw LoRa transceivers
>> operating as softMAC, much like with WiFi.
>>
>> Yes, I had raised the topic of wireless-regdb for Stefan's
>> conference -
>> currently it seems to only cover 2.4 GHz, 5 GHz and 60 GHz.
>> Not sure if
>> we can easily extend that to cover 433 MHz, 868 MHz, 915
>> MHz and 923 MHz
>> bands or whether we'd just need something similar... Is
>> 802.15.4 able to
>> share this database with Wifi?
>
> Well the README in the wireless-regdb doesn't bind itself to 80211, there are references to the other ETSI EN specs so this would be the place rather than duplicating.
> There would need a bit of additional information to capture duty-cycle requirements, however the SRD spec states the maximum bandwidths can be 'The whole band', so with a flag set we could hijack this band information for duty-cycle.
> I am unsure if 802.15.4 uses this database, most of the naming seems geared towards 80211, nl80211, cfg80211 so perhaps we need our own versions of these with a common component, I hope the maintainers can give some guidance here.
>
>> An argument to share with Wifi might be that Semtech's
>> SX1280 and SX1281
>> 2.4 GHz transceivers claim to support LoRa modulation, too.
>> Having two
>> different regulatory DBs interact with LoRa drivers seems a
>> bad idea,
>> and duplicating 2.4 GHz into a new DB doesn't sound
>> appealing either.
>
> Well I'm not sure if the modulation affects regulatory information, just bands, power, and techniques like DFS.
> As these chips are 2.4GHz only I expect they are bound by the existing regulatory information we would just need a path to access it.
>
>> https://www.semtech.com/products/wireless-rf/24-ghz-
>> transceivers
>>
>> Meanwhile my attempt to play with netlink during SUSE
>> Hackweek has been
>> going slow and I could use some guidance or a volunteer to
>> contribute: I
>> have a bare skeleton of registration, commands, attributes
>> and multicast
>> groups, but no plan yet how to connect that to the actual
>> drivers to
>> query or apply the settings...
>
> Happy to help, I will be starting from zero on netlink but I can contribute my existing work incorporating Marks comments for sx1301 etal.
>
>> https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/li
>> nux-lora.git/tree/net/lora/netlink.c?h=lora-next
Is this the repository used for the LoRa subsystem now?!!!
If it is, than great!
As the previous mails, I am trying to implement the LoRaWAN
specification as the soft MAC as the MAC layer over LoRa PHY.
This is the the talk about LoRaWAN class module I gave in Netdev Conf
https://www.slideshare.net/chienhungpan/lorawan-class-module-and-subsystem
The expectation is:
socket APIs:
send and receive the data
------------------------------------------------------------
LoRaWAN class module implements MAC:
append the header/footer, encryption/decryption, timing slot and MAC commands
------------------------------------------------------------
LoRa device drivers:
send and receive the messages for MAC layer
------------------------------------------------------------
LoRa devices
Is it possible that combine the LoRaWAN class module I implemented?
I start from the simplest class A end device's behavior, especially
the timing slot.
Also the encryption/decryption for uplink/downlink data messages.
I can send it as patches.
However, I have 2 problems right now.
1. Which Address and Protocol Family should we use? PF_LORA or PF_LORAWAN?
2. To test the LoRaWAN class module, adding more procedures in LoRa
device drivers to register as a LoRaWAN device is needed.
Regards,
Jian-Hong Pan
>> > LoRa radios used in Gateway devices are typically relatively
>> high power (capable of 27dBm) and operate in bands with
>> certain restrictions, eg the EU has keep out areas within
>> 868MHz for alarms and SRD devices must abide by certain
>> duty cycle restrictions, there are also maximum powers to
>> consider for sub-bands. (ETSI EN 300 220-2 V3.2.1, Bands K,
>> L, M, N, P, Q)
>>
>> > The certified AT style modules will (should) already have
>> this regulatory data baked in so it only applied to situations
>> where we drive the transceivers directly, but it wouldn't
>> hurt to check that the frequency being asked to transmit on
>> doesn't spill into a restricted band.
>>
>> Some do have configuration options that will need to be set
>> or checked.
>>
>> Regards,
>> Andreas
>>
>> --
>> SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
>> GF: Felix Imendörffer, Jane Smithard, Graham Norton
>> HRB 21284 (AG Nürnberg)
Hi Jian-Hong,
Am 02.08.2018 um 09:52 schrieb Jian-Hong Pan:
> 2018-07-18 19:28 GMT+08:00 Ben Whitten <[email protected]>:
>>> 1) PF_LORA/AF_LORA and associated identifiers are
>>> proposed to represent
>>> this technology. While for an SX1276 [...] it
>>> might work to
>>> layer LoRaWAN as a protocol option for PF_LORA and
> add
>>> LoRaWAN address
>>> fields to the union in my sockaddr_lora, how would that
>>> work for devices
>>> that only support LoRaWAN but not pure LoRa? Do we
>>> need both AF_LORA and
>>> AF_LORAWAN, or just a separate ETH_P_LORAWAN or
>>> ARPHRD_LORAWAN?
[...]
>>> Meanwhile my attempt to play with netlink during SUSE
>>> Hackweek has been
>>> going slow and I could use some guidance or a volunteer to
>>> contribute: I
>>> have a bare skeleton of registration, commands, attributes
>>> and multicast
>>> groups, but no plan yet how to connect that to the actual
>>> drivers to
>>> query or apply the settings...
>>
>> Happy to help, I will be starting from zero on netlink but I can contribute my existing work incorporating Marks comments for sx1301 etal.
>>
>>> https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/li
>>> nux-lora.git/tree/net/lora/netlink.c?h=lora-next
>
> Is this the repository used for the LoRa subsystem now?!!!
> If it is, than great!
Yes, my linux-lora.git contains this RFC patchset (modulo SX1276 fixes
spotted by kbuild bot) plus a new serdev driver for another module and
ongoing work by Ben and me for refactoring SX1301. It's monitored by the
0-day test service (kbuild bot).
As this patchset was an RFC and does not have any Acked-bys from
maintainers, the tree does not have a for-next branch integrated into
linux-next on basis of 4.18-rc1, but instead (like my personal GitHub
tree before) has a lora-next branch that rebases as patch queue on top
of linux-next for now.
The intent is to allow collaboration on getting things into a state that
I can later submit a clean (squashed) RFC v2 for review, with all issues
raised for this v1 addressed.
For contributing patches to my linux-lora.git I suggest to use
--subject-prefix="PATCH lora-next" to distinguish from net-next.
And I just realize I should add a MAINTAINERS entry in my tree to make
sure patches CC me, too. (I do monitor netdev for patches with subject
"lora", but chances are someone might omit that.)
> As the previous mails, I am trying to implement the LoRaWAN
> specification as the soft MAC as the MAC layer over LoRa PHY.
> This is the the talk about LoRaWAN class module I gave in Netdev Conf
> https://www.slideshare.net/chienhungpan/lorawan-class-module-and-subsystem
>
> The expectation is:
>
> socket APIs:
> send and receive the data
> ------------------------------------------------------------
> LoRaWAN class module implements MAC:
> append the header/footer, encryption/decryption, timing slot and MAC commands
> ------------------------------------------------------------
> LoRa device drivers:
> send and receive the messages for MAC layer
> ------------------------------------------------------------
> LoRa devices
Thanks for sharing your slides. We seem to be in alignment for the
abstract concept above. The devil is in the implementation details. ^.-
> Is it possible that combine the LoRaWAN class module I implemented?
Yes, as stated in this cover letter, I would love if you could help
merge your LoRaWAN implementation with my driver framework. Comparing
802.15.4 and 802.11, I think MAC code should go into net/maclorawan/ and
then is a fairly independent module, with you as maintainer.
> I start from the simplest class A end device's behavior, especially
> the timing slot.
> Also the encryption/decryption for uplink/downlink data messages.
> I can send it as patches.
>
> However, I have 2 problems right now.
> 1. Which Address and Protocol Family should we use? PF_LORA or PF_LORAWAN?
That was one of the questions I raised above. I now think we need both.
I'm not yet too deep into LoRaWAN, but from the AT command interfaces
I've seen there's confirmed and unconfirmed transmission modes that with
PF_LORAWAN might be mapped to SOCK_STREAM and SOCK_DGRAM. Or do you see
a way of doing both on a single PF_LORA SOCK_LORAWAN socket?
> 2. To test the LoRaWAN class module, adding more procedures in LoRa
> device drivers to register as a LoRaWAN device is needed.
No, I don't think so. There are (at least) two types of devices, LoRaWAN
and LoRa devices. The SX1276 is a pure LoRa device and thus should only
expose a LoRa network device. It'll be the task of the maclorawan module
to translate between the two layers, not the business of the device
driver; SX1276 should receive a ready-to-send LoRa skb. For Ethernet,
PF_INET and PF_INET6 don't need separate devices either, both use eth0.
What I do think we need is your struct lora_hw, maybe renamed to
lora_phy. That could be the missing piece for registration of the device
drivers with nllora module?
Note that similarly we'll also need an nllorawan module that needs to
work both with your maclorawan and with some of my device drivers that
implement the MAC in an MCU.
Additionally I've been looking into socket options at PF_LORA dgram
layer for some radio options, but discarded that again for lack of
precedence. Basically I wondered whether we could allow to choose SF,
bandwidth, etc. on socket level and then apply those settings before
sending one packet rather than expecting a global netlink operation that
affects all sockets for that interface.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Hi Jian-Hong,
Am 03.07.2018 um 17:11 schrieb Jian-Hong Pan:
> 2018-07-01 19:07 GMT+08:00 Andreas Färber <[email protected]>:
>> 2) PF_LORA is used with SOCK_DGRAM here. The assumption is that RAW mode
>> would be DGRAM plus preamble plus optional checksum.
>
> Is the preamble added by the hardware itself here or software here?
That depends on the driver. For SX127x and any SX127x based modules it
is added by hardware and a SOCK_RAW seems impossible. If you were to use
some SDR hardware, it would need to be done by software and might either
be done in the device driver for SOCK_DGRAM or by user with a SOCK_RAW.
Right now I don't see a practical use case for the latter. (CC Pieter)
>> 3) Only the transmit path is partially implemented already. The assumption
>> is that the devices should go into receive mode by default and only
>> interrupt that when asked to transmit.
>
> If it goes with LoRaWAN spec., end devices should be in idle (or
> called standby mode) by default. Unless the device is asked to be in
> transmitting or receiving timing slot.
Whatever the LoRaWAN spec says, in practice struct net_device_ops has an
ndo_start_xmit hook but no ndo_start_recv. All drivers that I've checked
start receiving via interrupts after ndo_open has set things up.
LoRa radio channels being half-duplex, we'd need to stop receiving in
ndo_start_xmit and re-start receiving in the TX interrupt handler AFAIU.
Yes, it's ugly - one reason I haven't implemented RX in sx1276 yet.
Are you suggesting to have dgram's recvmsg trigger the RX state somehow?
It gets access to the net_device and could access dev.h struct lora_priv
that we might extend to contain a LoRa-specific start_recv callback. And
probably rename it more uniquely to lora_dev_priv while at it.
The only other alternative I can think of would be to require the user
to perform some netlink operation, possibly wrapped in some lora_ API,
to actively put the device into receive mode. Doesn't sound desirable.
>> 4) Some hardware settings need to be supplied externally, such as the radio
>> frequency for some modules, but many others can be runtime-configured,
>> such as Spreading Factor, Bandwidth, Sync Word, or which antenna to use.
>> What settings should be implemented as socket option vs. netlink layer
>> vs. ioctl vs. sysfs? What are the criteria to apply?
>
> - We can have a pre-defined table according to LoRaWAN Regional Parameters.
> - Device driver declares the hardware's capability, for example
> frequency, TX power. And then registers as a LoRaWAN compatible
> device.
That sounds like a layering violation. We rather need to expose all
these tunable parameters individually at the LoRa layer, allowing the
LoRaWAN layer to configure them as it pleases. Not the other direction.
That still leaves my above question 4) open of how to implement each.
The use case I have in mind is this: User A opens a LoRaWAN socket and
using maclorawan sends a packet P1. Here the LoRaWAN Regional Parameters
and LoRaWAN Sync Word need to be applied.
User B then opens a pure LoRa socket and transmits a packet P1' with
different Sync Word, SF, BW, CR, etc.
Afterwards user A wants to send another packet P2 via LoRaWAN - this
needs to use the same LoRaWAN settings as before, not those used for
LoRa in between. Therefore I was thinking about socket-level options, as
netlink operations would be device-wide, with undesired side-effects.
Obviously in that scenario not both users can receive at the same time.
If there was a way to start receiving only when a user performs such a
socket operations, even this could be implemented - if incompatible
reception can get detected and if the packet gets delivered to both,
LoRa being broadcast.
> - LoRaWAN module handle the requests from upper layer or MAC commands
> from a gateway (netlink layer), than uses the pre-defined interface
> functions to set the parameters.
>
> LoRaWAN module will export the operation functions interface.
>
>> 5) Many of the modules support multiple modes, such as LoRa, LoRaWAN and FSK.
>> Lacking a LoRaWAN implementation, I am currently switching them into LoRa
>> mode at probe time wherever possible. How do we deal with that properly?
>
> - There are data rate tables defined in LoRaWAN Regional Parameters.
> Those contain which data rate uses LoRa mode or FSK mode.
That's missing my point, I fear. Independently of what LoRaWAN does, the
user needs to be able to send on the physical layer from a selection of
LoRa, GFSK, FSK, OOK, GMSK and MSK.
Supposedly Wireless M-Bus and IEEE 802.15.4 can be implemented via those
according to the SX1276 datasheet.
This opens a can of worms...
SX127x has a single channel, so I don't think there should be six
network interfaces lora0, gfsk0, fsk0, ook0, gmsk0 and msk0 exposed to
the user.
Having a lora0 interface speak non-LoRa modulations may be confusing,
but since the chip is sold as LoRa transceiver it might be acceptable.
SX130x has 8+2 channels, with IF9 dedicated to GFSK/FSK. It appears to
use one FIFO for receiving (up to 16 packets) and can only transmit one
packet at a time. So I think this should be one lora0 interface, too.
With a view to supporting non-LoRa RF chipsets such as Si4xxx (GFSK,
FSK, OOK) or nRF905 and nRF24L01+ (both GFSK) at a later date, I don't
think those modulations should be some netlink option on a PF_LORA
interface but cleanly distinguished as ETH_P_GFSK or something.
For example, the Chistera Pi HAT has both an RFM95W and an RFM22 module.
The next question arising is how the user would create such an skb. Just
like I was hesitant about PF_LORAWAN originally, I'd like to avoid
polluting the PF_ number space for each of these. Maybe have one PF_FSK
as equivalent to PF_LORA and then have either a socket option or
sockaddr field to select the modulation variants? Not sure how exactly
those others differ from each other, that's why I tried to postpone the
FSK topic and to focus on LoRa first - b) below.
At this point we could also argue whether we need a PF_LORA at all or
rather just some generic PF_RADIO with lots of options stored in its
sockaddr.
One criteria will be whether we need multiple protocol options per
modulation type or just one. For LoRa that was the dgram vs. raw
discussion; for FSK/OOK I read packet mode vs. continuous mode in SX127x
but not obvious if that affects the protocol or just the device driver.
Getting back to your point, the data rate selection at LoRaWAN layer
needs to determine how an skb gets passed between device driver and MAC.
Another aspect FSK touches on is interactions with other subsystems. For
example, how an sx1276 802.15.4 implementation for FSK/OOK (or LoRa, as
you mentioned in your slides) would best interact with ieee802154 and
mac802154 code when not done as standalone implementation. Or how to
deal with SigFox on the same module as LoRaWAN - not aware of any kernel
subsystem for SigFox. By comparison, on-module BLE sounds slightly
easier, since it uses a separate antenna.
(Haven't seen LoRa plus NB-IoT yet, to name the other LPWAN technology.)
> - LoRaWAN should handle the data rate changing requests and then calls
> the corresponding operation functions. Of course, the hardware's
> capability registered before should also be under consideration at the
> same time.
>
>> a) Is there any precedence from the Wifi world for dynamically selecting
>> between our own trusted Open Source implementation vs. hardware/firmware
>> accelerated and/or certified implementations?
>>
>> b) Would a proof of concept for FSK (non-LoRa) modes be required for
>> merging any LoRa driver for chipsets that support both? Or is there any
>> facility or design guidelines that would allow us to focus on LoRa and
>> LoRaWAN and leave non-LoRa radio modes to later contributors?
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Hi Andreas,
2018-08-03 16:44 GMT+08:00 Andreas Färber <[email protected]>:
> Hi Jian-Hong,
>
> Am 02.08.2018 um 09:52 schrieb Jian-Hong Pan:
>> 2018-07-18 19:28 GMT+08:00 Ben Whitten <[email protected]>:
>>>> 1) PF_LORA/AF_LORA and associated identifiers are
>>>> proposed to represent
>>>> this technology. While for an SX1276 [...] it
>>>> might work to
>>>> layer LoRaWAN as a protocol option for PF_LORA and
>> add
>>>> LoRaWAN address
>>>> fields to the union in my sockaddr_lora, how would that
>>>> work for devices
>>>> that only support LoRaWAN but not pure LoRa? Do we
>>>> need both AF_LORA and
>>>> AF_LORAWAN, or just a separate ETH_P_LORAWAN or
>>>> ARPHRD_LORAWAN?
> [...]
>>>> Meanwhile my attempt to play with netlink during SUSE
>>>> Hackweek has been
>>>> going slow and I could use some guidance or a volunteer to
>>>> contribute: I
>>>> have a bare skeleton of registration, commands, attributes
>>>> and multicast
>>>> groups, but no plan yet how to connect that to the actual
>>>> drivers to
>>>> query or apply the settings...
>>>
>>> Happy to help, I will be starting from zero on netlink but I can contribute my existing work incorporating Marks comments for sx1301 etal.
>>>
>>>> https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/li
>>>> nux-lora.git/tree/net/lora/netlink.c?h=lora-next
>>
>> Is this the repository used for the LoRa subsystem now?!!!
>> If it is, than great!
>
> Yes, my linux-lora.git contains this RFC patchset (modulo SX1276 fixes
> spotted by kbuild bot) plus a new serdev driver for another module and
> ongoing work by Ben and me for refactoring SX1301. It's monitored by the
> 0-day test service (kbuild bot).
>
> As this patchset was an RFC and does not have any Acked-bys from
> maintainers, the tree does not have a for-next branch integrated into
> linux-next on basis of 4.18-rc1, but instead (like my personal GitHub
> tree before) has a lora-next branch that rebases as patch queue on top
> of linux-next for now.
>
> The intent is to allow collaboration on getting things into a state that
> I can later submit a clean (squashed) RFC v2 for review, with all issues
> raised for this v1 addressed.
>
> For contributing patches to my linux-lora.git I suggest to use
> --subject-prefix="PATCH lora-next" to distinguish from net-next.
> And I just realize I should add a MAINTAINERS entry in my tree to make
> sure patches CC me, too. (I do monitor netdev for patches with subject
> "lora", but chances are someone might omit that.)
>
>> As the previous mails, I am trying to implement the LoRaWAN
>> specification as the soft MAC as the MAC layer over LoRa PHY.
>> This is the the talk about LoRaWAN class module I gave in Netdev Conf
>> https://www.slideshare.net/chienhungpan/lorawan-class-module-and-subsystem
>>
>> The expectation is:
>>
>> socket APIs:
>> send and receive the data
>> ------------------------------------------------------------
>> LoRaWAN class module implements MAC:
>> append the header/footer, encryption/decryption, timing slot and MAC commands
>> ------------------------------------------------------------
>> LoRa device drivers:
>> send and receive the messages for MAC layer
>> ------------------------------------------------------------
>> LoRa devices
>
> Thanks for sharing your slides. We seem to be in alignment for the
> abstract concept above. The devil is in the implementation details. ^.-
>
>> Is it possible that combine the LoRaWAN class module I implemented?
>
> Yes, as stated in this cover letter, I would love if you could help
> merge your LoRaWAN implementation with my driver framework. Comparing
> 802.15.4 and 802.11, I think MAC code should go into net/maclorawan/ and
> then is a fairly independent module, with you as maintainer.
>
>> I start from the simplest class A end device's behavior, especially
>> the timing slot.
>> Also the encryption/decryption for uplink/downlink data messages.
>> I can send it as patches.
>>
>> However, I have 2 problems right now.
>> 1. Which Address and Protocol Family should we use? PF_LORA or PF_LORAWAN?
>
> That was one of the questions I raised above. I now think we need both.
> I'm not yet too deep into LoRaWAN, but from the AT command interfaces
> I've seen there's confirmed and unconfirmed transmission modes that with
> PF_LORAWAN might be mapped to SOCK_STREAM and SOCK_DGRAM. Or do you see
> a way of doing both on a single PF_LORA SOCK_LORAWAN socket?
>
>> 2. To test the LoRaWAN class module, adding more procedures in LoRa
>> device drivers to register as a LoRaWAN device is needed.
>
> No, I don't think so. There are (at least) two types of devices, LoRaWAN
> and LoRa devices. The SX1276 is a pure LoRa device and thus should only
> expose a LoRa network device. It'll be the task of the maclorawan module
> to translate between the two layers, not the business of the device
> driver; SX1276 should receive a ready-to-send LoRa skb. For Ethernet,
> PF_INET and PF_INET6 don't need separate devices either, both use eth0.
>
> What I do think we need is your struct lora_hw, maybe renamed to
> lora_phy. That could be the missing piece for registration of the device
> drivers with nllora module?
> Note that similarly we'll also need an nllorawan module that needs to
> work both with your maclorawan and with some of my device drivers that
> implement the MAC in an MCU.
Let me think these twice.
> Additionally I've been looking into socket options at PF_LORA dgram
> layer for some radio options, but discarded that again for lack of
> precedence. Basically I wondered whether we could allow to choose SF,
> bandwidth, etc. on socket level and then apply those settings before
> sending one packet rather than expecting a global netlink operation that
> affects all sockets for that interface.
According to the LoRaWAN Regional Parameters, we should have the APIs
to set the SF, frequency, bandwidth ...
The idea is: LoRaWAN class module gets the region of this device and
the device's abilities. Then LoRaWAN class module calls the callback
functions to set the related parameters: SF, frequency, bandwidth ...
And of course, the setting actions only happen in the idle, standby or
stop status.
1. Interface is being startup.
2. Requested by MAC command.
But let start from simple case and add more features and complexity in
the future.
By the way, I am rearranging the module's code and try to make it
could be patches.
Regards,
Jian-Hong Pan
Hi Ben and Jian-Hong,
[- SPI - devicetree]
Am 18.07.2018 um 13:28 schrieb Ben Whitten:
>> Meanwhile my attempt to play with netlink during SUSE
>> Hackweek has been
>> going slow and I could use some guidance or a volunteer to
>> contribute: I
>> have a bare skeleton of registration, commands, attributes
>> and multicast
>> groups, but no plan yet how to connect that to the actual
>> drivers to
>> query or apply the settings...
>
> Happy to help, I will be starting from zero on netlink but I can contribute my existing work incorporating Marks comments for sx1301 etal.
I've figured out a way to retrieve a frequency value via netlink:
By passing the lora0 ifindex in as attribute, nllora can look up the
net_device by index, and then via struct lora_dev_priv call into hooks
that the device drivers can optionally implement. :)
https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-lora.git/tree/net/lora/netlink.c?h=lora-next
Tested with HIMO-01M module.
The command might need an additional channel attribute for SX130x.
The corresponding nltest program (using libnl-genl) is here:
https://github.com/afaerber/lora-modules/blob/master/nltest.c
Cheers,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
On Sun, 5 Aug 2018 02:11:25 +0200
Andreas Färber <[email protected]> wrote:
> Hi Jian-Hong,
>
> Am 03.07.2018 um 17:11 schrieb Jian-Hong Pan:
> > 2018-07-01 19:07 GMT+08:00 Andreas Färber <[email protected]>:
> >> 2) PF_LORA is used with SOCK_DGRAM here. The assumption is that RAW mode
> >> would be DGRAM plus preamble plus optional checksum.
> >
> > Is the preamble added by the hardware itself here or software here?
>
> That depends on the driver. For SX127x and any SX127x based modules it
> is added by hardware and a SOCK_RAW seems impossible. If you were to use
> some SDR hardware, it would need to be done by software and might either
> be done in the device driver for SOCK_DGRAM or by user with a SOCK_RAW.
> Right now I don't see a practical use case for the latter. (CC Pieter)
For TCP/IP a SOCK_RAW is a socket with raw IP level access. It doesn't
deal in IP headers and unless you ask it also does chunks of IP header
stuff for you.
SOCK_PACKET is the truely raw interface and even then it's rawness is
bounded. You can't for example talk truely raw wifi bit streams, and for
ethernet you get to generate MAC headers but not preambles etc.
> LoRa radio channels being half-duplex, we'd need to stop receiving in
> ndo_start_xmit and re-start receiving in the TX interrupt handler AFAIU.
> Yes, it's ugly - one reason I haven't implemented RX in sx1276 yet.
Why - the signal is still floating around in the air, you can't unhear it
at the antenna. If a given piece of hardware needs to flip between RX
and TX mode then it should be handled by that driver.
(Some ancient ethernet cards do this btw.. they can't listen and transmit
at the same time)
> > - We can have a pre-defined table according to LoRaWAN Regional Parameters.
> > - Device driver declares the hardware's capability, for example
> > frequency, TX power. And then registers as a LoRaWAN compatible
> > device.
>
> That sounds like a layering violation. We rather need to expose all
> these tunable parameters individually at the LoRa layer, allowing the
> LoRaWAN layer to configure them as it pleases. Not the other direction.
> That still leaves my above question 4) open of how to implement each.
Wifi already has the general policy database for the various existing
protocols. Please take a look at the CRDA agent and how it hooks into
wireless.It might need to some tweaking but it would be odd to have
different configuration schemes for different wifi protocols.
> The use case I have in mind is this: User A opens a LoRaWAN socket and
> using maclorawan sends a packet P1. Here the LoRaWAN Regional Parameters
> and LoRaWAN Sync Word need to be applied.
> User B then opens a pure LoRa socket and transmits a packet P1' with
> different Sync Word, SF, BW, CR, etc.
> Afterwards user A wants to send another packet P2 via LoRaWAN - this
> needs to use the same LoRaWAN settings as before, not those used for
> LoRa in between. Therefore I was thinking about socket-level options, as
> netlink operations would be device-wide, with undesired side-effects.
Agreed
> Obviously in that scenario not both users can receive at the same time.
That's a hardware question. Imagine a software defined radio. If your
limitation wouldn't exist in a pure software defined radio then it's
almost certainly a device level detal.
> interface but cleanly distinguished as ETH_P_GFSK or something.
> For example, the Chistera Pi HAT has both an RFM95W and an RFM22 module.
Agreed if you can flip per packet.
> The next question arising is how the user would create such an skb. Just
> like I was hesitant about PF_LORAWAN originally, I'd like to avoid
> polluting the PF_ number space for each of these. Maybe have one PF_FSK
> as equivalent to PF_LORA and then have either a socket option or
> sockaddr field to select the modulation variants? Not sure how exactly
> those others differ from each other, that's why I tried to postpone the
> FSK topic and to focus on LoRa first - b) below.
>
> At this point we could also argue whether we need a PF_LORA at all or
> rather just some generic PF_RADIO with lots of options stored in its
> sockaddr.
What matters most is mux/demux.
If you've got something listening to data but without the structure
needed to identify multiple listeners and split out the data meaningfully
to those listeners according to parts of the packet then you've got no
reason to make it a protocol just use SOCK_PACKET and if need be BPF.
The reason we have a socket layer not /dev/ethernet0 is that it's
meaningful to divide messages up into flows, and to partition those flows
securely amongst multiple consumers/generators.
Alan
Am 08.08.2018 um 22:36 schrieb Alan Cox:
> On Sun, 5 Aug 2018 02:11:25 +0200
> Andreas Färber <[email protected]> wrote:
>> Am 03.07.2018 um 17:11 schrieb Jian-Hong Pan:
>>> 2018-07-01 19:07 GMT+08:00 Andreas Färber <[email protected]>:
>> LoRa radio channels being half-duplex, we'd need to stop receiving in
>> ndo_start_xmit and re-start receiving in the TX interrupt handler AFAIU.
>> Yes, it's ugly - one reason I haven't implemented RX in sx1276 yet.
>
> Why - the signal is still floating around in the air, you can't unhear it
> at the antenna.
Why what? :)
> If a given piece of hardware needs to flip between RX
> and TX mode then it should be handled by that driver.
Yes, and we are talking about that concrete sx1276 driver here, whose
chipset has a state machine that only allows either rx or tx and also
has standby and sleep modes with differing levels of data retention.
The sx1301 is a different beast (and driver) and may allow both.
In any case, the ndo_start_xmit hook is in each device driver. But the
recvmsg hook I mentioned is at protocol layer, which exactly is the
layering problem I see.
> (Some ancient ethernet cards do this btw.. they can't listen and transmit
> at the same time)
So when do they start receiving?
The issue here was that my original description, which you appear to
have cut, suggested a continuous listen mode, interrupted by transmit.
Jian-Hong didn't like that, with reference to the LoRaWAN spec that
supposedly asks for only being in receive mode when expecting a message,
likely to save on battery. So the question is, could we cleanly
implement receiving only when the user asks us to, or is that a no-go?
>>> - We can have a pre-defined table according to LoRaWAN Regional Parameters.
>>> - Device driver declares the hardware's capability, for example
>>> frequency, TX power. And then registers as a LoRaWAN compatible
>>> device.
>>
>> That sounds like a layering violation. We rather need to expose all
>> these tunable parameters individually at the LoRa layer, allowing the
>> LoRaWAN layer to configure them as it pleases. Not the other direction.
>> That still leaves my above question 4) open of how to implement each.
>
> Wifi already has the general policy database for the various existing
> protocols. Please take a look at the CRDA agent and how it hooks into
> wireless.It might need to some tweaking but it would be odd to have
> different configuration schemes for different wifi protocols.
CRDA/wireless-regdb had been brought up and I've been pointed to nl80211
and nl802154, which I've tried to make sense of for my initial nllora.
Admittedly with limited success, as that code is slightly complex.
We also seemed to have consensus here that we _should_ be reusing
wireless-regdb but would need to extend it 1.) with sub-GHz frequency
bands and 2.) duty-cycle limits for some of those bands. No maintainer
commented on that so far. Thus I am working in tiny steps on providing
netlink-layer commands in nllora that can dispatch the individual radio
settings to drivers, which then upper layers can instrument as needed.
And making my very first steps with netlink here, it appeared as if each
technology has its own enums of commands and attributes, so I don't see
how to reuse anything from Wifi here apart from some design inspiration.
>> The use case I have in mind is this: User A opens a LoRaWAN socket and
>> using maclorawan sends a packet P1. Here the LoRaWAN Regional Parameters
>> and LoRaWAN Sync Word need to be applied.
>> User B then opens a pure LoRa socket and transmits a packet P1' with
>> different Sync Word, SF, BW, CR, etc.
>> Afterwards user A wants to send another packet P2 via LoRaWAN - this
>> needs to use the same LoRaWAN settings as before, not those used for
>> LoRa in between. Therefore I was thinking about socket-level options, as
>> netlink operations would be device-wide, with undesired side-effects.
>
> Agreed
>
>> Obviously in that scenario not both users can receive at the same time.
>
> That's a hardware question. Imagine a software defined radio. If your
> limitation wouldn't exist in a pure software defined radio then it's
> almost certainly a device level detal.
An SDR would not be using this sx1276 device driver, I imagine.
In fact I would expect an SDR device not to be in drivers/net/lora/ at
all but to live in drivers/net/sdr/ and to consume ETH_P_LORA etc. skbs
and just do the right thing for them depending on their type...
>> interface but cleanly distinguished as ETH_P_GFSK or something.
>> For example, the Chistera Pi HAT has both an RFM95W and an RFM22 module.
>
> Agreed if you can flip per packet.
The SX127x can - it might involve delays of course.
The SX130x doesn't even need to flip for sending AFAICT, it's just
metadata after the payload in the FIFO.
>> The next question arising is how the user would create such an skb. Just
>> like I was hesitant about PF_LORAWAN originally, I'd like to avoid
>> polluting the PF_ number space for each of these. Maybe have one PF_FSK
>> as equivalent to PF_LORA and then have either a socket option or
>> sockaddr field to select the modulation variants? Not sure how exactly
>> those others differ from each other, that's why I tried to postpone the
>> FSK topic and to focus on LoRa first - b) below.
>>
>> At this point we could also argue whether we need a PF_LORA at all or
>> rather just some generic PF_RADIO with lots of options stored in its
>> sockaddr.
>
> What matters most is mux/demux.
>
> If you've got something listening to data but without the structure
> needed to identify multiple listeners and split out the data meaningfully
> to those listeners according to parts of the packet then you've got no
> reason to make it a protocol just use SOCK_PACKET and if need be BPF.
Sorry, that doesn't parse for me. SOCK_PACKET must be a protocol on some
PF_ protocol family, no? Are you suggesting I use SOCK_PACKET instead of
SOCK_DGRAM in what is now net/lora/dgram.c? Or are you saying there's
some generic implementation that we can reuse and scratch mine?
> The reason we have a socket layer not /dev/ethernet0 is that it's
> meaningful to divide messages up into flows, and to partition those flows
> securely amongst multiple consumers/generators.
For me the distinction is that a /dev/whatever0 would seem more suited
for a stream of data to read/write, whereas sockets give us a bounded
skb for packets at device driver level.
These PHYs all broadcast something over the antenna when sending, with
any addressing of listeners or senders being optional and MAC-specific,
apart from the LoRa/FSK SyncWord as well as the various frequency etc.
settings that determine what the receiver listens for.
None of these PHYs define any mechanism like EtherType through which to
identify upper-layer protocols.
So in a way, listening is always in a promiscuous mode, and I guess we
would need to try to parse each incoming packet as e.g. a LoRaWAN packet
and just give up if it's too short or checksums don't match. Only at the
layer of LoRaWAN and competing proprietary or custom protocols can we
split received packets out to individual listeners.
Does that give us any further clues for the design discussion here?
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Am 05.08.2018 um 02:11 schrieb Andreas Färber:
> Am 03.07.2018 um 17:11 schrieb Jian-Hong Pan:
>> 2018-07-01 19:07 GMT+08:00 Andreas Färber <[email protected]>:
>>> 5) Many of the modules support multiple modes, such as LoRa, LoRaWAN and FSK.
>>> Lacking a LoRaWAN implementation, I am currently switching them into LoRa
>>> mode at probe time wherever possible. How do we deal with that properly?
[...]
> Independently of what LoRaWAN does, the
> user needs to be able to send on the physical layer from a selection of
> LoRa, GFSK, FSK, OOK, GMSK and MSK.
> Supposedly Wireless M-Bus and IEEE 802.15.4 can be implemented via those
> according to the SX1276 datasheet.
>
> This opens a can of worms...
>
> SX127x has a single channel, so I don't think there should be six
> network interfaces lora0, gfsk0, fsk0, ook0, gmsk0 and msk0 exposed to
> the user.
> Having a lora0 interface speak non-LoRa modulations may be confusing,
> but since the chip is sold as LoRa transceiver it might be acceptable.
>
> SX130x has 8+2 channels, with IF9 dedicated to GFSK/FSK. It appears to
> use one FIFO for receiving (up to 16 packets) and can only transmit one
> packet at a time. So I think this should be one lora0 interface, too.
>
> With a view to supporting non-LoRa RF chipsets such as Si4xxx (GFSK,
> FSK, OOK) or nRF905 and nRF24L01+ (both GFSK) at a later date, I don't
> think those modulations should be some netlink option on a PF_LORA
> interface but cleanly distinguished as ETH_P_GFSK or something.
> For example, the Chistera Pi HAT has both an RFM95W and an RFM22 module.
Answering myself here: MSK is a special case of FSK, and GMSK a special
case of GFSK. SX1276 allows to switch between LoRa and FSK/OOK modes,
and in FSK/OOK mode between FSK and OOK modulation types and for FSK
modulation allows to set Gaussian filter values. I assume (G)MSK is
selected indirectly through BitRate and Fdev settings or something.
So we would just need ETH_P_FSK and ETH_P_OOK plus some skb fields for
ETH_P_FSK.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
> Yes, and we are talking about that concrete sx1276 driver here, whose
> chipset has a state machine that only allows either rx or tx and also
> has standby and sleep modes with differing levels of data retention.
It's a hardware limit, it should never influence the protocol stack
itself just the driver. Linux always tries to design to optimize the
non-crappy case. In the long term that works out best because hardware
improves and you don't want to be tied to an old limit.
> > (Some ancient ethernet cards do this btw.. they can't listen and transmit
> > at the same time)
>
> So when do they start receiving?
When they are not transmitting. The transmit path switches modes and when
the frame send is done it goes back to receiving. As old ethernet was
also half duplex that worked.
> The issue here was that my original description, which you appear to
> have cut, suggested a continuous listen mode, interrupted by transmit.
I don't think I cut it but if so I didn't mean to and your approach is
the one I agree with.
> Jian-Hong didn't like that, with reference to the LoRaWAN spec that
> supposedly asks for only being in receive mode when expecting a message,
> likely to save on battery. So the question is, could we cleanly
> implement receiving only when the user asks us to, or is that a no-go?
Why would you do so ? You can't run Linux on a tiny little
micro-controller where that would matter. Sure it makes sense for some
tiny spec of embedded silicon buried in a sensor - but not a Linux box.
Now you might power it down when the interface is down, or when there is
nobody using that interface but that's really more about long term idle
power.
> bands and 2.) duty-cycle limits for some of those bands. No maintainer
> commented on that so far. Thus I am working in tiny steps on providing
> netlink-layer commands in nllora that can dispatch the individual radio
> settings to drivers, which then upper layers can instrument as needed.
Sounds right to me.
>
> And making my very first steps with netlink here, it appeared as if each
> technology has its own enums of commands and attributes, so I don't see
> how to reuse anything from Wifi here apart from some design inspiration.
That seems reasonable - you aren't likely to want to manage them with the
same tool.
> > That's a hardware question. Imagine a software defined radio. If your
> > limitation wouldn't exist in a pure software defined radio then it's
> > almost certainly a device level detal.
>
> An SDR would not be using this sx1276 device driver, I imagine.
>
> In fact I would expect an SDR device not to be in drivers/net/lora/ at
> all but to live in drivers/net/sdr/ and to consume ETH_P_LORA etc. skbs
> and just do the right thing for them depending on their type...
The point I was trying to make was that if you want to decide whether
something is driver level or protocol level ask 'is this something you
can't do even with an SDR'. Some things are protocol properties that no
fancy hardware will change. Others are hardware limits, in which case you
want them driver level - because at some point the hardware will get
better.
> > If you've got something listening to data but without the structure
> > needed to identify multiple listeners and split out the data meaningfully
> > to those listeners according to parts of the packet then you've got no
> > reason to make it a protocol just use SOCK_PACKET and if need be BPF.
>
> Sorry, that doesn't parse for me. SOCK_PACKET must be a protocol on some
> PF_ protocol family, no? Are you suggesting I use SOCK_PACKET instead of
> SOCK_DGRAM in what is now net/lora/dgram.c? Or are you saying there's
> some generic implementation that we can reuse and scratch mine?
There is a heirarchy. Let me us IP for an example
(historically it was SOCK_PACKET nowdays PF_PACKET - the layering got
sorted better)
PF_PACKET SOCK_RAW ETH_P_ALL
Everything on that device minus some things like hardware pre-ambles
PF_PACKET SOCK_RAW ETH_P_SOMETHING
Everything on that device that has the underlying protocol (and the
protocol might not be in the packet but a property of the interface
because it only does that format - simple example SLIP is IP packets over
a serial link a SLIP interface is IP, not because there is anything
saying it is but because that is *all* it can be)
You get the two above for free. PF_PACKET is built into the stack so
providing you label packets with the ETH_P_xxx you have for Lora, you can
use PF_PACKET interfaces to dump them and write raw packets at the kernel
layer.
PF_INET SOCK_RAW
Split the messages by protocol number in IP between multiple
listeners/writers
PF_INET SOCK_UDP / TCP etc
Split the messages by port numbers in the higher level protocol
For PF_LORA these would map to whatever goes on at the LORA protocol
level and divide LORA messages up between multiple processes on the
Linux system that are interested in some of the messages.
> > The reason we have a socket layer not /dev/ethernet0 is that it's
> > meaningful to divide messages up into flows, and to partition those flows
> > securely amongst multiple consumers/generators.
>
> For me the distinction is that a /dev/whatever0 would seem more suited
> for a stream of data to read/write, whereas sockets give us a bounded
> skb for packets at device driver level.
You could equally do that in a simple character device *if* you didn't
need to split messages up and share between users. Some protocol stacks
actually do that and then sort it out in user space, either because they
are really obscure or they are incredibly complicated and broken so want
to be out of kernel 8)
> These PHYs all broadcast something over the antenna when sending, with
> any addressing of listeners or senders being optional and MAC-specific,
> apart from the LoRa/FSK SyncWord as well as the various frequency etc.
> settings that determine what the receiver listens for.
>
> None of these PHYs define any mechanism like EtherType through which to
> identify upper-layer protocols.
>
> So in a way, listening is always in a promiscuous mode, and I guess we
> would need to try to parse each incoming packet as e.g. a LoRaWAN packet
> and just give up if it's too short or checksums don't match. Only at the
> layer of LoRaWAN and competing proprietary or custom protocols can we
> split received packets out to individual listeners.
My vote would be in that case that you either
1. Set the protocol type on the interface assuming you don't mix and
match (and if it's relying on random bits not looking like other packets
then it sounds a complete mess at the moment - but yeah its new tech)
2. You pass everything up to some magical agent which somehow splits them
up and labels them ETH_P_LORA / ETH_P_FOO etc
3. You do what ethernet does (which admittedly is *way* simpler for
ethernet) and you have a library routine you can pass an skbuff in the
driver itself which figures out wtf to label the packet. Look how
eth_type_trans() is used. Drivers then just do
skb->protocol = xxx_type_trans(skb, dev);
and the basic labelling gets done and any header pulls (you probably won't
have any given you don't have anything wrapping LORA), and
multicast/broadcast labelling - again meaningless I suspect.
#3 Is probably the nicest because you update it all in one place as
standards change and the market hopefully conslidates and develops some
kind of sane packet formats. It's also effectively covering #1 and it's
easy to start with because an initial implementation can just do 'return
htons(ETH_P_LORA)'.
>
> Does that give us any further clues for the design discussion here?
>
I think so yes
How do you plan to deal with routing if you've got multiple devices ?
Alan
Alan Cox <[email protected]> 於 2018年8月9日 週四 下午7:59寫道:
>
> > Yes, and we are talking about that concrete sx1276 driver here, whose
> > chipset has a state machine that only allows either rx or tx and also
> > has standby and sleep modes with differing levels of data retention.
>
> It's a hardware limit, it should never influence the protocol stack
> itself just the driver. Linux always tries to design to optimize the
> non-crappy case. In the long term that works out best because hardware
> improves and you don't want to be tied to an old limit.
>
> > > (Some ancient ethernet cards do this btw.. they can't listen and transmit
> > > at the same time)
> >
> > So when do they start receiving?
>
> When they are not transmitting. The transmit path switches modes and when
> the frame send is done it goes back to receiving. As old ethernet was
> also half duplex that worked.
>
> > The issue here was that my original description, which you appear to
> > have cut, suggested a continuous listen mode, interrupted by transmit.
>
> I don't think I cut it but if so I didn't mean to and your approach is
> the one I agree with.
>
> > Jian-Hong didn't like that, with reference to the LoRaWAN spec that
> > supposedly asks for only being in receive mode when expecting a message,
> > likely to save on battery. So the question is, could we cleanly
> > implement receiving only when the user asks us to, or is that a no-go?
>
> Why would you do so ? You can't run Linux on a tiny little
> micro-controller where that would matter. Sure it makes sense for some
> tiny spec of embedded silicon buried in a sensor - but not a Linux box.
>
> Now you might power it down when the interface is down, or when there is
> nobody using that interface but that's really more about long term idle
> power.
Except saving power, mitigating the wireless signal conflict on the
air is one of the reasons.
According to the LoRaWAN spec. defined by LoRa Alliance, all LoRaWAN
end devices must implement class A features, which is also the most
general and simplest.
1. The transmission slot scheduled by the end-device is based on its
own communication needs with a small variation based on a random time
basis (ALOHA-type of protocol).
2. Only require downlink communication from the server shortly after
the end-device has sent an uplink transmission. Downlink
communications from the server at any other time will have to wait
until the next scheduled uplink.
3. Each end-device‘s uplink transmission is followed by two short
downlink receive windows.
End device transmits an uplink message finished
->
End device goes to sleep/idle/stop
->
End device opens RX1 window. This is the time for the downlink message.
->
End device goes to sleep/idle/stop
->
End device opens RX2 window. This is another time for the downlink message.
->
End device goes to sleep/idle/stop
The time between end device transmit an uplink message finished and
end device opens RX1 window is RX delay #1.
The time between end device transmit an uplink message finished and
end device opens RX2 window is RX delay #2.
If end device gets the meaningful downlink message in RX1 window, it
will not open RX2 window.
The time of RX delay #1 and 2 are defined in LoRaWAN Regional Parameters.
The sleep/idle/stop mitigate the unconcerned RF signals or messages.
> > bands and 2.) duty-cycle limits for some of those bands. No maintainer
> > commented on that so far. Thus I am working in tiny steps on providing
> > netlink-layer commands in nllora that can dispatch the individual radio
> > settings to drivers, which then upper layers can instrument as needed.
>
> Sounds right to me.
> >
> > And making my very first steps with netlink here, it appeared as if each
> > technology has its own enums of commands and attributes, so I don't see
> > how to reuse anything from Wifi here apart from some design inspiration.
>
> That seems reasonable - you aren't likely to want to manage them with the
> same tool.
>
> > > That's a hardware question. Imagine a software defined radio. If your
> > > limitation wouldn't exist in a pure software defined radio then it's
> > > almost certainly a device level detal.
> >
> > An SDR would not be using this sx1276 device driver, I imagine.
> >
> > In fact I would expect an SDR device not to be in drivers/net/lora/ at
> > all but to live in drivers/net/sdr/ and to consume ETH_P_LORA etc. skbs
> > and just do the right thing for them depending on their type...
>
> The point I was trying to make was that if you want to decide whether
> something is driver level or protocol level ask 'is this something you
> can't do even with an SDR'. Some things are protocol properties that no
> fancy hardware will change. Others are hardware limits, in which case you
> want them driver level - because at some point the hardware will get
> better.
>
> > > If you've got something listening to data but without the structure
> > > needed to identify multiple listeners and split out the data meaningfully
> > > to those listeners according to parts of the packet then you've got no
> > > reason to make it a protocol just use SOCK_PACKET and if need be BPF.
> >
> > Sorry, that doesn't parse for me. SOCK_PACKET must be a protocol on some
> > PF_ protocol family, no? Are you suggesting I use SOCK_PACKET instead of
> > SOCK_DGRAM in what is now net/lora/dgram.c? Or are you saying there's
> > some generic implementation that we can reuse and scratch mine?
>
> There is a heirarchy. Let me us IP for an example
>
> (historically it was SOCK_PACKET nowdays PF_PACKET - the layering got
> sorted better)
>
> PF_PACKET SOCK_RAW ETH_P_ALL
>
> Everything on that device minus some things like hardware pre-ambles
>
> PF_PACKET SOCK_RAW ETH_P_SOMETHING
>
> Everything on that device that has the underlying protocol (and the
> protocol might not be in the packet but a property of the interface
> because it only does that format - simple example SLIP is IP packets over
> a serial link a SLIP interface is IP, not because there is anything
> saying it is but because that is *all* it can be)
>
> You get the two above for free. PF_PACKET is built into the stack so
> providing you label packets with the ETH_P_xxx you have for Lora, you can
> use PF_PACKET interfaces to dump them and write raw packets at the kernel
> layer.
>
> PF_INET SOCK_RAW
>
> Split the messages by protocol number in IP between multiple
> listeners/writers
>
> PF_INET SOCK_UDP / TCP etc
>
> Split the messages by port numbers in the higher level protocol
>
>
> For PF_LORA these would map to whatever goes on at the LORA protocol
> level and divide LORA messages up between multiple processes on the
> Linux system that are interested in some of the messages.
>
>
> > > The reason we have a socket layer not /dev/ethernet0 is that it's
> > > meaningful to divide messages up into flows, and to partition those flows
> > > securely amongst multiple consumers/generators.
> >
> > For me the distinction is that a /dev/whatever0 would seem more suited
> > for a stream of data to read/write, whereas sockets give us a bounded
> > skb for packets at device driver level.
>
> You could equally do that in a simple character device *if* you didn't
> need to split messages up and share between users. Some protocol stacks
> actually do that and then sort it out in user space, either because they
> are really obscure or they are incredibly complicated and broken so want
> to be out of kernel 8)
>
> > These PHYs all broadcast something over the antenna when sending, with
> > any addressing of listeners or senders being optional and MAC-specific,
> > apart from the LoRa/FSK SyncWord as well as the various frequency etc.
> > settings that determine what the receiver listens for.
> >
> > None of these PHYs define any mechanism like EtherType through which to
> > identify upper-layer protocols.
> >
> > So in a way, listening is always in a promiscuous mode, and I guess we
> > would need to try to parse each incoming packet as e.g. a LoRaWAN packet
> > and just give up if it's too short or checksums don't match. Only at the
> > layer of LoRaWAN and competing proprietary or custom protocols can we
> > split received packets out to individual listeners.
>
> My vote would be in that case that you either
>
> 1. Set the protocol type on the interface assuming you don't mix and
> match (and if it's relying on random bits not looking like other packets
> then it sounds a complete mess at the moment - but yeah its new tech)
>
> 2. You pass everything up to some magical agent which somehow splits them
> up and labels them ETH_P_LORA / ETH_P_FOO etc
>
> 3. You do what ethernet does (which admittedly is *way* simpler for
> ethernet) and you have a library routine you can pass an skbuff in the
> driver itself which figures out wtf to label the packet. Look how
> eth_type_trans() is used. Drivers then just do
>
> skb->protocol = xxx_type_trans(skb, dev);
>
> and the basic labelling gets done and any header pulls (you probably won't
> have any given you don't have anything wrapping LORA), and
> multicast/broadcast labelling - again meaningless I suspect.
>
> #3 Is probably the nicest because you update it all in one place as
> standards change and the market hopefully conslidates and develops some
> kind of sane packet formats. It's also effectively covering #1 and it's
> easy to start with because an initial implementation can just do 'return
> htons(ETH_P_LORA)'.
>
> >
> > Does that give us any further clues for the design discussion here?
> >
> I think so yes
>
> How do you plan to deal with routing if you've got multiple devices ?
For LoRaWAN, it is a star topology.
One end device and one LoRaWAN gateway:
An end device sends a uplink message to the LoRaWAN gateway directly.
A LoRaWAN gateway receives the uplink message, then transfers to the
LoRaWAN network server.
The network server sends a downlink message to the end device via the
transfer of the LoRaWAN gateway.
Multiple end devices and multiple LoRaWAN gateway:
Multiple end devices can send uplink messages to multiple LoRaWAN
gateways directly. Gateways receive the the uplink messages, then
transfer to the LoRaWAN network server.
The LoRaWAN network server compares the uplink messages' content to
filter out the duplicated messages.
The LoRaWAN network server sends a downlink message to the end device
via the transfer of only one chosen LoRaWAN gateway.
So, an end device does not talk to other end devices directly.
And, of course, the links between LoRaWAN gateway and the network
server could be any network technology, only if they can communicate
with each other fast enough.
Regards,
Jian-Hong Pan
Hi,
On Thu, Aug 09, 2018 at 12:59:39PM +0100, Alan Cox wrote:
> > Yes, and we are talking about that concrete sx1276 driver here, whose
> > chipset has a state machine that only allows either rx or tx and also
> > has standby and sleep modes with differing levels of data retention.
>
> It's a hardware limit, it should never influence the protocol stack
> itself just the driver. Linux always tries to design to optimize the
> non-crappy case. In the long term that works out best because hardware
> improves and you don't want to be tied to an old limit.
>
> > > (Some ancient ethernet cards do this btw.. they can't listen and transmit
> > > at the same time)
> >
> > So when do they start receiving?
>
> When they are not transmitting. The transmit path switches modes and when
> the frame send is done it goes back to receiving. As old ethernet was
> also half duplex that worked.
>
We do the same at some IEEE 802.15.4 transceivers. A transceiver has
_one_ framebuffer only for tx and rx. Another one has two framebuffer
separated tx and rx, but is half duplex.
There is a little performance tweak in separated framebuffers that you
can fill up the tx framebuffer while the transceiver receives the frame
(completely independet from any bus communicaten/linux handling).
> > The issue here was that my original description, which you appear to
> > have cut, suggested a continuous listen mode, interrupted by transmit.
>
> I don't think I cut it but if so I didn't mean to and your approach is
> the one I agree with.
>
...
>
> There is a heirarchy. Let me us IP for an example
>
> (historically it was SOCK_PACKET nowdays PF_PACKET - the layering got
> sorted better)
>
> PF_PACKET SOCK_RAW ETH_P_ALL
>
> Everything on that device minus some things like hardware pre-ambles
>
> PF_PACKET SOCK_RAW ETH_P_SOMETHING
>
> Everything on that device that has the underlying protocol (and the
> protocol might not be in the packet but a property of the interface
> because it only does that format - simple example SLIP is IP packets over
> a serial link a SLIP interface is IP, not because there is anything
> saying it is but because that is *all* it can be)
>
> You get the two above for free. PF_PACKET is built into the stack so
> providing you label packets with the ETH_P_xxx you have for Lora, you can
> use PF_PACKET interfaces to dump them and write raw packets at the kernel
> layer.
>
In 802.15.4:
We recommend nowadays to use PF_PACKET raw sockets for construct L2
frames in userspace. We use that mostly to connect some user space
stacks to make some interop testing without hardware being involved.
For DGRAM sockets, due lack of UAPI limitations of sockaddr_t we
have our own implementation. DGRAM in PF_PACKET use some limitated
feature to "just send something" to an unique address scheme... but some
users need more access because 802.15.4 address scheme is complex.
> PF_INET SOCK_RAW
I think LoRa should look into the 6lowpan subsystem of the Linux kernel
for that. 6lowpan is known as some "IPv6-over-foo" adaptations. Mostly
you just need to implement some mapping from L2 address to SLAAC IPv6 address
pattern only. I see there is a draft at 6lo wg [0] for that which is
expired 2016 (But I would not care about that).
At the end you have a master IP capable interface and your slave is your L2
interface. The 6lowpan interface is a RAW IP interface and do a protocol
translation in the background. On L2 interface you will see L2 + 6LoWPAN
+ $IP_UPPER_LAYER.
Current benefits are more compression, but there exists also some ndisc
optimizations for low power networks which we don't support right now.
- Alex
[0] https://www.ietf.org/archive/id/draft-vilajosana-6lpwa-lora-hc-01.txt
Hi,
On Thu, Aug 09, 2018 at 11:02:45PM +0800, Jian-Hong Pan wrote:
...
>
> For LoRaWAN, it is a star topology.
>
In sense of 6LoWPAN that means "border router" in the middle which runs
radvd on it and sending RA messages. Then doing a route (or even allow nd
proxy) between capable $IPv6 (ethernet?) and LoRa 6LoWPAN interface.
All others listen for RA messages with PIO and doing autoconfiguration.
(In 6LoWPAN you would also pass a 6CO option to compress the PIO
prefix, which would make sense to fully elide all IP addresses in the
header).
- Alex
> Except saving power, mitigating the wireless signal conflict on the
> air is one of the reasons.
If the device level is always receiving when not transmitting it has no
effect on this. The act of listening does not harm other traffic.
> The sleep/idle/stop mitigate the unconcerned RF signals or messages.
At the physical level it's irrelevant. If we are receiving then we might
hear more things we later discard. It's not running on a tiny
microcontroller so the extra CPU cycles are not going to kill us.
> > How do you plan to deal with routing if you've got multiple devices ?
>
> For LoRaWAN, it is a star topology.
No the question was much more how you plan to deal with it in the OS. If
for example I want to open a LORA connection to something, then there
needs to be a proper process to figure out where the target is and how to
get traffic to them.
I guess it's best phrased as
- What does a struct sockaddr_lora look like
- How does the kernel decide which interface it goes out of (if any), and
if it loops back
remembering we might only be talking to a hub, or we might even be a
virtualized LORA interface where we are pretending to be some kind of
sensor and feeding it back.
Long term yes I think Alexander is right the inevitable fate of all
networks is to become a link layer in order to transmit IP frames 8)
Alan
Hello.
On 08/10/2018 05:57 PM, Alan Cox wrote:
>
> Long term yes I think Alexander is right the inevitable fate of all
> networks is to become a link layer in order to transmit IP frames 8)
There should be a niche for both at the same time. LoRaWAN is relevant
right now and we should aim for making it possible to run a native Linux
gateway for it.
As for IP for long range low power there is the static context header
compression draft to adapt IPv6 to the characteristics for some use
cases of such networks. Implementing it within the existing 6lwopan
subsystem should be possible.
https://tools.ietf.org/html/draft-ietf-lpwan-ipv6-static-context-hc-16
regards
Stefan Schmidt
Alan Cox <[email protected]> 於 2018年8月10日 週五 下午11:57寫道:
>
> > Except saving power, mitigating the wireless signal conflict on the
> > air is one of the reasons.
>
> If the device level is always receiving when not transmitting it has no
> effect on this. The act of listening does not harm other traffic.
My friend had tested practically:
If he changes the LoRa interface to RX mode after TX completes
immediately, he will receive the signals like reflection echo some
times.
That is interesting!
There is a paper "Exploring LoRa and LoRaWAN A suitable protocol for
IoT weather stations?" by Kristoffer Olsson & Sveinn Finnsson
http://publications.lib.chalmers.se/records/fulltext/252610/252610.pdf
In chapter 3.2 Chirp Spread Spectrum, it describes the reflection echo
phenomenon.
I think that is why LoRaWAN places the RX delay time which avoids
receiving the reflection noise.
> > The sleep/idle/stop mitigate the unconcerned RF signals or messages.
>
> At the physical level it's irrelevant. If we are receiving then we might
> hear more things we later discard. It's not running on a tiny
> microcontroller so the extra CPU cycles are not going to kill us.
According different power resource, LoRaWAN defines Class A, B and C.
Class A is the basic and both Class B and C devices must also
implement the feature of Class A.
If the end device has sufficient power available, it can also
implement the Class C: Continuously listening end-device.
Here are the descriptions in LoRaWAN spec. for Class C:
- The Class C end-device will listen with RX2 windows parameters as
often as possible.
- The end-device listens on RX2 when it is not either (a) sending or
(b) receiving on RX1, according to Class A definition.
- 1. It will open a short window on RX2 parameters between the end of
the uplink transmission and the beginning of the RX1 reception window.
(*)
2. It will switch to RX2 reception parameters as soon as the RX1
reception window is closed; the RX2 reception window will remain open
until the end-device has to send another message.
According to the LoRaWAN Regional Parameters, the DataRate (including
spreading factor and bandwidth) and frequency channel of RX1 and RX2
windows may be different.(*)
So, yes! Class C opens the RX windows almost all the time, except the TX time.
And uses different channel to avoid the reflection noise (*).
However, Class C must also implements Class A and C is more complex than A.
I think starting from the simpler one and adding more features and
complexity in the future will be a better practice.
> > > How do you plan to deal with routing if you've got multiple devices ?
> >
> > For LoRaWAN, it is a star topology.
>
> No the question was much more how you plan to deal with it in the OS. If
> for example I want to open a LORA connection to something, then there
> needs to be a proper process to figure out where the target is and how to
> get traffic to them.
>
> I guess it's best phrased as
>
> - What does a struct sockaddr_lora look like
According to LoRaWAN spec, the Data Message only has the device's
DevAddr (the device's address in 4 bytes) field related to "address".
The device just sends the uplink Data Message through the interface
and does not know the destination. Then, a LoRaWAN gateway receives
the uplink Data Message and forwards to the designated network server.
So, end device does not care about the destination. It only knows
there is a gateway will forward its message to some where.
Therefore, only the DevAddr as the source address will be meaningful
for uplink Data Message.
> - How does the kernel decide which interface it goes out of (if any), and
> if it loops back
There is the MAC Header in the Data Message which is one byte.
Bits 5 to 7 indicate which kind of type the message is.
000: Join Request
001: Join Accept
010: Unconfirmed Data Up
011: Unconfirmed Data Down
100: Confirmed Data Up
101: Confirmed Data Down
110: RFU
111: Proprietary
So, end device only accepts the types of downlink and the matched
DevAddr (the device's address) in downlink Data Message for RX.
> remembering we might only be talking to a hub, or we might even be a
> virtualized LORA interface where we are pretending to be some kind of
> sensor and feeding it back.
>
> Long term yes I think Alexander is right the inevitable fate of all
> networks is to become a link layer in order to transmit IP frames 8)
Yeah, maybe. It will be easier for life.
But I have not seen the formal standard for that yet or I missed it.
If the standard appears, we can try to implement it.
Jian-Hong Pan
Hi,
Am 11.08.2018 um 20:30 schrieb Stefan Schmidt:
> On 08/10/2018 05:57 PM, Alan Cox wrote:
>>
>> Long term yes I think Alexander is right the inevitable fate of all
>> networks is to become a link layer in order to transmit IP frames 8)
>
> There should be a niche for both at the same time. LoRaWAN is relevant
> right now and we should aim for making it possible to run a native Linux
> gateway for it.
+1
> As for IP for long range low power there is the static context header
> compression draft to adapt IPv6 to the characteristics for some use
> cases of such networks. Implementing it within the existing 6lwopan
> subsystem should be possible.
>
> https://tools.ietf.org/html/draft-ietf-lpwan-ipv6-static-context-hc-16
I'm open to supporting by appropriate design all kinds of upper layers
people want to experiment with. Review feedback for that is welcome.
However I don't intend to implement each of those myself, and hardcoding
them is not really an option so they must be in appropriate modules.
Do keep in mind that depending on vendor implementation we have between
26 and 2048 bytes MTU available, less based on regulatory requirements,
often below 256 bytes. The more space we take away for frame headers
just because they're somehow standardized will make it less useful.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Am 12.08.2018 um 18:37 schrieb Jian-Hong Pan:
> Alan Cox <[email protected]> 於 2018年8月10日 週五 下午11:57寫道:
>>
>>> The sleep/idle/stop mitigate the unconcerned RF signals or messages.
>>
>> At the physical level it's irrelevant. If we are receiving then we might
>> hear more things we later discard. It's not running on a tiny
>> microcontroller so the extra CPU cycles are not going to kill us.
>
> According different power resource, LoRaWAN defines Class A, B and C.
> Class A is the basic and both Class B and C devices must also
> implement the feature of Class A.
[...]
> So, yes! Class C opens the RX windows almost all the time, except the TX time.
> And uses different channel to avoid the reflection noise (*).
>
> However, Class C must also implements Class A and C is more complex than A.
> I think starting from the simpler one and adding more features and
> complexity in the future will be a better practice.
Jian-Hong, you've failed to come up with any practical proposal or patch
how to implement what you are saying LoRaWAN requires. Doing the
impossible is never simpler!
Implementing a simple back-off timer sounds doable by comparison.
May I remind you, LoRa is the simpler step before LoRaWAN - if our
layering is done right, someone else might choose to implement LoRaWAN
in userspace based on PF_LORA. There is absolutely no reason to hardcode
any LoRaWAN settings at device driver level for e.g. SX1276.
>>>> How do you plan to deal with routing if you've got multiple devices ?
>>>
>>> For LoRaWAN, it is a star topology.
>>
>> No the question was much more how you plan to deal with it in the OS. If
>> for example I want to open a LORA connection to something, then there
>> needs to be a proper process to figure out where the target is and how to
>> get traffic to them.
>>
>> I guess it's best phrased as
>>
>> - What does a struct sockaddr_lora look like
>
> According to LoRaWAN spec, the Data Message only has the device's
> DevAddr (the device's address in 4 bytes) field related to "address".
> The device just sends the uplink Data Message through the interface
> and does not know the destination. Then, a LoRaWAN gateway receives
> the uplink Data Message and forwards to the designated network server.
> So, end device does not care about the destination. It only knows
> there is a gateway will forward its message to some where.
> Therefore, only the DevAddr as the source address will be meaningful
> for uplink Data Message.
Note that he was asking about sockaddr_lora, not LoRaWAN.
The simple answer is that, inspired by CAN, it uses an ifindex to select
the interface the user asked to use. That then also answers Alan's next
question: This ifindex determines which interface it goes out to.
sockaddr_lora was in patch 02/15, latest code here:
https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-lora.git/tree/include/uapi/linux/lora.h?h=lora-next
>> - How does the kernel decide which interface it goes out of (if any), and
>> if it loops back
>
> There is the MAC Header in the Data Message which is one byte.
> Bits 5 to 7 indicate which kind of type the message is.
> 000: Join Request
> 001: Join Accept
> 010: Unconfirmed Data Up
> 011: Unconfirmed Data Down
> 100: Confirmed Data Up
> 101: Confirmed Data Down
> 110: RFU
> 111: Proprietary
>
> So, end device only accepts the types of downlink and the matched
> DevAddr (the device's address) in downlink Data Message for RX.
LoRaWAN is an entirely different story, therefore my agreement that we
need a separate PF_LORAWAN and sockaddr_lorawan for EUI addressing.
I still think the user will need to explicitly say which interface they
want to bind their socket to. AFAIU the device EUI is more comparable to
an Ethernet MAC address than to an IP address that the user would
configure routes for. If you have e.g. four SX1301 devices then they may
be configured for different frequencies, bandwidths, etc. but unlikely
for certain destination/source addresses. So either all that data comes
via sockaddr (which I was discussing in the FSK/OOK part of this thread)
or the user needs to make the interface choice for us.
Loopback mode would require a separate virtual device driver such as
fakelr or vlora.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
> The simple answer is that, inspired by CAN, it uses an ifindex to select
> the interface the user asked to use. That then also answers Alan's next
> question: This ifindex determines which interface it goes out to.
>
> sockaddr_lora was in patch 02/15, latest code here:
> https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-lora.git/tree/include/uapi/linux/lora.h?h=lora-next
And any loopback just becomes an ifindex, likewise any virtual lorawan
device (eg when testing in a cloud or simulating radio properties).
> I still think the user will need to explicitly say which interface they
> want to bind their socket to. AFAIU the device EUI is more comparable to
It does make it very hard for any vaguely complex environment because if
for example you have two interfaces enumerated via USB they will appear
in random order each boot.
CANbus is a bit of a mess in this sense but it's so statically configured
and embedded into industrial devices it's less of a problem.
I just wonder if the name would be a better binding (so you can sort the
order out), or a local physical identifier of some kind so that your
enumeration is consistent.
> Loopback mode would require a separate virtual device driver such as
> fakelr or vlora.
And a tunnel device, which is easy enough if you've got tap support or
similar.
Alan
On 7/1/18 1:08 PM, Andreas Färber wrote:
> The IMST WiMOD uses a SLIP based binary UART protocol. Two separate
> firmwares are available. By default it ships with a LoRaWAN firmware.
> The alternative firmware is a custom P2P addressing mode based on LoRa.
>
> Cc: Jon Ortego <[email protected]>
> Signed-off-by: Andreas Färber <[email protected]>
> ---
> drivers/net/lora/Kconfig | 8 +
> drivers/net/lora/Makefile | 3 +
> drivers/net/lora/wimod.c | 597 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 608 insertions(+)
> create mode 100644 drivers/net/lora/wimod.c
>
> diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
> index 940bd2cbe106..2e05caef8645 100644
> --- a/drivers/net/lora/Kconfig
> +++ b/drivers/net/lora/Kconfig
> @@ -31,6 +31,14 @@ config LORA_SX1276
> help
> Semtech SX1272/1276/1278
>
> +config LORA_WIMOD
> + tristate "IMST WiMOD driver"
scripts/checkpatch.pl throws this warning:
WARNING: please write a paragraph that describes the config symbol fully
IMST has multiple products related to "WiMOD":
* WiMOD iC880A
* WiMOD module iM871A
* WSA01-iM880B - WiMOD Shield for Arduino
* iM880B-L - Long Range Radio Module
And IMST is not very consistent about what is called "WiMOD". So this
leaves me clueless concerning what this Kconfig option is about. Please,
provide a description.
There are dozens of warnings given by scripts/checkpatch. Please, have a
look at them.
Best regards
Heinrich
Hello Heinrich,
+ linux-lpwan, - other vendors
Am 06.01.19 um 15:57 schrieb Heinrich Schuchardt:
> On 7/1/18 1:08 PM, Andreas Färber wrote:
>> The IMST WiMOD uses a SLIP based binary UART protocol. Two separate
>> firmwares are available. By default it ships with a LoRaWAN firmware.
>> The alternative firmware is a custom P2P addressing mode based on LoRa.
>>
>> Cc: Jon Ortego <[email protected]>
>> Signed-off-by: Andreas Färber <[email protected]>
>> ---
>> drivers/net/lora/Kconfig | 8 +
>> drivers/net/lora/Makefile | 3 +
>> drivers/net/lora/wimod.c | 597 ++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 608 insertions(+)
>> create mode 100644 drivers/net/lora/wimod.c
>>
>> diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig
>> index 940bd2cbe106..2e05caef8645 100644
>> --- a/drivers/net/lora/Kconfig
>> +++ b/drivers/net/lora/Kconfig
>> @@ -31,6 +31,14 @@ config LORA_SX1276
>> help
>> Semtech SX1272/1276/1278
>>
>> +config LORA_WIMOD
>> + tristate "IMST WiMOD driver"
>
> scripts/checkpatch.pl throws this warning:
> WARNING: please write a paragraph that describes the config symbol fully
>
> IMST has multiple products related to "WiMOD":
> * WiMOD iC880A
> * WiMOD module iM871A
> * WSA01-iM880B - WiMOD Shield for Arduino
> * iM880B-L - Long Range Radio Module
True, and I notice that my wimod.c doesn't explain it either.
Similarly, in patch 12/15 USI is the name of its vendor (but that one
does mention the model in commit message, Kconfig help and header).
>
> And IMST is not very consistent about what is called "WiMOD". So this
> leaves me clueless concerning what this Kconfig option is about. Please,
> provide a description.
So, do you actually have any of them? :) Otherwise the Kconfig symbol,
patch subject and cover letter all clearly indicate LoRa.
* iM871A is rather a Wireless M-Bus module - if you have it and are
interested in collaborating on a driver, let me know. Its HCI message
format looks slightly different, so it would need a separate driver.
Currently I only have access to multiple chipsets with pure FSK support
that one would need a Linux userspace/kernel implementation of wM-Bus
for, plus something to test against. Some LoRa gateway vendors have
expressed a need to consume the FSK support in SX1301 for wM-Bus; sx127x
might still be the easiest path to testing that, but FSK has not been an
implementation priority for me yet, more a design consideration.
Side note: A serdev driver like this might be used to implement also
(non-Wireless) M-Bus in kernel space, if desired.
* iC880A uses the sx130x SPI driver directly. You'll have a hard time
using this serdev driver with it... Please don't waste time commenting
on patch 15/15, it has largely been rewritten since and is under active
work on linux-lpwan list, still in preparation of an RFC v2 patchset.
https://lists.infradead.org/pipermail/linux-lpwan/
The sx130x driver is being tested with iC880A (and on RPi3B+ suffers
from the same clk_prepare() issue as RAK831 on Pine64+).
* iM880B, iM880B-L, iM881A and iM980A should hopefully be pretty much
compatible, and they were the only LoRa modules at the time of posting.
This driver had been tested with the Arduino Shield, using ArPi-600
adapter to Raspberry Pi pinout, connected to a Pine64+ board.
Sadly my Pine64+ board that this Arduino Shield was connected to had
stopped booting (and annoyingly not for the first time). Looks like it
damaged U-Boot somehow... (no serial output anymore)
Writing a new U-Boot onto the card or starting with a new image, I ran
into a U-Boot/TF-A issue related to UEFI that I've now reported:
https://bugzilla.opensuse.org/show_bug.cgi?id=1120869
I now have a new booting image again to continue this driver but need to
set up my test environment with DT Overlays and kernel tree again.
* I hope the new iM282A can work with the same driver - it uses LR-Base+
firmware, whereas iM880B ships with LR-LoRaWAN and has an optional
LR-Base firmware. I am hopeful to cover both with one self-detecting
driver, somehow. A difficulty with LR-Base/LR-Base+ is that it prepends
some custom addressing fields to the user-supplied data, so is not
really PF_LORA / ETH_P_LORA in the sense of this series.
=> This driver covers more than one model. At the moment only iM880B is
verified, iM282A TBD. If you have better naming suggestions let us know.
And if you have experience flashing firmware onto the Arduino Shield
using Linux/stm32flash, do let me know. I'll need that to test LR-Base.
> There are dozens of warnings given by scripts/checkpatch. Please, have a
> look at them.
Please take a look at the date of these patches: It is not helpful to
nitpick about early RFC patches from July now. If my linux-lora.git repo
still has that issue (which I'm sure it does, but you didn't mention you
checked!) then you're welcome to send a fix-up patch against that repo
to linux-lpwan list. Otherwise this is not a priority for me, given
current clk_prepare() lockups and other bigger issues under discussion!
The cover letter mentioned a number of big open socket questions, and so
did my ELCE 2018 presentation:
https://events.linuxfoundation.org/wp-content/uploads/2017/12/ELCE2018_LoRa_final_Andreas-Farber.pdf
https://www.youtube.com/watch?v=Jjel65sZO9M
The purpose of this RFC series was to discuss the _socket layer_ design
and how it would interface with different drivers. This driver didn't
expose any netdev yet, focusing on a PoC of its binary as opposed to AT
protocol. Therefore it is not ready for merging or for unfamiliar users.
Back then there was no LoRaWAN patchset yet (discussing how Jian-Hong's
and my work could be combined was one purpose of this RFC!), now we have
some preliminary constants and an early PF_LORAWAN socket implementation
staged that we can revisit this driver with.
It still carries two implementations for sending the HCI commands that
I'd be interested in feedback for deciding on which to pursue. Also the
receive path of all my serdev drivers could use some review and help.
This driver as-is is nowhere near merging, so please understand that
line length etc. warnings are the least of my concern for this WIP code.
Please don't forget to CC the new linux-lpwan mailing list on any reply
- it contains people for whom the netdev traffic simply is too much.
Thanks,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)