Netdev List
 help / color / mirror / Atom feed
* Re: 862591bf4("xfrm: skip policies marked as dead while rehashing")
From: Greg KH @ 2018-07-01 13:28 UTC (permalink / raw)
  To: Zubin Mithra; +Cc: netdev, groeck, stable
In-Reply-To: <20180620214118.GA37804@zsmcros.c.googlers.com>

On Wed, Jun 20, 2018 at 05:42:51PM -0400, Zubin Mithra wrote:
> Hello,
> 
> Syzkaller has reported a crash here[1] for a slab OOB read in
> xfrm_hash_rebuild.
> 
> Could the following 2 patches be applied in order to on 4.4.y?
> 
> 6916fb3b10("xfrm: Ignore socket policies when rebuilding hash tables")
> 862591bf4f("xfrm: skip policies marked as dead while rehashing")
> 
> [1] https://syzkaller.appspot.com/bug?id=1c11a638b7d27e871aa297f3b4d5fd5bc90f0cb4

Both now queued up, thanks.

greg k-h

^ permalink raw reply

* Re: [PATCH net-next] r8169: remove old PHY reset hack
From: Heiner Kallweit @ 2018-07-01 13:16 UTC (permalink / raw)
  To: Realtek linux nic maintainers, David Miller
  Cc: netdev@vger.kernel.org, Andrew Lunn, Florian Fainelli
In-Reply-To: <bcb32d0d-3a7d-6514-d73a-5f06c7008bc7@gmail.com>

On 01.07.2018 00:25, Heiner Kallweit wrote:
> This hack (affecting the non-PCIe models only) was introduced in 2004
> to deal with link negotiation failures in 1GBit mode. Based on a
> comment in the r8169 vendor driver I assume the issue affects RTL8169sb
> in combination with particular 1GBit switch models.
> 
Some remarks after further digging into this issue:
I have a card with RTL8169sb chip and the PHY identifies as RTL8211C.
This PHY seems to have a PLL issue when operating in Gigabit slave mode,
see the following commit in Uboot:
https://lists.denx.de/pipermail/u-boot/2016-March/249712.html

RTL8211C doesn't have a dedicated PHY driver yet, I'll add one incl.
the quirk to force Gigabit master mode. This should fix the root cause
and we can definitely get rid of the PHY reset hack in r8169.


