All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jian-Hong Pan <starnight@g.ncu.edu.tw>
To: "Andreas Färber" <afaerber@suse.de>,
	"David S . Miller" <davem@davemloft.net>,
	"Alan Cox" <gnomes@lxorguk.ukuu.org.uk>
Cc: linux-lpwan@lists.infradead.org, netdev@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org,
	Marcel Holtmann <marcel@holtmann.org>,
	Dollar Chen <dollar.chen@wtmec.com>,
	Ken Yu <ken.yu@rakwireless.com>,
	linux-wpan@vger.kernel.org,
	Jian-Hong Pan <starnight@g.ncu.edu.tw>
Subject: [PATCH v5 5/6] net: maclorawan: Implement maclorawan class module
Date: Sun, 16 Dec 2018 18:18:59 +0800	[thread overview]
Message-ID: <20181216101858.9585-6-starnight@g.ncu.edu.tw> (raw)
In-Reply-To: <CAC=mGziyi1ierhg++SUcHMrq1JQ0vH4gZCKUZgchzb8aD1Rv5Q@mail.gmail.com>

LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa devices.

This patch implements part of Class A end-devices SoftMAC defined in
LoRaWAN(TM) Specification Ver. 1.0.2:
1. End-device receive slot timing
2. Only single channel and single data rate for now
3. Unconfirmed data up/down message types

On the other side, it defines the basic interface and operation
functions for compatible LoRa device drivers.

Signed-off-by: Jian-Hong Pan <starnight@g.ncu.edu.tw>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
  LoRaWAN Soft MAC modules
- Modify for Big/Little-Endian
- Use SPDX license identifiers

V3:
- Remove the decoration word - inline of the functions
- Order local variables from longest to shortest line in the functions
- Change the calling mac_cb function to lrw_get_mac_cb macro

V4:
- Fix the delay period between RX window#1 and window#2
- Fix by coding style report from scripts/checkpatch.pl

V5:
- Initial rx_skb_list when it is allocated with LoRa hardware
- Check the sk_buff's data length before access it
- Deal FPort field and decrypt payload in lrw_parse_frame function
- Drop the recieved frame if parse failed
- Fix the bug which passes wrong skb properties from maclorawan to lorawan module

 net/maclorawan/Kconfig  |  14 +
 net/maclorawan/Makefile |   2 +
 net/maclorawan/mac.c    | 555 ++++++++++++++++++++++++++++++++++++
 net/maclorawan/main.c   | 606 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1177 insertions(+)
 create mode 100644 net/maclorawan/Kconfig
 create mode 100644 net/maclorawan/Makefile
 create mode 100644 net/maclorawan/mac.c
 create mode 100644 net/maclorawan/main.c