> Resetting the PHY every 10s and hoping that one fine day we will make
> it to establish the link seems to be very hacky to me. I'd say:
> If 1GBit doesn't work reliably in a users environment then the user
> should remove 1GBit from the advertised modes, e.g. by using
> ethtool -s <if> advertise <10/100 modes>
> 
> If the issue affects one chip version only and that with most link
> partners, then we could also think of removing 1GBit from the
> advertised modes for this chip version in the driver.
> 
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
>  drivers/net/ethernet/realtek/r8169.c | 57 +---------------------------
>  1 file changed, 1 insertion(+), 56 deletions(-)
> 
> diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
> index 72a7778b..b0a06902 100644
> --- a/drivers/net/ethernet/realtek/r8169.c
> +++ b/drivers/net/ethernet/realtek/r8169.c
> @@ -80,7 +80,6 @@ static const int multicast_filter_limit = 32;
>  #define R8169_RX_RING_BYTES	(NUM_RX_DESC * sizeof(struct RxDesc))
>  
>  #define RTL8169_TX_TIMEOUT	(6*HZ)
> -#define RTL8169_PHY_TIMEOUT	(10*HZ)
>  
>  /* write/read MMIO register */
>  #define RTL_W8(tp, reg, val8)	writeb((val8), tp->mmio_addr + (reg))
> @@ -703,7 +702,6 @@ enum rtl_flag {
>  	RTL_FLAG_TASK_ENABLED,
>  	RTL_FLAG_TASK_SLOW_PENDING,
>  	RTL_FLAG_TASK_RESET_PENDING,
> -	RTL_FLAG_TASK_PHY_PENDING,
>  	RTL_FLAG_MAX
>  };
>  
> @@ -731,7 +729,6 @@ struct rtl8169_private {
>  	dma_addr_t RxPhyAddr;
>  	void *Rx_databuff[NUM_RX_DESC];	/* Rx data buffers */
>  	struct ring_info tx_skb[NUM_TX_DESC];	/* Tx data buffers */
> -	struct timer_list timer;
>  	u16 cp_cmd;
>  
>  	u16 event_slow;
> @@ -1788,20 +1785,7 @@ static int rtl8169_set_speed_xmii(struct net_device *dev,
>  static int rtl8169_set_speed(struct net_device *dev,
>  			     u8 autoneg, u16 speed, u8 duplex, u32 advertising)
>  {
> -	struct rtl8169_private *tp = netdev_priv(dev);
> -	int ret;
> -
> -	ret = rtl8169_set_speed_xmii(dev, autoneg, speed, duplex, advertising);
> -	if (ret < 0)
> -		goto out;
> -
> -	if (netif_running(dev) && (autoneg == AUTONEG_ENABLE) &&
> -	    (advertising & ADVERTISED_1000baseT_Full) &&
> -	    !pci_is_pcie(tp->pci_dev)) {
> -		mod_timer(&tp->timer, jiffies + RTL8169_PHY_TIMEOUT);
> -	}
> -out:
> -	return ret;
> +	return rtl8169_set_speed_xmii(dev, autoneg, speed, duplex, advertising);
>  }
>  
>  static netdev_features_t rtl8169_fix_features(struct net_device *dev,
> @@ -1888,8 +1872,6 @@ static int rtl8169_set_link_ksettings(struct net_device *dev,
>  	    cmd->link_modes.advertising))
>  		return -EINVAL;
>  
> -	del_timer_sync(&tp->timer);
> -
>  	rtl_lock_work(tp);
>  	rc = rtl8169_set_speed(dev, cmd->base.autoneg, cmd->base.speed,
>  			       cmd->base.duplex, advertising);
> @@ -4293,44 +4275,12 @@ static void rtl_hw_phy_config(struct net_device *dev)
>  	}
>  }
>  
> -static void rtl_phy_work(struct rtl8169_private *tp)
> -{
> -	struct timer_list *timer = &tp->timer;
> -	unsigned long timeout = RTL8169_PHY_TIMEOUT;
> -
> -	if (rtl8169_xmii_reset_pending(tp)) {
> -		/*
> -		 * A busy loop could burn quite a few cycles on nowadays CPU.
> -		 * Let's delay the execution of the timer for a few ticks.
> -		 */
> -		timeout = HZ/10;
> -		goto out_mod_timer;
> -	}
> -
> -	if (rtl8169_xmii_link_ok(tp))
> -		return;
> -
> -	netif_dbg(tp, link, tp->dev, "PHY reset until link up\n");
> -
> -	rtl8169_xmii_reset_enable(tp);
> -
> -out_mod_timer:
> -	mod_timer(timer, jiffies + timeout);
> -}
> -
>  static void rtl_schedule_task(struct rtl8169_private *tp, enum rtl_flag flag)
>  {
>  	if (!test_and_set_bit(flag, tp->wk.flags))
>  		schedule_work(&tp->wk.work);
>  }
>  
> -static void rtl8169_phy_timer(struct timer_list *t)
> -{
> -	struct rtl8169_private *tp = from_timer(tp, t, timer);
> -
> -	rtl_schedule_task(tp, RTL_FLAG_TASK_PHY_PENDING);
> -}
> -
>  DECLARE_RTL_COND(rtl_phy_reset_cond)
>  {
>  	return rtl8169_xmii_reset_pending(tp);
> @@ -6909,7 +6859,6 @@ static void rtl_task(struct work_struct *work)
>  		/* XXX - keep rtl_slow_event_work() as first element. */
>  		{ RTL_FLAG_TASK_SLOW_PENDING,	rtl_slow_event_work },
>  		{ RTL_FLAG_TASK_RESET_PENDING,	rtl_reset_work },
> -		{ RTL_FLAG_TASK_PHY_PENDING,	rtl_phy_work }
>  	};
>  	struct rtl8169_private *tp =
>  		container_of(work, struct rtl8169_private, wk.work);
> @@ -6982,8 +6931,6 @@ static void rtl8169_down(struct net_device *dev)
>  {
>  	struct rtl8169_private *tp = netdev_priv(dev);
>  
> -	del_timer_sync(&tp->timer);
> -
>  	napi_disable(&tp->napi);
>  	netif_stop_queue(dev);
>  
> @@ -7694,8 +7641,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	tp->event_slow = cfg->event_slow;
>  	tp->coalesce_info = cfg->coalesce_info;
>  
> -	timer_setup(&tp->timer, rtl8169_phy_timer, 0);
> -
>  	tp->rtl_fw = RTL_FIRMWARE_UNKNOWN;
>  
>  	tp->counters = dmam_alloc_coherent (&pdev->dev, sizeof(*tp->counters),
> 

^ permalink raw reply

* Re: [PATCH linux-firmware] Mellanox: Add new mlxsw_spectrum firmware 13.1701.2
From: Ido Schimmel @ 2018-07-01 13:15 UTC (permalink / raw)
  To: Nir Dotan; +Cc: linux-firmware, netdev, dsahern, mlxsw
In-Reply-To: <20180619063306.1470-1-nird@mellanox.com>

On Tue, Jun 19, 2018 at 09:33:06AM +0300, Nir Dotan wrote:
> This new firmware contains:
> 	- Support for new types of cables
> 	- Support for flashing future firmware without reboot
> 	- Support for Router ARP BC and UC traps

Please disregard the patch. We found a problem with this version. Will
send a new version once it's ready.

Thanks

^ permalink raw reply

* Re: [RFC net-next 07/15] net: lora: Add Semtech SX1276
From: Andreas Färber @ 2018-07-01 12:02 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu
In-Reply-To: <20180701110804.32415-8-afaerber@suse.de>

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)

^ permalink raw reply

* [RFC net-next 13/15] net: lora: Prepare RAK RAK811
From: Andreas Färber @ 2018-07-01 11:08 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

The RAK811 and RAK812 offer a UART based AT command interface.
It allows both LoRaWAN and LoRa modes.

Cc: Ken Yu (禹凯) <ken.yu@rakwireless.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 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 <afaerber@suse.de>");
+MODULE_LICENSE("GPL");
-- 
2.16.4

^ permalink raw reply related

* [RFC net-next 00/15] net: A socket API for LoRa
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber,
	Konstantin Böhm, Jan Jongboom, Jon Ortego, contact,
	Ben Whitten, Brian Ray, lora, 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/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 <starnight@g.ncu.edu.tw>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Matthias Brugger <mbrugger@suse.com>
Cc: Konstantin Böhm <konstantin.boehm@ancud.de>
Cc: Jan Jongboom <jan.jongboom@arm.com>
Cc: Janus Piwek <jpiwek@arroweurope.com>
Cc: Michael Röder <michael.roeder@avnet.eu>
Cc: Dollar Chen (陳義元) <dollar.chen@wtmec.com>
Cc: Ken Yu (禹凯) <ken.yu@rakwireless.com>
Cc: Jon Ortego <Jon.Ortego@imst.de>
Cc: contact@snootlab.com
Cc: Ben Whitten <ben.whitten@lairdtech.com>
Cc: Brian Ray <brian.ray@link-labs.com>
Cc: lora@globalsat.com.tw
Cc: lora@radioshuttle.de
Cc: Alexander Graf <agraf@suse.de>
Cc: Michal Kubeček <mkubecek@suse.cz>
Cc: Rob Herring <robh@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: Steve deRosier <derosier@gmail.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: linux-spi@vger.kernel.org

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

^ permalink raw reply

* [RFC net-next 12/15] net: lora: Add USI WM-SG-SM-42
From: Andreas Färber @ 2018-07-01 11:08 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

The USI WM-SG-SM-42 offers a UART based AT command interface.

Cc: Dollar Chen (陳義元) <dollar.chen@wtmec.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 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 <afaerber@suse.de>");
+MODULE_LICENSE("GPL");
-- 
2.16.4

^ permalink raw reply related

* [RFC net-next 10/15] net: lora: Add Microchip RN2483
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

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 <afaerber@suse.de>
---
 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 <afaerber@suse.de>");
+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

^ permalink raw reply related

* [RFC net-next 09/15] net: lora: Prepare EUI helpers
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

These will be used by the RN2483 and other LoRaWAN capable modules.

Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 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

^ permalink raw reply related

* [RFC net-next 07/15] net: lora: Add Semtech SX1276
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

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 <afaerber@suse.de>
---
 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 <afaerber@suse.de>");
+MODULE_LICENSE("GPL");
-- 
2.16.4

^ permalink raw reply related

* [RFC net-next 15/15] net: lora: Add Semtech SX1301
From: Andreas Färber @ 2018-07-01 11:08 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber,
	Ben Whitten, Steve deRosier, Mark Brown, linux-spi
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

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 <ben.whitten@lairdtech.com>
Cc: Steve deRosier <derosier@gmail.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Michael Röder <michael.roeder@avnet.eu>
Cc: Ken Yu (禹凯) <ken.yu@rakwireless.com>
Cc: linux-spi@vger.kernel.org
Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 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 <afaerber@suse.de>");
+MODULE_LICENSE("GPL");
-- 
2.16.4

^ permalink raw reply related

* [RFC net-next 14/15] net: lora: Prepare Semtech SX1257
From: Andreas Färber @ 2018-07-01 11:08 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber,
	Ben Whitten, Steve deRosier, Mark Brown
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

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 <ben.whitten@lairdtech.com>
Cc: Steve deRosier <derosier@gmail.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Michael Röder <michael.roeder@avnet.eu>
Cc: Ken Yu (禹凯) <ken.yu@rakwireless.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 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 <afaerber@suse.de>");
+MODULE_LICENSE("GPL");
-- 
2.16.4

^ permalink raw reply related

* [RFC net-next 11/15] net: lora: Add IMST WiMOD
From: Andreas Färber @ 2018-07-01 11:08 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber,
	Jon Ortego
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

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 <Jon.Ortego@imst.de>
Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 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 <afaerber@suse.de>");
+MODULE_LICENSE("GPL");
-- 
2.16.4

^ permalink raw reply related

* [RFC net-next 08/15] net: lora: sx1276: Add debugfs
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

Allow some interactive inspection at runtime via debugfs.

Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 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

^ permalink raw reply related

* [RFC net-next 06/15] net: lora: Prepare for device drivers
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: Matthias Brugger, Jiri Pirko, Marcel Holtmann, Dollar Chen,
	linux-kernel, Michael Röder, Janus Piwek,
	Andreas Färber, Jian-Hong Pan, Ken Yu, David S . Miller,
	linux-arm-kernel
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

Implement helper functions for use by LoRa device drivers.

Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 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


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [RFC net-next 05/15] HACK: net: lora: Deal with .poll_mask in 4.18-rc2
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: Matthias Brugger, Jiri Pirko, Marcel Holtmann, Dollar Chen,
	linux-kernel, Michael Röder, Janus Piwek,
	Andreas Färber, Jian-Hong Pan, Ken Yu, David S . Miller,
	linux-arm-kernel
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

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 <afaerber@suse.de>
---
 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


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [RFC net-next 04/15] net: Add lora subsystem
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: Matthias Brugger, Jiri Pirko, Marcel Holtmann, Dollar Chen,
	linux-kernel, Michael Röder, Janus Piwek,
	Andreas Färber, Jian-Hong Pan, Ken Yu, David S . Miller,
	linux-arm-kernel
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

Implement or stub out PF_LORA net_proto_family and datagram proto_ops.

Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 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 <afaerber@suse.de>");
+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


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [RFC net-next 03/15] net: lora: Add protocol numbers
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Jian-Hong Pan, Jiri Pirko,
	Marcel Holtmann, David S . Miller, Matthias Brugger, Janus Piwek,
	Michael Röder, Dollar Chen, Ken Yu, Andreas Färber
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

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 <afaerber@suse.de>
---
 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

^ permalink raw reply related

* [RFC net-next 02/15] net: lora: Define sockaddr_lora
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: Matthias Brugger, Jiri Pirko, Marcel Holtmann, Dollar Chen,
	linux-kernel, Michael Röder, Janus Piwek,
	Andreas Färber, Jian-Hong Pan, Ken Yu, David S . Miller,
	linux-arm-kernel
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

Prepare a uapi linux/lora.h header defining sockaddr_lora for AF_LORA.

Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 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-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [RFC net-next 01/15] net: Reserve protocol numbers for LoRa
From: Andreas Färber @ 2018-07-01 11:07 UTC (permalink / raw)
  To: netdev
  Cc: Matthias Brugger, Jiri Pirko, Paul Moore, Marcel Holtmann,
	Dollar Chen, linux-kernel, Michael Röder, Janus Piwek,
	selinux, Andreas Färber, Eric Paris, Jian-Hong Pan, Ken Yu,
	Stephen Smalley, David S . Miller, linux-arm-kernel
In-Reply-To: <20180701110804.32415-1-afaerber@suse.de>

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 <afaerber@suse.de>
---
 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


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* Re: [PATCH net] tcp: prevent bogus FRTO undos with non-SACK flows
From: David Miller @ 2018-07-01 10:24 UTC (permalink / raw)
  To: ilpo.jarvinen; +Cc: netdev, ycheng, edumazet, ncardwell, mkubecek
In-Reply-To: <alpine.DEB.2.20.1806291300450.29120@whs-18.cs.helsinki.fi>

From: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Date: Fri, 29 Jun 2018 13:07:53 +0300 (EEST)

> If SACK is not enabled and the first cumulative ACK after the RTO
> retransmission covers more than the retransmitted skb, a spurious
> FRTO undo will trigger (assuming FRTO is enabled for that RTO).
> The reason is that any non-retransmitted segment acknowledged will
> set FLAG_ORIG_SACK_ACKED in tcp_clean_rtx_queue even if there is
> no indication that it would have been delivered for real (the
> scoreboard is not kept with TCPCB_SACKED_ACKED bits in the non-SACK
> case so the check for that bit won't help like it does with SACK).
> Having FLAG_ORIG_SACK_ACKED set results in the spurious FRTO undo
> in tcp_process_loss.
> 
> We need to use more strict condition for non-SACK case and check
> that none of the cumulatively ACKed segments were retransmitted
> to prove that progress is due to original transmissions. Only then
> keep FLAG_ORIG_SACK_ACKED set, allowing FRTO undo to proceed in
> non-SACK case.
> 
> (FLAG_ORIG_SACK_ACKED is planned to be renamed to FLAG_ORIG_PROGRESS
> to better indicate its purpose but to keep this change minimal, it
> will be done in another patch).
> 
> Besides burstiness and congestion control violations, this problem
> can result in RTO loop: When the loss recovery is prematurely
> undoed, only new data will be transmitted (if available) and
> the next retransmission can occur only after a new RTO which in case
> of multiple losses (that are not for consecutive packets) requires
> one RTO per loss to recover.
> 
> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>

Applied and queued up for -stable, thanks!

^ permalink raw reply

* Re: [PATCH bpf-next 2/3] bpf: btf: add btf json print functionality
From: Okash Khawaja @ 2018-07-01 10:31 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Daniel Borkmann, Martin KaFai Lau, Alexei Starovoitov,
	Yonghong Song, Quentin Monnet, David S. Miller, netdev,
	kernel-team, linux-kernel
In-Reply-To: <0360a47f-1056-3117-31ec-7c3f5d6dbccb@iogearbox.net>

On Wed, Jun 27, 2018 at 02:56:49PM +0200, Daniel Borkmann wrote:
> On 06/27/2018 01:47 PM, Okash Khawaja wrote:
> > On Wed, Jun 27, 2018 at 12:34:35PM +0200, Daniel Borkmann wrote:
> >> On 06/27/2018 12:35 AM, Jakub Kicinski wrote:
> >>> On Tue, 26 Jun 2018 15:27:09 -0700, Martin KaFai Lau wrote:
> >>>> On Tue, Jun 26, 2018 at 01:31:33PM -0700, Jakub Kicinski wrote:
> >> [...]
> >>>>> Implementing both outputs in one series will help you structure your
> >>>>> code to best suit both of the formats up front.  
> >>>> hex and "formatted" are the only things missing?  As always, things
> >>>> can be refactored when new use case comes up.  Lets wait for
> >>>> Okash input.
> >>>>
> >>>> Regardless, plaintext is our current use case.  Having the current
> >>>> patchset in does not stop us or others from contributing other use
> >>>> cases (json, "bpftool map find"...etc),  and IMO it is actually
> >>>> the opposite.  Others may help us get there faster than us alone.
> >>>> We should not stop making forward progress and take this patch
> >>>> as hostage because "abc" and "xyz" are not done together.
> >>>
> >>> Parity between JSON and plain text output is non negotiable.
> >>
> >> Longish discussion and some confusion in this thread. :-) First of all
> >> thanks a lot for working on it, very useful! 
> > Thanks :)
> > 
> >> My $0.02 on it is that so far
> >> great care has been taken in bpftool to indeed have feature parity between
> >> JSON and plain text, so it would be highly desirable to keep continuing
> >> this practice if the consensus is that it indeed is feasible and makes
> >> sense wrt BTF data. There has been mentioned that given BTF data can be
> >> dynamic depending on what the user loads via bpf(2) so a potential JSON
> >> output may look different/break each time anyway. This however could all be
> >> embedded under a container object that has a fixed key like 'formatted'
> >> where tools like jq(1) can query into it. I think this would be fine since
> >> the rest of the (non-dynamic) output is still retained as-is and then
> >> wouldn't confuse or collide with existing users, and anyone programmatically
> >> parsing deeper into the BTF data under such JSON container object needs
> >> to have awareness of what specific data it wants to query from it; so
> >> there's no conflict wrt breaking anything here. Imho, both outputs would
> >> be very valuable.
> > Okay I can add "formatted" object under json output.
> > 
> > One thing to note here is that the fixed output will change if the map
> > itself changes. So someone writing a program that consumes that fixed
> > output will have to account for his program breaking in future, thus
> 
> Yes, that aspect is fine though, any program/script parsing this would need
> to be aware of the underlying map type to make sense of it (e.g. per-cpu vs
> non per-cpu maps to name one). But that info it could query/verify already
> beforehand via bpftool as well (via normal map info dump for a given id).
> 
> > breaking backward compatibility anyway as far as the developer is
> > concerned :)
> > 
> > I will go ahead with work on "formatted" object.
> 
> Cool, thanks,
> Daniel


hi,

couple of questions:

1. just to be sure, formatted section will be on the same level as "key"
and "value"? so something like following:


$ bpftool map dump -p id 8
[{
        "key": ["0x00","0x00","0x00","0x00"
        ],
        "value": [...
        ],
        "formatted": {
                "key": 0,
                "value": {
                        "int_field":  3,
                        "pointerfield": 2152930552,
                        ...
                }
        }
}]

2. i noticed that the ouput in v1 has all the keys and values on the
same level. in v2, i'll change them so that each key-value pair is a
separate object. let me know what you think.

finally, i noticed there is a map lookup command which also prints map
entries. do want that to also be btf-printed in this patchset?

thanks,
okash

^ permalink raw reply

* [PATCH v3 4/4] Hook into set_rx_mode to admit multicast traffic
From: Miguel Rodríguez Pérez @ 2018-07-01  9:05 UTC (permalink / raw)
  To: oliver, linux-usb, netdev, gregkh; +Cc: Miguel Rodríguez Pérez
In-Reply-To: <20180701090553.7776-1-miguel@det.uvigo.gal>

We set set_rx_mode to usbnet_cdc_update_filter provided
by cdc_ether that simply admits all multicast traffic
if there is more than one multicast filter configured.

Signed-off-by: Miguel Rodríguez Pérez <miguel@det.uvigo.gal>
---
 drivers/net/usb/cdc_ncm.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index d6b51e2b9495..fda0af0b5d3c 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1652,6 +1652,7 @@ static const struct driver_info cdc_ncm_info = {
 	.status = cdc_ncm_status,
 	.rx_fixup = cdc_ncm_rx_fixup,
 	.tx_fixup = cdc_ncm_tx_fixup,
+	.set_rx_mode = usbnet_cdc_update_filter,
 };
 
 /* Same as cdc_ncm_info, but with FLAG_WWAN */