diff --git a/net/maclorawan/Kconfig b/net/maclorawan/Kconfig
new file mode 100644
index 000000000000..d700314edf26
--- /dev/null
+++ b/net/maclorawan/Kconfig
@@ -0,0 +1,14 @@
+config MACLORAWAN
+	tristate "Generic LoRaWAN Soft Networking Stack (maclorawan)"
+	depends on LORAWAN
+	select CRYPTO
+	select CRYPTO_CMAC
+	select CRYPTO_CBC
+	select CRYPTO_AES
+	help
+	  This option enables the hardware independent LoRaWAN
+	  networking stack for SoftMAC devices (the ones implementing
+	  only PHY level of LoRa standard).
+
+	  If you plan to use HardMAC LoRaWAN devices, you can say N
+	  here.  Alternatively you can say M to compile it as a module.
diff --git a/net/maclorawan/Makefile b/net/maclorawan/Makefile
new file mode 100644
index 000000000000..562831e66c82
--- /dev/null
+++ b/net/maclorawan/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MACLORAWAN)	+= maclorawan.o
+maclorawan-objs			:= main.o mac.o crypto.o
diff --git a/net/maclorawan/mac.c b/net/maclorawan/mac.c
new file mode 100644
index 000000000000..459ab95ff52d
--- /dev/null
+++ b/net/maclorawan/mac.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <starnight@g.ncu.edu.tw>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/lora/lorawan.h>
+#include <linux/lora/lorawan_netdev.h>
+
+#include "maclorawan.h"
+#include "crypto.h"
+
+static void rx_timeout_work(struct work_struct *work);
+
+struct lrw_session *
+lrw_alloc_ss(struct lrw_struct *lrw_st)
+{
+	struct lrw_session *ss;
+
+	ss = kzalloc(sizeof(*ss), GFP_KERNEL);
+	if (!ss)
+		goto lrw_alloc_ss_end;
+
+	ss->lrw_st = lrw_st;
+	ss->devaddr = lrw_st->devaddr;
+	INIT_LIST_HEAD(&ss->entry);
+
+	ss->tx_should_ack = false;
+	ss->retry = 3;
+	spin_lock_init(&ss->state_lock);
+	INIT_WORK(&ss->timeout_work, rx_timeout_work);
+
+lrw_alloc_ss_end:
+	return ss;
+}
+
+void
+lrw_free_ss(struct lrw_session *ss)
+{
+	netdev_dbg(ss->lrw_st->ndev, "%s\n", __func__);
+	if (ss->tx_skb)
+		consume_skb(ss->tx_skb);
+	netdev_dbg(ss->lrw_st->ndev, "%s: free rx skb\n", __func__);
+	if (ss->rx_skb)
+		consume_skb(ss->rx_skb);
+
+	netdev_dbg(ss->lrw_st->ndev, "%s: free ss\n", __func__);
+	kfree(ss);
+}
+
+void
+lrw_del_ss(struct lrw_session *ss)
+{
+	netdev_dbg(ss->lrw_st->ndev, "%s\n", __func__);
+	list_del(&ss->entry);
+	lrw_free_ss(ss);
+}
+
+void
+lrw_del_all_ss(struct lrw_struct *lrw_st)
+{
+	struct lrw_session *ss, *tmp;
+
+	mutex_lock(&lrw_st->ss_list_lock);
+	lrw_st->_cur_ss = NULL;
+	list_for_each_entry_safe(ss, tmp, &lrw_st->ss_list, entry) {
+		del_timer(&ss->timer);
+		lrw_del_ss(ss);
+	}
+	mutex_unlock(&lrw_st->ss_list_lock);
+}
+
+void
+lrw_ready_hw(struct lrw_struct *lrw_st)
+{
+	lrw_st->state = LRW_STATE_IDLE;
+}
+
+int
+lrw_start_hw(struct lrw_struct *lrw_st)
+{
+	int ret = 0;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+	lrw_st->nwks_shash_tfm = lrw_mic_key_setup(lrw_st->nwkskey,
+						   LRW_KEY_LEN);
+	lrw_st->nwks_skc_tfm = lrw_encrypt_key_setup(lrw_st->nwkskey,
+						     LRW_KEY_LEN);
+	lrw_st->apps_skc_tfm = lrw_encrypt_key_setup(lrw_st->appskey,
+						     LRW_KEY_LEN);
+	lrw_st->state = LRW_START;
+	ret = lrw_st->ops->start(&lrw_st->hw);
+	if (!ret)
+		lrw_ready_hw(lrw_st);
+
+	return ret;
+}
+
+void
+lrw_stop_hw(struct lrw_struct *lrw_st)
+{
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+	lrw_st->state = LRW_STOP;
+	netdev_dbg(lrw_st->ndev, "%s: going to stop hardware\n", __func__);
+	lrw_st->ops->stop(&lrw_st->hw);
+
+	netdev_dbg(lrw_st->ndev, "%s: going to kill tasks & flush works",
+		   __func__);
+	tasklet_kill(&lrw_st->xmit_task);
+	flush_work(&lrw_st->rx_work);
+
+	netdev_dbg(lrw_st->ndev, "%s: going to delete all session\n", __func__);
+	lrw_del_all_ss(lrw_st);
+
+	netdev_dbg(lrw_st->ndev, "%s: going to free mic tfm\n", __func__);
+	lrw_mic_key_free(lrw_st->nwks_shash_tfm);
+	netdev_dbg(lrw_st->ndev, "%s: going to free nwks tfm\n", __func__);
+	lrw_encrypt_key_free(lrw_st->nwks_skc_tfm);
+	netdev_dbg(lrw_st->ndev, "%s: going to free apps tfm\n", __func__);
+	lrw_encrypt_key_free(lrw_st->apps_skc_tfm);
+}
+
+void
+lrw_prepare_tx_frame(struct lrw_session *ss)
+{
+	struct lrw_struct *lrw_st = ss->lrw_st;
+	struct sk_buff *skb = ss->tx_skb;
+	u8 mhdr, fctrl, fport;
+	u8 mic[LRW_MIC_LEN];
+	__le32 le_devaddr;
+	__le16 le_fcnt_up;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	/* Encrypt the plain buffer content */
+	lrw_encrypt_buf(lrw_st->apps_skc_tfm, LRW_UPLINK,
+			ss->devaddr, ss->fcnt_up, skb->data, skb->len);
+
+	/* Push FPort */
+	if (skb->len) {
+		fport = ss->fport;
+		memcpy(skb_push(skb, LRW_FPORT_LEN), &fport, LRW_FPORT_LEN);
+	}
+
+	/* Push FCnt_Up */
+	le_fcnt_up = cpu_to_le16(ss->fcnt_up);
+	memcpy(skb_push(skb, LRW_FCNT_LEN), &le_fcnt_up, LRW_FCNT_LEN);
+
+	/* Push FCtrl */
+	fctrl = 0;
+	if (lrw_st->rx_should_ack) {
+		fctrl |= 0x20;
+		lrw_st->rx_should_ack = false;
+	}
+	memcpy(skb_push(skb, LRW_FCTRL_LEN), &fctrl, LRW_FCTRL_LEN);
+
+	/* Push DevAddr */
+	le_devaddr = cpu_to_le32(ss->devaddr);
+	memcpy(skb_push(skb, LRW_DEVADDR_LEN), &le_devaddr, LRW_DEVADDR_LEN);
+
+	/* Push MHDR */
+	mhdr = LRW_UNCONFIRMED_DATA_UP << 5;
+	if ((mhdr & (0x6 << 5)) == (0x4 << 5))
+		ss->tx_should_ack = true;
+	memcpy(skb_push(skb, LRW_MHDR_LEN), &mhdr, LRW_MHDR_LEN);
+
+	/* Put MIC */
+	lrw_calc_mic(lrw_st->nwks_shash_tfm, LRW_UPLINK,
+		     ss->devaddr, ss->fcnt_up, skb->data, skb->len, mic);
+	memcpy(skb_put(skb, LRW_MIC_LEN), mic, LRW_MIC_LEN);
+}
+
+void
+lrw_xmit(unsigned long data)
+{
+	struct lrw_struct *lrw_st = (struct lrw_struct *)data;
+	struct lrw_session *ss = lrw_st->_cur_ss;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+	ss->state = LRW_XMITTING_SS;
+	lrw_st->ops->xmit_async(&lrw_st->hw, ss->tx_skb);
+}
+
+int
+lrw_parse_frame(struct lrw_session *ss, struct sk_buff *skb)
+{
+	struct lrw_struct *lrw_st = ss->lrw_st;
+	struct lrw_fhdr *fhdr = &ss->rx_fhdr;
+	__le16 *p_fcnt;
+
+	pr_debug("%s: %s\n", LORAWAN_MODULE_NAME, __func__);
+
+	/* Get message type */
+	fhdr->mtype = skb->data[0];
+	skb_pull(skb, LRW_MHDR_LEN);
+
+	/* Trim Device Address */
+	skb_pull(skb, LRW_DEVADDR_LEN);
+
+	/* Get frame control */
+	fhdr->fctrl = skb->data[0];
+	skb_pull(skb, LRW_FCTRL_LEN);
+
+	/* Ack the original TX frame if it should be acked */
+	if (ss->tx_should_ack && (fhdr->fctrl & 0x20))
+		ss->tx_should_ack = false;
+
+	/* Get frame count */
+	p_fcnt = (__le16 *)skb->data;
+	fhdr->fcnt = le16_to_cpu(*p_fcnt);
+	skb_pull(skb, LRW_FCNT_LEN);
+
+	/* Get frame options */
+	fhdr->fopts_len = fhdr->fctrl & 0xF;
+	if (fhdr->fopts_len > 0) {
+		if (skb->len < fhdr->fopts_len)
+			goto lrw_parse_frame_err;
+		memcpy(fhdr->fopts, skb->data, fhdr->fopts_len);
+		skb_pull(skb, fhdr->fopts_len);
+	}
+
+	/* TODO: Parse frame options */
+
+	/* Parse FPort and decrypt payload */
+	if (skb->len > 0) {
+		if (skb->len <= LRW_FPORT_LEN)
+			goto lrw_parse_frame_err;
+
+		/* TODO: Deal FPort */
+
+		skb_pull(skb, LRW_FPORT_LEN);
+
+		lrw_decrypt_buf(lrw_st->apps_skc_tfm, LRW_DOWNLINK,
+				ss->devaddr, fhdr->fcnt, skb->data, skb->len);
+	}
+
+	return 0;
+
+lrw_parse_frame_err:
+	return -EMSGSIZE;
+}
+
+struct lrw_session *
+lrw_rx_skb_2_session(struct lrw_struct *lrw_st, struct sk_buff *rx_skb)
+{
+	struct lrw_session *ss;
+	__le16 *p_fcnt;
+	u16 fcnt;
+	u16 ofs;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	ofs = LRW_MHDR_LEN + LRW_DEVADDR_LEN + LRW_FCTRL_LEN;
+	p_fcnt = (__le16 *)(rx_skb->data + ofs);
+	fcnt = le16_to_cpu(*p_fcnt);
+
+	/* Find the corresponding session */
+	ss = lrw_st->_cur_ss;
+
+	/* Frame count downlink check */
+	if (fcnt >= (ss->fcnt_down & 0xFFFF))
+		ss->rx_skb = rx_skb;
+	else
+		ss = NULL;
+
+	return ss;
+}
+
+void
+lrw_rx_work(struct work_struct *work)
+{
+	struct lrw_struct *lrw_st;
+	struct lrw_session *ss;
+	struct sk_buff *skb;
+
+	lrw_st = container_of(work, struct lrw_struct, rx_work);
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	skb = skb_dequeue(&lrw_st->rx_skb_list);
+
+	/* Check and parse the RX frame */
+	ss = lrw_rx_skb_2_session(lrw_st, skb);
+	if (!ss)
+		goto lrw_rx_work_not_new_frame;
+
+	if (lrw_parse_frame(ss, skb)) {
+		ss->rx_skb = NULL;
+		goto lrw_rx_work_not_new_frame;
+	}
+
+	/* Check the TX frame is acked or not */
+	if (ss->tx_should_ack) {
+		ss->rx_skb = NULL;
+		goto lrw_rx_work_not_new_frame;
+	}
+
+	/* The TX frame is acked or no need to be acked */
+	del_timer(&ss->timer);
+	lrw_st->rx_should_ack = (ss->rx_fhdr.mtype & 0xC0) == 0x40;
+
+	lrw_st->ndev->stats.rx_packets++;
+	lrw_st->ndev->stats.rx_bytes += ss->rx_skb->len;
+
+	spin_lock_bh(&ss->state_lock);
+	ss->state = LRW_RXRECEIVED_SS;
+	spin_unlock_bh(&ss->state_lock);
+
+	if (ss->rx_skb->len > 0) {
+		lrw_get_mac_cb(skb)->devaddr = lrw_st->devaddr;
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		skb->protocol = htons(ETH_P_LORAWAN);
+		skb->pkt_type = PACKET_HOST;
+		netif_receive_skb(skb);
+	}
+
+	ss->rx_skb = NULL;
+
+	mutex_lock(&lrw_st->ss_list_lock);
+	lrw_st->fcnt_down = ss->rx_fhdr.fcnt;
+	lrw_st->_cur_ss = NULL;
+	lrw_del_ss(ss);
+	lrw_st->state = LRW_STATE_IDLE;
+	mutex_unlock(&lrw_st->ss_list_lock);
+
+	return;
+
+lrw_rx_work_not_new_frame:
+	/* Drop the RX frame if checked failed */
+	if (skb)
+		kfree_skb(skb);
+}
+
+int
+lrw_check_mic(struct crypto_shash *tfm, struct sk_buff *skb)
+{
+	u8 cks[LRW_MIC_LEN];
+	u32 devaddr;
+	size_t len;
+	u16 fcnt;
+	u8 *buf;
+	u8 *mic;
+	u16 ofs;
+
+	buf = skb->data;
+	devaddr = le32_to_cpu(*((__le32 *)(buf + LRW_MHDR_LEN)));
+	ofs = LRW_MHDR_LEN + LRW_DEVADDR_LEN + LRW_FCTRL_LEN;
+	fcnt = le16_to_cpu(*(__le16 *)(buf + ofs));
+	len = skb->len - LRW_MIC_LEN;
+	mic = skb->data + len;
+
+	lrw_calc_mic(tfm, LRW_DOWNLINK, devaddr, fcnt, buf, len, cks);
+
+	return (!memcmp(cks, mic, LRW_MIC_LEN));
+}
+
+/**
+ * lrw_rx_irqsave - Tell LoRaWAN module that there is new received frame
+ * @hw:		the LoRa device
+ * @skb:	the new received frame
+ */
+void
+lrw_rx_irqsave(struct lrw_hw *hw, struct sk_buff *skb)
+{
+	struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+	u32 devaddr;
+	u8 mtype;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	if (skb->len < LRW_MHDR_LEN + LRW_DEVADDR_LEN + LRW_FCTRL_LEN +
+	    LRW_FCNT_LEN + LRW_MIC_LEN)
+		goto lrw_rx_irqsave_err;
+
+	mtype = skb->data[0] >> 5;
+	devaddr = le32_to_cpu(*(__le32 *)(skb->data + LRW_MHDR_LEN));
+
+	/* Check the frame is the downlink frame */
+	if ((mtype == LRW_UNCONFIRMED_DATA_DOWN ||
+	     mtype == LRW_CONFIRMED_DATA_DOWN) &&
+	     devaddr == lrw_st->devaddr &&
+	     lrw_check_mic(lrw_st->nwks_shash_tfm, skb)) {
+		/* Remove message integrity code (MIC) */
+		skb_trim(skb, skb->len - LRW_MIC_LEN);
+		skb_queue_tail(&lrw_st->rx_skb_list, skb);
+		schedule_work(&lrw_st->rx_work);
+		return;
+	}
+
+lrw_rx_irqsave_err:
+	kfree_skb(skb);
+}
+EXPORT_SYMBOL(lrw_rx_irqsave);
+
+static void
+lrw_rexmit(struct timer_list *timer)
+{
+	struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+	struct lrw_struct *lrw_st = ss->lrw_st;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	lrw_st->state = LRW_STATE_TX;
+	lrw_xmit((unsigned long)lrw_st);
+}
+
+static void
+rx_timeout_work(struct work_struct *work)
+{
+	struct lrw_struct *lrw_st;
+	struct lrw_session *ss;
+
+	ss = container_of(work, struct lrw_session, timeout_work);
+	lrw_st = ss->lrw_st;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+	mutex_lock(&lrw_st->ss_list_lock);
+	lrw_st->_cur_ss = NULL;
+	lrw_st->state = LRW_STATE_IDLE;
+	lrw_del_ss(ss);
+	mutex_unlock(&lrw_st->ss_list_lock);
+}
+
+static void
+rx2_timeout_isr(struct timer_list *timer)
+{
+	struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+	struct lrw_struct *lrw_st = ss->lrw_st;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	/* Check TX is acked or not */
+	if (!ss->tx_should_ack) {
+		spin_lock_bh(&ss->state_lock);
+		if (ss->state != LRW_RXRECEIVED_SS)
+			ss->state = LRW_RXTIMEOUT_SS;
+		spin_unlock_bh(&ss->state_lock);
+
+		if (ss->state == LRW_RXTIMEOUT_SS) {
+			netdev_dbg(lrw_st->ndev, "%s: rx time out\n", __func__);
+			goto rx2_timeout_isr_no_retry_rx_frame;
+		} else {
+			return;
+		}
+	}
+
+	/* Check the session need to be retransmitted or not */
+	if (ss->retry > 0) {
+		ss->state = LRW_RETRANSMIT_SS;
+		ss->retry--;
+
+		/* Start timer for ack timeout and retransmit */
+		ss->timer.function = lrw_rexmit;
+		ss->timer.expires = jiffies_64 + ss->ack_timeout * HZ;
+		add_timer(&ss->timer);
+	} else {
+		/* Retry failed */
+rx2_timeout_isr_no_retry_rx_frame:
+		schedule_work(&ss->timeout_work);
+	}
+}
+
+static void
+rx2_delay_isr(struct timer_list *timer)
+{
+	struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+	struct lrw_struct *lrw_st = ss->lrw_st;
+	unsigned long delay;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	/* Start timer for RX2 window */
+	ss->timer.function = rx2_timeout_isr;
+	delay = jiffies_64 + (ss->rx2_window + 20) * HZ / 1000 + HZ;
+	ss->timer.expires = delay;
+	add_timer(&ss->timer);
+
+	/* Start LoRa hardware to RX2 window */
+	ss->state = LRW_RX2_SS;
+	lrw_st->ops->start_rx_window(&lrw_st->hw, ss->rx2_window + 20);
+}
+
+static void
+rx1_delay_isr(struct timer_list *timer)
+{
+	struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+	struct lrw_struct *lrw_st = ss->lrw_st;
+	unsigned long delay;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	/* Start timer for RX_Delay2 - RX_Delay2 */
+	ss->timer.function = rx2_delay_isr;
+	delay = jiffies_64 + (ss->rx_delay2 - ss->rx_delay1) * HZ;
+	ss->timer.expires = delay;
+	add_timer(&ss->timer);
+
+	/* Start LoRa hardware to RX1 window */
+	ss->state = LRW_RX1_SS;
+	lrw_st->ops->start_rx_window(&lrw_st->hw, ss->rx1_window + 20);
+}
+
+void
+lrw_sent_tx_work(struct lrw_struct *lrw_st, struct sk_buff *skb)
+{
+	struct lrw_session *ss = lrw_st->_cur_ss;
+	struct net_device *ndev;
+	unsigned long delay;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	ss->state = LRW_XMITTED;
+
+	/* Start session timer for RX_Delay1 */
+	timer_setup(&ss->timer, rx1_delay_isr, 0);
+	delay = jiffies_64 + ss->rx_delay1 * HZ - 20 * HZ / 1000;
+	ss->timer.expires = delay;
+	add_timer(&ss->timer);
+
+	ndev = skb->dev;
+	ndev->stats.tx_packets++;
+	ndev->stats.tx_bytes += skb->len;
+	dev_consume_skb_any(skb);
+	ss->tx_skb = NULL;
+}
+
+/**
+ * lrw_xmit_complete - Tell LoRaWAN module that the frame is xmitted completely
+ * @hw:		the LoRa device
+ * @skb:	the xmitted frame
+ */
+void
+lrw_xmit_complete(struct lrw_hw *hw, struct sk_buff *skb)
+{
+	struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	lrw_sent_tx_work(lrw_st, skb);
+	lrw_st->state = LRW_STATE_RX;
+}
+EXPORT_SYMBOL(lrw_xmit_complete);
diff --git a/net/maclorawan/main.c b/net/maclorawan/main.c
new file mode 100644
index 000000000000..caecfb3f66c6
--- /dev/null
+++ b/net/maclorawan/main.c
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <starnight@g.ncu.edu.tw>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+
+#include <linux/lora/lorawan.h>
+#include <linux/lora/lorawan_netdev.h>
+#include "maclorawan.h"
+
+#define	PHY_NAME		"lora"
+
+/* Need to find a way to define or assign */
+#define	LORAWAN_MTU		20
+
+static struct class *lrw_sys_class;
+
+static void
+lrw_if_setup(struct net_device *ndev)
+{
+	ndev->addr_len = LRW_DEVADDR_LEN;
+	memset(ndev->broadcast, 0xFF, ndev->addr_len);
+	ndev->type = ARPHRD_LORAWAN;
+
+	ndev->hard_header_len = LRW_MHDR_LEN + LRW_FHDR_MAX_LEN + LRW_FPORT_LEN;
+	ndev->needed_tailroom = LRW_MIC_LEN;
+
+	/**
+	 * TODO: M should be a dynamic value defined by Regional Parameters,
+	 *	 Being fixed for now.  Going to be changed.
+	 */
+	ndev->mtu = LORAWAN_MTU;
+}
+
+/**
+ * lrw_alloc_hw - Allocate a memory space for the LoRa device
+ * @priv_data_len:	the private data size
+ * @lrw_operations:	the implemented operations of the LoRa device
+ *
+ * Return:		address of the LoRa device or NULL for failed
+ */
+struct lrw_hw *
+lrw_alloc_hw(size_t priv_data_len, struct lrw_operations *ops)
+{
+	struct lrw_struct *lrw_st;
+	struct net_device *ndev;
+	int ret;
+
+	if (WARN_ON(!ops || !ops->start || !ops->stop || !ops->xmit_async ||
+		    !ops->set_txpower || !ops->set_dr ||
+		    !ops->start_rx_window || !ops->set_state))
+		return NULL;
+
+	/* In memory it'll be like this:
+	 *
+	 * +-----------------------+
+	 * | struct net_device     |
+	 * +-----------------------+
+	 * | struct lrw_struct     |
+	 * +-----------------------+
+	 * | driver's private data |
+	 * +-----------------------+
+	 */
+	ndev = alloc_netdev(sizeof(struct lrw_struct) + priv_data_len,
+			    PHY_NAME "%d", NET_NAME_ENUM, lrw_if_setup);
+	if (!ndev)
+		return ERR_PTR(-ENOMEM);
+	ret = dev_alloc_name(ndev, ndev->name);
+	if (ret < 0)
+		goto lrw_alloc_hw_err;
+
+	lrw_st = (struct lrw_struct *)netdev_priv(ndev);
+	lrw_st->ndev = ndev;
+
+	lrw_st->state = LRW_STOP;
+	lrw_st->ops = ops;
+	lrw_st->hw.priv = (u8 *)lrw_st + sizeof(struct lrw_struct);
+	skb_queue_head_init(&lrw_st->rx_skb_list);
+
+	ndev->flags |= IFF_NOARP;
+	ndev->features |= NETIF_F_HW_CSUM;
+
+	return &lrw_st->hw;
+
+lrw_alloc_hw_err:
+	free_netdev(ndev);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(lrw_alloc_hw);
+
+/**
+ * lrw_free_hw - Free the LoRa device's memory resource
+ * @hw:		the LoRa device going to be freed
+ */
+void
+lrw_free_hw(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	free_netdev(lrw_st->ndev);
+}
+EXPORT_SYMBOL(lrw_free_hw);
+
+/**
+ * lrw_set_deveui - Set the LoRa device's DevEUI
+ * @hw:		the LoRa device going to be set
+ * @eui:	the global end-device ID in IEEE EUI64 address space
+ */
+void
+lrw_set_deveui(struct lrw_hw *hw, u64 eui)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	lrw_st->dev_eui = eui;
+}
+EXPORT_SYMBOL(lrw_set_deveui);
+
+/**
+ * lrw_get_deveui - Get the LoRa device's DevEUI
+ * @hw:		the LoRa device going to be got from
+ *
+ * Return:	the device's DevEUI in IEEE EUI64 address space
+ */
+u64
+lrw_get_deveui(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	return lrw_st->dev_eui;
+}
+EXPORT_SYMBOL(lrw_get_deveui);
+
+/**
+ * lrw_set_appeui - Set the LoRa device's AppEUI
+ * @hw:		the LoRa device going to be set
+ * @eui:	the global end-device ID in IEEE EUI64 address space
+ */
+void
+lrw_set_appeui(struct lrw_hw *hw, u64 eui)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	lrw_st->app_eui = eui;
+}
+EXPORT_SYMBOL(lrw_set_appeui);
+
+/**
+ * lrw_get_appeui - Get the LoRa device's AppEUI
+ * @hw:		the LoRa device going to be got from
+ *
+ * Return:	the device's AppEUI in IEEE EUI64 address space
+ */
+u64
+lrw_get_appeui(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	return lrw_st->app_eui;
+}
+EXPORT_SYMBOL(lrw_get_appeui);
+
+/**
+ * lrw_set_devaddr - Set the LoRa device's address
+ * @hw:		the LoRa device going to be set
+ * @devaddr:	the device address
+ */
+void
+lrw_set_devaddr(struct lrw_hw *hw, u32 devaddr)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	lrw_st->devaddr = devaddr;
+}
+EXPORT_SYMBOL(lrw_set_devaddr);
+
+/**
+ * lrw_get_devaddr - Get the LoRa device's address
+ * @hw:		the LoRa device going to be got from
+ *
+ * Return:	the device address
+ */
+u32
+lrw_get_devaddr(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	return lrw_st->devaddr;
+}
+EXPORT_SYMBOL(lrw_get_devaddr);
+
+/**
+ * lrw_add_hw - Add a LoRaWAN hardware as a network device
+ * @lrw_st:	the LoRa device going to be added
+ *
+ * Return:	0 / other number for success / failed
+ */
+static int
+lrw_add_hw(struct lrw_struct *lrw_st)
+{
+	struct net_device *ndev = lrw_st->ndev;
+	__be32 be_addr;
+	int ret;
+
+	lrw_st->fcnt_up = 0;
+	lrw_st->fcnt_down = 0;
+	lrw_st->_cur_ss = NULL;
+
+	mutex_init(&lrw_st->ss_list_lock);
+	INIT_LIST_HEAD(&lrw_st->ss_list);
+
+	tasklet_init(&lrw_st->xmit_task, lrw_xmit, (unsigned long)lrw_st);
+	INIT_WORK(&lrw_st->rx_work, lrw_rx_work);
+
+	be_addr = cpu_to_be32(lrw_st->devaddr);
+	memcpy(ndev->perm_addr, &be_addr, ndev->addr_len);
+	memcpy(ndev->dev_addr, ndev->perm_addr, ndev->addr_len);
+
+	write_pnet(&lrw_st->_net, &init_net);
+	ret = register_netdev(ndev);
+
+	return ret;
+}
+
+/**
+ * lrw_remove_hw - Remove a LoRaWAN hardware from a network device
+ * @lrw_st:	the LoRa device going to be removed
+ */
+static void
+lrw_remove_hw(struct lrw_struct *lrw_st)
+{
+	unregister_netdev(lrw_st->ndev);
+	tasklet_kill(&lrw_st->xmit_task);
+}
+
+bool
+ready2write(struct lrw_struct *lrw_st)
+{
+	bool status = false;
+
+	if (!lrw_st->_cur_ss && lrw_st->state == LRW_STATE_IDLE)
+		status = true;
+
+	return status;
+}
+
+bool
+ready2read(struct lrw_struct *lrw_st)
+{
+	struct lrw_session *ss;
+	bool status = false;
+
+	if (!list_empty(&lrw_st->ss_list) && lrw_st->state != LRW_STOP) {
+		ss = list_first_entry(&lrw_st->ss_list,
+				      struct lrw_session,
+				      entry);
+		if (ss->state == LRW_RXRECEIVED_SS)
+			status = true;
+	}
+
+	return status;
+}
+
+static int
+lrw_if_up(struct net_device *ndev)
+{
+	struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+	int ret = -EBUSY;
+
+	if (lrw_st->state == LRW_STOP) {
+		ret = lrw_start_hw(lrw_st);
+		netif_start_queue(ndev);
+	}
+
+	return ret;
+}
+
+static int
+lrw_if_down(struct net_device *ndev)
+{
+	struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+
+	if (lrw_st->state != LRW_STOP) {
+		netif_stop_queue(ndev);
+		lrw_stop_hw(lrw_st);
+	}
+
+	return 0;
+}
+
+static netdev_tx_t
+lrw_if_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+	struct lrw_session *ss;
+	netdev_tx_t ret;
+
+	ret = NETDEV_TX_OK;
+
+	ss = lrw_alloc_ss(lrw_st);
+	if (!ss)
+		return NETDEV_TX_BUSY;
+
+	mutex_lock(&lrw_st->ss_list_lock);
+	if (ready2write(lrw_st)) {
+		list_add_tail(&ss->entry, &lrw_st->ss_list);
+		lrw_st->state = LRW_STATE_TX;
+		lrw_st->_cur_ss = ss;
+		ss->fcnt_up = lrw_st->fcnt_up;
+		ss->fcnt_down = lrw_st->fcnt_down;
+		/* TODO: RX delay #1/#2 should be set by regional parameters */
+		ss->rx_delay1 = 1;
+		ss->rx_delay2 = 2;
+		ss->rx1_window = 500;
+		ss->rx2_window = 500;
+	} else {
+		ret = NETDEV_TX_BUSY;
+	}
+	mutex_unlock(&lrw_st->ss_list_lock);
+
+	if (ret == NETDEV_TX_OK) {
+		ss->state = LRW_INIT_SS;
+		ss->tx_skb = skb;
+		lrw_prepare_tx_frame(ss);
+		tasklet_schedule(&lrw_st->xmit_task);
+	} else {
+		lrw_free_ss(ss);
+	}
+
+	return ret;
+}
+
+static int
+lrw_if_get_addr(struct lrw_struct *lrw_st, struct sockaddr_lorawan *addr)
+{
+	int ret = 0;
+
+	switch (addr->addr_in.addr_type) {
+	case LRW_ADDR_DEVADDR:
+		addr->addr_in.devaddr = lrw_st->devaddr;
+		break;
+	case LRW_ADDR_DEVEUI:
+		addr->addr_in.dev_eui = lrw_st->dev_eui;
+		break;
+	case LRW_ADDR_APPEUI:
+		addr->addr_in.app_eui = lrw_st->app_eui;
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+
+static int
+lrw_if_set_addr(struct lrw_struct *lrw_st, struct sockaddr_lorawan *addr)
+{
+	struct lrw_hw *hw = &lrw_st->hw;
+	int ret = 0;
+
+	if (netif_running(lrw_st->ndev))
+		return -EBUSY;
+
+	switch (addr->addr_in.addr_type) {
+	case LRW_ADDR_DEVADDR:
+		lrw_set_devaddr(hw, addr->addr_in.devaddr);
+		break;
+	case LRW_ADDR_DEVEUI:
+		lrw_set_deveui(hw, addr->addr_in.dev_eui);
+		break;
+	case LRW_ADDR_APPEUI:
+		lrw_set_appeui(hw, addr->addr_in.app_eui);
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+
+static void
+swap_bytes(u8 *dst, u8 *src, size_t l)
+{
+	/* Human reading is big-endian, but LoRaWAN is little-endian */
+	unsigned int i;
+
+	for (i = 0; i < l; i++)
+		dst[i] = src[l - i - 1];
+}
+
+int
+lrw_set_key(struct lrw_hw *hw, u8 type, u8 *key, size_t key_len)
+{
+	struct lrw_struct *lrw_st;
+	int ret = 0;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+
+	netdev_dbg(lrw_st->ndev, "%s: type=%d\n", __func__, type);
+	if (lrw_st->state != LRW_STOP)
+		return -EINVAL;
+
+	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,
+		       16, 1, key, key_len, true);
+	switch (type) {
+	case LRW_APPKEY:
+		swap_bytes(lrw_st->appkey, key, key_len);
+		break;
+	case LRW_NWKSKEY:
+		swap_bytes(lrw_st->nwkskey, key, key_len);
+		break;
+	case LRW_APPSKEY:
+		swap_bytes(lrw_st->appskey, key, key_len);
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(lrw_set_key);
+
+int
+lrw_get_key(struct lrw_hw *hw, u8 type, u8 *key, size_t key_len)
+{
+	struct lrw_struct *lrw_st;
+	int ret = 0;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+
+	netdev_dbg(lrw_st->ndev, "%s: type=%d\n", __func__, type);
+	switch (type) {
+	case LRW_APPKEY:
+		swap_bytes(key, lrw_st->appkey, key_len);
+		break;
+	case LRW_NWKSKEY:
+		swap_bytes(key, lrw_st->nwkskey, key_len);
+		break;
+	case LRW_APPSKEY:
+		swap_bytes(key, lrw_st->appskey, key_len);
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+
+static int
+lrw_if_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+	struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+	struct sockaddr_lorawan *addr;
+	int ret = 0;
+
+	netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
+
+	/* I/O control by each command */
+	switch (cmd) {
+	/* Set & get the DevAddr, DevEUI and AppEUI */
+	case SIOCSIFADDR:
+		addr = (struct sockaddr_lorawan *)&ifr->ifr_addr;
+		ret = lrw_if_set_addr(lrw_st, addr);
+		break;
+	case SIOCGIFADDR:
+		addr = (struct sockaddr_lorawan *)&ifr->ifr_addr;
+		ret = lrw_if_get_addr(lrw_st, addr);
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+
+static int
+lrw_if_set_mac(struct net_device *ndev, void *p)
+{
+	struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+	struct sockaddr *addr = p;
+	__be32 *be_addr;
+
+	be_addr = (__be32 *)addr->sa_data;
+
+	netdev_dbg(ndev, "%s: AF_TYPE:%d set mac address %X\n",
+		   __func__, addr->sa_family, be32_to_cpu(*be_addr));
+
+	if (netif_running(ndev))
+		return -EBUSY;
+
+	lrw_set_devaddr(&lrw_st->hw, be32_to_cpu(*be_addr));
+	memcpy(ndev->dev_addr, be_addr, ndev->addr_len);
+
+	return 0;
+}
+
+static const struct net_device_ops lrw_if_ops = {
+	.ndo_open = lrw_if_up,
+	.ndo_stop = lrw_if_down,
+	.ndo_start_xmit = lrw_if_start_xmit,
+	.ndo_do_ioctl = lrw_if_ioctl,
+	.ndo_set_mac_address = lrw_if_set_mac,
+};
+
+/**
+ * lrw_register_hw - Register as a LoRaWAN compatible device
+ * @hw:		LoRa device going to be registered
+ *
+ * Return:	0 / negative number for success / error number
+ */
+int
+lrw_register_hw(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+	int ret;
+
+	device_initialize(&lrw_st->dev);
+	dev_set_name(&lrw_st->dev, netdev_name(lrw_st->ndev));
+	lrw_st->dev.class = lrw_sys_class;
+	lrw_st->dev.platform_data = lrw_st;
+
+	ret = device_add(&lrw_st->dev);
+	if (ret)
+		goto lrw_register_hw_end;
+
+	/* Add a LoRa device node as a network device */
+	lrw_st->ndev->netdev_ops = &lrw_if_ops;
+	ret = lrw_add_hw(lrw_st);
+	if (!ret)
+		netdev_info(lrw_st->ndev, "register\n");
+
+lrw_register_hw_end:
+	return ret;
+}
+EXPORT_SYMBOL(lrw_register_hw);
+
+/**
+ * lrw_unregister_hw - Unregister the LoRaWAN compatible device
+ * @hw:		LoRa device going to be unregistered
+ */
+void
+lrw_unregister_hw(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+
+	netdev_info(lrw_st->ndev, "unregister\n");
+
+	/* Stop and remove the LoRaWAM hardware from system */
+	if (lrw_st->state != LRW_STOP)
+		lrw_stop_hw(lrw_st);
+	device_del(&lrw_st->dev);
+	lrw_remove_hw(lrw_st);
+}
+EXPORT_SYMBOL(lrw_unregister_hw);
+
+static int __init
+lrw_init(void)
+{
+	int err = 0;
+
+	pr_info("%s: module inserted\n", LORAWAN_MODULE_NAME);
+
+	/* Create device class */
+	lrw_sys_class = class_create(THIS_MODULE, LORAWAN_MODULE_NAME);
+	if (IS_ERR(lrw_sys_class)) {
+		pr_err("%s: Failed to create a class of LoRaWAN\n",
+		       LORAWAN_MODULE_NAME);
+		err = PTR_ERR(lrw_sys_class);
+		goto lrw_init_end;
+	}
+
+	pr_debug("%s: class created\n", LORAWAN_MODULE_NAME);
+
+lrw_init_end:
+	return err;
+}
+
+static void __exit
+lrw_exit(void)
+{
+	/* Delete device class */
+	class_destroy(lrw_sys_class);
+	pr_info("%s: module removed\n", LORAWAN_MODULE_NAME);
+}
+
+module_init(lrw_init);
+module_exit(lrw_exit);
+
+MODULE_AUTHOR("Jian-Hong Pan, <starnight@g.ncu.edu.tw>");
+MODULE_DESCRIPTION("LoRaWAN soft MAC kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
2.20.0


WARNING: multiple messages have this Message-ID (diff)
From: Jian-Hong Pan <starnight@g.ncu.edu.tw>
To: "Andreas Färber" <afaerber@suse.de>,
	"David S . Miller" <davem@davemloft.net>,
	"Alan Cox" <gnomes@lxorguk.ukuu.org.uk>
Cc: netdev@vger.kernel.org, Marcel Holtmann <marcel@holtmann.org>,
	linux-lpwan@lists.infradead.org, linux-kernel@vger.kernel.org,
	Dollar Chen <dollar.chen@wtmec.com>,
	Ken Yu <ken.yu@rakwireless.com>,
	linux-wpan@vger.kernel.org,
	Jian-Hong Pan <starnight@g.ncu.edu.tw>,
	linux-arm-kernel@lists.infradead.org
Subject: [PATCH v5 5/6] net: maclorawan: Implement maclorawan class module
Date: Sun, 16 Dec 2018 18:18:59 +0800	[thread overview]
Message-ID: <20181216101858.9585-6-starnight@g.ncu.edu.tw> (raw)
In-Reply-To: <CAC=mGziyi1ierhg++SUcHMrq1JQ0vH4gZCKUZgchzb8aD1Rv5Q@mail.gmail.com>

LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa devices.

This patch implements part of Class A end-devices SoftMAC defined in
LoRaWAN(TM) Specification Ver. 1.0.2:
1. End-device receive slot timing
2. Only single channel and single data rate for now
3. Unconfirmed data up/down message types

On the other side, it defines the basic interface and operation
functions for compatible LoRa device drivers.

Signed-off-by: Jian-Hong Pan <starnight@g.ncu.edu.tw>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
  LoRaWAN Soft MAC modules
- Modify for Big/Little-Endian
- Use SPDX license identifiers

V3:
- Remove the decoration word - inline of the functions
- Order local variables from longest to shortest line in the functions
- Change the calling mac_cb function to lrw_get_mac_cb macro

V4:
- Fix the delay period between RX window#1 and window#2
- Fix by coding style report from scripts/checkpatch.pl

V5:
- Initial rx_skb_list when it is allocated with LoRa hardware
- Check the sk_buff's data length before access it
- Deal FPort field and decrypt payload in lrw_parse_frame function
- Drop the recieved frame if parse failed
- Fix the bug which passes wrong skb properties from maclorawan to lorawan module

 net/maclorawan/Kconfig  |  14 +
 net/maclorawan/Makefile |   2 +
 net/maclorawan/mac.c    | 555 ++++++++++++++++++++++++++++++++++++
 net/maclorawan/main.c   | 606 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1177 insertions(+)
 create mode 100644 net/maclorawan/Kconfig
 create mode 100644 net/maclorawan/Makefile
 create mode 100644 net/maclorawan/mac.c
 create mode 100644 net/maclorawan/main.c

diff --git a/net/maclorawan/Kconfig b/net/maclorawan/Kconfig
new file mode 100644
index 000000000000..d700314edf26
--- /dev/null
+++ b/net/maclorawan/Kconfig
@@ -0,0 +1,14 @@
+config MACLORAWAN
+	tristate "Generic LoRaWAN Soft Networking Stack (maclorawan)"
+	depends on LORAWAN
+	select CRYPTO
+	select CRYPTO_CMAC
+	select CRYPTO_CBC
+	select CRYPTO_AES
+	help
+	  This option enables the hardware independent LoRaWAN
+	  networking stack for SoftMAC devices (the ones implementing
+	  only PHY level of LoRa standard).
+
+	  If you plan to use HardMAC LoRaWAN devices, you can say N
+	  here.  Alternatively you can say M to compile it as a module.
diff --git a/net/maclorawan/Makefile b/net/maclorawan/Makefile
new file mode 100644
index 000000000000..562831e66c82
--- /dev/null
+++ b/net/maclorawan/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MACLORAWAN)	+= maclorawan.o
+maclorawan-objs			:= main.o mac.o crypto.o
diff --git a/net/maclorawan/mac.c b/net/maclorawan/mac.c
new file mode 100644
index 000000000000..459ab95ff52d
--- /dev/null
+++ b/net/maclorawan/mac.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <starnight@g.ncu.edu.tw>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/lora/lorawan.h>
+#include <linux/lora/lorawan_netdev.h>
+
+#include "maclorawan.h"
+#include "crypto.h"
+
+static void rx_timeout_work(struct work_struct *work);
+
+struct lrw_session *
+lrw_alloc_ss(struct lrw_struct *lrw_st)
+{
+	struct lrw_session *ss;
+
+	ss = kzalloc(sizeof(*ss), GFP_KERNEL);
+	if (!ss)
+		goto lrw_alloc_ss_end;
+
+	ss->lrw_st = lrw_st;
+	ss->devaddr = lrw_st->devaddr;
+	INIT_LIST_HEAD(&ss->entry);
+
+	ss->tx_should_ack = false;
+	ss->retry = 3;
+	spin_lock_init(&ss->state_lock);
+	INIT_WORK(&ss->timeout_work, rx_timeout_work);
+
+lrw_alloc_ss_end:
+	return ss;
+}
+
+void
+lrw_free_ss(struct lrw_session *ss)
+{
+	netdev_dbg(ss->lrw_st->ndev, "%s\n", __func__);
+	if (ss->tx_skb)
+		consume_skb(ss->tx_skb);
+	netdev_dbg(ss->lrw_st->ndev, "%s: free rx skb\n", __func__);
+	if (ss->rx_skb)
+		consume_skb(ss->rx_skb);
+
+	netdev_dbg(ss->lrw_st->ndev, "%s: free ss\n", __func__);
+	kfree(ss);
+}
+
+void
+lrw_del_ss(struct lrw_session *ss)
+{
+	netdev_dbg(ss->lrw_st->ndev, "%s\n", __func__);
+	list_del(&ss->entry);
+	lrw_free_ss(ss);
+}
+
+void
+lrw_del_all_ss(struct lrw_struct *lrw_st)
+{
+	struct lrw_session *ss, *tmp;
+
+	mutex_lock(&lrw_st->ss_list_lock);
+	lrw_st->_cur_ss = NULL;
+	list_for_each_entry_safe(ss, tmp, &lrw_st->ss_list, entry) {
+		del_timer(&ss->timer);
+		lrw_del_ss(ss);
+	}
+	mutex_unlock(&lrw_st->ss_list_lock);
+}
+
+void
+lrw_ready_hw(struct lrw_struct *lrw_st)
+{
+	lrw_st->state = LRW_STATE_IDLE;
+}
+
+int
+lrw_start_hw(struct lrw_struct *lrw_st)
+{
+	int ret = 0;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+	lrw_st->nwks_shash_tfm = lrw_mic_key_setup(lrw_st->nwkskey,
+						   LRW_KEY_LEN);
+	lrw_st->nwks_skc_tfm = lrw_encrypt_key_setup(lrw_st->nwkskey,
+						     LRW_KEY_LEN);
+	lrw_st->apps_skc_tfm = lrw_encrypt_key_setup(lrw_st->appskey,
+						     LRW_KEY_LEN);
+	lrw_st->state = LRW_START;
+	ret = lrw_st->ops->start(&lrw_st->hw);
+	if (!ret)
+		lrw_ready_hw(lrw_st);
+
+	return ret;
+}
+
+void
+lrw_stop_hw(struct lrw_struct *lrw_st)
+{
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+	lrw_st->state = LRW_STOP;
+	netdev_dbg(lrw_st->ndev, "%s: going to stop hardware\n", __func__);
+	lrw_st->ops->stop(&lrw_st->hw);
+
+	netdev_dbg(lrw_st->ndev, "%s: going to kill tasks & flush works",
+		   __func__);
+	tasklet_kill(&lrw_st->xmit_task);
+	flush_work(&lrw_st->rx_work);
+
+	netdev_dbg(lrw_st->ndev, "%s: going to delete all session\n", __func__);
+	lrw_del_all_ss(lrw_st);
+
+	netdev_dbg(lrw_st->ndev, "%s: going to free mic tfm\n", __func__);
+	lrw_mic_key_free(lrw_st->nwks_shash_tfm);
+	netdev_dbg(lrw_st->ndev, "%s: going to free nwks tfm\n", __func__);
+	lrw_encrypt_key_free(lrw_st->nwks_skc_tfm);
+	netdev_dbg(lrw_st->ndev, "%s: going to free apps tfm\n", __func__);
+	lrw_encrypt_key_free(lrw_st->apps_skc_tfm);
+}
+
+void
+lrw_prepare_tx_frame(struct lrw_session *ss)
+{
+	struct lrw_struct *lrw_st = ss->lrw_st;
+	struct sk_buff *skb = ss->tx_skb;
+	u8 mhdr, fctrl, fport;
+	u8 mic[LRW_MIC_LEN];
+	__le32 le_devaddr;
+	__le16 le_fcnt_up;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	/* Encrypt the plain buffer content */
+	lrw_encrypt_buf(lrw_st->apps_skc_tfm, LRW_UPLINK,
+			ss->devaddr, ss->fcnt_up, skb->data, skb->len);
+
+	/* Push FPort */
+	if (skb->len) {
+		fport = ss->fport;
+		memcpy(skb_push(skb, LRW_FPORT_LEN), &fport, LRW_FPORT_LEN);
+	}
+
+	/* Push FCnt_Up */
+	le_fcnt_up = cpu_to_le16(ss->fcnt_up);
+	memcpy(skb_push(skb, LRW_FCNT_LEN), &le_fcnt_up, LRW_FCNT_LEN);
+
+	/* Push FCtrl */
+	fctrl = 0;
+	if (lrw_st->rx_should_ack) {
+		fctrl |= 0x20;
+		lrw_st->rx_should_ack = false;
+	}
+	memcpy(skb_push(skb, LRW_FCTRL_LEN), &fctrl, LRW_FCTRL_LEN);
+
+	/* Push DevAddr */
+	le_devaddr = cpu_to_le32(ss->devaddr);
+	memcpy(skb_push(skb, LRW_DEVADDR_LEN), &le_devaddr, LRW_DEVADDR_LEN);
+
+	/* Push MHDR */
+	mhdr = LRW_UNCONFIRMED_DATA_UP << 5;
+	if ((mhdr & (0x6 << 5)) == (0x4 << 5))
+		ss->tx_should_ack = true;
+	memcpy(skb_push(skb, LRW_MHDR_LEN), &mhdr, LRW_MHDR_LEN);
+
+	/* Put MIC */
+	lrw_calc_mic(lrw_st->nwks_shash_tfm, LRW_UPLINK,
+		     ss->devaddr, ss->fcnt_up, skb->data, skb->len, mic);
+	memcpy(skb_put(skb, LRW_MIC_LEN), mic, LRW_MIC_LEN);
+}
+
+void
+lrw_xmit(unsigned long data)
+{
+	struct lrw_struct *lrw_st = (struct lrw_struct *)data;
+	struct lrw_session *ss = lrw_st->_cur_ss;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+	ss->state = LRW_XMITTING_SS;
+	lrw_st->ops->xmit_async(&lrw_st->hw, ss->tx_skb);
+}
+
+int
+lrw_parse_frame(struct lrw_session *ss, struct sk_buff *skb)
+{
+	struct lrw_struct *lrw_st = ss->lrw_st;
+	struct lrw_fhdr *fhdr = &ss->rx_fhdr;
+	__le16 *p_fcnt;
+
+	pr_debug("%s: %s\n", LORAWAN_MODULE_NAME, __func__);
+
+	/* Get message type */
+	fhdr->mtype = skb->data[0];
+	skb_pull(skb, LRW_MHDR_LEN);
+
+	/* Trim Device Address */
+	skb_pull(skb, LRW_DEVADDR_LEN);
+
+	/* Get frame control */
+	fhdr->fctrl = skb->data[0];
+	skb_pull(skb, LRW_FCTRL_LEN);
+
+	/* Ack the original TX frame if it should be acked */
+	if (ss->tx_should_ack && (fhdr->fctrl & 0x20))
+		ss->tx_should_ack = false;
+
+	/* Get frame count */
+	p_fcnt = (__le16 *)skb->data;
+	fhdr->fcnt = le16_to_cpu(*p_fcnt);
+	skb_pull(skb, LRW_FCNT_LEN);
+
+	/* Get frame options */
+	fhdr->fopts_len = fhdr->fctrl & 0xF;
+	if (fhdr->fopts_len > 0) {
+		if (skb->len < fhdr->fopts_len)
+			goto lrw_parse_frame_err;
+		memcpy(fhdr->fopts, skb->data, fhdr->fopts_len);
+		skb_pull(skb, fhdr->fopts_len);
+	}
+
+	/* TODO: Parse frame options */
+
+	/* Parse FPort and decrypt payload */
+	if (skb->len > 0) {
+		if (skb->len <= LRW_FPORT_LEN)
+			goto lrw_parse_frame_err;
+
+		/* TODO: Deal FPort */
+
+		skb_pull(skb, LRW_FPORT_LEN);
+
+		lrw_decrypt_buf(lrw_st->apps_skc_tfm, LRW_DOWNLINK,
+				ss->devaddr, fhdr->fcnt, skb->data, skb->len);
+	}
+
+	return 0;
+
+lrw_parse_frame_err:
+	return -EMSGSIZE;
+}
+
+struct lrw_session *
+lrw_rx_skb_2_session(struct lrw_struct *lrw_st, struct sk_buff *rx_skb)
+{
+	struct lrw_session *ss;
+	__le16 *p_fcnt;
+	u16 fcnt;
+	u16 ofs;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	ofs = LRW_MHDR_LEN + LRW_DEVADDR_LEN + LRW_FCTRL_LEN;
+	p_fcnt = (__le16 *)(rx_skb->data + ofs);
+	fcnt = le16_to_cpu(*p_fcnt);
+
+	/* Find the corresponding session */
+	ss = lrw_st->_cur_ss;
+
+	/* Frame count downlink check */
+	if (fcnt >= (ss->fcnt_down & 0xFFFF))
+		ss->rx_skb = rx_skb;
+	else
+		ss = NULL;
+
+	return ss;
+}
+
+void
+lrw_rx_work(struct work_struct *work)
+{
+	struct lrw_struct *lrw_st;
+	struct lrw_session *ss;
+	struct sk_buff *skb;
+
+	lrw_st = container_of(work, struct lrw_struct, rx_work);
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	skb = skb_dequeue(&lrw_st->rx_skb_list);
+
+	/* Check and parse the RX frame */
+	ss = lrw_rx_skb_2_session(lrw_st, skb);
+	if (!ss)
+		goto lrw_rx_work_not_new_frame;
+
+	if (lrw_parse_frame(ss, skb)) {
+		ss->rx_skb = NULL;
+		goto lrw_rx_work_not_new_frame;
+	}
+
+	/* Check the TX frame is acked or not */
+	if (ss->tx_should_ack) {
+		ss->rx_skb = NULL;
+		goto lrw_rx_work_not_new_frame;
+	}
+
+	/* The TX frame is acked or no need to be acked */
+	del_timer(&ss->timer);
+	lrw_st->rx_should_ack = (ss->rx_fhdr.mtype & 0xC0) == 0x40;
+
+	lrw_st->ndev->stats.rx_packets++;
+	lrw_st->ndev->stats.rx_bytes += ss->rx_skb->len;
+
+	spin_lock_bh(&ss->state_lock);
+	ss->state = LRW_RXRECEIVED_SS;
+	spin_unlock_bh(&ss->state_lock);
+
+	if (ss->rx_skb->len > 0) {
+		lrw_get_mac_cb(skb)->devaddr = lrw_st->devaddr;
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		skb->protocol = htons(ETH_P_LORAWAN);
+		skb->pkt_type = PACKET_HOST;
+		netif_receive_skb(skb);
+	}
+
+	ss->rx_skb = NULL;
+
+	mutex_lock(&lrw_st->ss_list_lock);
+	lrw_st->fcnt_down = ss->rx_fhdr.fcnt;
+	lrw_st->_cur_ss = NULL;
+	lrw_del_ss(ss);
+	lrw_st->state = LRW_STATE_IDLE;
+	mutex_unlock(&lrw_st->ss_list_lock);
+
+	return;
+
+lrw_rx_work_not_new_frame:
+	/* Drop the RX frame if checked failed */
+	if (skb)
+		kfree_skb(skb);
+}
+
+int
+lrw_check_mic(struct crypto_shash *tfm, struct sk_buff *skb)
+{
+	u8 cks[LRW_MIC_LEN];
+	u32 devaddr;
+	size_t len;
+	u16 fcnt;
+	u8 *buf;
+	u8 *mic;
+	u16 ofs;
+
+	buf = skb->data;
+	devaddr = le32_to_cpu(*((__le32 *)(buf + LRW_MHDR_LEN)));
+	ofs = LRW_MHDR_LEN + LRW_DEVADDR_LEN + LRW_FCTRL_LEN;
+	fcnt = le16_to_cpu(*(__le16 *)(buf + ofs));
+	len = skb->len - LRW_MIC_LEN;
+	mic = skb->data + len;
+
+	lrw_calc_mic(tfm, LRW_DOWNLINK, devaddr, fcnt, buf, len, cks);
+
+	return (!memcmp(cks, mic, LRW_MIC_LEN));
+}
+
+/**
+ * lrw_rx_irqsave - Tell LoRaWAN module that there is new received frame
+ * @hw:		the LoRa device
+ * @skb:	the new received frame
+ */
+void
+lrw_rx_irqsave(struct lrw_hw *hw, struct sk_buff *skb)
+{
+	struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+	u32 devaddr;
+	u8 mtype;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	if (skb->len < LRW_MHDR_LEN + LRW_DEVADDR_LEN + LRW_FCTRL_LEN +
+	    LRW_FCNT_LEN + LRW_MIC_LEN)
+		goto lrw_rx_irqsave_err;
+
+	mtype = skb->data[0] >> 5;
+	devaddr = le32_to_cpu(*(__le32 *)(skb->data + LRW_MHDR_LEN));
+
+	/* Check the frame is the downlink frame */
+	if ((mtype == LRW_UNCONFIRMED_DATA_DOWN ||
+	     mtype == LRW_CONFIRMED_DATA_DOWN) &&
+	     devaddr == lrw_st->devaddr &&
+	     lrw_check_mic(lrw_st->nwks_shash_tfm, skb)) {
+		/* Remove message integrity code (MIC) */
+		skb_trim(skb, skb->len - LRW_MIC_LEN);
+		skb_queue_tail(&lrw_st->rx_skb_list, skb);
+		schedule_work(&lrw_st->rx_work);
+		return;
+	}
+
+lrw_rx_irqsave_err:
+	kfree_skb(skb);
+}
+EXPORT_SYMBOL(lrw_rx_irqsave);
+
+static void
+lrw_rexmit(struct timer_list *timer)
+{
+	struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+	struct lrw_struct *lrw_st = ss->lrw_st;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	lrw_st->state = LRW_STATE_TX;
+	lrw_xmit((unsigned long)lrw_st);
+}
+
+static void
+rx_timeout_work(struct work_struct *work)
+{
+	struct lrw_struct *lrw_st;
+	struct lrw_session *ss;
+
+	ss = container_of(work, struct lrw_session, timeout_work);
+	lrw_st = ss->lrw_st;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+	mutex_lock(&lrw_st->ss_list_lock);
+	lrw_st->_cur_ss = NULL;
+	lrw_st->state = LRW_STATE_IDLE;
+	lrw_del_ss(ss);
+	mutex_unlock(&lrw_st->ss_list_lock);
+}
+
+static void
+rx2_timeout_isr(struct timer_list *timer)
+{
+	struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+	struct lrw_struct *lrw_st = ss->lrw_st;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	/* Check TX is acked or not */
+	if (!ss->tx_should_ack) {
+		spin_lock_bh(&ss->state_lock);
+		if (ss->state != LRW_RXRECEIVED_SS)
+			ss->state = LRW_RXTIMEOUT_SS;
+		spin_unlock_bh(&ss->state_lock);
+
+		if (ss->state == LRW_RXTIMEOUT_SS) {
+			netdev_dbg(lrw_st->ndev, "%s: rx time out\n", __func__);
+			goto rx2_timeout_isr_no_retry_rx_frame;
+		} else {
+			return;
+		}
+	}
+
+	/* Check the session need to be retransmitted or not */
+	if (ss->retry > 0) {
+		ss->state = LRW_RETRANSMIT_SS;
+		ss->retry--;
+
+		/* Start timer for ack timeout and retransmit */
+		ss->timer.function = lrw_rexmit;
+		ss->timer.expires = jiffies_64 + ss->ack_timeout * HZ;
+		add_timer(&ss->timer);
+	} else {
+		/* Retry failed */
+rx2_timeout_isr_no_retry_rx_frame:
+		schedule_work(&ss->timeout_work);
+	}
+}
+
+static void
+rx2_delay_isr(struct timer_list *timer)
+{
+	struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+	struct lrw_struct *lrw_st = ss->lrw_st;
+	unsigned long delay;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	/* Start timer for RX2 window */
+	ss->timer.function = rx2_timeout_isr;
+	delay = jiffies_64 + (ss->rx2_window + 20) * HZ / 1000 + HZ;
+	ss->timer.expires = delay;
+	add_timer(&ss->timer);
+
+	/* Start LoRa hardware to RX2 window */
+	ss->state = LRW_RX2_SS;
+	lrw_st->ops->start_rx_window(&lrw_st->hw, ss->rx2_window + 20);
+}
+
+static void
+rx1_delay_isr(struct timer_list *timer)
+{
+	struct lrw_session *ss = container_of(timer, struct lrw_session, timer);
+	struct lrw_struct *lrw_st = ss->lrw_st;
+	unsigned long delay;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	/* Start timer for RX_Delay2 - RX_Delay2 */
+	ss->timer.function = rx2_delay_isr;
+	delay = jiffies_64 + (ss->rx_delay2 - ss->rx_delay1) * HZ;
+	ss->timer.expires = delay;
+	add_timer(&ss->timer);
+
+	/* Start LoRa hardware to RX1 window */
+	ss->state = LRW_RX1_SS;
+	lrw_st->ops->start_rx_window(&lrw_st->hw, ss->rx1_window + 20);
+}
+
+void
+lrw_sent_tx_work(struct lrw_struct *lrw_st, struct sk_buff *skb)
+{
+	struct lrw_session *ss = lrw_st->_cur_ss;
+	struct net_device *ndev;
+	unsigned long delay;
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	ss->state = LRW_XMITTED;
+
+	/* Start session timer for RX_Delay1 */
+	timer_setup(&ss->timer, rx1_delay_isr, 0);
+	delay = jiffies_64 + ss->rx_delay1 * HZ - 20 * HZ / 1000;
+	ss->timer.expires = delay;
+	add_timer(&ss->timer);
+
+	ndev = skb->dev;
+	ndev->stats.tx_packets++;
+	ndev->stats.tx_bytes += skb->len;
+	dev_consume_skb_any(skb);
+	ss->tx_skb = NULL;
+}
+
+/**
+ * lrw_xmit_complete - Tell LoRaWAN module that the frame is xmitted completely
+ * @hw:		the LoRa device
+ * @skb:	the xmitted frame
+ */
+void
+lrw_xmit_complete(struct lrw_hw *hw, struct sk_buff *skb)
+{
+	struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+
+	netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+	lrw_sent_tx_work(lrw_st, skb);
+	lrw_st->state = LRW_STATE_RX;
+}
+EXPORT_SYMBOL(lrw_xmit_complete);
diff --git a/net/maclorawan/main.c b/net/maclorawan/main.c
new file mode 100644
index 000000000000..caecfb3f66c6
--- /dev/null
+++ b/net/maclorawan/main.c
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <starnight@g.ncu.edu.tw>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+
+#include <linux/lora/lorawan.h>
+#include <linux/lora/lorawan_netdev.h>
+#include "maclorawan.h"
+
+#define	PHY_NAME		"lora"
+
+/* Need to find a way to define or assign */
+#define	LORAWAN_MTU		20
+
+static struct class *lrw_sys_class;
+
+static void
+lrw_if_setup(struct net_device *ndev)
+{
+	ndev->addr_len = LRW_DEVADDR_LEN;
+	memset(ndev->broadcast, 0xFF, ndev->addr_len);
+	ndev->type = ARPHRD_LORAWAN;
+
+	ndev->hard_header_len = LRW_MHDR_LEN + LRW_FHDR_MAX_LEN + LRW_FPORT_LEN;
+	ndev->needed_tailroom = LRW_MIC_LEN;
+
+	/**
+	 * TODO: M should be a dynamic value defined by Regional Parameters,
+	 *	 Being fixed for now.  Going to be changed.
+	 */
+	ndev->mtu = LORAWAN_MTU;
+}
+
+/**
+ * lrw_alloc_hw - Allocate a memory space for the LoRa device
+ * @priv_data_len:	the private data size
+ * @lrw_operations:	the implemented operations of the LoRa device
+ *
+ * Return:		address of the LoRa device or NULL for failed
+ */
+struct lrw_hw *
+lrw_alloc_hw(size_t priv_data_len, struct lrw_operations *ops)
+{
+	struct lrw_struct *lrw_st;
+	struct net_device *ndev;
+	int ret;
+
+	if (WARN_ON(!ops || !ops->start || !ops->stop || !ops->xmit_async ||
+		    !ops->set_txpower || !ops->set_dr ||
+		    !ops->start_rx_window || !ops->set_state))
+		return NULL;
+
+	/* In memory it'll be like this:
+	 *
+	 * +-----------------------+
+	 * | struct net_device     |
+	 * +-----------------------+
+	 * | struct lrw_struct     |
+	 * +-----------------------+
+	 * | driver's private data |
+	 * +-----------------------+
+	 */
+	ndev = alloc_netdev(sizeof(struct lrw_struct) + priv_data_len,
+			    PHY_NAME "%d", NET_NAME_ENUM, lrw_if_setup);
+	if (!ndev)
+		return ERR_PTR(-ENOMEM);
+	ret = dev_alloc_name(ndev, ndev->name);
+	if (ret < 0)
+		goto lrw_alloc_hw_err;
+
+	lrw_st = (struct lrw_struct *)netdev_priv(ndev);
+	lrw_st->ndev = ndev;
+
+	lrw_st->state = LRW_STOP;
+	lrw_st->ops = ops;
+	lrw_st->hw.priv = (u8 *)lrw_st + sizeof(struct lrw_struct);
+	skb_queue_head_init(&lrw_st->rx_skb_list);
+
+	ndev->flags |= IFF_NOARP;
+	ndev->features |= NETIF_F_HW_CSUM;
+
+	return &lrw_st->hw;
+
+lrw_alloc_hw_err:
+	free_netdev(ndev);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(lrw_alloc_hw);
+
+/**
+ * lrw_free_hw - Free the LoRa device's memory resource
+ * @hw:		the LoRa device going to be freed
+ */
+void
+lrw_free_hw(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	free_netdev(lrw_st->ndev);
+}
+EXPORT_SYMBOL(lrw_free_hw);
+
+/**
+ * lrw_set_deveui - Set the LoRa device's DevEUI
+ * @hw:		the LoRa device going to be set
+ * @eui:	the global end-device ID in IEEE EUI64 address space
+ */
+void
+lrw_set_deveui(struct lrw_hw *hw, u64 eui)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	lrw_st->dev_eui = eui;
+}
+EXPORT_SYMBOL(lrw_set_deveui);
+
+/**
+ * lrw_get_deveui - Get the LoRa device's DevEUI
+ * @hw:		the LoRa device going to be got from
+ *
+ * Return:	the device's DevEUI in IEEE EUI64 address space
+ */
+u64
+lrw_get_deveui(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	return lrw_st->dev_eui;
+}
+EXPORT_SYMBOL(lrw_get_deveui);
+
+/**
+ * lrw_set_appeui - Set the LoRa device's AppEUI
+ * @hw:		the LoRa device going to be set
+ * @eui:	the global end-device ID in IEEE EUI64 address space
+ */
+void
+lrw_set_appeui(struct lrw_hw *hw, u64 eui)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	lrw_st->app_eui = eui;
+}
+EXPORT_SYMBOL(lrw_set_appeui);
+
+/**
+ * lrw_get_appeui - Get the LoRa device's AppEUI
+ * @hw:		the LoRa device going to be got from
+ *
+ * Return:	the device's AppEUI in IEEE EUI64 address space
+ */
+u64
+lrw_get_appeui(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	return lrw_st->app_eui;
+}
+EXPORT_SYMBOL(lrw_get_appeui);
+
+/**
+ * lrw_set_devaddr - Set the LoRa device's address
+ * @hw:		the LoRa device going to be set
+ * @devaddr:	the device address
+ */
+void
+lrw_set_devaddr(struct lrw_hw *hw, u32 devaddr)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	lrw_st->devaddr = devaddr;
+}
+EXPORT_SYMBOL(lrw_set_devaddr);
+
+/**
+ * lrw_get_devaddr - Get the LoRa device's address
+ * @hw:		the LoRa device going to be got from
+ *
+ * Return:	the device address
+ */
+u32
+lrw_get_devaddr(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+	return lrw_st->devaddr;
+}
+EXPORT_SYMBOL(lrw_get_devaddr);
+
+/**
+ * lrw_add_hw - Add a LoRaWAN hardware as a network device
+ * @lrw_st:	the LoRa device going to be added
+ *
+ * Return:	0 / other number for success / failed
+ */
+static int
+lrw_add_hw(struct lrw_struct *lrw_st)
+{
+	struct net_device *ndev = lrw_st->ndev;
+	__be32 be_addr;
+	int ret;
+
+	lrw_st->fcnt_up = 0;
+	lrw_st->fcnt_down = 0;
+	lrw_st->_cur_ss = NULL;
+
+	mutex_init(&lrw_st->ss_list_lock);
+	INIT_LIST_HEAD(&lrw_st->ss_list);
+
+	tasklet_init(&lrw_st->xmit_task, lrw_xmit, (unsigned long)lrw_st);
+	INIT_WORK(&lrw_st->rx_work, lrw_rx_work);
+
+	be_addr = cpu_to_be32(lrw_st->devaddr);
+	memcpy(ndev->perm_addr, &be_addr, ndev->addr_len);
+	memcpy(ndev->dev_addr, ndev->perm_addr, ndev->addr_len);
+
+	write_pnet(&lrw_st->_net, &init_net);
+	ret = register_netdev(ndev);
+
+	return ret;
+}
+
+/**
+ * lrw_remove_hw - Remove a LoRaWAN hardware from a network device
+ * @lrw_st:	the LoRa device going to be removed
+ */
+static void
+lrw_remove_hw(struct lrw_struct *lrw_st)
+{
+	unregister_netdev(lrw_st->ndev);
+	tasklet_kill(&lrw_st->xmit_task);
+}
+
+bool
+ready2write(struct lrw_struct *lrw_st)
+{
+	bool status = false;
+
+	if (!lrw_st->_cur_ss && lrw_st->state == LRW_STATE_IDLE)
+		status = true;
+
+	return status;
+}
+
+bool
+ready2read(struct lrw_struct *lrw_st)
+{
+	struct lrw_session *ss;
+	bool status = false;
+
+	if (!list_empty(&lrw_st->ss_list) && lrw_st->state != LRW_STOP) {
+		ss = list_first_entry(&lrw_st->ss_list,
+				      struct lrw_session,
+				      entry);
+		if (ss->state == LRW_RXRECEIVED_SS)
+			status = true;
+	}
+
+	return status;
+}
+
+static int
+lrw_if_up(struct net_device *ndev)
+{
+	struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+	int ret = -EBUSY;
+
+	if (lrw_st->state == LRW_STOP) {
+		ret = lrw_start_hw(lrw_st);
+		netif_start_queue(ndev);
+	}
+
+	return ret;
+}
+
+static int
+lrw_if_down(struct net_device *ndev)
+{
+	struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+
+	if (lrw_st->state != LRW_STOP) {
+		netif_stop_queue(ndev);
+		lrw_stop_hw(lrw_st);
+	}
+
+	return 0;
+}
+
+static netdev_tx_t
+lrw_if_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+	struct lrw_session *ss;
+	netdev_tx_t ret;
+
+	ret = NETDEV_TX_OK;
+
+	ss = lrw_alloc_ss(lrw_st);
+	if (!ss)
+		return NETDEV_TX_BUSY;
+
+	mutex_lock(&lrw_st->ss_list_lock);
+	if (ready2write(lrw_st)) {
+		list_add_tail(&ss->entry, &lrw_st->ss_list);
+		lrw_st->state = LRW_STATE_TX;
+		lrw_st->_cur_ss = ss;
+		ss->fcnt_up = lrw_st->fcnt_up;
+		ss->fcnt_down = lrw_st->fcnt_down;
+		/* TODO: RX delay #1/#2 should be set by regional parameters */
+		ss->rx_delay1 = 1;
+		ss->rx_delay2 = 2;
+		ss->rx1_window = 500;
+		ss->rx2_window = 500;
+	} else {
+		ret = NETDEV_TX_BUSY;
+	}
+	mutex_unlock(&lrw_st->ss_list_lock);
+
+	if (ret == NETDEV_TX_OK) {
+		ss->state = LRW_INIT_SS;
+		ss->tx_skb = skb;
+		lrw_prepare_tx_frame(ss);
+		tasklet_schedule(&lrw_st->xmit_task);
+	} else {
+		lrw_free_ss(ss);
+	}
+
+	return ret;
+}
+
+static int
+lrw_if_get_addr(struct lrw_struct *lrw_st, struct sockaddr_lorawan *addr)
+{
+	int ret = 0;
+
+	switch (addr->addr_in.addr_type) {
+	case LRW_ADDR_DEVADDR:
+		addr->addr_in.devaddr = lrw_st->devaddr;
+		break;
+	case LRW_ADDR_DEVEUI:
+		addr->addr_in.dev_eui = lrw_st->dev_eui;
+		break;
+	case LRW_ADDR_APPEUI:
+		addr->addr_in.app_eui = lrw_st->app_eui;
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+
+static int
+lrw_if_set_addr(struct lrw_struct *lrw_st, struct sockaddr_lorawan *addr)
+{
+	struct lrw_hw *hw = &lrw_st->hw;
+	int ret = 0;
+
+	if (netif_running(lrw_st->ndev))
+		return -EBUSY;
+
+	switch (addr->addr_in.addr_type) {
+	case LRW_ADDR_DEVADDR:
+		lrw_set_devaddr(hw, addr->addr_in.devaddr);
+		break;
+	case LRW_ADDR_DEVEUI:
+		lrw_set_deveui(hw, addr->addr_in.dev_eui);
+		break;
+	case LRW_ADDR_APPEUI:
+		lrw_set_appeui(hw, addr->addr_in.app_eui);
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+
+static void
+swap_bytes(u8 *dst, u8 *src, size_t l)
+{
+	/* Human reading is big-endian, but LoRaWAN is little-endian */
+	unsigned int i;
+
+	for (i = 0; i < l; i++)
+		dst[i] = src[l - i - 1];
+}
+
+int
+lrw_set_key(struct lrw_hw *hw, u8 type, u8 *key, size_t key_len)
+{
+	struct lrw_struct *lrw_st;
+	int ret = 0;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+
+	netdev_dbg(lrw_st->ndev, "%s: type=%d\n", __func__, type);
+	if (lrw_st->state != LRW_STOP)
+		return -EINVAL;
+
+	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,
+		       16, 1, key, key_len, true);
+	switch (type) {
+	case LRW_APPKEY:
+		swap_bytes(lrw_st->appkey, key, key_len);
+		break;
+	case LRW_NWKSKEY:
+		swap_bytes(lrw_st->nwkskey, key, key_len);
+		break;
+	case LRW_APPSKEY:
+		swap_bytes(lrw_st->appskey, key, key_len);
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(lrw_set_key);
+
+int
+lrw_get_key(struct lrw_hw *hw, u8 type, u8 *key, size_t key_len)
+{
+	struct lrw_struct *lrw_st;
+	int ret = 0;
+
+	lrw_st = container_of(hw, struct lrw_struct, hw);
+
+	netdev_dbg(lrw_st->ndev, "%s: type=%d\n", __func__, type);
+	switch (type) {
+	case LRW_APPKEY:
+		swap_bytes(key, lrw_st->appkey, key_len);
+		break;
+	case LRW_NWKSKEY:
+		swap_bytes(key, lrw_st->nwkskey, key_len);
+		break;
+	case LRW_APPSKEY:
+		swap_bytes(key, lrw_st->appskey, key_len);
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+
+static int
+lrw_if_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+	struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+	struct sockaddr_lorawan *addr;
+	int ret = 0;
+
+	netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
+
+	/* I/O control by each command */
+	switch (cmd) {
+	/* Set & get the DevAddr, DevEUI and AppEUI */
+	case SIOCSIFADDR:
+		addr = (struct sockaddr_lorawan *)&ifr->ifr_addr;
+		ret = lrw_if_set_addr(lrw_st, addr);
+		break;
+	case SIOCGIFADDR:
+		addr = (struct sockaddr_lorawan *)&ifr->ifr_addr;
+		ret = lrw_if_get_addr(lrw_st, addr);
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+
+static int
+lrw_if_set_mac(struct net_device *ndev, void *p)
+{
+	struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+	struct sockaddr *addr = p;
+	__be32 *be_addr;
+
+	be_addr = (__be32 *)addr->sa_data;
+
+	netdev_dbg(ndev, "%s: AF_TYPE:%d set mac address %X\n",
+		   __func__, addr->sa_family, be32_to_cpu(*be_addr));
+
+	if (netif_running(ndev))
+		return -EBUSY;
+
+	lrw_set_devaddr(&lrw_st->hw, be32_to_cpu(*be_addr));
+	memcpy(ndev->dev_addr, be_addr, ndev->addr_len);
+
+	return 0;
+}
+
+static const struct net_device_ops lrw_if_ops = {
+	.ndo_open = lrw_if_up,
+	.ndo_stop = lrw_if_down,
+	.ndo_start_xmit = lrw_if_start_xmit,
+	.ndo_do_ioctl = lrw_if_ioctl,
+	.ndo_set_mac_address = lrw_if_set_mac,
+};
+
+/**
+ * lrw_register_hw - Register as a LoRaWAN compatible device
+ * @hw:		LoRa device going to be registered
+ *
+ * Return:	0 / negative number for success / error number
+ */
+int
+lrw_register_hw(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+	int ret;
+
+	device_initialize(&lrw_st->dev);
+	dev_set_name(&lrw_st->dev, netdev_name(lrw_st->ndev));
+	lrw_st->dev.class = lrw_sys_class;
+	lrw_st->dev.platform_data = lrw_st;
+
+	ret = device_add(&lrw_st->dev);
+	if (ret)
+		goto lrw_register_hw_end;
+
+	/* Add a LoRa device node as a network device */
+	lrw_st->ndev->netdev_ops = &lrw_if_ops;
+	ret = lrw_add_hw(lrw_st);
+	if (!ret)
+		netdev_info(lrw_st->ndev, "register\n");
+
+lrw_register_hw_end:
+	return ret;
+}
+EXPORT_SYMBOL(lrw_register_hw);
+
+/**
+ * lrw_unregister_hw - Unregister the LoRaWAN compatible device
+ * @hw:		LoRa device going to be unregistered
+ */
+void
+lrw_unregister_hw(struct lrw_hw *hw)
+{
+	struct lrw_struct *lrw_st = container_of(hw, struct lrw_struct, hw);
+
+	netdev_info(lrw_st->ndev, "unregister\n");
+
+	/* Stop and remove the LoRaWAM hardware from system */
+	if (lrw_st->state != LRW_STOP)
+		lrw_stop_hw(lrw_st);
+	device_del(&lrw_st->dev);
+	lrw_remove_hw(lrw_st);
+}
+EXPORT_SYMBOL(lrw_unregister_hw);
+
+static int __init
+lrw_init(void)
+{
+	int err = 0;
+
+	pr_info("%s: module inserted\n", LORAWAN_MODULE_NAME);
+
+	/* Create device class */
+	lrw_sys_class = class_create(THIS_MODULE, LORAWAN_MODULE_NAME);
+	if (IS_ERR(lrw_sys_class)) {
+		pr_err("%s: Failed to create a class of LoRaWAN\n",
+		       LORAWAN_MODULE_NAME);
+		err = PTR_ERR(lrw_sys_class);
+		goto lrw_init_end;
+	}
+
+	pr_debug("%s: class created\n", LORAWAN_MODULE_NAME);
+
+lrw_init_end:
+	return err;
+}
+
+static void __exit
+lrw_exit(void)
+{
+	/* Delete device class */
+	class_destroy(lrw_sys_class);
+	pr_info("%s: module removed\n", LORAWAN_MODULE_NAME);
+}
+
+module_init(lrw_init);
+module_exit(lrw_exit);
+
+MODULE_AUTHOR("Jian-Hong Pan, <starnight@g.ncu.edu.tw>");
+MODULE_DESCRIPTION("LoRaWAN soft MAC kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
2.20.0


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

  parent reply	other threads:[~2018-12-16 10:20 UTC|newest]

Thread overview: 162+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-23 17:15 [RFC 0/3 net] lorawan: Add LoRaWAN soft MAC module Jian-Hong Pan
2018-08-23 17:15 ` Jian-Hong Pan
2018-08-23 17:15 ` Jian-Hong Pan
2018-08-23 17:15 ` [RFC 1/3 net] lorawan: Add LoRaWAN class module Jian-Hong Pan
2018-08-23 17:15   ` Jian-Hong Pan
2018-08-23 17:15   ` Jian-Hong Pan
2018-08-23 17:43   ` Randy Dunlap
2018-08-23 17:43     ` Randy Dunlap
2018-08-23 17:43     ` Randy Dunlap
2018-08-23 17:43     ` Randy Dunlap
2018-08-24 15:58     ` Jian-Hong Pan
2018-08-24 15:58       ` Jian-Hong Pan
2018-08-24 15:58       ` Jian-Hong Pan
2018-09-23 16:40   ` Andreas Färber
2018-09-23 16:40     ` Andreas Färber
2018-09-23 16:40     ` Andreas Färber
2018-09-26 15:52     ` Jian-Hong Pan
2018-09-26 15:52       ` Jian-Hong Pan
2018-09-26 15:52       ` Jian-Hong Pan
2018-11-05 16:55   ` [PATCH V2 0/7] net: lorawan: Add LoRaWAN soft MAC module Jian-Hong Pan
2018-11-05 16:55     ` Jian-Hong Pan
2018-11-05 16:55   ` [PATCH V2 1/7] net: lorawan: Add macro and definition for LoRaWAN Jian-Hong Pan
2018-11-05 16:55     ` Jian-Hong Pan
2018-11-05 16:55   ` [PATCH V2 2/7] net: lorawan: Add LoRaWAN socket module Jian-Hong Pan
2018-11-05 16:55     ` Jian-Hong Pan
2018-11-05 18:16     ` David Miller
2018-11-05 18:16       ` David Miller
2018-11-06 14:28       ` Jian-Hong Pan
2018-11-06 14:28         ` Jian-Hong Pan
2018-11-14 16:01       ` [PATCH V3 0/7] net: lorawan: Add LoRaWAN soft MAC module Jian-Hong Pan
2018-11-14 16:01         ` Jian-Hong Pan
2018-11-14 16:01       ` [PATCH V3 1/7] net: lorawan: Add macro and definition for LoRaWAN Jian-Hong Pan
2018-11-14 16:01         ` Jian-Hong Pan
2018-11-14 16:12         ` Andreas Färber
2018-11-14 16:12           ` Andreas Färber
2018-11-17  6:47           ` Jian-Hong Pan
2018-11-17  6:47             ` Jian-Hong Pan
2018-11-17  6:47             ` Jian-Hong Pan
2018-11-14 16:01       ` [PATCH V3 2/7] net: lorawan: Add LoRaWAN socket module Jian-Hong Pan
2018-11-14 16:01         ` Jian-Hong Pan
2018-11-17  4:32         ` David Miller
2018-11-17  4:32           ` David Miller
2018-11-17 14:54           ` Jian-Hong Pan
2018-11-17 14:54             ` Jian-Hong Pan
2018-12-04 14:13             ` [PATCH V4 0/6] net: lorawan: Add LoRaWAN soft MAC module Jian-Hong Pan
2018-12-04 14:13               ` Jian-Hong Pan
2018-12-04 14:13             ` [PATCH V4 1/6] net: lorawan: Add LoRaWAN socket module Jian-Hong Pan
2018-12-04 14:13               ` Jian-Hong Pan
2018-12-04 14:13             ` [PATCH V4 2/6] net: lorawan: Add LoRaWAN API declaration for LoRa devices Jian-Hong Pan
2018-12-04 14:13               ` Jian-Hong Pan
2018-12-04 14:13             ` [PATCH V4 3/6] net: maclorawan: Add maclorawan module declaration Jian-Hong Pan
2018-12-04 14:13               ` Jian-Hong Pan
2018-12-04 14:13             ` [PATCH V4 4/6] net: maclorawan: Implement the crypto of maclorawan module Jian-Hong Pan
2018-12-04 14:13               ` Jian-Hong Pan
2018-12-04 14:13             ` [PATCH V4 5/6] net: maclorawan: Implement maclorawan class module Jian-Hong Pan
2018-12-04 14:13               ` Jian-Hong Pan
2018-12-04 20:45               ` Alan Cox
2018-12-04 20:45                 ` Alan Cox
2018-12-04 20:45                 ` Alan Cox
2018-12-09  8:27                 ` Jian-Hong Pan
2018-12-09  8:27                   ` Jian-Hong Pan
2018-12-16 10:18                   ` [PATCH v5 0/6] net: lorawan: Add LoRaWAN soft MAC module Jian-Hong Pan
2018-12-16 10:18                     ` Jian-Hong Pan
2018-12-17 13:51                     ` Jiri Pirko
2018-12-17 13:51                       ` Jiri Pirko
2018-12-16 10:18                   ` [PATCH v5 1/6] net: lorawan: Add LoRaWAN socket module Jian-Hong Pan
2018-12-16 10:18                     ` Jian-Hong Pan
2018-12-29  7:27                     ` Andreas Färber
2018-12-29  7:27                       ` Andreas Färber
2019-01-07 14:47                       ` Jian-Hong Pan
2019-01-07 14:47                         ` Jian-Hong Pan
2019-01-07 14:47                         ` Jian-Hong Pan
2019-01-13 14:51                         ` Jian-Hong Pan
2019-01-13 14:51                           ` Jian-Hong Pan
2019-01-13 14:51                           ` Jian-Hong Pan
2018-12-16 10:18                   ` [PATCH v5 2/6] net: lorawan: Add LoRaWAN API declaration for LoRa devices Jian-Hong Pan
2018-12-16 10:18                     ` Jian-Hong Pan
2018-12-16 10:18                   ` [PATCH v5 3/6] net: maclorawan: Add maclorawan module declaration Jian-Hong Pan
2018-12-16 10:18                     ` Jian-Hong Pan
2018-12-16 10:18                   ` [PATCH v5 4/6] net: maclorawan: Implement the crypto of maclorawan module Jian-Hong Pan
2018-12-16 10:18                     ` Jian-Hong Pan
2018-12-16 10:18                   ` Jian-Hong Pan [this message]
2018-12-16 10:18                     ` [PATCH v5 5/6] net: maclorawan: Implement maclorawan class module Jian-Hong Pan
2018-12-17 14:02                     ` Jiri Pirko
2018-12-17 14:02                       ` Jiri Pirko
2018-12-18 14:27                       ` Jian-Hong Pan
2018-12-18 14:27                         ` Jian-Hong Pan
2018-12-18 14:27                         ` Jiri Pirko
2018-12-18 14:27                           ` Jiri Pirko
2018-12-18 15:34                           ` Jian-Hong Pan
2018-12-18 15:34                             ` Jian-Hong Pan
2018-12-18 18:49                         ` Andreas Färber
2018-12-18 18:49                           ` Andreas Färber
2018-12-19 11:27                           ` Ben Whitten
2018-12-19 11:27                             ` Ben Whitten
2018-12-19 11:27                             ` Ben Whitten
2018-12-19 16:26                             ` Jian-Hong Pan
2018-12-19 16:26                               ` Jian-Hong Pan
2018-12-20  9:20                               ` Xue Liu
2018-12-20 16:00                                 ` Jian-Hong Pan
2018-12-28  8:11                                   ` Netlink userspace tools for LoRa(WAN), FSK, Sigfox, BLE, etc. (was: [PATCH v5 5/6] net: maclorawan: Implement maclorawan class module) Andreas Färber
2018-12-28 15:49                                     ` Alexander Aring
2018-12-20 10:19                               ` [PATCH v5 5/6] net: maclorawan: Implement maclorawan class module Ben Whitten
2018-12-20 10:19                                 ` Ben Whitten
2018-12-20 15:31                                 ` Andreas Färber
2018-12-20 15:31                                   ` Andreas Färber
2018-12-20 15:31                                   ` Andreas Färber
2018-12-16 10:19                   ` [PATCH v5 6/6] net: lorawan: List LORAWAN in menuconfig Jian-Hong Pan
2018-12-16 10:19                     ` Jian-Hong Pan
2018-12-17  8:50                     ` Xue Liu
2018-12-17  8:50                       ` Xue Liu
2018-12-17 14:19                       ` Andreas Färber
2018-12-17 14:19                         ` Andreas Färber
2018-12-18 13:50                         ` Xue Liu
2018-12-18 13:50                           ` Xue Liu
2018-12-24 15:32                           ` Alexander Aring
2018-12-24 15:32                             ` Alexander Aring
2018-12-28  4:57                             ` Andreas Färber
2018-12-28  4:57                               ` Andreas Färber
2018-12-28 15:43                               ` Alexander Aring
2018-12-28 15:43                                 ` Alexander Aring
2018-12-29  6:28                                 ` Andreas Färber
2018-12-29  6:28                                   ` Andreas Färber
2018-12-04 14:13             ` [PATCH V4 " Jian-Hong Pan
2018-12-04 14:13               ` Jian-Hong Pan
2018-11-14 16:01       ` [PATCH V3 3/7] net: lorawan: Add LoRaWAN API declaration for LoRa devices Jian-Hong Pan
2018-11-14 16:01         ` Jian-Hong Pan
2018-11-14 16:01       ` [PATCH V3 4/7] net: maclorawan: Add maclorawan module declaration Jian-Hong Pan
2018-11-14 16:01         ` Jian-Hong Pan
2018-11-17  4:32         ` David Miller
2018-11-17  4:32           ` David Miller
2018-11-17  6:32           ` Jian-Hong Pan
2018-11-17  6:32             ` Jian-Hong Pan
2018-11-14 16:01       ` [PATCH V3 5/7] net: maclorawan: Implement the crypto of maclorawan module Jian-Hong Pan
2018-11-14 16:01         ` Jian-Hong Pan
2018-11-14 16:01       ` [PATCH V3 6/7] net: maclorawan: Implement maclorawan class module Jian-Hong Pan
2018-11-14 16:01         ` Jian-Hong Pan
2018-11-14 16:01       ` [PATCH V3 7/7] net: lorawan: List LORAWAN in menuconfig Jian-Hong Pan
2018-11-14 16:01         ` Jian-Hong Pan
2018-11-05 16:55   ` [PATCH V2 3/7] net: lorawan: Add LoRaWAN API declaration for LoRa devices Jian-Hong Pan
2018-11-05 16:55     ` Jian-Hong Pan
2018-11-05 16:55   ` [PATCH V2 4/7] net: maclorawan: Add maclorawan module declaration Jian-Hong Pan
2018-11-05 16:55     ` Jian-Hong Pan
2018-11-05 16:55   ` [PATCH V2 5/7] net: maclorawan: Implement the crypto of maclorawan module Jian-Hong Pan
2018-11-05 16:55     ` Jian-Hong Pan
2018-11-05 16:55     ` Jian-Hong Pan
2018-11-05 16:55   ` [PATCH V2 6/7] net: maclorawan: Implement maclorawan class module Jian-Hong Pan
2018-11-05 16:55     ` Jian-Hong Pan
2018-11-05 16:55   ` [PATCH V2 7/7] net: lorawan: List LORAWAN in menuconfig Jian-Hong Pan
2018-11-05 16:55     ` Jian-Hong Pan
2018-08-23 17:15 ` [RFC 2/3 net] lorawan: Add macro and definition for LoRaWAN class modlue Jian-Hong Pan
2018-08-23 17:15   ` Jian-Hong Pan
2018-08-23 17:15   ` Jian-Hong Pan
2018-09-23 16:06   ` Andreas Färber
2018-09-23 16:06     ` Andreas Färber
2018-09-23 16:06     ` Andreas Färber
2018-09-26 14:46     ` Jian-Hong Pan
2018-09-26 14:46       ` Jian-Hong Pan
2018-09-26 14:46       ` Jian-Hong Pan
2018-08-23 17:15 ` [RFC 3/3 net] lorawan: List LORAWAN in menuconfig Jian-Hong Pan
2018-08-23 17:15   ` Jian-Hong Pan
2018-08-23 17:15   ` Jian-Hong Pan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181216101858.9585-6-starnight@g.ncu.edu.tw \
    --to=starnight@g.ncu.edu.tw \
    --cc=afaerber@suse.de \
    --cc=davem@davemloft.net \
    --cc=dollar.chen@wtmec.com \
    --cc=gnomes@lxorguk.ukuu.org.uk \
    --cc=ken.yu@rakwireless.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-lpwan@lists.infradead.org \
    --cc=linux-wpan@vger.kernel.org \
    --cc=marcel@holtmann.org \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.