@@ -1665,6 +1666,7 @@ static const struct driver_info wwan_info = {
 	.status = cdc_ncm_status,
 	.rx_fixup = cdc_ncm_rx_fixup,
 	.tx_fixup = cdc_ncm_tx_fixup,
+	.set_rx_mode = usbnet_cdc_update_filter,
 };
 
 /* Same as wwan_info, but with FLAG_NOARP  */
@@ -1678,6 +1680,7 @@ static const struct driver_info wwan_noarp_info = {
 	.status = cdc_ncm_status,
 	.rx_fixup = cdc_ncm_rx_fixup,
 	.tx_fixup = cdc_ncm_tx_fixup,
+	.set_rx_mode = usbnet_cdc_update_filter,
 };
 
 static const struct usb_device_id cdc_devs[] = {
-- 
2.17.1

^ permalink raw reply related

* [PATCH v3 3/4] Replace the way cdc_ncm hooks into usbnet_change_mtu
From: Miguel Rodríguez Pérez @ 2018-07-01  9:05 UTC (permalink / raw)
  To: oliver, linux-usb, netdev, gregkh; +Cc: Miguel Rodríguez Pérez
In-Reply-To: <20180701090553.7776-1-miguel@det.uvigo.gal>

Previously cdc_ncm overwrited netdev_ops used by usbnet
thus preventing hooking into set_rx_mode. This patch
preserves usbnet hooks into netdev_ops, and add an
additional one for change_mtu needed by cdc_ncm.

Signed-off-by: Miguel Rodríguez Pérez <miguel@det.uvigo.gal>
---
 drivers/net/usb/cdc_ncm.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 9e1b74590682..d6b51e2b9495 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -750,16 +750,7 @@ int cdc_ncm_change_mtu(struct net_device *net, int new_mtu)
 }
 EXPORT_SYMBOL_GPL(cdc_ncm_change_mtu);
 
-static const struct net_device_ops cdc_ncm_netdev_ops = {
-	.ndo_open	     = usbnet_open,
-	.ndo_stop	     = usbnet_stop,
-	.ndo_start_xmit	     = usbnet_start_xmit,
-	.ndo_tx_timeout	     = usbnet_tx_timeout,
-	.ndo_get_stats64     = usbnet_get_stats64,
-	.ndo_change_mtu	     = cdc_ncm_change_mtu,
-	.ndo_set_mac_address = eth_mac_addr,
-	.ndo_validate_addr   = eth_validate_addr,
-};
+static struct net_device_ops cdc_ncm_netdev_ops;
 
 int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags)
 {
@@ -939,6 +930,8 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
 	dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group;
 
 	/* must handle MTU changes */
+	cdc_ncm_netdev_ops = *dev->net->netdev_ops;
+	cdc_ncm_netdev_ops.ndo_change_mtu = cdc_ncm_change_mtu;
 	dev->net->netdev_ops = &cdc_ncm_netdev_ops;
 	dev->net->max_mtu = cdc_ncm_max_dgram_size(dev) - cdc_ncm_eth_hlen(dev);
 
-- 
2.17.1

^ permalink raw reply related

* [PATCH v3 2/4] Export usbnet_cdc_update_filter
From: Miguel Rodríguez Pérez @ 2018-07-01  9:05 UTC (permalink / raw)
  To: oliver, linux-usb, netdev, gregkh; +Cc: Miguel Rodríguez Pérez
In-Reply-To: <20180701090553.7776-1-miguel@det.uvigo.gal>

This makes the function avaiable to other drivers, like cdn_ncm.

Signed-off-by: Miguel Rodríguez Pérez <miguel@det.uvigo.gal>
---
 drivers/net/usb/cdc_ether.c | 3 ++-
 include/linux/usb/usbnet.h  | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 815ed0dc18fe..54472ab77b90 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -75,7 +75,7 @@ static const u8 mbm_guid[16] = {
 	0xa6, 0x07, 0xc0, 0xff, 0xcb, 0x7e, 0x39, 0x2a,
 };
 
-static void usbnet_cdc_update_filter(struct usbnet *dev)
+void usbnet_cdc_update_filter(struct usbnet *dev)
 {
 	struct net_device *net = dev->net;
 
@@ -99,6 +99,7 @@ static void usbnet_cdc_update_filter(struct usbnet *dev)
 			NULL,
 			0);
 }
+EXPORT_SYMBOL_GPL(usbnet_cdc_update_filter);
 
 /* probes control interface, claims data interface, collects the bulk
  * endpoints, activates data interface (if needed), maybe sets MTU.
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index e2ec3582e549..7821cf1dcd60 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -286,4 +286,5 @@ extern void usbnet_update_max_qlen(struct usbnet *dev);
 extern void usbnet_get_stats64(struct net_device *dev,
 			       struct rtnl_link_stats64 *stats);
 
+extern void usbnet_cdc_update_filter(struct usbnet *);
 #endif /* __LINUX_USB_USBNET_H */
-- 
2.17.1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox