LoRaWAN(TM) is the MAC layer defined by LoRa Alliance(TM) over LoRa
devices. LoRa is one of Low-Power Wide-Area Network (LPWAN) technology.
LoRaWAN networks typically are laid out in a star-of-stars topology in
which gateways relay messages between end-devices and a central network
server at the backend. Gateways are connected to the network server via
standard IP connections while end-devices use single hop LoRa(TM) or FSK
communication to one or many gateways.
A LoRa network distinguishes between a basic LoRaWAN (named Class A) and
optional features (Class B, Class C ...):
* Bi-directional end-devices (Class A)
* Bi-directional end-devices with scheduled receive slots (Class B)
* Bi-directional end-devices with maximal receive slots (Class C)
This patch set add LoRaWAN class module implementing the stack,
especially the soft MAC, between socket APIs and LoRa device drivers.
socket APIs:
send and receive the data
------------------------------------------------------------------------
LoRaWAN class module implements soft MAC:
append the header/footer, encryption/decryption, timing slot and MAC
commands
------------------------------------------------------------------------
LoRa device drivers:
send and receive the messages for MAC layer
------------------------------------------------------------------------
LoRa devices
This module starts from simple and implements partial Class A
end-devices features defined in LoRaWAN(TM) Specification Ver. 1.0.2.
More features and complexity, for example regional parameters, confirmed
data messages, join request/accept messages for Over-The-Air Activation,
MAC commands ... will be added in the future.
Besides, I upload these patches and open discussion. For example, the
more common/flexible interface for compatible LoRa device drivers and
protocols implementation, etc.
Another idea mentioned by Andreas: Make PF_LORA and PF_LORAWAN like
PF_INET and PF_INET6 exist at the same time. That is also interesting.
Jian-Hong Pan (3):
lorawan: Add LoRaWAN class module
lorawan: Add macro and definition for LoRaWAN class modlue
lorawan: List LORAWAN in menuconfig
include/linux/maclorawan/lora.h | 239 ++++++++++
include/linux/socket.h | 5 +-
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 1 +
net/Kconfig | 1 +
net/Makefile | 1 +
net/core/dev.c | 4 +-
net/maclorawan/Kconfig | 14 +
net/maclorawan/Makefile | 2 +
net/maclorawan/lorawan.h | 219 +++++++++
net/maclorawan/lrwsec.c | 237 ++++++++++
net/maclorawan/lrwsec.h | 57 +++
net/maclorawan/mac.c | 552 ++++++++++++++++++++++
net/maclorawan/main.c | 665 ++++++++++++++++++++++++++
net/maclorawan/socket.c | 700 ++++++++++++++++++++++++++++
security/selinux/hooks.c | 4 +-
security/selinux/include/classmap.h | 4 +-
17 files changed, 2701 insertions(+), 5 deletions(-)
create mode 100644 include/linux/maclorawan/lora.h
create mode 100644 net/maclorawan/Kconfig
create mode 100644 net/maclorawan/Makefile
create mode 100644 net/maclorawan/lorawan.h
create mode 100644 net/maclorawan/lrwsec.c
create mode 100644 net/maclorawan/lrwsec.h
create mode 100644 net/maclorawan/mac.c
create mode 100644 net/maclorawan/main.c
create mode 100644 net/maclorawan/socket.c
--
2.18.0
LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa devices.
This patch implements part of Class A end-devices features 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
4. Encryption/decryption for up/down link data messages
It also implements the the functions and maps to Datagram socket for
LoRaWAN unconfirmed data messages.
On the other side, it defines the basic interface and operation
functions for compatible LoRa device drivers.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
include/linux/maclorawan/lora.h | 239 +++++++++++
net/maclorawan/Kconfig | 14 +
net/maclorawan/Makefile | 2 +
net/maclorawan/lorawan.h | 219 ++++++++++
net/maclorawan/lrwsec.c | 237 +++++++++++
net/maclorawan/lrwsec.h | 57 +++
net/maclorawan/mac.c | 552 +++++++++++++++++++++++++
net/maclorawan/main.c | 665 ++++++++++++++++++++++++++++++
net/maclorawan/socket.c | 700 ++++++++++++++++++++++++++++++++
9 files changed, 2685 insertions(+)
create mode 100644 include/linux/maclorawan/lora.h
create mode 100644 net/maclorawan/Kconfig
create mode 100644 net/maclorawan/Makefile
create mode 100644 net/maclorawan/lorawan.h
create mode 100644 net/maclorawan/lrwsec.c
create mode 100644 net/maclorawan/lrwsec.h
create mode 100644 net/maclorawan/mac.c
create mode 100644 net/maclorawan/main.c
create mode 100644 net/maclorawan/socket.c
diff --git a/include/linux/maclorawan/lora.h b/include/linux/maclorawan/lora.h
new file mode 100644
index 000000000000..228160bf0691
--- /dev/null
+++ b/include/linux/maclorawan/lora.h
@@ -0,0 +1,239 @@
+/*-
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef __LRW_H__
+#define __LRW_H__
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/random.h>
+
+/* List the role of the LoRaWAN hardware */
+enum {
+ LRW_GATEWAY,
+ LRW_CLASS_A_NODE,
+ LRW_CLASS_B_NODE,
+ LRW_CLASS_C_NODE,
+};
+
+/* List the regions */
+enum {
+ LRW_EU863_870,
+ LRW_US902_928,
+ LRW_CN779_787,
+ LRW_EU443,
+ LRW_AU915_928,
+ LRW_CN470_510,
+ LRW_AS923,
+ LRW_KR920_923,
+ LRW_INDIA865_867,
+ LRW_MAX_REGION,
+};
+
+enum {
+ LRW_LORA,
+ LRW_FSK,
+};
+
+#define LRW_UPLINK 0
+#define LRW_DOWNLINK 1
+
+#define LRW_DEVADDR_LEN (sizeof(__le32))
+
+/* List the message types of LoRaWAN */
+enum {
+ LRW_JOIN_REQUEST,
+ LRW_JOIN_ACCEPT,
+ LRW_UNCONFIRMED_DATA_UP,
+ LRW_UNCONFIRMED_DATA_DOWN,
+ LRW_CONFIRMED_DATA_UP,
+ LRW_CONFIRMED_DATA_DOWN,
+ LRW_RFU,
+ LRW_PROPRIETARY,
+};
+
+enum {
+ LRW_ADDR_APPEUI,
+ LRW_ADDR_DEVEUI,
+ LRW_ADDR_DEVADDR,
+};
+
+struct lrw_addr_in {
+ int addr_type;
+ union {
+ u64 app_eui;
+ u64 dev_eui;
+ u32 devaddr;
+ };
+};
+
+struct sockaddr_lorawan {
+ sa_family_t family; /* AF_LORAWAN */
+ struct lrw_addr_in addr_in;
+};
+
+/* List the LoRa device's states of LoRaWAN hardware */
+enum {
+ LRW_STOP,
+ LRW_START,
+ LRW_STATE_IDLE,
+ LRW_STATE_TX,
+ LRW_STATE_RX,
+ LRW_STATE_CAD,
+};
+
+/**
+ * lrw_hw - This structure holds the LoRa device of LoRaWAN hardware.
+ *
+ * @parent: points to the parent device
+ * @priv: points to the private data
+ * @channels: bits array of RF channels could be used
+ * @tx_powers: points to the emitting RF power array
+ * @tx_powers_size: the size of emitting RF power array in bytes
+ * @transmit_power: the current emitting RF power in mBm
+ */
+struct lrw_hw {
+ struct device *parent;
+ void *priv;
+ u32 channels;
+ u8 current_channel;
+ s32 *tx_powers;
+ size_t tx_powers_size;
+ s32 transmit_power;
+};
+
+/**
+ * lrw_operations - Lists the LoRaWAN device/interface's operations.
+ * These are callback functions for the LoRaWAN module. Compatible LoRa device
+ * driver should implement some of them according to the usage. The
+ * unimplemented callback functions must be assigned as NULL.
+ *
+ * @start:
+ * called when the interface is being up state
+ *
+ * @stop:
+ * called when the interface is being down state
+ *
+ * @xmit_async:
+ * called to xmit the data through the interface asynchronously
+ *
+ * @set_txpower:
+ * called to set xmitting RF power in mBm of the interface
+ *
+ * @set_frq:
+ * called to set carrier frequency Hz of the interface
+ *
+ * @set_bw:
+ * called to set RF bandwidth in Hz of the interface
+ *
+ * @set_mod:
+ * called to set the LoRa device's working mode: LoRa or FSK mode
+ *
+ * @set_sf:
+ * called to set the CSS modulation's spreading factor of LoRa
+ *
+ * @start_rx_window:
+ * called to ask the LoRa device open a receiving window
+ *
+ * @set_state:
+ * called to set the LoRa device's working state
+ *
+ * @get_state:
+ * called to get the LoRa device's current working state
+ */
+struct lrw_operations {
+ int (*start)(struct lrw_hw *);
+ void (*stop)(struct lrw_hw *);
+
+ int (*xmit_async)(struct lrw_hw *, struct sk_buff *);
+ int (*set_txpower)(struct lrw_hw *, s32);
+ int (*set_frq)(struct lrw_hw *, u32);
+ int (*set_bw)(struct lrw_hw *, u32);
+ int (*set_mod)(struct lrw_hw *, u8);
+ int (*set_sf)(struct lrw_hw *, u8);
+ int (*start_rx_window)(struct lrw_hw *, u32);
+
+ /* Set & get the state of the LoRa device. */
+ int (*set_state)(struct lrw_hw *, u8);
+ int (*get_state)(struct lrw_hw *, u8);
+};
+
+struct lrw_hw *lrw_alloc_hw(size_t, struct lrw_operations *);
+void lrw_free_hw(struct lrw_hw *);
+int lrw_register_hw(struct lrw_hw *);
+void lrw_unregister_hw(struct lrw_hw *);
+int lrw_set_region(struct lrw_hw *, u8);
+void lrw_rx_irqsave(struct lrw_hw *, struct sk_buff *);
+void lrw_xmit_complete(struct lrw_hw *, struct sk_buff *);
+
+static inline void lrw_random_addr(__le64 *addr)
+{
+ get_random_bytes(addr, sizeof(__le64));
+}
+
+void lrw_set_deveui(struct lrw_hw *, __le64);
+__le64 lrw_get_deveui(struct lrw_hw *);
+void lrw_set_appeui(struct lrw_hw *, __le64);
+__le64 lrw_get_appeui(struct lrw_hw *);
+void lrw_set_devaddr(struct lrw_hw *, __le32);
+__le32 lrw_get_devaddr(struct lrw_hw *);
+
+enum {
+ LRW_APPKEY,
+ LRW_NWKSKEY,
+ LRW_APPSKEY,
+};
+
+#define LRW_KEY_LEN 16
+
+struct lrw_key {
+ int type;
+ u8 key[LRW_KEY_LEN];
+};
+
+int lrw_set_key(struct lrw_hw *, u8, u8 *, size_t);
+
+/* Need to find a way to define or assign */
+#define LORAWAN_MTU 20
+
+#define le32_to_be32(n) (cpu_to_be32(le32_to_cpu(n)))
+#define be32_to_le32(n) (cpu_to_le32(be32_to_cpu(n)))
+#define le64_to_be64(n) (cpu_to_be64(le64_to_cpu(n)))
+#define be64_to_le64(n) (cpu_to_le64(be64_to_cpu(n)))
+
+#endif
diff --git a/net/maclorawan/Kconfig b/net/maclorawan/Kconfig
new file mode 100644
index 000000000000..741992b059c6
--- /dev/null
+++ b/net/maclorawan/Kconfig
@@ -0,0 +1,14 @@
+config LORAWAN
+ tristate "MAC layer of LoRaWAN Network support"
+ select CRYPTO
+ select CRYPTO_CMAC
+ select CRYPTO_CBC
+ select CRYPTO_AES
+ ---help---
+ LoRaWAN defines a low data rate, low power and long range wireless
+ wide area networks. It was designed to organise networks of sensors,
+ switches and actuators, etc automation devices. It could operate as
+ multiple kilometers wide.
+
+ Say Y here to compile LoRaWAN support into the kernel or say M to
+ compile it as modules.
diff --git a/net/maclorawan/Makefile b/net/maclorawan/Makefile
new file mode 100644
index 000000000000..fd7427c9ce52
--- /dev/null
+++ b/net/maclorawan/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_LORAWAN) += lorawan.o
+lorawan-objs := main.o mac.o lrwsec.o socket.o
diff --git a/net/maclorawan/lorawan.h b/net/maclorawan/lorawan.h
new file mode 100644
index 000000000000..3e49524a16fb
--- /dev/null
+++ b/net/maclorawan/lorawan.h
@@ -0,0 +1,219 @@
+/*-
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef __LORAWAN_H__
+#define __LORAWAN_H__
+
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <uapi/linux/if_arp.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include <linux/maclorawan/lora.h>
+
+#define LORAWAN_MODULE_NAME "lorawan"
+
+/* States of LoRaWAN slot timing */
+enum {
+ LRW_INIT_SS,
+ LRW_XMITTING_SS,
+ LRW_XMITTED,
+ LRW_RX1_SS,
+ LRW_RX2_SS,
+ LRW_RXTIMEOUT_SS,
+ LRW_RXRECEIVED_SS,
+ LRW_RETRANSMIT_SS,
+};
+
+#define LRW_MHDR_LEN 1
+#define LRW_FHDR_MAX_LEN 22
+#define LRW_FOPS_MAX_LEN 15
+#define LRW_FPORT_LEN 1
+#define LRW_MIC_LEN 4
+
+/**
+ * lrw_fhdr - Hold the message's basic information of the frame
+ *
+ * @mtype: this message's type
+ * @fctrl: the frame control byte
+ * @fcnt: this message's frame counter value
+ * @fopts: this frame's options field
+ * @fopts_len: the length of the fopts
+ */
+struct lrw_fhdr {
+ u8 mtype;
+ u8 fctrl;
+ u16 fcnt;
+ u8 fopts[LRW_FPORT_LEN];
+ u8 fopts_len;
+};
+
+/**
+ * lrw_session - LoRaWAN session for Class A end device
+ *
+ * @lrw_st: points to the belonging lrw_st
+ * @entry: the entry of the ss_list in lrw_struct
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ * @fcnt_up: uplink frame counter
+ * @fcnt_down: downlink frame counter
+ * @fport: the LoRaWAN data message's port field
+ * @tx_skb: points to the TX skb, the frame
+ * @rx_skb: points to the RX skb, the frame
+ * @tx_fhdr: hold the message's basic information of the TX frame
+ * @rx_fhdr: hold the message's basic information of the RX frame
+ * @tx_should_ack: flag for determining the TX which should be acked or not
+ * @retry: retry times for xmitting failed
+ * @state: this session's current state
+ * @state_lock: lock of the session's state
+ * @timer: timing for this session and the state transition
+ * @timeout_work: work if waiting acknowledge time out
+ * @rx_delay1: RX1 delay time in seconds
+ * @rx_delay2: RX2 delay time in seconds
+ * @rx1_window: RX1 window opening time in mini-seconds
+ * @rx2_window: RX2 window opening time in mini-seconds
+ * @ack_timeout: time out time for waiting acknowledge in seconds
+ */
+struct lrw_session {
+ struct lrw_struct *lrw_st;
+ struct list_head entry;
+
+ u8 devaddr[LRW_DEVADDR_LEN];
+ u16 fcnt_up;
+ u16 fcnt_down;
+ u8 fport;
+ struct sk_buff *tx_skb;
+ struct sk_buff *rx_skb;
+ struct lrw_fhdr tx_fhdr;
+ struct lrw_fhdr rx_fhdr;
+
+ bool tx_should_ack;
+ u8 retry;
+ u8 state;
+ spinlock_t state_lock;
+
+ struct timer_list timer;
+ struct work_struct timeout_work;
+ unsigned long rx_delay1;
+ unsigned long rx_delay2;
+ unsigned long rx1_window;
+ unsigned long rx2_window;
+ unsigned long ack_timeout;
+};
+
+/**
+ * lrw_struct - The full LoRaWAN hardware to the LoRa device.
+ *
+ * @dev: this LoRa device registed in system
+ * @lrw_hw: the LoRa device of this LoRaWAN hardware
+ * @ops: handle of LoRa operations interfaces
+ * @rx_skb_list: the list of received frames
+ * @ss_list: LoRaWAN session list of this LoRaWAN hardware
+ * @_cur_ss: pointer of the current processing session
+ * @rx_should_ack: represent the current session should be acked or not
+ * @state: the state of this LoRaWAN hardware
+ * @app_eui: the LoRaWAN application EUI
+ * @dev_eui: the LoRaWAN device EUI
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ * @appky: the Application key
+ * @nwkskey: the Network session key
+ * @appskey: the Application session key
+ * @nwks_shash_tfm: the hash handler for LoRaWAN network session
+ * @nwks_skc_tfm: the crypto handler for LoRaWAN network session
+ * @apps_skc_tfm: the crypto handler for LoRaWAN application session
+ * @fcnt_up: the counter of this LoRaWAN hardware's up frame
+ * @fcnt_down: the counter of this LoRaWAN hardware's down frame
+ * @xmit_task: the xmit task for the current LoRaWAN session
+ * @rx_work: the RX work in workqueue for the current LoRaWAN session
+ * @ndev: points to the emulating network device
+ * @_net: the current network namespace of this LoRaWAN hardware
+ */
+struct lrw_struct {
+ struct device dev;
+ struct lrw_hw hw;
+ struct lrw_operations *ops;
+
+ struct sk_buff_head rx_skb_list;
+ struct list_head ss_list;
+ struct mutex ss_list_lock;
+ struct lrw_session *_cur_ss;
+ bool rx_should_ack;
+ u8 state;
+
+ __le64 app_eui;
+ __le64 dev_eui;
+ __le32 devaddr;
+ u8 appkey[LRW_KEY_LEN];
+ u8 nwkskey[LRW_KEY_LEN];
+ u8 appskey[LRW_KEY_LEN];
+ struct crypto_shash *nwks_shash_tfm;
+ struct crypto_skcipher *nwks_skc_tfm;
+ struct crypto_skcipher *apps_skc_tfm;
+
+ u16 fcnt_up;
+ u16 fcnt_down;
+
+ struct tasklet_struct xmit_task;
+ struct work_struct rx_work;
+
+ struct net_device *ndev;
+ possible_net_t _net;
+};
+
+#define NETDEV_2_LRW(ndev) ((struct lrw_struct *)netdev_priv(ndev))
+
+struct lrw_session * lrw_alloc_ss(struct lrw_struct *);
+void lrw_free_ss(struct lrw_session *);
+void lrw_del_ss(struct lrw_session *);
+int lrw_start_hw(struct lrw_struct *);
+void lrw_stop_hw(struct lrw_struct *);
+void lrw_prepare_tx_frame(struct lrw_session *);
+void lrw_xmit(unsigned long);
+void lrw_rx_work(struct work_struct *);
+
+int lrw_sock_init(void);
+void lrw_sock_exit(void);
+
+struct lrw_mac_cb {
+ int rssi;
+ u32 devaddr;
+};
+
+#endif
diff --git a/net/maclorawan/lrwsec.c b/net/maclorawan/lrwsec.c
new file mode 100644
index 000000000000..5a9ba184343d
--- /dev/null
+++ b/net/maclorawan/lrwsec.c
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include <linux/scatterlist.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include "lrwsec.h"
+
+struct crypto_shash *
+lrw_mic_key_setup(u8 *k, size_t k_len)
+{
+ char *algo = "cmac(aes)";
+ struct crypto_shash *tfm;
+ int err;
+
+ tfm = crypto_alloc_shash(algo, 0, 0);
+ if (!IS_ERR(tfm)) {
+ err = crypto_shash_setkey(tfm, k, k_len);
+ if (err) {
+ crypto_free_shash(tfm);
+ tfm = NULL;
+ }
+ }
+
+ return tfm;
+}
+
+int
+lrw_aes_cmac(struct crypto_shash *tfm, u8 *bz, u8 *data, size_t len, u8 *out)
+{
+ SHASH_DESC_ON_STACK(desc, tfm);
+ int err;
+
+ desc->tfm = tfm;
+
+ err = crypto_shash_init(desc);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_update(desc, bz, 16);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_update(desc, data, len);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_final(desc, out);
+
+lrw_aes_cmac_end:
+ return err;
+}
+
+int
+lrw_set_bzero(u8 dir, u8 *devaddr, u32 fcnt, u8 len, u8 *bz)
+{
+ __le32 _fcnt = cpu_to_le32(fcnt);
+
+ bz[0] = 0x49;
+ memset(bz + 1, 0x00, 4);
+ bz[5] = dir;
+ memcpy(bz + 6, devaddr, 4);
+ memcpy(bz + 10, &_fcnt, 4);
+ bz[14] = 0x00;
+ bz[15] = len;
+
+ return 0;
+}
+
+int
+lrw_calc_mic(struct crypto_shash *tfm,
+ u8 dir, u8 *devaddr, u32 fcnt, u8* buf, size_t len, u8 *mic4)
+{
+ u8 mic[16];
+ u8 bz[16];
+ int err;
+
+ /* According to LoRaWAN Specification Version 1.0.2
+ * - 4.4 Massege Integrity Code (MIC) */
+ lrw_set_bzero(dir, devaddr, fcnt, len, bz);
+ err = lrw_aes_cmac(tfm, bz, buf, len, mic);
+ if (!err)
+ memcpy(mic4, mic, 4);
+
+ return err;
+}
+
+void
+lrw_mic_key_free(struct crypto_shash *tfm)
+{
+ crypto_free_shash(tfm);
+}
+
+struct crypto_skcipher *
+lrw_aes_enc_key_setup(char *algo, u8 *k, size_t k_len)
+{
+ struct crypto_skcipher *tfm;
+ int err;
+
+ tfm = crypto_alloc_skcipher(algo, 0, CRYPTO_ALG_ASYNC);
+ if (!IS_ERR(tfm)) {
+ err = crypto_skcipher_setkey(tfm, k, k_len);
+ if (err) {
+ crypto_free_skcipher(tfm);
+ tfm = NULL;
+ }
+ }
+
+ return tfm;
+}
+
+struct crypto_skcipher *
+lrw_encrypt_key_setup(u8 *k, size_t k_len)
+{
+ return lrw_aes_enc_key_setup("cbc(aes)", k, k_len);
+}
+
+int
+lrw_aes_enc(struct crypto_skcipher *tfm, u8 *in, size_t len, u8 *out)
+{
+ u8 iv[16];
+ struct scatterlist src, dst;
+ SKCIPHER_REQUEST_ON_STACK(req, tfm);
+ int err;
+
+ memset(iv, 0, 16);
+ /* The buffer for sg_init_one cannot be a global or const local
+ * (will confuse the scatterlist) */
+ sg_init_one(&src, in, len);
+ sg_init_one(&dst, out, len);
+
+ skcipher_request_set_tfm(req, tfm);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+ skcipher_request_set_crypt(req, &src, &dst, len, iv);
+ err = crypto_skcipher_encrypt(req);
+ skcipher_request_zero(req);
+
+ return err;
+}
+
+#define LRW_SEQUENCE_OF_BLOCK_LEN 16
+
+int
+lrw_set_sob(u8 dir, u8 *devaddr, u32 fcnt, u8 index, u8 *sob)
+{
+ __le32 _fcnt = cpu_to_le32(fcnt);
+
+ sob[0] = 0x01;
+ memset(sob + 1, 0x00, 4);
+ sob[5] = dir;
+ memcpy(sob + 6, devaddr, 4);
+ memcpy(sob + 10, &_fcnt, 4);
+ sob[14] = 0x00;
+ sob[15] = index;
+
+ return 0;
+}
+
+int
+lrw_encrypt_sob(struct crypto_skcipher *tfm, u8 *sob)
+{
+ return lrw_aes_enc(tfm, sob, LRW_SEQUENCE_OF_BLOCK_LEN, sob);
+}
+
+int
+lrw_encrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u8 *devaddr, u32 fcnt, u8 *buf, size_t len)
+{
+ u8 sob[LRW_SEQUENCE_OF_BLOCK_LEN];
+ u8 i, j;
+
+ /* According to LoRaWAN Specification Version 1.0.2
+ * - 4.3.3 MAC Frame Payload Encryption (FRMPayload) */
+ for (i = 0; (i * LRW_SEQUENCE_OF_BLOCK_LEN) < len; i++) {
+ lrw_set_sob(dir, devaddr, fcnt, i, sob);
+ lrw_encrypt_sob(tfm, sob);
+ for (j = 0; (i * LRW_SEQUENCE_OF_BLOCK_LEN + j) < len; j++)
+ buf[i * LRW_SEQUENCE_OF_BLOCK_LEN + j] ^= sob[j];
+ }
+
+ return 0;
+}
+
+int
+lrw_decrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u8 *devaddr, u32 fcnt, u8 *buf, size_t len)
+{
+ /* Accoding to XOR swap algorithm */
+ return lrw_encrypt_buf(tfm, dir, devaddr, fcnt, buf, len);
+}
+
+void
+lrw_aes_enc_key_free(struct crypto_skcipher *tfm)
+{
+ crypto_free_skcipher(tfm);
+}
+
+void
+lrw_encrypt_key_free(struct crypto_skcipher *tfm)
+{
+ lrw_aes_enc_key_free(tfm);
+}
diff --git a/net/maclorawan/lrwsec.h b/net/maclorawan/lrwsec.h
new file mode 100644
index 000000000000..cd8a2d71b0f2
--- /dev/null
+++ b/net/maclorawan/lrwsec.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef __LORA_CRYPTO_H__
+#define __LORA_CRYPTO_H__
+
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+
+struct crypto_shash *lrw_mic_key_setup(u8 *k, size_t k_len);
+int lrw_calc_mic(struct crypto_shash *tfm,
+ u8 dir, u8 *devaddr, u32 fcnt, u8* buf, size_t len, u8 *mic4);
+void lrw_mic_key_free(struct crypto_shash *tfm);
+
+struct crypto_skcipher *lrw_encrypt_key_setup(u8 *k, size_t k_len);
+int lrw_encrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u8 *devaddr, u32 fcnt, u8 *buf, size_t len);
+int lrw_decrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u8 *devaddr, u32 fcnt, u8 *buf, size_t len);
+void lrw_encrypt_key_free(struct crypto_skcipher *tfm);
+
+#endif
diff --git a/net/maclorawan/mac.c b/net/maclorawan/mac.c
new file mode 100644
index 000000000000..a6b75749d05c
--- /dev/null
+++ b/net/maclorawan/mac.c
@@ -0,0 +1,552 @@
+/*-
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/poll.h>
+#include <asm/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/maclorawan/lora.h>
+#include "lorawan.h"
+#include "lrwsec.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(struct lrw_session), GFP_KERNEL);
+ if (!ss)
+ goto lrw_alloc_ss_end;
+
+ ss->lrw_st = lrw_st;
+ memcpy(ss->devaddr, &lrw_st->devaddr, LRW_DEVADDR_LEN);
+ 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[4];
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ mhdr = LRW_UNCONFIRMED_DATA_UP << 5;
+ if ((mhdr & (0x6 << 5)) == (0x4 << 5))
+ ss->tx_should_ack = true;
+
+ fctrl = 0;
+ if (lrw_st->rx_should_ack) {
+ fctrl |= 0x20;
+ lrw_st->rx_should_ack = false;
+ }
+
+ /* 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 */
+ memcpy(skb_push(skb, 2), &ss->fcnt_up, 2);
+
+ /* Push FCtrl */
+ memcpy(skb_push(skb, 1), &fctrl, 1);
+
+ /* Push DevAddr */
+ memcpy(skb_push(skb, LRW_DEVADDR_LEN), ss->devaddr, LRW_DEVADDR_LEN);
+
+ /* Push MHDR */
+ 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);
+}
+
+void
+lrw_parse_frame(struct lrw_session *ss, struct sk_buff *skb)
+{
+ 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, 4);
+
+ /* Get frame control */
+ fhdr->fctrl = skb->data[0];
+ skb_pull(skb, 1);
+
+ /* 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, 2);
+
+ /* Get frame options */
+ fhdr->fopts_len = fhdr->fctrl & 0xF;
+ if (fhdr->fopts_len > 0) {
+ memcpy(fhdr->fopts, skb->data, fhdr->fopts_len);
+ skb_pull(skb, fhdr->fopts_len);
+ }
+
+ /* TODO: Parse frame options */
+
+ /* Remove message integrity code */
+ skb_trim(skb, skb->len - LRW_MIC_LEN);
+}
+
+struct lrw_session *
+lrw_rx_skb_2_session(struct lrw_struct *lrw_st, struct sk_buff *rx_skb)
+{
+ struct lrw_session *ss;
+ u16 fcnt;
+ __le16 *p_fcnt;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ p_fcnt = (__le16 *)(rx_skb->data + 6);
+ 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 = lrw_st->rx_skb_list.next;
+ 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;
+
+ lrw_parse_frame(ss, skb);
+
+ /* 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;
+
+ if (ss->rx_skb->len > 0) {
+ spin_lock_bh(&ss->state_lock);
+ ss->state = LRW_RXRECEIVED_SS;
+ spin_unlock_bh(&ss->state_lock);
+
+ 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 */
+ kfree_skb(skb);
+}
+
+int
+lrw_check_mic(struct crypto_shash *tfm, struct sk_buff *skb)
+{
+ u8 *buf;
+ size_t len;
+ u8 *devaddr;
+ u16 fcnt;
+ __le16 *p_fcnt;
+ u8 cks[4];
+ u8 *mic;
+
+ buf = skb->data;
+ len = skb->len - 4;
+ devaddr = buf + 1;
+ p_fcnt = (__le16 *)(buf + 6);
+ fcnt = le16_to_cpu(*p_fcnt);
+ mic = skb->data + len;
+
+ lrw_calc_mic(tfm, LRW_DOWNLINK, devaddr, fcnt, buf, len, cks);
+
+ return (!memcmp(cks, mic, 4));
+}
+
+/**
+ * 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);
+ u8 mtype;
+ bool is_new_frame;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ mtype = skb->data[0] >> 5;
+ is_new_frame = 0;
+
+ /* Check the frame is downlink frame */
+ if (((mtype == LRW_UNCONFIRMED_DATA_DOWN)
+ || (mtype == LRW_CONFIRMED_DATA_DOWN))
+ && (memcmp(&lrw_st->devaddr, skb->data + LRW_MHDR_LEN, 4) != 0)
+ && lrw_check_mic(lrw_st->nwks_shash_tfm, skb))
+ is_new_frame = true;
+
+ if (is_new_frame) {
+ skb_queue_tail(&lrw_st->rx_skb_list, skb);
+ schedule_work(&lrw_st->rx_work);
+ }
+ else {
+ 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_session *ss;
+ struct lrw_struct *lrw_st;
+
+ 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 - 20 * HZ / 1000;
+ 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..4fcb5a442686
--- /dev/null
+++ b/net/maclorawan/main.c
@@ -0,0 +1,665 @@
+/*-
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <net/rtnetlink.h>
+
+#include <linux/maclorawan/lora.h>
+#include "lorawan.h"
+
+#define PHY_NAME "lora"
+
+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 = 20;
+}
+
+/**
+ * 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 net_device *ndev;
+ struct lrw_struct *lrw_st;
+ int ret;
+
+ if (WARN_ON(!ops || !ops->start || !ops->stop || !ops->xmit_async ||
+ !ops->set_txpower || !ops->set_frq || !ops->set_bw ||
+ !ops->set_mod || !ops->set_sf || !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 = (void *) lrw_st + sizeof(struct lrw_struct);
+
+ 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, __le64 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
+ */
+__le64
+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, __le64 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
+ */
+__le64
+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, __le32 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
+ */
+__le32
+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
+ */
+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 = le32_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
+ */
+void
+lrw_remove_hw(struct lrw_struct *lrw_st)
+{
+ unregister_netdev(lrw_st->ndev);
+ tasklet_kill(&lrw_st->xmit_task);
+}
+
+int
+lrw_set_hw_state(struct lrw_struct *lrw_st, void __user *arg)
+{
+ int ret = 0;
+ u8 state;
+
+ ret = copy_from_user(&state, arg, 1);
+ if (ret) {
+ ret = -EACCES;
+ goto lrw_set_hw_state_end;
+ }
+
+ switch (state) {
+ case LRW_START:
+ if (lrw_st->state == LRW_STOP)
+ lrw_start_hw(lrw_st);
+ break;
+ case LRW_STOP:
+ if (lrw_st->state != LRW_STOP)
+ lrw_stop_hw(lrw_st);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+
+lrw_set_hw_state_end:
+ return ret;
+}
+
+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)
+{
+ bool status = false;
+ struct lrw_session *ss;
+
+ 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;
+}
+
+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 = 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;
+}
+
+inline 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 = le32_to_cpu(lrw_st->devaddr);
+ break;
+ case LRW_ADDR_DEVEUI:
+ addr->addr_in.dev_eui = le64_to_cpu(lrw_st->dev_eui);
+ break;
+ case LRW_ADDR_APPEUI:
+ addr->addr_in.app_eui = le64_to_cpu(lrw_st->app_eui);
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+
+inline 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, cpu_to_le32(addr->addr_in.devaddr));
+ break;
+ case LRW_ADDR_DEVEUI:
+ lrw_set_deveui(hw, cpu_to_le32(addr->addr_in.dev_eui));
+ break;
+ case LRW_ADDR_APPEUI:
+ lrw_set_appeui(hw, cpu_to_le32(addr->addr_in.app_eui));
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+
+ return ret;
+}
+
+inline 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 = (__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_le32(*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);
+
+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);
+
+ pr_info("%s: unregister %s\n",
+ LORAWAN_MODULE_NAME, dev_name(&lrw_st->dev));
+
+ /* 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);
+
+ return;
+}
+EXPORT_SYMBOL(lrw_unregister_hw);
+
+static int __init
+lrw_init(void)
+{
+ int err;
+
+ 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);
+
+ /* Initial LoRaWAN socket API */
+ err = lrw_sock_init();
+
+lrw_init_end:
+ return err;
+}
+
+static void __exit
+lrw_exit(void)
+{
+ /* Release LoRaWAN socket API */
+ lrw_sock_exit();
+ /* Delete device class */
+ class_destroy(lrw_sys_class);
+ pr_info("%s: module removed\n", LORAWAN_MODULE_NAME);
+
+ return;
+}
+
+module_init(lrw_init);
+module_exit(lrw_exit);
+
+MODULE_AUTHOR("Jian-Hong Pan, <[email protected]>");
+MODULE_DESCRIPTION("LoRaWAN kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/net/maclorawan/socket.c b/net/maclorawan/socket.c
new file mode 100644
index 000000000000..cfb4156ec556
--- /dev/null
+++ b/net/maclorawan/socket.c
@@ -0,0 +1,700 @@
+/*-
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/net.h>
+#include <linux/if_arp.h>
+#include <linux/termios.h> /* For TIOCOUTQ/INQ */
+#include <net/sock.h>
+
+#include "lorawan.h"
+
+struct dgram_sock {
+ struct sock sk;
+ u32 src_devaddr;
+
+ unsigned int bound:1;
+ unsigned int connected:1;
+ unsigned int want_ack:1;
+ unsigned int secen:1;
+ unsigned int secen_override:1;
+ unsigned int seclevel:3;
+ unsigned int seclevel_override:1;
+};
+
+static HLIST_HEAD(dgram_head);
+static DEFINE_RWLOCK(dgram_lock);
+
+inline struct dgram_sock *
+dgram_sk(const struct sock *sk)
+{
+ return container_of(sk, struct dgram_sock, sk);
+}
+
+inline struct net_device *
+lrw_get_dev_by_addr(struct net *net, u32 devaddr)
+{
+ struct net_device *ndev = NULL;
+ __be32 be_addr = cpu_to_be32(devaddr);
+
+ rcu_read_lock();
+ ndev = dev_getbyhwaddr_rcu(net, ARPHRD_LORAWAN, (char *)&be_addr);
+ if (ndev)
+ dev_hold(ndev);
+ rcu_read_unlock();
+
+ return ndev;
+}
+
+inline struct lrw_mac_cb *
+mac_cb(struct sk_buff *skb)
+{
+ return (struct lrw_mac_cb *)skb->cb;
+}
+
+static int
+dgram_init(struct sock *sk)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ ro->want_ack = 1;
+ return 0;
+}
+
+static void
+dgram_close(struct sock *sk, long timeout)
+{
+ sk_common_release(sk);
+}
+
+static int
+dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
+ struct dgram_sock *ro = dgram_sk(sk);
+ struct net_device *ndev;
+ int ret;
+
+ lock_sock(sk);
+ ro->bound = 0;
+
+ ret = -EINVAL;
+ if (len < sizeof(*addr))
+ goto dgram_bind_end;
+
+ if (addr->family != AF_LORAWAN)
+ goto dgram_bind_end;
+
+ if (addr->addr_in.addr_type != LRW_ADDR_DEVADDR)
+ goto dgram_bind_end;
+
+ pr_debug("lorawan: %s: bind address %X\n", __func__, addr->addr_in.devaddr);
+ ndev = lrw_get_dev_by_addr(sock_net(sk), addr->addr_in.devaddr);
+ if (!ndev) {
+ ret = -ENODEV;
+ goto dgram_bind_end;
+ }
+ netdev_dbg(ndev, "%s: get ndev\n", __func__);
+
+ if (ndev->type != ARPHRD_LORAWAN) {
+ ret = -ENODEV;
+ goto dgram_bind_end;
+ }
+
+ ro->src_devaddr = addr->addr_in.devaddr;
+ ro->bound = 1;
+ ret = 0;
+ dev_put(ndev);
+ pr_debug("lorawan: %s: bound address %X\n", __func__, ro->src_devaddr);
+
+dgram_bind_end:
+ release_sock(sk);
+ return ret;
+}
+
+inline int
+lrw_dev_hard_header(struct sk_buff *skb, struct net_device *ndev,
+ const u32 src_devaddr, size_t len)
+{
+ /* TODO: Prepare the LoRaWAN sending header here */
+ return 0;
+}
+
+static int
+dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ size_t hlen;
+ size_t mtu;
+ size_t tlen;
+ int ret;
+
+ pr_debug("%s: %s: going to send %zu bytes", LORAWAN_MODULE_NAME, __func__, size);
+ if (msg->msg_flags & MSG_OOB) {
+ pr_debug("%s: msg->msg_flags = 0x%x\n", LORAWAN_MODULE_NAME, msg->msg_flags);
+ return -EOPNOTSUPP;
+ }
+
+ pr_debug("%s: %s: check msg_name\n", LORAWAN_MODULE_NAME, __func__);
+ if (!ro->connected && !msg->msg_name)
+ return -EDESTADDRREQ;
+ else if (ro->connected && msg->msg_name)
+ return -EISCONN;
+
+ pr_debug("%s: %s: check bound\n", LORAWAN_MODULE_NAME, __func__);
+ if (!ro->bound)
+ ndev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_LORAWAN);
+ else
+ ndev = lrw_get_dev_by_addr(sock_net(sk), ro->src_devaddr);
+
+ if (!ndev) {
+ pr_debug("%s: no dev\n", LORAWAN_MODULE_NAME);
+ ret = -ENXIO;
+ goto dgram_sendmsg_end;
+ }
+
+ /* TODO: MTU should be the regional defined */
+ mtu = LORAWAN_MTU;
+ if (size > mtu){
+ netdev_dbg(ndev, "size = %zu, mtu = %zu\n", size, mtu);
+ ret = -EMSGSIZE;
+ goto dgram_sendmsg_end;
+ }
+
+ netdev_dbg(ndev, "%s: create skb\n", __func__);
+ hlen = LL_RESERVED_SPACE(ndev);
+ tlen = ndev->needed_tailroom;
+ skb = sock_alloc_send_skb(sk, hlen + tlen + size,
+ msg->msg_flags & MSG_DONTWAIT,
+ &ret);
+
+ if (!skb)
+ goto dgram_sendmsg_no_skb;
+
+ skb_reserve(skb, hlen);
+ skb_reset_network_header(skb);
+
+ ret = lrw_dev_hard_header(skb, ndev, ro->bound ? ro->src_devaddr : 0, size);
+ if (ret < 0)
+ goto dgram_sendmsg_no_skb;
+
+ ret = memcpy_from_msg(skb_put(skb, size), msg, size);
+ if (ret > 0)
+ goto dgram_sendmsg_err_skb;
+
+ skb->dev = ndev;
+ skb->protocol = htons(ETH_P_LORAWAN);
+
+ netdev_dbg(ndev, "%s: push skb to xmit queue\n", __func__);
+ ret = dev_queue_xmit(skb);
+ if (ret > 0)
+ ret = net_xmit_errno(ret);
+ netdev_dbg(ndev, "%s: pushed skb to xmit queue with ret=%d\n", __func__, ret);
+ dev_put(ndev);
+
+ return ret ?: size;
+
+dgram_sendmsg_err_skb:
+ kfree_skb(skb);
+dgram_sendmsg_no_skb:
+ dev_put(ndev);
+
+dgram_sendmsg_end:
+ return ret;
+}
+
+static int
+dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+ int noblock, int flags, int *addr_len)
+{
+ struct sk_buff *skb;
+ size_t copied = 0;
+ DECLARE_SOCKADDR(struct sockaddr_lorawan *, saddr, msg->msg_name);
+ int err;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto dgram_recvmsg_end;
+
+ copied = skb->len;
+ if (len < copied) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
+ if (err)
+ goto dgram_recvmsg_done;
+
+ sock_recv_ts_and_drops(msg, sk, skb);
+ if(saddr) {
+ memset(saddr, 0, sizeof(*saddr));
+ saddr->family = AF_LORAWAN;
+ saddr->addr_in.devaddr = mac_cb(skb)->devaddr;
+ *addr_len = sizeof(*saddr);
+ }
+
+ if (flags & MSG_TRUNC)
+ copied = skb->len;
+
+dgram_recvmsg_done:
+ skb_free_datagram(sk, skb);
+
+dgram_recvmsg_end:
+ if (err)
+ return err;
+ return copied;
+}
+
+static int
+dgram_hash(struct sock *sk)
+{
+ pr_debug("lorawan: %s\n", __func__);
+ write_lock_bh(&dgram_lock);
+ sk_add_node(sk, &dgram_head);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ write_unlock_bh(&dgram_lock);
+
+ return 0;
+}
+
+static void
+dgram_unhash(struct sock *sk)
+{
+ pr_debug("lorawan: %s\n", __func__);
+ write_lock_bh(&dgram_lock);
+ if (sk_del_node_init(sk))
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ write_unlock_bh(&dgram_lock);
+}
+
+static int
+dgram_connect(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ /* Nodes of LoRaWAN send data to a gateway only, then data is received
+ * and transferred to servers with the gateway's policy.
+ * So, the destination address is not used by nodes.
+ */
+ lock_sock(sk);
+ ro->connected = 1;
+ release_sock(sk);
+
+ return 0;
+}
+
+static int
+dgram_disconnect(struct sock *sk, int flags)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ lock_sock(sk);
+ ro->connected = 0;
+ release_sock(sk);
+
+ return 0;
+}
+
+static int
+dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ struct sk_buff *skb;
+ int amount;
+ int err;
+ struct net_device *ndev = sk->sk_dst_cache->dev;
+
+ netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
+ switch (cmd) {
+ case SIOCOUTQ:
+ amount = sk_wmem_alloc_get(sk);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ case SIOCINQ:
+ amount = 0;
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb) {
+ /* We will only return the amount of this packet
+ * since that is all that will be read.
+ */
+ amount = skb->len - 0;//lrw_hdr_length(skb);
+ }
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ default:
+ err = -ENOIOCTLCMD;
+ }
+
+ return err;
+}
+
+static int
+dgram_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ int val, len;
+
+ if (level != SOL_LORAWAN)
+ return -EOPNOTSUPP;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ len = min_t(unsigned int, len, sizeof(int));
+
+ switch (optname) {
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+dgram_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ int val;
+ int err = 0;
+
+ if (optlen < sizeof(int))
+ return -EINVAL;
+
+ if (get_user(val, (int __user *)optval))
+ return -EFAULT;
+
+ lock_sock(sk);
+
+ switch (optname) {
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ release_sock(sk);
+
+ return err;
+}
+
+static struct proto lrw_dgram_prot = {
+ .name = "LoRaWAN-MAC",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct dgram_sock),
+ .init = dgram_init,
+ .close = dgram_close,
+ .bind = dgram_bind,
+ .sendmsg = dgram_sendmsg,
+ .recvmsg = dgram_recvmsg,
+ .hash = dgram_hash,
+ .unhash = dgram_unhash,
+ .connect = dgram_connect,
+ .disconnect = dgram_disconnect,
+ .ioctl = dgram_ioctl,
+ .getsockopt = dgram_getsockopt,
+ .setsockopt = dgram_setsockopt,
+};
+
+static int
+lrw_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk) {
+ sock->sk = NULL;
+ sk->sk_prot->close(sk, 0);
+ }
+
+ return 0;
+}
+
+static int
+lrw_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
+
+ pr_debug("lorawan: %s: bind address %X\n", __func__, addr->addr_in.devaddr);
+ if (sk->sk_prot->bind)
+ return sk->sk_prot->bind(sk, uaddr, addr_len);
+
+ return sock_no_bind(sock, uaddr, addr_len);
+}
+
+static int
+lrw_sock_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ if (addr_len < sizeof(uaddr->sa_family))
+ return -EINVAL;
+
+ return sk->sk_prot->connect(sk, uaddr, addr_len);
+}
+
+static int
+lrw_ndev_ioctl(struct sock *sk, struct ifreq __user *arg, unsigned int cmd)
+{
+ struct ifreq ifr;
+ int ret = -ENOIOCTLCMD;
+ struct net_device *ndev;
+
+ pr_debug("lorawan: %s: cmd %ud\n", __func__, cmd);
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
+
+ ifr.ifr_name[IFNAMSIZ-1] = 0;
+
+ dev_load(sock_net(sk), ifr.ifr_name);
+ ndev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
+
+ netdev_dbg(ndev, "%s: cmd %ud\n", __func__, cmd);
+ if (!ndev)
+ return -ENODEV;
+
+ if (ndev->type == ARPHRD_LORAWAN && ndev->netdev_ops->ndo_do_ioctl)
+ ret = ndev->netdev_ops->ndo_do_ioctl(ndev, &ifr, cmd);
+
+ if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ ret = -EFAULT;
+ dev_put(ndev);
+
+ return ret;
+}
+
+static int
+lrw_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+
+ pr_debug("lorawan: %s: cmd %ud\n", __func__, cmd);
+ switch (cmd) {
+ case SIOCGSTAMP:
+ return sock_get_timestamp(sk, (struct timeval __user *)arg);
+ case SIOCGSTAMPNS:
+ return sock_get_timestampns(sk, (struct timespec __user *)arg);
+ case SIOCOUTQ:
+ case SIOCINQ:
+ if (!sk->sk_prot->ioctl)
+ return -ENOIOCTLCMD;
+ return sk->sk_prot->ioctl(sk, cmd, arg);
+ default:
+ return lrw_ndev_ioctl(sk, (struct ifreq __user *)arg, cmd);
+ }
+}
+
+static int
+lrw_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+
+ pr_debug("%s: going to send %zu bytes\n", __func__, len);
+ return sk->sk_prot->sendmsg(sk, msg, len);
+}
+
+static const struct proto_ops lrw_dgram_ops = {
+ .family = PF_LORAWAN,
+ .owner = THIS_MODULE,
+ .release = lrw_sock_release,
+ .bind = lrw_sock_bind,
+ .connect = lrw_sock_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = lrw_sock_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = lrw_sock_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static int
+lorawan_creat(struct net *net, struct socket *sock, int protocol, int kern)
+{
+ struct sock *sk;
+ int ret;
+
+ if (!net_eq(net, &init_net))
+ return -EAFNOSUPPORT;
+
+ if (sock->type != SOCK_DGRAM)
+ return -EAFNOSUPPORT;
+
+ sk = sk_alloc(net, PF_LORAWAN, GFP_KERNEL, &lrw_dgram_prot, kern);
+ if (!sk)
+ return -ENOMEM;
+
+ sock->ops = &lrw_dgram_ops;
+ sock_init_data(sock, sk);
+ sk->sk_family = PF_LORAWAN;
+ sock_set_flag(sk, SOCK_ZAPPED);
+
+ if (sk->sk_prot->hash) {
+ ret = sk->sk_prot->hash(sk);
+ if (ret) {
+ sk_common_release(sk);
+ goto lorawan_creat_end;
+ }
+ }
+
+ if (sk->sk_prot->init) {
+ ret = sk->sk_prot->init(sk);
+ if (ret)
+ sk_common_release(sk);
+ }
+
+lorawan_creat_end:
+ return ret;
+}
+
+static const struct net_proto_family lorawan_family_ops = {
+ .owner = THIS_MODULE,
+ .family = PF_LORAWAN,
+ .create = lorawan_creat,
+};
+
+inline int
+lrw_dgram_deliver(struct net_device *ndev, struct sk_buff *skb)
+{
+ struct lrw_struct *lrw_st = NETDEV_2_LRW(ndev);
+ struct sock *sk;
+ struct dgram_sock *ro;
+ bool found = false;
+ int ret = NET_RX_SUCCESS;
+
+ read_lock(&dgram_lock);
+ sk_for_each(sk, &dgram_head) {
+ ro = dgram_sk(sk);
+ if(cpu_to_le32(ro->src_devaddr) == lrw_st->devaddr) {
+ found = true;
+ break;
+ }
+ }
+ read_unlock(&dgram_lock);
+
+ if (!found)
+ goto lrw_dgram_deliver_err;
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NET_RX_DROP;
+
+ if (sock_queue_rcv_skb(sk, skb) < 0)
+ goto lrw_dgram_deliver_err;
+
+ return ret;
+
+lrw_dgram_deliver_err:
+ kfree_skb(skb);
+ ret = NET_RX_DROP;
+ return ret;
+}
+
+static int
+lorawan_rcv(struct sk_buff *skb, struct net_device *ndev,
+ struct packet_type *pt, struct net_device *orig_ndev)
+{
+ if (!netif_running(ndev))
+ goto lorawan_rcv_drop;
+
+ if (!net_eq(dev_net(ndev), &init_net))
+ goto lorawan_rcv_drop;
+
+ if (ndev->type != ARPHRD_LORAWAN)
+ goto lorawan_rcv_drop;
+
+ if (skb->pkt_type != PACKET_OTHERHOST)
+ return lrw_dgram_deliver(ndev, skb);
+
+lorawan_rcv_drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static struct packet_type lorawan_packet_type = {
+ .type = htons(ETH_P_LORAWAN),
+ .func = lorawan_rcv,
+};
+
+int
+lrw_sock_init(void)
+{
+ int ret;
+
+ ret = proto_register(&lrw_dgram_prot, 1);
+ if(ret)
+ goto lrw_sock_init_end;
+
+ /* Tell SOCKET that we are alive */
+ ret = sock_register(&lorawan_family_ops);
+ if(ret)
+ goto lrw_sock_init_err;
+
+ dev_add_pack(&lorawan_packet_type);
+ ret = 0;
+ goto lrw_sock_init_end;
+
+lrw_sock_init_err:
+ proto_unregister(&lrw_dgram_prot);
+
+lrw_sock_init_end:
+ return 0;
+}
+
+void
+lrw_sock_exit(void)
+{
+ dev_remove_pack(&lorawan_packet_type);
+ sock_unregister(PF_LORAWAN);
+ proto_unregister(&lrw_dgram_prot);
+}
--
2.18.0
List LORAWAN in menuconfig and enable it to be built.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
net/Kconfig | 1 +
net/Makefile | 1 +
2 files changed, 2 insertions(+)
diff --git a/net/Kconfig b/net/Kconfig
index 053b36998c18..499c931ba0fa 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -224,6 +224,7 @@ source "net/6lowpan/Kconfig"
source "net/ieee802154/Kconfig"
source "net/mac802154/Kconfig"
source "net/lora/Kconfig"
+source "net/maclorawan/Kconfig"
source "net/sched/Kconfig"
source "net/dcb/Kconfig"
source "net/dns_resolver/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index e80b84313851..5341d0e23586 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_6LOWPAN) += 6lowpan/
obj-$(CONFIG_IEEE802154) += ieee802154/
obj-$(CONFIG_MAC802154) += mac802154/
obj-$(CONFIG_LORA) += lora/
+obj-$(CONFIG_LORAWAN) += maclorawan/
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
--
2.18.0
This patch add the macro and definition for the implementation of
LoRaWAN protocol.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
include/linux/socket.h | 5 ++++-
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 1 +
net/core/dev.c | 4 ++--
security/selinux/hooks.c | 4 +++-
security/selinux/include/classmap.h | 4 +++-
6 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index aa1e288b1659..e5c8381fd1aa 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -209,8 +209,9 @@ struct ucred {
*/
#define AF_XDP 44 /* XDP sockets */
#define AF_LORA 45 /* LoRa sockets */
+#define AF_LORAWAN 46 /* LoRaWAN sockets */
-#define AF_MAX 46 /* For now.. */
+#define AF_MAX 47 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -261,6 +262,7 @@ struct ucred {
#define PF_SMC AF_SMC
#define PF_XDP AF_XDP
#define PF_LORA AF_LORA
+#define PF_LORAWAN AF_LORAWAN
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
@@ -343,6 +345,7 @@ struct ucred {
#define SOL_KCM 281
#define SOL_TLS 282
#define SOL_XDP 283
+#define SOL_LORAWAN 284
/* IPX options */
#define IPX_TYPE 1
diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
index 1ed7cb3f2129..2376f7839355 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -99,6 +99,7 @@
#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */
#define ARPHRD_VSOCKMON 826 /* Vsock monitor header */
#define ARPHRD_LORA 827 /* LoRa */
+#define ARPHRD_LORAWAN 828 /* LoRaWAN */
#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 45644dcf5b39..b1ac70d4a377 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -148,6 +148,7 @@
* aggregation protocol
*/
#define ETH_P_LORA 0x00FA /* LoRa */
+#define ETH_P_LORAWAN 0x00FB /* LoRaWAN */
/*
* This is an Ethernet frame header.
diff --git a/net/core/dev.c b/net/core/dev.c
index f68122f0ab02..b95ce79ec5a8 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -297,7 +297,7 @@ static const unsigned short netdev_lock_type[] = {
ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
ARPHRD_FCFABRIC, ARPHRD_IEEE80211, ARPHRD_IEEE80211_PRISM,
ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET, ARPHRD_PHONET_PIPE,
- ARPHRD_IEEE802154, ARPHRD_VOID, ARPHRD_NONE};
+ ARPHRD_IEEE802154, ARPHRD_LORAWAN, ARPHRD_VOID, ARPHRD_NONE};
static const char *const netdev_lock_name[] = {
"_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
@@ -314,7 +314,7 @@ static const char *const netdev_lock_name[] = {
"_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
"_xmit_FCFABRIC", "_xmit_IEEE80211", "_xmit_IEEE80211_PRISM",
"_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET", "_xmit_PHONET_PIPE",
- "_xmit_IEEE802154", "_xmit_VOID", "_xmit_NONE"};
+ "_xmit_IEEE802154", "_xmit_LORAWAN", "_xmit_VOID", "_xmit_NONE"};
static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
static struct lock_class_key netdev_addr_lock_key[ARRAY_SIZE(netdev_lock_type)];
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index aaf520a689d8..0da3a1d69cb8 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1477,7 +1477,9 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
return SECCLASS_XDP_SOCKET;
case PF_LORA:
return SECCLASS_LORA_SOCKET;
-#if PF_MAX > 46
+ case PF_LORAWAN:
+ return SECCLASS_LORAWAN_SOCKET;
+#if PF_MAX > 47
#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 060d4bf8385e..fa0151fe6f32 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -244,9 +244,11 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_SOCK_PERMS, NULL } },
{ "lora_socket",
{ COMMON_SOCK_PERMS, NULL } },
+ { "lorawan_socket",
+ { COMMON_SOCK_PERMS, NULL } },
{ NULL }
};
-#if PF_MAX > 46
+#if PF_MAX > 47
#error New address family defined, please update secclass_map.
#endif
--
2.18.0
Hi,
On 08/23/2018 10:15 AM, Jian-Hong Pan wrote:
> diff --git a/net/maclorawan/Kconfig b/net/maclorawan/Kconfig
> new file mode 100644
> index 000000000000..741992b059c6
> --- /dev/null
> +++ b/net/maclorawan/Kconfig
> @@ -0,0 +1,14 @@
> +config LORAWAN
> + tristate "MAC layer of LoRaWAN Network support"
> + select CRYPTO
> + select CRYPTO_CMAC
> + select CRYPTO_CBC
> + select CRYPTO_AES
> + ---help---
> + LoRaWAN defines a low data rate, low power and long range wireless
> + wide area networks. It was designed to organise networks of sensors,
> + switches and actuators, etc automation devices. It could operate as
> + multiple kilometers wide.
There are several things that I would change. How about this instead?
LoRaWAN defines low data rate, low power and long range wireless
wide area networks. It was designed to organise networks of automation
devices, such as sensors, switches and actuators. It can operate
multiple kilometers wide.
> +
> + Say Y here to compile LoRaWAN support into the kernel or say M to
> + compile it as modules.
--
~Randy
Randy Dunlap <[email protected]> 於 2018年8月24日 週五 上午1:43寫道:
>
> Hi,
>
> On 08/23/2018 10:15 AM, Jian-Hong Pan wrote:
> > diff --git a/net/maclorawan/Kconfig b/net/maclorawan/Kconfig
> > new file mode 100644
> > index 000000000000..741992b059c6
> > --- /dev/null
> > +++ b/net/maclorawan/Kconfig
> > @@ -0,0 +1,14 @@
> > +config LORAWAN
> > + tristate "MAC layer of LoRaWAN Network support"
> > + select CRYPTO
> > + select CRYPTO_CMAC
> > + select CRYPTO_CBC
> > + select CRYPTO_AES
> > + ---help---
> > + LoRaWAN defines a low data rate, low power and long range wireless
> > + wide area networks. It was designed to organise networks of sensors,
> > + switches and actuators, etc automation devices. It could operate as
> > + multiple kilometers wide.
>
> There are several things that I would change. How about this instead?
>
>
> LoRaWAN defines low data rate, low power and long range wireless
> wide area networks. It was designed to organise networks of automation
> devices, such as sensors, switches and actuators. It can operate
> multiple kilometers wide.
That becomes better! Thank you
> > +
> > + Say Y here to compile LoRaWAN support into the kernel or say M to
> > + compile it as modules.
>
>
> --
> ~Randy
Hi Jian-Hong,
Many thanks and sorry for the delay. This patch mostly looks good and
should go in before its first uses, so I would like to queue it soon for
my hardware-MAC module drivers - but some questions below. Also a typo
in the subject, and we should probably prepend "net: " and I would
personally not mention the module here as it's a userspace API.
Am 23.08.18 um 19:15 schrieb Jian-Hong Pan:
> This patch add the macro and definition for the implementation of
> LoRaWAN protocol.
I would fix up the grammar nits in my tree then.
>
> Signed-off-by: Jian-Hong Pan <[email protected]>
> ---
> include/linux/socket.h | 5 ++++-
> include/uapi/linux/if_arp.h | 1 +
> include/uapi/linux/if_ether.h | 1 +
> net/core/dev.c | 4 ++--
> security/selinux/hooks.c | 4 +++-
> security/selinux/include/classmap.h | 4 +++-
> 6 files changed, 14 insertions(+), 5 deletions(-)
>
> diff --git a/include/linux/socket.h b/include/linux/socket.h
> index aa1e288b1659..e5c8381fd1aa 100644
> --- a/include/linux/socket.h
> +++ b/include/linux/socket.h
> @@ -209,8 +209,9 @@ struct ucred {
> */
> #define AF_XDP 44 /* XDP sockets */
> #define AF_LORA 45 /* LoRa sockets */
> +#define AF_LORAWAN 46 /* LoRaWAN sockets */
>
> -#define AF_MAX 46 /* For now.. */
> +#define AF_MAX 47 /* For now.. */
>
> /* Protocol families, same as address families. */
> #define PF_UNSPEC AF_UNSPEC
> @@ -261,6 +262,7 @@ struct ucred {
> #define PF_SMC AF_SMC
> #define PF_XDP AF_XDP
> #define PF_LORA AF_LORA
> +#define PF_LORAWAN AF_LORAWAN
> #define PF_MAX AF_MAX
>
> /* Maximum queue length specifiable by listen. */
> @@ -343,6 +345,7 @@ struct ucred {
> #define SOL_KCM 281
> #define SOL_TLS 282
> #define SOL_XDP 283
> +#define SOL_LORAWAN 284
>
> /* IPX options */
> #define IPX_TYPE 1
> diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
> index 1ed7cb3f2129..2376f7839355 100644
> --- a/include/uapi/linux/if_arp.h
> +++ b/include/uapi/linux/if_arp.h
> @@ -99,6 +99,7 @@
> #define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */
> #define ARPHRD_VSOCKMON 826 /* Vsock monitor header */
> #define ARPHRD_LORA 827 /* LoRa */
> +#define ARPHRD_LORAWAN 828 /* LoRaWAN */
>
> #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 45644dcf5b39..b1ac70d4a377 100644
> --- a/include/uapi/linux/if_ether.h
> +++ b/include/uapi/linux/if_ether.h
> @@ -148,6 +148,7 @@
> * aggregation protocol
> */
> #define ETH_P_LORA 0x00FA /* LoRa */
> +#define ETH_P_LORAWAN 0x00FB /* LoRaWAN */
>
> /*
> * This is an Ethernet frame header.
> diff --git a/net/core/dev.c b/net/core/dev.c
> index f68122f0ab02..b95ce79ec5a8 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -297,7 +297,7 @@ static const unsigned short netdev_lock_type[] = {
> ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
> ARPHRD_FCFABRIC, ARPHRD_IEEE80211, ARPHRD_IEEE80211_PRISM,
> ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET, ARPHRD_PHONET_PIPE,
> - ARPHRD_IEEE802154, ARPHRD_VOID, ARPHRD_NONE};
> + ARPHRD_IEEE802154, ARPHRD_LORAWAN, ARPHRD_VOID, ARPHRD_NONE};
>
> static const char *const netdev_lock_name[] = {
> "_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
> @@ -314,7 +314,7 @@ static const char *const netdev_lock_name[] = {
> "_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
> "_xmit_FCFABRIC", "_xmit_IEEE80211", "_xmit_IEEE80211_PRISM",
> "_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET", "_xmit_PHONET_PIPE",
> - "_xmit_IEEE802154", "_xmit_VOID", "_xmit_NONE"};
> + "_xmit_IEEE802154", "_xmit_LORAWAN", "_xmit_VOID", "_xmit_NONE"};
>
> static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
> static struct lock_class_key netdev_addr_lock_key[ARRAY_SIZE(netdev_lock_type)];
All your new constants except SOL_LORAWAN are always next to a LoRa one,
but not in these two arrays. I don't have such changes in my original
LoRa patch - am I missing such an array extension for ARPHRD_LORA then,
or what is the criteria for when to add this?
Other changes look fine to me.
Regards,
Andreas
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index aaf520a689d8..0da3a1d69cb8 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -1477,7 +1477,9 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
> return SECCLASS_XDP_SOCKET;
> case PF_LORA:
> return SECCLASS_LORA_SOCKET;
> -#if PF_MAX > 46
> + case PF_LORAWAN:
> + return SECCLASS_LORAWAN_SOCKET;
> +#if PF_MAX > 47
> #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 060d4bf8385e..fa0151fe6f32 100644
> --- a/security/selinux/include/classmap.h
> +++ b/security/selinux/include/classmap.h
> @@ -244,9 +244,11 @@ struct security_class_mapping secclass_map[] = {
> { COMMON_SOCK_PERMS, NULL } },
> { "lora_socket",
> { COMMON_SOCK_PERMS, NULL } },
> + { "lorawan_socket",
> + { COMMON_SOCK_PERMS, NULL } },
> { NULL }
> };
>
> -#if PF_MAX > 46
> +#if PF_MAX > 47
> #error New address family defined, please update secclass_map.
> #endif
>
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Hi Jian-Hong,
[+ Afonso]
Am 23.08.18 um 19:15 schrieb Jian-Hong Pan:
> LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa devices.
>
> This patch implements part of Class A end-devices features 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
> 4. Encryption/decryption for up/down link data messages
>
> It also implements the the functions and maps to Datagram socket for
> LoRaWAN unconfirmed data messages.
>
> On the other side, it defines the basic interface and operation
> functions for compatible LoRa device drivers.
>
> Signed-off-by: Jian-Hong Pan <[email protected]>
> ---
> include/linux/maclorawan/lora.h | 239 +++++++++++
> net/maclorawan/Kconfig | 14 +
> net/maclorawan/Makefile | 2 +
> net/maclorawan/lorawan.h | 219 ++++++++++
> net/maclorawan/lrwsec.c | 237 +++++++++++
> net/maclorawan/lrwsec.h | 57 +++
> net/maclorawan/mac.c | 552 +++++++++++++++++++++++++
> net/maclorawan/main.c | 665 ++++++++++++++++++++++++++++++
> net/maclorawan/socket.c | 700 ++++++++++++++++++++++++++++++++
> 9 files changed, 2685 insertions(+)
> create mode 100644 include/linux/maclorawan/lora.h
Can we use include/linux/lora/lorawan.h for simplicity?
> create mode 100644 net/maclorawan/Kconfig
> create mode 100644 net/maclorawan/Makefile
> create mode 100644 net/maclorawan/lorawan.h
> create mode 100644 net/maclorawan/lrwsec.c
> create mode 100644 net/maclorawan/lrwsec.h
> create mode 100644 net/maclorawan/mac.c
> create mode 100644 net/maclorawan/main.c
> create mode 100644 net/maclorawan/socket.c
This patch is much too large for me to review...
It also doesn't seem to follow the structure I suggested: 802.15.4 has
two separate directories, net/ieee802154/ and net/mac802154/. Therefore
I had suggested net/maclorawan/ only for code that translates from
ETH_P_LORAWAN to ETH_P_LORA socket buffers, i.e. your soft MAC. Generic
socket code that applies also to hard MAC drivers I expected to go
either to net/lora/lorawan/ or better net/lorawan/ or some other clearly
separate location, with its own Kconfig symbol - any reason not to?
Consider which parts can be enabled/used independently of each other,
then you can put them into two (or more?) different patches.
Also please use SPDX license identifiers in your headers.
And please don't put the whole world in To, use CC.
Thanks,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Andreas Färber <[email protected]> 於 2018年9月24日 週一 上午12:06寫道:
>
> Hi Jian-Hong,
>
> Many thanks and sorry for the delay. This patch mostly looks good and
> should go in before its first uses, so I would like to queue it soon for
> my hardware-MAC module drivers - but some questions below. Also a typo
> in the subject, and we should probably prepend "net: " and I would
> personally not mention the module here as it's a userspace API.
>
> Am 23.08.18 um 19:15 schrieb Jian-Hong Pan:
> > This patch add the macro and definition for the implementation of
> > LoRaWAN protocol.
>
> I would fix up the grammar nits in my tree then.
Oh! The grammar things should be fixed. Thanks
And, I am preparing V2 patch, too.
> >
> > Signed-off-by: Jian-Hong Pan <[email protected]>
> > ---
> > include/linux/socket.h | 5 ++++-
> > include/uapi/linux/if_arp.h | 1 +
> > include/uapi/linux/if_ether.h | 1 +
> > net/core/dev.c | 4 ++--
> > security/selinux/hooks.c | 4 +++-
> > security/selinux/include/classmap.h | 4 +++-
> > 6 files changed, 14 insertions(+), 5 deletions(-)
> >
> > diff --git a/include/linux/socket.h b/include/linux/socket.h
> > index aa1e288b1659..e5c8381fd1aa 100644
> > --- a/include/linux/socket.h
> > +++ b/include/linux/socket.h
> > @@ -209,8 +209,9 @@ struct ucred {
> > */
> > #define AF_XDP 44 /* XDP sockets */
> > #define AF_LORA 45 /* LoRa sockets */
> > +#define AF_LORAWAN 46 /* LoRaWAN sockets */
> >
> > -#define AF_MAX 46 /* For now.. */
> > +#define AF_MAX 47 /* For now.. */
> >
> > /* Protocol families, same as address families. */
> > #define PF_UNSPEC AF_UNSPEC
> > @@ -261,6 +262,7 @@ struct ucred {
> > #define PF_SMC AF_SMC
> > #define PF_XDP AF_XDP
> > #define PF_LORA AF_LORA
> > +#define PF_LORAWAN AF_LORAWAN
> > #define PF_MAX AF_MAX
> >
> > /* Maximum queue length specifiable by listen. */
> > @@ -343,6 +345,7 @@ struct ucred {
> > #define SOL_KCM 281
> > #define SOL_TLS 282
> > #define SOL_XDP 283
> > +#define SOL_LORAWAN 284
> >
> > /* IPX options */
> > #define IPX_TYPE 1
> > diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
> > index 1ed7cb3f2129..2376f7839355 100644
> > --- a/include/uapi/linux/if_arp.h
> > +++ b/include/uapi/linux/if_arp.h
> > @@ -99,6 +99,7 @@
> > #define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */
> > #define ARPHRD_VSOCKMON 826 /* Vsock monitor header */
> > #define ARPHRD_LORA 827 /* LoRa */
> > +#define ARPHRD_LORAWAN 828 /* LoRaWAN */
> >
> > #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 45644dcf5b39..b1ac70d4a377 100644
> > --- a/include/uapi/linux/if_ether.h
> > +++ b/include/uapi/linux/if_ether.h
> > @@ -148,6 +148,7 @@
> > * aggregation protocol
> > */
> > #define ETH_P_LORA 0x00FA /* LoRa */
> > +#define ETH_P_LORAWAN 0x00FB /* LoRaWAN */
> >
> > /*
> > * This is an Ethernet frame header.
> > diff --git a/net/core/dev.c b/net/core/dev.c
> > index f68122f0ab02..b95ce79ec5a8 100644
> > --- a/net/core/dev.c
> > +++ b/net/core/dev.c
> > @@ -297,7 +297,7 @@ static const unsigned short netdev_lock_type[] = {
> > ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
> > ARPHRD_FCFABRIC, ARPHRD_IEEE80211, ARPHRD_IEEE80211_PRISM,
> > ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET, ARPHRD_PHONET_PIPE,
> > - ARPHRD_IEEE802154, ARPHRD_VOID, ARPHRD_NONE};
> > + ARPHRD_IEEE802154, ARPHRD_LORAWAN, ARPHRD_VOID, ARPHRD_NONE};
> >
> > static const char *const netdev_lock_name[] = {
> > "_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
> > @@ -314,7 +314,7 @@ static const char *const netdev_lock_name[] = {
> > "_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
> > "_xmit_FCFABRIC", "_xmit_IEEE80211", "_xmit_IEEE80211_PRISM",
> > "_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET", "_xmit_PHONET_PIPE",
> > - "_xmit_IEEE802154", "_xmit_VOID", "_xmit_NONE"};
> > + "_xmit_IEEE802154", "_xmit_LORAWAN", "_xmit_VOID", "_xmit_NONE"};
> >
> > static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
> > static struct lock_class_key netdev_addr_lock_key[ARRAY_SIZE(netdev_lock_type)];
>
> All your new constants except SOL_LORAWAN are always next to a LoRa one,
> but not in these two arrays. I don't have such changes in my original
> LoRa patch - am I missing such an array extension for ARPHRD_LORA then,
> or what is the criteria for when to add this?
That is for "ip" related commands or something else I did not discover.
For example:
$ ip link show lora0
4: lora0: <NOARP,UP,LOWER_UP> mtu 20 qdisc noqueue state UNKNOWN mode
DEFAULT group default qlen 1000
link/[828] 01:02:03:04 brd ff:ff:ff:ff
The 828 is ARPHRD_LORAWAN.
Regards,
Jian-Hong Pan
Andreas Färber <[email protected]> 於 2018年9月24日 週一 上午12:40寫道:
>
> Hi Jian-Hong,
>
> [+ Afonso]
>
> Am 23.08.18 um 19:15 schrieb Jian-Hong Pan:
> > LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa devices.
> >
> > This patch implements part of Class A end-devices features 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
> > 4. Encryption/decryption for up/down link data messages
> >
> > It also implements the the functions and maps to Datagram socket for
> > LoRaWAN unconfirmed data messages.
> >
> > On the other side, it defines the basic interface and operation
> > functions for compatible LoRa device drivers.
> >
> > Signed-off-by: Jian-Hong Pan <[email protected]>
> > ---
> > include/linux/maclorawan/lora.h | 239 +++++++++++
> > net/maclorawan/Kconfig | 14 +
> > net/maclorawan/Makefile | 2 +
> > net/maclorawan/lorawan.h | 219 ++++++++++
> > net/maclorawan/lrwsec.c | 237 +++++++++++
> > net/maclorawan/lrwsec.h | 57 +++
> > net/maclorawan/mac.c | 552 +++++++++++++++++++++++++
> > net/maclorawan/main.c | 665 ++++++++++++++++++++++++++++++
> > net/maclorawan/socket.c | 700 ++++++++++++++++++++++++++++++++
> > 9 files changed, 2685 insertions(+)
> > create mode 100644 include/linux/maclorawan/lora.h
>
> Can we use include/linux/lora/lorawan.h for simplicity?
Technically, yes
> > create mode 100644 net/maclorawan/Kconfig
> > create mode 100644 net/maclorawan/Makefile
> > create mode 100644 net/maclorawan/lorawan.h
> > create mode 100644 net/maclorawan/lrwsec.c
> > create mode 100644 net/maclorawan/lrwsec.h
> > create mode 100644 net/maclorawan/mac.c
> > create mode 100644 net/maclorawan/main.c
> > create mode 100644 net/maclorawan/socket.c
>
> This patch is much too large for me to review...
>
> It also doesn't seem to follow the structure I suggested: 802.15.4 has
> two separate directories, net/ieee802154/ and net/mac802154/. Therefore
> I had suggested net/maclorawan/ only for code that translates from
> ETH_P_LORAWAN to ETH_P_LORA socket buffers, i.e. your soft MAC. Generic
> socket code that applies also to hard MAC drivers I expected to go
> either to net/lora/lorawan/ or better net/lorawan/ or some other clearly
> separate location, with its own Kconfig symbol - any reason not to?
> Consider which parts can be enabled/used independently of each other,
> then you can put them into two (or more?) different patches.
Maybe I misunderstood before. Besides, the header files need to be
split for different paths or folders.
Anyway, I will try to separate it into parts in version 2 patches.
> Also please use SPDX license identifiers in your headers.
> And please don't put the whole world in To, use CC.
Regards,
Jian-Hong Pan
LoRaWAN(TM) is the MAC layer defined by LoRa Alliance(TM) over LoRa
devices. LoRa is one of Low-Power Wide-Area Network (LPWAN) technology.
LoRaWAN networks typically are laid out in a star-of-stars topology in
which gateways relay messages between end-devices and a central network
server at the backend. Gateways are connected to the network server via
standard IP connections while end-devices use single hop LoRa(TM) or FSK
communication to one or many gateways.
A LoRa network distinguishes between a basic LoRaWAN (named Class A) and
optional features (Class B, Class C ...):
* Bi-directional end-devices (Class A)
* Bi-directional end-devices with scheduled receive slots (Class B)
* Bi-directional end-devices with maximal receive slots (Class C)
This patch set add LoRaWAN class module implementing the stack,
especially the soft MAC, between socket APIs and LoRa device drivers.
socket APIs:
send and receive the data
------------------------------------------------------------------------
LoRaWAN class module implements soft MAC:
append the header/footer, encryption/decryption, timing slot and MAC
commands
------------------------------------------------------------------------
LoRa device drivers:
send and receive the messages for MAC layer
------------------------------------------------------------------------
LoRa devices
This module starts from simple and implements partial Class A
end-devices features defined in LoRaWAN(TM) Specification Ver. 1.0.2.
More features and complexity, for example regional parameters, confirmed
data messages, join request/accept messages for Over-The-Air Activation,
MAC commands ... will be added in the future.
Jian-Hong Pan (7):
net: lorawan: Add macro and definition for LoRaWAN
net: lorawan: Add LoRaWAN socket module
net: lorawan: Add LoRaWAN API declaration for LoRa devices
net: maclorawan: Add maclorawan module declaration
net: maclorawan: Implement the crypto of maclorawan module
net: maclorawan: Implement maclorawan class module
net: lorawan: List LORAWAN in menuconfig
include/linux/lora/lorawan.h | 137 ++++++
include/linux/lora/lorawan_netdev.h | 52 +++
include/linux/socket.h | 5 +-
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 1 +
net/Kconfig | 2 +
net/Makefile | 2 +
net/core/dev.c | 4 +-
net/lorawan/Kconfig | 10 +
net/lorawan/Makefile | 2 +
net/lorawan/socket.c | 681 ++++++++++++++++++++++++++++
net/maclorawan/Kconfig | 14 +
net/maclorawan/Makefile | 2 +
net/maclorawan/crypto.c | 209 +++++++++
net/maclorawan/crypto.h | 27 ++
net/maclorawan/mac.c | 522 +++++++++++++++++++++
net/maclorawan/maclorawan.h | 199 ++++++++
net/maclorawan/main.c | 600 ++++++++++++++++++++++++
security/selinux/hooks.c | 4 +-
security/selinux/include/classmap.h | 4 +-
20 files changed, 2473 insertions(+), 5 deletions(-)
create mode 100644 include/linux/lora/lorawan.h
create mode 100644 include/linux/lora/lorawan_netdev.h
create mode 100644 net/lorawan/Kconfig
create mode 100644 net/lorawan/Makefile
create mode 100644 net/lorawan/socket.c
create mode 100644 net/maclorawan/Kconfig
create mode 100644 net/maclorawan/Makefile
create mode 100644 net/maclorawan/crypto.c
create mode 100644 net/maclorawan/crypto.h
create mode 100644 net/maclorawan/mac.c
create mode 100644 net/maclorawan/maclorawan.h
create mode 100644 net/maclorawan/main.c
--
2.19.1
This patch adds the macro and definition for the implementation of
LoRaWAN protocol.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Modify the commit message
include/linux/socket.h | 5 ++++-
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 1 +
net/core/dev.c | 4 ++--
security/selinux/hooks.c | 4 +++-
security/selinux/include/classmap.h | 4 +++-
6 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index aa1e288b1659..e5c8381fd1aa 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -209,8 +209,9 @@ struct ucred {
*/
#define AF_XDP 44 /* XDP sockets */
#define AF_LORA 45 /* LoRa sockets */
+#define AF_LORAWAN 46 /* LoRaWAN sockets */
-#define AF_MAX 46 /* For now.. */
+#define AF_MAX 47 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -261,6 +262,7 @@ struct ucred {
#define PF_SMC AF_SMC
#define PF_XDP AF_XDP
#define PF_LORA AF_LORA
+#define PF_LORAWAN AF_LORAWAN
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
@@ -343,6 +345,7 @@ struct ucred {
#define SOL_KCM 281
#define SOL_TLS 282
#define SOL_XDP 283
+#define SOL_LORAWAN 284
/* IPX options */
#define IPX_TYPE 1
diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
index 1ed7cb3f2129..2376f7839355 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -99,6 +99,7 @@
#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */
#define ARPHRD_VSOCKMON 826 /* Vsock monitor header */
#define ARPHRD_LORA 827 /* LoRa */
+#define ARPHRD_LORAWAN 828 /* LoRaWAN */
#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 45644dcf5b39..b1ac70d4a377 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -148,6 +148,7 @@
* aggregation protocol
*/
#define ETH_P_LORA 0x00FA /* LoRa */
+#define ETH_P_LORAWAN 0x00FB /* LoRaWAN */
/*
* This is an Ethernet frame header.
diff --git a/net/core/dev.c b/net/core/dev.c
index f68122f0ab02..b95ce79ec5a8 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -297,7 +297,7 @@ static const unsigned short netdev_lock_type[] = {
ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
ARPHRD_FCFABRIC, ARPHRD_IEEE80211, ARPHRD_IEEE80211_PRISM,
ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET, ARPHRD_PHONET_PIPE,
- ARPHRD_IEEE802154, ARPHRD_VOID, ARPHRD_NONE};
+ ARPHRD_IEEE802154, ARPHRD_LORAWAN, ARPHRD_VOID, ARPHRD_NONE};
static const char *const netdev_lock_name[] = {
"_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
@@ -314,7 +314,7 @@ static const char *const netdev_lock_name[] = {
"_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
"_xmit_FCFABRIC", "_xmit_IEEE80211", "_xmit_IEEE80211_PRISM",
"_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET", "_xmit_PHONET_PIPE",
- "_xmit_IEEE802154", "_xmit_VOID", "_xmit_NONE"};
+ "_xmit_IEEE802154", "_xmit_LORAWAN", "_xmit_VOID", "_xmit_NONE"};
static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
static struct lock_class_key netdev_addr_lock_key[ARRAY_SIZE(netdev_lock_type)];
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index aaf520a689d8..0da3a1d69cb8 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1477,7 +1477,9 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
return SECCLASS_XDP_SOCKET;
case PF_LORA:
return SECCLASS_LORA_SOCKET;
-#if PF_MAX > 46
+ case PF_LORAWAN:
+ return SECCLASS_LORAWAN_SOCKET;
+#if PF_MAX > 47
#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 060d4bf8385e..fa0151fe6f32 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -244,9 +244,11 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_SOCK_PERMS, NULL } },
{ "lora_socket",
{ COMMON_SOCK_PERMS, NULL } },
+ { "lorawan_socket",
+ { COMMON_SOCK_PERMS, NULL } },
{ NULL }
};
-#if PF_MAX > 46
+#if PF_MAX > 47
#error New address family defined, please update secclass_map.
#endif
--
2.19.1
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 <[email protected]>
---
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
net/maclorawan/Kconfig | 14 +
net/maclorawan/Makefile | 2 +
net/maclorawan/mac.c | 522 ++++++++++++++++++++++++++++++++++
net/maclorawan/main.c | 600 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 1138 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..177537d5f59f
--- /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..343fe729a883
--- /dev/null
+++ b/net/maclorawan/mac.c
@@ -0,0 +1,522 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/poll.h>
+#include <asm/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(struct lrw_session), 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;
+ __le32 le_devaddr = cpu_to_le32(ss->devaddr);
+ u8 mhdr, fctrl, fport;
+ u8 mic[4];
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ mhdr = LRW_UNCONFIRMED_DATA_UP << 5;
+ if ((mhdr & (0x6 << 5)) == (0x4 << 5))
+ ss->tx_should_ack = true;
+
+ fctrl = 0;
+ if (lrw_st->rx_should_ack) {
+ fctrl |= 0x20;
+ lrw_st->rx_should_ack = false;
+ }
+
+ /* 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 */
+ memcpy(skb_push(skb, 2), &ss->fcnt_up, 2);
+
+ /* Push FCtrl */
+ memcpy(skb_push(skb, 1), &fctrl, 1);
+
+ /* Push DevAddr */
+ memcpy(skb_push(skb, LRW_DEVADDR_LEN), &le_devaddr, LRW_DEVADDR_LEN);
+
+ /* Push MHDR */
+ 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);
+}
+
+void
+lrw_parse_frame(struct lrw_session *ss, struct sk_buff *skb)
+{
+ 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, 4);
+
+ /* Get frame control */
+ fhdr->fctrl = skb->data[0];
+ skb_pull(skb, 1);
+
+ /* 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, 2);
+
+ /* Get frame options */
+ fhdr->fopts_len = fhdr->fctrl & 0xF;
+ if (fhdr->fopts_len > 0) {
+ memcpy(fhdr->fopts, skb->data, fhdr->fopts_len);
+ skb_pull(skb, fhdr->fopts_len);
+ }
+
+ /* TODO: Parse frame options */
+
+ /* Remove message integrity code */
+ skb_trim(skb, skb->len - LRW_MIC_LEN);
+}
+
+struct lrw_session *
+lrw_rx_skb_2_session(struct lrw_struct *lrw_st, struct sk_buff *rx_skb)
+{
+ struct lrw_session *ss;
+ u16 fcnt;
+ __le16 *p_fcnt;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ p_fcnt = (__le16 *)(rx_skb->data + 6);
+ 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 = lrw_st->rx_skb_list.next;
+ 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;
+
+ lrw_parse_frame(ss, skb);
+
+ /* 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;
+
+ if (ss->rx_skb->len > 0) {
+ spin_lock_bh(&ss->state_lock);
+ ss->state = LRW_RXRECEIVED_SS;
+ spin_unlock_bh(&ss->state_lock);
+
+ mac_cb(skb)->devaddr = lrw_st->devaddr;
+ 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 */
+ kfree_skb(skb);
+}
+
+int
+lrw_check_mic(struct crypto_shash *tfm, struct sk_buff *skb)
+{
+ u8 *buf;
+ size_t len;
+ u32 devaddr;
+ u16 fcnt;
+ __le16 *p_fcnt;
+ u8 cks[4];
+ u8 *mic;
+
+ buf = skb->data;
+ len = skb->len - 4;
+ devaddr = le32_to_cpu(*((__le32 *)(buf + 1)));
+ p_fcnt = (__le16 *)(buf + 6);
+ fcnt = le16_to_cpu(*p_fcnt);
+ mic = skb->data + len;
+
+ lrw_calc_mic(tfm, LRW_DOWNLINK, devaddr, fcnt, buf, len, cks);
+
+ return (!memcmp(cks, mic, 4));
+}
+
+/**
+ * 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);
+ u8 mtype;
+ u32 devaddr;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ 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)) {
+ skb_queue_tail(&lrw_st->rx_skb_list, skb);
+ schedule_work(&lrw_st->rx_work);
+ }
+ else {
+ 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_session *ss;
+ struct lrw_struct *lrw_st;
+
+ 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 - 20 * HZ / 1000;
+ 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..002781295369
--- /dev/null
+++ b/net/maclorawan/main.c
@@ -0,0 +1,600 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#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 net_device *ndev;
+ struct lrw_struct *lrw_st;
+ 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 = (void *) lrw_st + sizeof(struct lrw_struct);
+
+ 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
+ */
+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
+ */
+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)
+{
+ bool status = false;
+ struct lrw_session *ss;
+
+ 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;
+}
+
+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 = 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;
+}
+
+inline 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;
+}
+
+inline 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;
+}
+
+inline 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 = (__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);
+
+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);
+
+ pr_info("%s: unregister %s\n",
+ LORAWAN_MODULE_NAME, dev_name(&lrw_st->dev));
+
+ /* 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);
+
+ return;
+}
+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, <[email protected]>");
+MODULE_DESCRIPTION("LoRaWAN soft MAC kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
--
2.19.1
Add the maclorawan header file for common APIs in the module.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Use SPDX license identifiers
net/maclorawan/maclorawan.h | 199 ++++++++++++++++++++++++++++++++++++
1 file changed, 199 insertions(+)
create mode 100644 net/maclorawan/maclorawan.h
diff --git a/net/maclorawan/maclorawan.h b/net/maclorawan/maclorawan.h
new file mode 100644
index 000000000000..66b87f051d51
--- /dev/null
+++ b/net/maclorawan/maclorawan.h
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __MAC_LORAWAN_H__
+#define __MAC_LORAWAN_H__
+
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <uapi/linux/if_arp.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include <linux/lora/lorawan.h>
+
+#define LORAWAN_MODULE_NAME "maclorawan"
+
+/* List the message types of LoRaWAN */
+enum {
+ LRW_JOIN_REQUEST,
+ LRW_JOIN_ACCEPT,
+ LRW_UNCONFIRMED_DATA_UP,
+ LRW_UNCONFIRMED_DATA_DOWN,
+ LRW_CONFIRMED_DATA_UP,
+ LRW_CONFIRMED_DATA_DOWN,
+ LRW_RFU,
+ LRW_PROPRIETARY,
+};
+
+/* List the communication directions */
+enum {
+ LRW_UPLINK,
+ LRW_DOWNLINK,
+};
+
+/* States of LoRaWAN slot timing */
+enum {
+ LRW_INIT_SS,
+ LRW_XMITTING_SS,
+ LRW_XMITTED,
+ LRW_RX1_SS,
+ LRW_RX2_SS,
+ LRW_RXTIMEOUT_SS,
+ LRW_RXRECEIVED_SS,
+ LRW_RETRANSMIT_SS,
+};
+
+#define LRW_MHDR_LEN 1
+#define LRW_FHDR_MAX_LEN 22
+#define LRW_FOPS_MAX_LEN 15
+#define LRW_FPORT_LEN 1
+#define LRW_MIC_LEN 4
+
+/**
+ * lrw_fhdr - Hold the message's basic information of the frame
+ *
+ * @mtype: this message's type
+ * @fctrl: the frame control byte
+ * @fcnt: this message's frame counter value
+ * @fopts: this frame's options field
+ * @fopts_len: the length of the fopts
+ */
+struct lrw_fhdr {
+ u8 mtype;
+ u8 fctrl;
+ u16 fcnt;
+ u8 fopts[LRW_FPORT_LEN];
+ u8 fopts_len;
+};
+
+/**
+ * lrw_session - LoRaWAN session for Class A end device
+ *
+ * @lrw_st: points to the belonging lrw_st
+ * @entry: the entry of the ss_list in lrw_struct
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ * @fcnt_up: uplink frame counter
+ * @fcnt_down: downlink frame counter
+ * @fport: the LoRaWAN data message's port field
+ * @tx_skb: points to the TX skb, the frame
+ * @rx_skb: points to the RX skb, the frame
+ * @tx_fhdr: hold the message's basic information of the TX frame
+ * @rx_fhdr: hold the message's basic information of the RX frame
+ * @tx_should_ack: flag for determining the TX which should be acked or not
+ * @retry: retry times for xmitting failed
+ * @state: this session's current state
+ * @state_lock: lock of the session's state
+ * @timer: timing for this session and the state transition
+ * @timeout_work: work if waiting acknowledge time out
+ * @rx_delay1: RX1 delay time in seconds
+ * @rx_delay2: RX2 delay time in seconds
+ * @rx1_window: RX1 window opening time in mini-seconds
+ * @rx2_window: RX2 window opening time in mini-seconds
+ * @ack_timeout: time out time for waiting acknowledge in seconds
+ */
+struct lrw_session {
+ struct lrw_struct *lrw_st;
+ struct list_head entry;
+
+ u32 devaddr;
+ u16 fcnt_up;
+ u16 fcnt_down;
+ u8 fport;
+ struct sk_buff *tx_skb;
+ struct sk_buff *rx_skb;
+ struct lrw_fhdr tx_fhdr;
+ struct lrw_fhdr rx_fhdr;
+
+ bool tx_should_ack;
+ u8 retry;
+ u8 state;
+ spinlock_t state_lock;
+
+ struct timer_list timer;
+ struct work_struct timeout_work;
+ unsigned long rx_delay1;
+ unsigned long rx_delay2;
+ unsigned long rx1_window;
+ unsigned long rx2_window;
+ unsigned long ack_timeout;
+};
+
+/**
+ * lrw_struct - The full LoRaWAN hardware to the LoRa device.
+ *
+ * @dev: this LoRa device registed in system
+ * @hw: the LoRa device of this LoRaWAN hardware
+ * @ops: handle of LoRa operations interfaces
+ * @rx_skb_list: the list of received frames
+ * @ss_list: LoRaWAN session list of this LoRaWAN hardware
+ * @_cur_ss: pointer of the current processing session
+ * @rx_should_ack: represent the current session should be acked or not
+ * @state: the state of this LoRaWAN hardware
+ * @app_eui: the LoRaWAN application EUI
+ * @dev_eui: the LoRaWAN device EUI
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ * @appky: the Application key
+ * @nwkskey: the Network session key
+ * @appskey: the Application session key
+ * @nwks_shash_tfm: the hash handler for LoRaWAN network session
+ * @nwks_skc_tfm: the crypto handler for LoRaWAN network session
+ * @apps_skc_tfm: the crypto handler for LoRaWAN application session
+ * @fcnt_up: the counter of this LoRaWAN hardware's up frame
+ * @fcnt_down: the counter of this LoRaWAN hardware's down frame
+ * @xmit_task: the xmit task for the current LoRaWAN session
+ * @rx_work: the RX work in workqueue for the current LoRaWAN session
+ * @ndev: points to the emulating network device
+ * @_net: the current network namespace of this LoRaWAN hardware
+ */
+struct lrw_struct {
+ struct device dev;
+ struct lrw_hw hw;
+ struct lrw_operations *ops;
+
+ struct sk_buff_head rx_skb_list;
+ struct list_head ss_list;
+ struct mutex ss_list_lock;
+ struct lrw_session *_cur_ss;
+ bool rx_should_ack;
+ u8 state;
+
+ u64 app_eui;
+ u64 dev_eui;
+ u32 devaddr;
+ u8 appkey[LRW_KEY_LEN];
+ u8 nwkskey[LRW_KEY_LEN];
+ u8 appskey[LRW_KEY_LEN];
+ struct crypto_shash *nwks_shash_tfm;
+ struct crypto_skcipher *nwks_skc_tfm;
+ struct crypto_skcipher *apps_skc_tfm;
+
+ u16 fcnt_up;
+ u16 fcnt_down;
+
+ struct tasklet_struct xmit_task;
+ struct work_struct rx_work;
+
+ struct net_device *ndev;
+ possible_net_t _net;
+};
+
+#define NETDEV_2_LRW(ndev) ((struct lrw_struct *)netdev_priv(ndev))
+
+struct lrw_session * lrw_alloc_ss(struct lrw_struct *);
+void lrw_free_ss(struct lrw_session *);
+void lrw_del_ss(struct lrw_session *);
+int lrw_start_hw(struct lrw_struct *);
+void lrw_stop_hw(struct lrw_struct *);
+void lrw_prepare_tx_frame(struct lrw_session *);
+void lrw_xmit(unsigned long);
+void lrw_rx_work(struct work_struct *);
+
+#endif
--
2.19.1
Add public LoRaWAN API for compatible LoRa device drivers.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Merge the lrw_operations: set_bw, set_mod, set_sf into set_dr
- Use SPDX license identifiers
include/linux/lora/lorawan.h | 137 +++++++++++++++++++++++++++++++++++
1 file changed, 137 insertions(+)
create mode 100644 include/linux/lora/lorawan.h
diff --git a/include/linux/lora/lorawan.h b/include/linux/lora/lorawan.h
new file mode 100644
index 000000000000..684c9e2eb018
--- /dev/null
+++ b/include/linux/lora/lorawan.h
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN compatible hardware's definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_H__
+#define __LORAWAN_H__
+
+#include <linux/skbuff.h>
+#include <linux/random.h>
+
+/* List the role of the LoRaWAN hardware */
+enum {
+ LRW_GATEWAY,
+ LRW_CLASS_A_NODE,
+ LRW_CLASS_B_NODE,
+ LRW_CLASS_C_NODE,
+};
+
+/* List the RF modes */
+enum {
+ LRW_LORA,
+ LRW_FSK,
+};
+
+/**
+ * lrw_dr - This structure holds the RF related configuration of the data rate.
+ * @bw:
+ * Bandwidth in Hz
+ *
+ * @sf:
+ * Spread factor of CSS modulation used by LoRa mode
+ *
+ * @mode:
+ * LoRa or FSK mode
+ */
+struct lrw_dr {
+ u32 bw;
+ u8 sf;
+ u8 mode;
+};
+
+#define LRW_DEVADDR_LEN (sizeof(__le32))
+
+/* List the LoRa device's states of LoRaWAN hardware */
+enum {
+ LRW_STOP,
+ LRW_START,
+ LRW_STATE_IDLE,
+ LRW_STATE_TX,
+ LRW_STATE_RX,
+};
+
+/**
+ * lrw_hw - This structure holds the LoRa device of LoRaWAN hardware.
+ * @priv:
+ * points to the private data of the LoRa device
+ */
+struct lrw_hw {
+ void *priv;
+};
+
+/**
+ * lrw_operations - Lists the LoRaWAN device/interface's operations.
+ * These are callback functions for the LoRaWAN module. Compatible LoRa device
+ * driver should implement some of them according to the usage. The
+ * unimplemented callback functions must be assigned as NULL.
+ *
+ * @start:
+ * called when the interface is being up state
+ *
+ * @stop:
+ * called when the interface is being down state
+ *
+ * @xmit_async:
+ * called to xmit the data through the interface asynchronously
+ *
+ * @set_txpower:
+ * called to set xmitting RF power in mBm of the interface
+ *
+ * @set_frq:
+ * called to set carrier frequency in Hz of the interface
+ *
+ * @set_dr:
+ * called to set related RF configuration of the LoRaWAN data rate
+ *
+ * @start_rx_window:
+ * called to ask the LoRa device open a receiving window
+ *
+ * @set_state:
+ * called to set the LoRa device's working state
+ */
+struct lrw_operations {
+ int (*start)(struct lrw_hw *);
+ void (*stop)(struct lrw_hw *);
+
+ int (*xmit_async)(struct lrw_hw *, struct sk_buff *);
+ int (*set_txpower)(struct lrw_hw *, s32);
+ int (*set_frq)(struct lrw_hw *, u32);
+ int (*set_dr)(struct lrw_hw *, struct lrw_dr *);
+ int (*start_rx_window)(struct lrw_hw *, u32);
+ int (*set_state)(struct lrw_hw *, u8);
+};
+
+struct lrw_hw *lrw_alloc_hw(size_t, struct lrw_operations *);
+void lrw_free_hw(struct lrw_hw *);
+int lrw_register_hw(struct lrw_hw *);
+void lrw_unregister_hw(struct lrw_hw *);
+void lrw_rx_irqsave(struct lrw_hw *, struct sk_buff *);
+void lrw_xmit_complete(struct lrw_hw *, struct sk_buff *);
+
+static inline void lrw_random_addr(u64 *addr)
+{
+ get_random_bytes(addr, sizeof(u64));
+}
+
+void lrw_set_deveui(struct lrw_hw *, u64);
+u64 lrw_get_deveui(struct lrw_hw *);
+void lrw_set_appeui(struct lrw_hw *, u64);
+u64 lrw_get_appeui(struct lrw_hw *);
+void lrw_set_devaddr(struct lrw_hw *, u32);
+u32 lrw_get_devaddr(struct lrw_hw *);
+
+enum {
+ LRW_APPKEY,
+ LRW_NWKSKEY,
+ LRW_APPSKEY,
+};
+
+#define LRW_KEY_LEN 16
+
+int lrw_set_key(struct lrw_hw *, u8, u8 *, size_t);
+
+#endif
--
2.19.1
List LORAWAN and MACLORAWAN in menuconfig and enable them to be built.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
net/Kconfig | 2 ++
net/Makefile | 2 ++
2 files changed, 4 insertions(+)
diff --git a/net/Kconfig b/net/Kconfig
index 053b36998c18..b12b8bed6abb 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -224,6 +224,8 @@ source "net/6lowpan/Kconfig"
source "net/ieee802154/Kconfig"
source "net/mac802154/Kconfig"
source "net/lora/Kconfig"
+source "net/lorawan/Kconfig"
+source "net/maclorawan/Kconfig"
source "net/sched/Kconfig"
source "net/dcb/Kconfig"
source "net/dns_resolver/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index e80b84313851..9d5515965a8f 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -63,6 +63,8 @@ obj-$(CONFIG_6LOWPAN) += 6lowpan/
obj-$(CONFIG_IEEE802154) += ieee802154/
obj-$(CONFIG_MAC802154) += mac802154/
obj-$(CONFIG_LORA) += lora/
+obj-$(CONFIG_LORAWAN) += lorawan/
+obj-$(CONFIG_MACLORAWAN) += maclorawan/
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
--
2.19.1
Implement the crypto for encryption/decryption and message
integrity code (MIC) according to LoRaWAN(TM) Specification Ver. 1.0.2.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Rename the lrwsec files to crypto files
- Modify for Big/Little-Endian
- Use SPDX license identifiers
net/maclorawan/crypto.c | 209 ++++++++++++++++++++++++++++++++++++++++
net/maclorawan/crypto.h | 27 ++++++
2 files changed, 236 insertions(+)
create mode 100644 net/maclorawan/crypto.c
create mode 100644 net/maclorawan/crypto.h
diff --git a/net/maclorawan/crypto.c b/net/maclorawan/crypto.c
new file mode 100644
index 000000000000..a839fd074ad8
--- /dev/null
+++ b/net/maclorawan/crypto.c
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#include <linux/scatterlist.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include "crypto.h"
+
+struct crypto_shash *
+lrw_mic_key_setup(u8 *k, size_t k_len)
+{
+ char *algo = "cmac(aes)";
+ struct crypto_shash *tfm;
+ int err;
+
+ tfm = crypto_alloc_shash(algo, 0, 0);
+ if (!IS_ERR(tfm)) {
+ err = crypto_shash_setkey(tfm, k, k_len);
+ if (err) {
+ crypto_free_shash(tfm);
+ tfm = NULL;
+ }
+ }
+
+ return tfm;
+}
+
+int
+lrw_aes_cmac(struct crypto_shash *tfm, u8 *bz, u8 *data, size_t len, u8 *out)
+{
+ SHASH_DESC_ON_STACK(desc, tfm);
+ int err;
+
+ desc->tfm = tfm;
+
+ err = crypto_shash_init(desc);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_update(desc, bz, 16);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_update(desc, data, len);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_final(desc, out);
+
+lrw_aes_cmac_end:
+ return err;
+}
+
+int
+lrw_set_bzero(u8 dir, u32 devaddr, u32 fcnt, u8 len, u8 *bz)
+{
+ __le32 le_devaddr = cpu_to_le32(devaddr);
+ __le32 _fcnt = cpu_to_le32(fcnt);
+
+ bz[0] = 0x49;
+ memset(bz + 1, 0x00, 4);
+ bz[5] = dir;
+ memcpy(bz + 6, &le_devaddr, 4);
+ memcpy(bz + 10, &_fcnt, 4);
+ bz[14] = 0x00;
+ bz[15] = len;
+
+ return 0;
+}
+
+int
+lrw_calc_mic(struct crypto_shash *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8* buf, size_t len, u8 *mic4)
+{
+ u8 mic[16];
+ u8 bz[16];
+ int err;
+
+ /* According to LoRaWAN Specification Version 1.0.2
+ * - 4.4 Massege Integrity Code (MIC) */
+ lrw_set_bzero(dir, devaddr, fcnt, len, bz);
+ err = lrw_aes_cmac(tfm, bz, buf, len, mic);
+ if (!err)
+ memcpy(mic4, mic, 4);
+
+ return err;
+}
+
+void
+lrw_mic_key_free(struct crypto_shash *tfm)
+{
+ crypto_free_shash(tfm);
+}
+
+struct crypto_skcipher *
+lrw_aes_enc_key_setup(char *algo, u8 *k, size_t k_len)
+{
+ struct crypto_skcipher *tfm;
+ int err;
+
+ tfm = crypto_alloc_skcipher(algo, 0, CRYPTO_ALG_ASYNC);
+ if (!IS_ERR(tfm)) {
+ err = crypto_skcipher_setkey(tfm, k, k_len);
+ if (err) {
+ crypto_free_skcipher(tfm);
+ tfm = NULL;
+ }
+ }
+
+ return tfm;
+}
+
+struct crypto_skcipher *
+lrw_encrypt_key_setup(u8 *k, size_t k_len)
+{
+ return lrw_aes_enc_key_setup("cbc(aes)", k, k_len);
+}
+
+int
+lrw_aes_enc(struct crypto_skcipher *tfm, u8 *in, size_t len, u8 *out)
+{
+ u8 iv[16];
+ struct scatterlist src, dst;
+ SKCIPHER_REQUEST_ON_STACK(req, tfm);
+ int err;
+
+ memset(iv, 0, 16);
+ /* The buffer for sg_init_one cannot be a global or const local
+ * (will confuse the scatterlist) */
+ sg_init_one(&src, in, len);
+ sg_init_one(&dst, out, len);
+
+ skcipher_request_set_tfm(req, tfm);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+ skcipher_request_set_crypt(req, &src, &dst, len, iv);
+ err = crypto_skcipher_encrypt(req);
+ skcipher_request_zero(req);
+
+ return err;
+}
+
+#define LRW_SEQUENCE_OF_BLOCK_LEN 16
+
+int
+lrw_set_sob(u8 dir, u32 devaddr, u32 fcnt, u8 index, u8 *sob)
+{
+ __le32 le_devaddr = cpu_to_le32(devaddr);
+ __le32 _fcnt = cpu_to_le32(fcnt);
+
+ sob[0] = 0x01;
+ memset(sob + 1, 0x00, 4);
+ sob[5] = dir;
+ memcpy(sob + 6, &le_devaddr, 4);
+ memcpy(sob + 10, &_fcnt, 4);
+ sob[14] = 0x00;
+ sob[15] = index;
+
+ return 0;
+}
+
+int
+lrw_encrypt_sob(struct crypto_skcipher *tfm, u8 *sob)
+{
+ return lrw_aes_enc(tfm, sob, LRW_SEQUENCE_OF_BLOCK_LEN, sob);
+}
+
+int
+lrw_encrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len)
+{
+ u8 sob[LRW_SEQUENCE_OF_BLOCK_LEN];
+ u8 i, j;
+
+ /* According to LoRaWAN Specification Version 1.0.2
+ * - 4.3.3 MAC Frame Payload Encryption (FRMPayload) */
+ for (i = 0; (i * LRW_SEQUENCE_OF_BLOCK_LEN) < len; i++) {
+ lrw_set_sob(dir, devaddr, fcnt, i, sob);
+ lrw_encrypt_sob(tfm, sob);
+ for (j = 0; (i * LRW_SEQUENCE_OF_BLOCK_LEN + j) < len; j++)
+ buf[i * LRW_SEQUENCE_OF_BLOCK_LEN + j] ^= sob[j];
+ }
+
+ return 0;
+}
+
+int
+lrw_decrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len)
+{
+ /* Accoding to XOR swap algorithm */
+ return lrw_encrypt_buf(tfm, dir, devaddr, fcnt, buf, len);
+}
+
+void
+lrw_aes_enc_key_free(struct crypto_skcipher *tfm)
+{
+ crypto_free_skcipher(tfm);
+}
+
+void
+lrw_encrypt_key_free(struct crypto_skcipher *tfm)
+{
+ lrw_aes_enc_key_free(tfm);
+}
diff --git a/net/maclorawan/crypto.h b/net/maclorawan/crypto.h
new file mode 100644
index 000000000000..2ede02efb8c6
--- /dev/null
+++ b/net/maclorawan/crypto.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_CRYPTO_H__
+#define __LORAWAN_CRYPTO_H__
+
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+
+struct crypto_shash *lrw_mic_key_setup(u8 *k, size_t k_len);
+int lrw_calc_mic(struct crypto_shash *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8* buf, size_t len, u8 *mic4);
+void lrw_mic_key_free(struct crypto_shash *tfm);
+
+struct crypto_skcipher *lrw_encrypt_key_setup(u8 *k, size_t k_len);
+int lrw_encrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len);
+int lrw_decrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len);
+void lrw_encrypt_key_free(struct crypto_skcipher *tfm);
+
+#endif
--
2.19.1
This patch adds a new address/protocol family for LoRaWAN network.
It also implements the the functions and maps to Datagram socket for
LoRaWAN unconfirmed data messages.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Add lorawan_netdev.h header file for network address related
declaration
- Use SPDX license identifiers
include/linux/lora/lorawan_netdev.h | 52 +++
net/lorawan/Kconfig | 10 +
net/lorawan/Makefile | 2 +
net/lorawan/socket.c | 681 ++++++++++++++++++++++++++++
4 files changed, 745 insertions(+)
create mode 100644 include/linux/lora/lorawan_netdev.h
create mode 100644 net/lorawan/Kconfig
create mode 100644 net/lorawan/Makefile
create mode 100644 net/lorawan/socket.c
diff --git a/include/linux/lora/lorawan_netdev.h b/include/linux/lora/lorawan_netdev.h
new file mode 100644
index 000000000000..4adf93fd06c5
--- /dev/null
+++ b/include/linux/lora/lorawan_netdev.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN stack related definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_NET_DEVICE_H__
+#define __LORAWAN_NET_DEVICE_H__
+
+enum {
+ LRW_ADDR_APPEUI,
+ LRW_ADDR_DEVEUI,
+ LRW_ADDR_DEVADDR,
+};
+
+struct lrw_addr_in {
+ int addr_type;
+ union {
+ u64 app_eui;
+ u64 dev_eui;
+ u32 devaddr;
+ };
+};
+
+struct sockaddr_lorawan {
+ sa_family_t family; /* AF_LORAWAN */
+ struct lrw_addr_in addr_in;
+};
+
+/**
+ * lrw_mac_cb - This structure holds the control buffer (cb) of sk_buff
+ *
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ */
+struct lrw_mac_cb {
+ u32 devaddr;
+};
+
+/**
+ * mac_cb - Get the LoRaWAN MAC control buffer of the sk_buff
+ * @skb: the exchanging sk_buff
+ *
+ * Return: the pointer of LoRaWAN MAC control buffer
+ */
+static inline struct lrw_mac_cb * mac_cb(struct sk_buff *skb)
+{
+ return (struct lrw_mac_cb *)skb->cb;
+}
+
+#endif
diff --git a/net/lorawan/Kconfig b/net/lorawan/Kconfig
new file mode 100644
index 000000000000..7f2f344085c4
--- /dev/null
+++ b/net/lorawan/Kconfig
@@ -0,0 +1,10 @@
+config LORAWAN
+ tristate "LoRaWAN Network support"
+ ---help---
+ LoRaWAN defines low data rate, low power and long range wireless
+ wide area networks. It was designed to organize networks of automation
+ devices, such as sensors, switches and actuators. It can operate
+ multiple kilometers wide.
+
+ Say Y here to compile LoRaWAN support into the kernel or say M to
+ compile it as a module.
diff --git a/net/lorawan/Makefile b/net/lorawan/Makefile
new file mode 100644
index 000000000000..8c923ca6541a
--- /dev/null
+++ b/net/lorawan/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_LORAWAN) += lorawan.o
+lorawan-objs := socket.o
diff --git a/net/lorawan/socket.c b/net/lorawan/socket.c
new file mode 100644
index 000000000000..0c03f7a0fb0e
--- /dev/null
+++ b/net/lorawan/socket.c
@@ -0,0 +1,681 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN stack related definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/net.h>
+#include <linux/if_arp.h>
+#include <linux/termios.h> /* For TIOCOUTQ/INQ */
+#include <net/sock.h>
+#include <linux/lora/lorawan_netdev.h>
+
+#define LORAWAN_MODULE_NAME "lorawan"
+
+#define LRW_DBG_STR(fmt) LORAWAN_MODULE_NAME": "fmt
+#define lrw_info(fmt, ...) (pr_info(LRW_DBG_STR(fmt), ##__VA_ARGS__))
+#define lrw_dbg(fmt, ...) (pr_debug(LRW_DBG_STR(fmt), ##__VA_ARGS__))
+
+/**
+ * dgram_sock - This structure holds the states of Datagram socket
+ *
+ * @sk: network layer representation of the socket
+ * sk must be the first member of dgram_sock
+ * @src_devaddr: the LoRaWAN device address for this connection
+ * @bound: this socket is bound or not
+ * @connected: this socket is connected to the destination or not
+ * @want_ack: this socket needs to ack for the connection or not
+ */
+struct dgram_sock {
+ struct sock sk;
+ u32 src_devaddr;
+
+ u8 bound:1;
+ u8 connected:1;
+};
+
+static HLIST_HEAD(dgram_head);
+static DEFINE_RWLOCK(dgram_lock);
+
+static inline struct dgram_sock *
+dgram_sk(const struct sock *sk)
+{
+ return container_of(sk, struct dgram_sock, sk);
+}
+
+static inline struct net_device *
+lrw_get_dev_by_addr(struct net *net, u32 devaddr)
+{
+ struct net_device *ndev = NULL;
+ __be32 be_addr = cpu_to_be32(devaddr);
+
+ rcu_read_lock();
+ ndev = dev_getbyhwaddr_rcu(net, ARPHRD_LORAWAN, (char *)&be_addr);
+ if (ndev)
+ dev_hold(ndev);
+ rcu_read_unlock();
+
+ return ndev;
+}
+
+static int
+dgram_init(struct sock *sk)
+{
+ return 0;
+}
+
+static void
+dgram_close(struct sock *sk, long timeout)
+{
+ sk_common_release(sk);
+}
+
+static int
+dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
+ struct dgram_sock *ro = dgram_sk(sk);
+ struct net_device *ndev;
+ int ret;
+
+ lock_sock(sk);
+ ro->bound = 0;
+
+ ret = -EINVAL;
+ if (len < sizeof(*addr))
+ goto dgram_bind_end;
+
+ if (addr->family != AF_LORAWAN)
+ goto dgram_bind_end;
+
+ if (addr->addr_in.addr_type != LRW_ADDR_DEVADDR)
+ goto dgram_bind_end;
+
+ lrw_dbg("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
+ ndev = lrw_get_dev_by_addr(sock_net(sk), addr->addr_in.devaddr);
+ if (!ndev) {
+ ret = -ENODEV;
+ goto dgram_bind_end;
+ }
+ netdev_dbg(ndev, "%s: get ndev\n", __func__);
+
+ if (ndev->type != ARPHRD_LORAWAN) {
+ ret = -ENODEV;
+ goto dgram_bind_end;
+ }
+
+ ro->src_devaddr = addr->addr_in.devaddr;
+ ro->bound = 1;
+ ret = 0;
+ dev_put(ndev);
+ lrw_dbg("%s: bound address %X\n", __func__, ro->src_devaddr);
+
+dgram_bind_end:
+ release_sock(sk);
+ return ret;
+}
+
+static inline int
+lrw_dev_hard_header(struct sk_buff *skb, struct net_device *ndev,
+ const u32 src_devaddr, size_t len)
+{
+ /* TODO: Prepare the LoRaWAN sending header here */
+ return 0;
+}
+
+static int
+dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ size_t hlen;
+ size_t tlen;
+ int ret;
+
+ lrw_dbg("%s: going to send %zu bytes", __func__, size);
+ if (msg->msg_flags & MSG_OOB) {
+ lrw_dbg("msg->msg_flags = 0x%x\n", msg->msg_flags);
+ return -EOPNOTSUPP;
+ }
+
+ lrw_dbg("%s: check msg_name\n", __func__);
+ if (!ro->connected && !msg->msg_name)
+ return -EDESTADDRREQ;
+ else if (ro->connected && msg->msg_name)
+ return -EISCONN;
+
+ lrw_dbg("%s: check bound\n", __func__);
+ if (!ro->bound)
+ ndev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_LORAWAN);
+ else
+ ndev = lrw_get_dev_by_addr(sock_net(sk), ro->src_devaddr);
+
+ if (!ndev) {
+ lrw_dbg("no dev\n");
+ ret = -ENXIO;
+ goto dgram_sendmsg_end;
+ }
+
+ if (size > ndev->mtu){
+ netdev_dbg(ndev, "size = %zu, mtu = %u\n", size, ndev->mtu);
+ ret = -EMSGSIZE;
+ goto dgram_sendmsg_end;
+ }
+
+ netdev_dbg(ndev, "%s: create skb\n", __func__);
+ hlen = LL_RESERVED_SPACE(ndev);
+ tlen = ndev->needed_tailroom;
+ skb = sock_alloc_send_skb(sk, hlen + tlen + size,
+ msg->msg_flags & MSG_DONTWAIT,
+ &ret);
+
+ if (!skb)
+ goto dgram_sendmsg_no_skb;
+
+ skb_reserve(skb, hlen);
+ skb_reset_network_header(skb);
+
+ ret = lrw_dev_hard_header(skb, ndev, 0, size);
+ if (ret < 0)
+ goto dgram_sendmsg_no_skb;
+
+ ret = memcpy_from_msg(skb_put(skb, size), msg, size);
+ if (ret > 0)
+ goto dgram_sendmsg_err_skb;
+
+ skb->dev = ndev;
+ skb->protocol = htons(ETH_P_LORAWAN);
+
+ netdev_dbg(ndev, "%s: push skb to xmit queue\n", __func__);
+ ret = dev_queue_xmit(skb);
+ if (ret > 0)
+ ret = net_xmit_errno(ret);
+ netdev_dbg(ndev, "%s: pushed skb to xmit queue with ret=%d\n",
+ __func__, ret);
+ dev_put(ndev);
+
+ return ret ?: size;
+
+dgram_sendmsg_err_skb:
+ kfree_skb(skb);
+dgram_sendmsg_no_skb:
+ dev_put(ndev);
+
+dgram_sendmsg_end:
+ return ret;
+}
+
+static int
+dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+ int noblock, int flags, int *addr_len)
+{
+ struct sk_buff *skb;
+ size_t copied = 0;
+ DECLARE_SOCKADDR(struct sockaddr_lorawan *, saddr, msg->msg_name);
+ int err;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto dgram_recvmsg_end;
+
+ copied = skb->len;
+ if (len < copied) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
+ if (err)
+ goto dgram_recvmsg_done;
+
+ sock_recv_ts_and_drops(msg, sk, skb);
+ if(saddr) {
+ memset(saddr, 0, sizeof(*saddr));
+ saddr->family = AF_LORAWAN;
+ saddr->addr_in.devaddr = mac_cb(skb)->devaddr;
+ *addr_len = sizeof(*saddr);
+ }
+
+ if (flags & MSG_TRUNC)
+ copied = skb->len;
+
+dgram_recvmsg_done:
+ skb_free_datagram(sk, skb);
+
+dgram_recvmsg_end:
+ if (err)
+ return err;
+ return copied;
+}
+
+static int
+dgram_hash(struct sock *sk)
+{
+ lrw_dbg("%s\n", __func__);
+ write_lock_bh(&dgram_lock);
+ sk_add_node(sk, &dgram_head);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ write_unlock_bh(&dgram_lock);
+
+ return 0;
+}
+
+static void
+dgram_unhash(struct sock *sk)
+{
+ lrw_dbg("%s\n", __func__);
+ write_lock_bh(&dgram_lock);
+ if (sk_del_node_init(sk))
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ write_unlock_bh(&dgram_lock);
+}
+
+static int
+dgram_connect(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ /* Nodes of LoRaWAN send data to a gateway only, then data is received
+ * and transferred to servers with the gateway's policy.
+ * So, the destination address is not used by nodes.
+ */
+ lock_sock(sk);
+ ro->connected = 1;
+ release_sock(sk);
+
+ return 0;
+}
+
+static int
+dgram_disconnect(struct sock *sk, int flags)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ lock_sock(sk);
+ ro->connected = 0;
+ release_sock(sk);
+
+ return 0;
+}
+
+static int
+dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ struct sk_buff *skb;
+ int amount;
+ int err;
+ struct net_device *ndev = sk->sk_dst_cache->dev;
+
+ netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
+ switch (cmd) {
+ case SIOCOUTQ:
+ amount = sk_wmem_alloc_get(sk);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ case SIOCINQ:
+ amount = 0;
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb) {
+ /* We will only return the amount of this packet
+ * since that is all that will be read.
+ */
+ amount = skb->len;
+ }
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ default:
+ err = -ENOIOCTLCMD;
+ }
+
+ return err;
+}
+
+static int
+dgram_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ int val, len;
+
+ if (level != SOL_LORAWAN)
+ return -EOPNOTSUPP;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ len = min_t(unsigned int, len, sizeof(int));
+
+ switch (optname) {
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+dgram_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ int val;
+ int err = 0;
+
+ if (optlen < sizeof(int))
+ return -EINVAL;
+
+ if (get_user(val, (int __user *)optval))
+ return -EFAULT;
+
+ lock_sock(sk);
+
+ switch (optname) {
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ release_sock(sk);
+
+ return err;
+}
+
+static struct proto lrw_dgram_prot = {
+ .name = "LoRaWAN",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct dgram_sock),
+ .init = dgram_init,
+ .close = dgram_close,
+ .bind = dgram_bind,
+ .sendmsg = dgram_sendmsg,
+ .recvmsg = dgram_recvmsg,
+ .hash = dgram_hash,
+ .unhash = dgram_unhash,
+ .connect = dgram_connect,
+ .disconnect = dgram_disconnect,
+ .ioctl = dgram_ioctl,
+ .getsockopt = dgram_getsockopt,
+ .setsockopt = dgram_setsockopt,
+};
+
+static int
+lrw_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk) {
+ sock->sk = NULL;
+ sk->sk_prot->close(sk, 0);
+ }
+
+ return 0;
+}
+
+static int
+lrw_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
+
+ lrw_dbg("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
+ if (sk->sk_prot->bind)
+ return sk->sk_prot->bind(sk, uaddr, addr_len);
+
+ return sock_no_bind(sock, uaddr, addr_len);
+}
+
+static int
+lrw_sock_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ if (addr_len < sizeof(uaddr->sa_family))
+ return -EINVAL;
+
+ return sk->sk_prot->connect(sk, uaddr, addr_len);
+}
+
+static int
+lrw_ndev_ioctl(struct sock *sk, struct ifreq __user *arg, unsigned int cmd)
+{
+ struct ifreq ifr;
+ int ret = -ENOIOCTLCMD;
+ struct net_device *ndev;
+
+ lrw_dbg("%s: cmd %ud\n", __func__, cmd);
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
+
+ ifr.ifr_name[IFNAMSIZ-1] = 0;
+
+ dev_load(sock_net(sk), ifr.ifr_name);
+ ndev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
+
+ netdev_dbg(ndev, "%s: cmd %ud\n", __func__, cmd);
+ if (!ndev)
+ return -ENODEV;
+
+ if (ndev->type == ARPHRD_LORAWAN && ndev->netdev_ops->ndo_do_ioctl)
+ ret = ndev->netdev_ops->ndo_do_ioctl(ndev, &ifr, cmd);
+
+ if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ ret = -EFAULT;
+ dev_put(ndev);
+
+ return ret;
+}
+
+static int
+lrw_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+
+ lrw_dbg("%s: cmd %ud\n", __func__, cmd);
+ switch (cmd) {
+ case SIOCGSTAMP:
+ return sock_get_timestamp(sk, (struct timeval __user *)arg);
+ case SIOCGSTAMPNS:
+ return sock_get_timestampns(sk, (struct timespec __user *)arg);
+ case SIOCOUTQ:
+ case SIOCINQ:
+ if (!sk->sk_prot->ioctl)
+ return -ENOIOCTLCMD;
+ return sk->sk_prot->ioctl(sk, cmd, arg);
+ default:
+ return lrw_ndev_ioctl(sk, (struct ifreq __user *)arg, cmd);
+ }
+}
+
+static int
+lrw_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+
+ lrw_dbg("%s: going to send %zu bytes\n", __func__, len);
+ return sk->sk_prot->sendmsg(sk, msg, len);
+}
+
+static const struct proto_ops lrw_dgram_ops = {
+ .family = PF_LORAWAN,
+ .owner = THIS_MODULE,
+ .release = lrw_sock_release,
+ .bind = lrw_sock_bind,
+ .connect = lrw_sock_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = lrw_sock_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = lrw_sock_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static int
+lorawan_creat(struct net *net, struct socket *sock, int protocol, int kern)
+{
+ struct sock *sk;
+ int ret;
+
+ if (!net_eq(net, &init_net))
+ return -EAFNOSUPPORT;
+
+ if (sock->type != SOCK_DGRAM)
+ return -EAFNOSUPPORT;
+
+ /* Allocates enough memory for dgram_sock whose first member is sk */
+ sk = sk_alloc(net, PF_LORAWAN, GFP_KERNEL, &lrw_dgram_prot, kern);
+ if (!sk)
+ return -ENOMEM;
+
+ sock->ops = &lrw_dgram_ops;
+ sock_init_data(sock, sk);
+ sk->sk_family = PF_LORAWAN;
+ sock_set_flag(sk, SOCK_ZAPPED);
+
+ if (sk->sk_prot->hash) {
+ ret = sk->sk_prot->hash(sk);
+ if (ret) {
+ sk_common_release(sk);
+ goto lorawan_creat_end;
+ }
+ }
+
+ if (sk->sk_prot->init) {
+ ret = sk->sk_prot->init(sk);
+ if (ret)
+ sk_common_release(sk);
+ }
+
+lorawan_creat_end:
+ return ret;
+}
+
+static const struct net_proto_family lorawan_family_ops = {
+ .owner = THIS_MODULE,
+ .family = PF_LORAWAN,
+ .create = lorawan_creat,
+};
+
+static inline int
+lrw_dgram_deliver(struct net_device *ndev, struct sk_buff *skb)
+{
+ struct sock *sk;
+ struct dgram_sock *ro;
+ bool found = false;
+ int ret = NET_RX_SUCCESS;
+
+ read_lock(&dgram_lock);
+ sk_for_each(sk, &dgram_head) {
+ ro = dgram_sk(sk);
+ if(cpu_to_le32(ro->src_devaddr) == *(__le32 *)ndev->dev_addr) {
+ found = true;
+ break;
+ }
+ }
+ read_unlock(&dgram_lock);
+
+ if (!found)
+ goto lrw_dgram_deliver_err;
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NET_RX_DROP;
+
+ if (sock_queue_rcv_skb(sk, skb) < 0)
+ goto lrw_dgram_deliver_err;
+
+ return ret;
+
+lrw_dgram_deliver_err:
+ kfree_skb(skb);
+ ret = NET_RX_DROP;
+ return ret;
+}
+
+static int
+lorawan_rcv(struct sk_buff *skb, struct net_device *ndev,
+ struct packet_type *pt, struct net_device *orig_ndev)
+{
+ if (!netif_running(ndev))
+ goto lorawan_rcv_drop;
+
+ if (!net_eq(dev_net(ndev), &init_net))
+ goto lorawan_rcv_drop;
+
+ if (ndev->type != ARPHRD_LORAWAN)
+ goto lorawan_rcv_drop;
+
+ if (skb->pkt_type != PACKET_OTHERHOST)
+ return lrw_dgram_deliver(ndev, skb);
+
+lorawan_rcv_drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static struct packet_type lorawan_packet_type = {
+ .type = htons(ETH_P_LORAWAN),
+ .func = lorawan_rcv,
+};
+
+static int __init
+lrw_sock_init(void)
+{
+ int ret;
+
+ lrw_info("module inserted\n");
+ ret = proto_register(&lrw_dgram_prot, 1);
+ if(ret)
+ goto lrw_sock_init_end;
+
+ /* Tell SOCKET that we are alive */
+ ret = sock_register(&lorawan_family_ops);
+ if(ret)
+ goto lrw_sock_init_err;
+
+ dev_add_pack(&lorawan_packet_type);
+ ret = 0;
+ goto lrw_sock_init_end;
+
+lrw_sock_init_err:
+ proto_unregister(&lrw_dgram_prot);
+
+lrw_sock_init_end:
+ return 0;
+}
+
+static void __exit
+lrw_sock_exit(void)
+{
+ dev_remove_pack(&lorawan_packet_type);
+ sock_unregister(PF_LORAWAN);
+ proto_unregister(&lrw_dgram_prot);
+ lrw_info("module removed\n");
+}
+
+module_init(lrw_sock_init);
+module_exit(lrw_sock_exit);
+
+MODULE_AUTHOR("Jian-Hong Pan, <[email protected]>");
+MODULE_DESCRIPTION("LoRaWAN socket kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS_NETPROTO(PF_LORAWAN);
--
2.19.1
From: Jian-Hong Pan <[email protected]>
Date: Tue, 6 Nov 2018 00:55:40 +0800
> +static inline struct lrw_mac_cb * mac_cb(struct sk_buff *skb)
"mac_cb()" is pretty generic for a name, and leads to namespace pollution,
please use lrw_mac_cb() or similar.
> +static inline struct dgram_sock *
> +dgram_sk(const struct sock *sk)
> +{
> + return container_of(sk, struct dgram_sock, sk);
> +}
> +
> +static inline struct net_device *
> +lrw_get_dev_by_addr(struct net *net, u32 devaddr)
Never use inline for functions in a foo.c file, let the compiler decide.
> +{
> + struct net_device *ndev = NULL;
> + __be32 be_addr = cpu_to_be32(devaddr);
Always order local variables from longest to shortest line.
> +static int
> +dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
> + int noblock, int flags, int *addr_len)
> +{
> + struct sk_buff *skb;
> + size_t copied = 0;
> + DECLARE_SOCKADDR(struct sockaddr_lorawan *, saddr, msg->msg_name);
> + int err;
Likewise.
I'm not going to point out every single place where you have made these
two errors.
Please audit your entire submission and fix the problems wherever they
occur.
Thank you.
David Miller <[email protected]> 於 2018年11月6日 週二 上午2:16寫道:
>
> From: Jian-Hong Pan <[email protected]>
> Date: Tue, 6 Nov 2018 00:55:40 +0800
>
> > +static inline struct lrw_mac_cb * mac_cb(struct sk_buff *skb)
>
> "mac_cb()" is pretty generic for a name, and leads to namespace pollution,
> please use lrw_mac_cb() or similar.
>
> > +static inline struct dgram_sock *
> > +dgram_sk(const struct sock *sk)
> > +{
> > + return container_of(sk, struct dgram_sock, sk);
> > +}
> > +
> > +static inline struct net_device *
> > +lrw_get_dev_by_addr(struct net *net, u32 devaddr)
>
> Never use inline for functions in a foo.c file, let the compiler decide.
>
> > +{
> > + struct net_device *ndev = NULL;
> > + __be32 be_addr = cpu_to_be32(devaddr);
>
> Always order local variables from longest to shortest line.
>
> > +static int
> > +dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
> > + int noblock, int flags, int *addr_len)
> > +{
> > + struct sk_buff *skb;
> > + size_t copied = 0;
> > + DECLARE_SOCKADDR(struct sockaddr_lorawan *, saddr, msg->msg_name);
> > + int err;
>
> Likewise.
>
> I'm not going to point out every single place where you have made these
> two errors.
>
> Please audit your entire submission and fix the problems wherever they
> occur.
Thanks for the reviewing. I will check the submission again.
Jian-Hong Pan
This patch adds the macro and definition for the implementation of
LoRaWAN protocol.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Modify the commit message
include/linux/socket.h | 5 ++++-
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 1 +
net/core/dev.c | 4 ++--
security/selinux/hooks.c | 4 +++-
security/selinux/include/classmap.h | 4 +++-
6 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index aa1e288b1659..e5c8381fd1aa 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -209,8 +209,9 @@ struct ucred {
*/
#define AF_XDP 44 /* XDP sockets */
#define AF_LORA 45 /* LoRa sockets */
+#define AF_LORAWAN 46 /* LoRaWAN sockets */
-#define AF_MAX 46 /* For now.. */
+#define AF_MAX 47 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -261,6 +262,7 @@ struct ucred {
#define PF_SMC AF_SMC
#define PF_XDP AF_XDP
#define PF_LORA AF_LORA
+#define PF_LORAWAN AF_LORAWAN
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
@@ -343,6 +345,7 @@ struct ucred {
#define SOL_KCM 281
#define SOL_TLS 282
#define SOL_XDP 283
+#define SOL_LORAWAN 284
/* IPX options */
#define IPX_TYPE 1
diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
index 1ed7cb3f2129..2376f7839355 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -99,6 +99,7 @@
#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */
#define ARPHRD_VSOCKMON 826 /* Vsock monitor header */
#define ARPHRD_LORA 827 /* LoRa */
+#define ARPHRD_LORAWAN 828 /* LoRaWAN */
#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 45644dcf5b39..b1ac70d4a377 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -148,6 +148,7 @@
* aggregation protocol
*/
#define ETH_P_LORA 0x00FA /* LoRa */
+#define ETH_P_LORAWAN 0x00FB /* LoRaWAN */
/*
* This is an Ethernet frame header.
diff --git a/net/core/dev.c b/net/core/dev.c
index f68122f0ab02..b95ce79ec5a8 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -297,7 +297,7 @@ static const unsigned short netdev_lock_type[] = {
ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
ARPHRD_FCFABRIC, ARPHRD_IEEE80211, ARPHRD_IEEE80211_PRISM,
ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET, ARPHRD_PHONET_PIPE,
- ARPHRD_IEEE802154, ARPHRD_VOID, ARPHRD_NONE};
+ ARPHRD_IEEE802154, ARPHRD_LORAWAN, ARPHRD_VOID, ARPHRD_NONE};
static const char *const netdev_lock_name[] = {
"_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
@@ -314,7 +314,7 @@ static const char *const netdev_lock_name[] = {
"_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
"_xmit_FCFABRIC", "_xmit_IEEE80211", "_xmit_IEEE80211_PRISM",
"_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET", "_xmit_PHONET_PIPE",
- "_xmit_IEEE802154", "_xmit_VOID", "_xmit_NONE"};
+ "_xmit_IEEE802154", "_xmit_LORAWAN", "_xmit_VOID", "_xmit_NONE"};
static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
static struct lock_class_key netdev_addr_lock_key[ARRAY_SIZE(netdev_lock_type)];
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index aaf520a689d8..0da3a1d69cb8 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1477,7 +1477,9 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
return SECCLASS_XDP_SOCKET;
case PF_LORA:
return SECCLASS_LORA_SOCKET;
-#if PF_MAX > 46
+ case PF_LORAWAN:
+ return SECCLASS_LORAWAN_SOCKET;
+#if PF_MAX > 47
#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 060d4bf8385e..fa0151fe6f32 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -244,9 +244,11 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_SOCK_PERMS, NULL } },
{ "lora_socket",
{ COMMON_SOCK_PERMS, NULL } },
+ { "lorawan_socket",
+ { COMMON_SOCK_PERMS, NULL } },
{ NULL }
};
-#if PF_MAX > 46
+#if PF_MAX > 47
#error New address family defined, please update secclass_map.
#endif
--
2.19.1
Add public LoRaWAN API for compatible LoRa device drivers.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Merge the lrw_operations: set_bw, set_mod, set_sf into set_dr
- Use SPDX license identifiers
V3:
- Remove the unused lrw_random_addr function
include/linux/lora/lorawan.h | 131 +++++++++++++++++++++++++++++++++++
1 file changed, 131 insertions(+)
create mode 100644 include/linux/lora/lorawan.h
diff --git a/include/linux/lora/lorawan.h b/include/linux/lora/lorawan.h
new file mode 100644
index 000000000000..cdeb280b2261
--- /dev/null
+++ b/include/linux/lora/lorawan.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN compatible hardware's definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_H__
+#define __LORAWAN_H__
+
+#include <linux/skbuff.h>
+
+/* List the role of the LoRaWAN hardware */
+enum {
+ LRW_GATEWAY,
+ LRW_CLASS_A_NODE,
+ LRW_CLASS_B_NODE,
+ LRW_CLASS_C_NODE,
+};
+
+/* List the RF modes */
+enum {
+ LRW_LORA,
+ LRW_FSK,
+};
+
+/**
+ * lrw_dr - This structure holds the RF related configuration of the data rate.
+ * @bw:
+ * Bandwidth in Hz
+ *
+ * @sf:
+ * Spread factor of CSS modulation used by LoRa mode
+ *
+ * @mode:
+ * LoRa or FSK mode
+ */
+struct lrw_dr {
+ u32 bw;
+ u8 sf;
+ u8 mode;
+};
+
+#define LRW_DEVADDR_LEN (sizeof(__le32))
+
+/* List the LoRa device's states of LoRaWAN hardware */
+enum {
+ LRW_STOP,
+ LRW_START,
+ LRW_STATE_IDLE,
+ LRW_STATE_TX,
+ LRW_STATE_RX,
+};
+
+/**
+ * lrw_hw - This structure holds the LoRa device of LoRaWAN hardware.
+ * @priv:
+ * points to the private data of the LoRa device
+ */
+struct lrw_hw {
+ void *priv;
+};
+
+/**
+ * lrw_operations - Lists the LoRaWAN device/interface's operations.
+ * These are callback functions for the LoRaWAN module. Compatible LoRa device
+ * driver should implement some of them according to the usage. The
+ * unimplemented callback functions must be assigned as NULL.
+ *
+ * @start:
+ * called when the interface is being up state
+ *
+ * @stop:
+ * called when the interface is being down state
+ *
+ * @xmit_async:
+ * called to xmit the data through the interface asynchronously
+ *
+ * @set_txpower:
+ * called to set xmitting RF power in mBm of the interface
+ *
+ * @set_frq:
+ * called to set carrier frequency in Hz of the interface
+ *
+ * @set_dr:
+ * called to set related RF configuration of the LoRaWAN data rate
+ *
+ * @start_rx_window:
+ * called to ask the LoRa device open a receiving window
+ *
+ * @set_state:
+ * called to set the LoRa device's working state
+ */
+struct lrw_operations {
+ int (*start)(struct lrw_hw *);
+ void (*stop)(struct lrw_hw *);
+
+ int (*xmit_async)(struct lrw_hw *, struct sk_buff *);
+ int (*set_txpower)(struct lrw_hw *, s32);
+ int (*set_frq)(struct lrw_hw *, u32);
+ int (*set_dr)(struct lrw_hw *, struct lrw_dr *);
+ int (*start_rx_window)(struct lrw_hw *, u32);
+ int (*set_state)(struct lrw_hw *, u8);
+};
+
+struct lrw_hw *lrw_alloc_hw(size_t, struct lrw_operations *);
+void lrw_free_hw(struct lrw_hw *);
+int lrw_register_hw(struct lrw_hw *);
+void lrw_unregister_hw(struct lrw_hw *);
+void lrw_rx_irqsave(struct lrw_hw *, struct sk_buff *);
+void lrw_xmit_complete(struct lrw_hw *, struct sk_buff *);
+
+void lrw_set_deveui(struct lrw_hw *, u64);
+u64 lrw_get_deveui(struct lrw_hw *);
+void lrw_set_appeui(struct lrw_hw *, u64);
+u64 lrw_get_appeui(struct lrw_hw *);
+void lrw_set_devaddr(struct lrw_hw *, u32);
+u32 lrw_get_devaddr(struct lrw_hw *);
+
+enum {
+ LRW_APPKEY,
+ LRW_NWKSKEY,
+ LRW_APPSKEY,
+};
+
+#define LRW_KEY_LEN 16
+
+int lrw_set_key(struct lrw_hw *, u8, u8 *, size_t);
+
+#endif
--
2.19.1
Implement the crypto for encryption/decryption and message
integrity code (MIC) according to LoRaWAN(TM) Specification Ver. 1.0.2.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Rename the lrwsec files to crypto files
- Modify for Big/Little-Endian
- Use SPDX license identifiers
V3:
- Order local variables from longest to shortest line in the functions
net/maclorawan/crypto.c | 209 ++++++++++++++++++++++++++++++++++++++++
net/maclorawan/crypto.h | 27 ++++++
2 files changed, 236 insertions(+)
create mode 100644 net/maclorawan/crypto.c
create mode 100644 net/maclorawan/crypto.h
diff --git a/net/maclorawan/crypto.c b/net/maclorawan/crypto.c
new file mode 100644
index 000000000000..5337a661c9d0
--- /dev/null
+++ b/net/maclorawan/crypto.c
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#include <linux/scatterlist.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include "crypto.h"
+
+struct crypto_shash *
+lrw_mic_key_setup(u8 *k, size_t k_len)
+{
+ struct crypto_shash *tfm;
+ char *algo = "cmac(aes)";
+ int err;
+
+ tfm = crypto_alloc_shash(algo, 0, 0);
+ if (!IS_ERR(tfm)) {
+ err = crypto_shash_setkey(tfm, k, k_len);
+ if (err) {
+ crypto_free_shash(tfm);
+ tfm = NULL;
+ }
+ }
+
+ return tfm;
+}
+
+int
+lrw_aes_cmac(struct crypto_shash *tfm, u8 *bz, u8 *data, size_t len, u8 *out)
+{
+ SHASH_DESC_ON_STACK(desc, tfm);
+ int err;
+
+ desc->tfm = tfm;
+
+ err = crypto_shash_init(desc);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_update(desc, bz, 16);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_update(desc, data, len);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_final(desc, out);
+
+lrw_aes_cmac_end:
+ return err;
+}
+
+int
+lrw_set_bzero(u8 dir, u32 devaddr, u32 fcnt, u8 len, u8 *bz)
+{
+ __le32 le_devaddr = cpu_to_le32(devaddr);
+ __le32 le_fcnt = cpu_to_le32(fcnt);
+
+ bz[0] = 0x49;
+ memset(bz + 1, 0x00, 4);
+ bz[5] = dir;
+ memcpy(bz + 6, &le_devaddr, 4);
+ memcpy(bz + 10, &le_fcnt, 4);
+ bz[14] = 0x00;
+ bz[15] = len;
+
+ return 0;
+}
+
+int
+lrw_calc_mic(struct crypto_shash *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8* buf, size_t len, u8 *mic4)
+{
+ u8 mic[16];
+ u8 bz[16];
+ int err;
+
+ /* According to LoRaWAN Specification Version 1.0.2
+ * - 4.4 Massege Integrity Code (MIC) */
+ lrw_set_bzero(dir, devaddr, fcnt, len, bz);
+ err = lrw_aes_cmac(tfm, bz, buf, len, mic);
+ if (!err)
+ memcpy(mic4, mic, 4);
+
+ return err;
+}
+
+void
+lrw_mic_key_free(struct crypto_shash *tfm)
+{
+ crypto_free_shash(tfm);
+}
+
+struct crypto_skcipher *
+lrw_aes_enc_key_setup(char *algo, u8 *k, size_t k_len)
+{
+ struct crypto_skcipher *tfm;
+ int err;
+
+ tfm = crypto_alloc_skcipher(algo, 0, CRYPTO_ALG_ASYNC);
+ if (!IS_ERR(tfm)) {
+ err = crypto_skcipher_setkey(tfm, k, k_len);
+ if (err) {
+ crypto_free_skcipher(tfm);
+ tfm = NULL;
+ }
+ }
+
+ return tfm;
+}
+
+struct crypto_skcipher *
+lrw_encrypt_key_setup(u8 *k, size_t k_len)
+{
+ return lrw_aes_enc_key_setup("cbc(aes)", k, k_len);
+}
+
+int
+lrw_aes_enc(struct crypto_skcipher *tfm, u8 *in, size_t len, u8 *out)
+{
+ SKCIPHER_REQUEST_ON_STACK(req, tfm);
+ struct scatterlist src, dst;
+ u8 iv[16];
+ int err;
+
+ memset(iv, 0, 16);
+ /* The buffer for sg_init_one cannot be a global or const local
+ * (will confuse the scatterlist) */
+ sg_init_one(&src, in, len);
+ sg_init_one(&dst, out, len);
+
+ skcipher_request_set_tfm(req, tfm);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+ skcipher_request_set_crypt(req, &src, &dst, len, iv);
+ err = crypto_skcipher_encrypt(req);
+ skcipher_request_zero(req);
+
+ return err;
+}
+
+#define LRW_SEQUENCE_OF_BLOCK_LEN 16
+
+int
+lrw_set_sob(u8 dir, u32 devaddr, u32 fcnt, u8 index, u8 *sob)
+{
+ __le32 le_devaddr = cpu_to_le32(devaddr);
+ __le32 _fcnt = cpu_to_le32(fcnt);
+
+ sob[0] = 0x01;
+ memset(sob + 1, 0x00, 4);
+ sob[5] = dir;
+ memcpy(sob + 6, &le_devaddr, 4);
+ memcpy(sob + 10, &_fcnt, 4);
+ sob[14] = 0x00;
+ sob[15] = index;
+
+ return 0;
+}
+
+int
+lrw_encrypt_sob(struct crypto_skcipher *tfm, u8 *sob)
+{
+ return lrw_aes_enc(tfm, sob, LRW_SEQUENCE_OF_BLOCK_LEN, sob);
+}
+
+int
+lrw_encrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len)
+{
+ u8 sob[LRW_SEQUENCE_OF_BLOCK_LEN];
+ u8 i, j;
+
+ /* According to LoRaWAN Specification Version 1.0.2
+ * - 4.3.3 MAC Frame Payload Encryption (FRMPayload) */
+ for (i = 0; (i * LRW_SEQUENCE_OF_BLOCK_LEN) < len; i++) {
+ lrw_set_sob(dir, devaddr, fcnt, i, sob);
+ lrw_encrypt_sob(tfm, sob);
+ for (j = 0; (i * LRW_SEQUENCE_OF_BLOCK_LEN + j) < len; j++)
+ buf[i * LRW_SEQUENCE_OF_BLOCK_LEN + j] ^= sob[j];
+ }
+
+ return 0;
+}
+
+int
+lrw_decrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len)
+{
+ /* Accoding to XOR swap algorithm */
+ return lrw_encrypt_buf(tfm, dir, devaddr, fcnt, buf, len);
+}
+
+void
+lrw_aes_enc_key_free(struct crypto_skcipher *tfm)
+{
+ crypto_free_skcipher(tfm);
+}
+
+void
+lrw_encrypt_key_free(struct crypto_skcipher *tfm)
+{
+ lrw_aes_enc_key_free(tfm);
+}
diff --git a/net/maclorawan/crypto.h b/net/maclorawan/crypto.h
new file mode 100644
index 000000000000..2ede02efb8c6
--- /dev/null
+++ b/net/maclorawan/crypto.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_CRYPTO_H__
+#define __LORAWAN_CRYPTO_H__
+
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+
+struct crypto_shash *lrw_mic_key_setup(u8 *k, size_t k_len);
+int lrw_calc_mic(struct crypto_shash *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8* buf, size_t len, u8 *mic4);
+void lrw_mic_key_free(struct crypto_shash *tfm);
+
+struct crypto_skcipher *lrw_encrypt_key_setup(u8 *k, size_t k_len);
+int lrw_encrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len);
+int lrw_decrypt_buf(struct crypto_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len);
+void lrw_encrypt_key_free(struct crypto_skcipher *tfm);
+
+#endif
--
2.19.1
List LORAWAN and MACLORAWAN in menuconfig and make they can be built.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
net/Kconfig | 2 ++
net/Makefile | 2 ++
2 files changed, 4 insertions(+)
diff --git a/net/Kconfig b/net/Kconfig
index 053b36998c18..b12b8bed6abb 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -224,6 +224,8 @@ source "net/6lowpan/Kconfig"
source "net/ieee802154/Kconfig"
source "net/mac802154/Kconfig"
source "net/lora/Kconfig"
+source "net/lorawan/Kconfig"
+source "net/maclorawan/Kconfig"
source "net/sched/Kconfig"
source "net/dcb/Kconfig"
source "net/dns_resolver/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index e80b84313851..9d5515965a8f 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -63,6 +63,8 @@ obj-$(CONFIG_6LOWPAN) += 6lowpan/
obj-$(CONFIG_IEEE802154) += ieee802154/
obj-$(CONFIG_MAC802154) += mac802154/
obj-$(CONFIG_LORA) += lora/
+obj-$(CONFIG_LORAWAN) += lorawan/
+obj-$(CONFIG_MACLORAWAN) += maclorawan/
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
--
2.19.1
LoRaWAN(TM) is the MAC layer defined by LoRa Alliance(TM) over LoRa
devices. LoRa is one of Low-Power Wide-Area Network (LPWAN) technology.
LoRaWAN networks typically are laid out in a star-of-stars topology in
which gateways relay messages between end-devices and a central network
server at the backend. Gateways are connected to the network server via
standard IP connections while end-devices use single hop LoRa(TM) or FSK
communication to one or many gateways.
A LoRa network distinguishes between a basic LoRaWAN (named Class A) and
optional features (Class B, Class C ...):
* Bi-directional end-devices (Class A)
* Bi-directional end-devices with scheduled receive slots (Class B)
* Bi-directional end-devices with maximal receive slots (Class C)
This patch set add LoRaWAN class module implementing the stack,
especially the soft MAC, between socket APIs and LoRa device drivers.
socket APIs:
send and receive the data
------------------------------------------------------------------------
LoRaWAN class module implements soft MAC:
append the header/footer, encryption/decryption, timing slot and MAC
commands
------------------------------------------------------------------------
LoRa device drivers:
send and receive the messages for MAC layer
------------------------------------------------------------------------
LoRa devices
This module starts from simple and implements partial Class A
end-devices features defined in LoRaWAN(TM) Specification Ver. 1.0.2.
More features and complexity, for example regional parameters, confirmed
data messages, join request/accept messages for Over-The-Air Activation,
MAC commands ... will be added in the future.
Jian-Hong Pan (7):
net: lorawan: Add macro and definition for LoRaWAN
net: lorawan: Add LoRaWAN socket module
net: lorawan: Add LoRaWAN API declaration for LoRa devices
net: maclorawan: Add maclorawan module declaration
net: maclorawan: Implement the crypto of maclorawan module
net: maclorawan: Implement maclorawan class module
net: lorawan: List LORAWAN in menuconfig
include/linux/lora/lorawan.h | 131 ++++++
include/linux/lora/lorawan_netdev.h | 49 ++
include/linux/socket.h | 5 +-
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 1 +
net/Kconfig | 2 +
net/Makefile | 2 +
net/core/dev.c | 4 +-
net/lorawan/Kconfig | 10 +
net/lorawan/Makefile | 2 +
net/lorawan/socket.c | 688 ++++++++++++++++++++++++++++
net/maclorawan/Kconfig | 14 +
net/maclorawan/Makefile | 2 +
net/maclorawan/crypto.c | 209 +++++++++
net/maclorawan/crypto.h | 27 ++
net/maclorawan/mac.c | 520 +++++++++++++++++++++
net/maclorawan/maclorawan.h | 199 ++++++++
net/maclorawan/main.c | 605 ++++++++++++++++++++++++
security/selinux/hooks.c | 4 +-
security/selinux/include/classmap.h | 4 +-
20 files changed, 2474 insertions(+), 5 deletions(-)
create mode 100644 include/linux/lora/lorawan.h
create mode 100644 include/linux/lora/lorawan_netdev.h
create mode 100644 net/lorawan/Kconfig
create mode 100644 net/lorawan/Makefile
create mode 100644 net/lorawan/socket.c
create mode 100644 net/maclorawan/Kconfig
create mode 100644 net/maclorawan/Makefile
create mode 100644 net/maclorawan/crypto.c
create mode 100644 net/maclorawan/crypto.h
create mode 100644 net/maclorawan/mac.c
create mode 100644 net/maclorawan/maclorawan.h
create mode 100644 net/maclorawan/main.c
--
2.19.1
This patch adds a new address/protocol family for LoRaWAN network.
It also implements the the functions and maps to Datagram socket for
LoRaWAN unconfirmed data messages.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Add lorawan_netdev.h header file for network address related
declaration
- Use SPDX license identifiers
V3:
- Change the inline functions to a single line macro or just remove the
decoration word - inline
- Order local variables from longest to shortest line in the functions
- Change mac_cb inline function to lrw_get_mac_cb macro
include/linux/lora/lorawan_netdev.h | 49 ++
net/lorawan/Kconfig | 10 +
net/lorawan/Makefile | 2 +
net/lorawan/socket.c | 688 ++++++++++++++++++++++++++++
4 files changed, 749 insertions(+)
create mode 100644 include/linux/lora/lorawan_netdev.h
create mode 100644 net/lorawan/Kconfig
create mode 100644 net/lorawan/Makefile
create mode 100644 net/lorawan/socket.c
diff --git a/include/linux/lora/lorawan_netdev.h b/include/linux/lora/lorawan_netdev.h
new file mode 100644
index 000000000000..f6689b7c4cb0
--- /dev/null
+++ b/include/linux/lora/lorawan_netdev.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN stack related definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_NET_DEVICE_H__
+#define __LORAWAN_NET_DEVICE_H__
+
+enum {
+ LRW_ADDR_APPEUI,
+ LRW_ADDR_DEVEUI,
+ LRW_ADDR_DEVADDR,
+};
+
+struct lrw_addr_in {
+ int addr_type;
+ union {
+ u64 app_eui;
+ u64 dev_eui;
+ u32 devaddr;
+ };
+};
+
+struct sockaddr_lorawan {
+ sa_family_t family; /* AF_LORAWAN */
+ struct lrw_addr_in addr_in;
+};
+
+/**
+ * lrw_mac_cb - This structure holds the control buffer (cb) of sk_buff
+ *
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ */
+struct lrw_mac_cb {
+ u32 devaddr;
+};
+
+/**
+ * lrw_get_mac_cb - Get the LoRaWAN MAC control buffer of the sk_buff
+ * @skb: the exchanging sk_buff
+ *
+ * Return: the pointer of LoRaWAN MAC control buffer
+ */
+#define lrw_get_mac_cb(skb) ((struct lrw_mac_cb *)skb->cb)
+
+#endif
diff --git a/net/lorawan/Kconfig b/net/lorawan/Kconfig
new file mode 100644
index 000000000000..7f2f344085c4
--- /dev/null
+++ b/net/lorawan/Kconfig
@@ -0,0 +1,10 @@
+config LORAWAN
+ tristate "LoRaWAN Network support"
+ ---help---
+ LoRaWAN defines low data rate, low power and long range wireless
+ wide area networks. It was designed to organize networks of automation
+ devices, such as sensors, switches and actuators. It can operate
+ multiple kilometers wide.
+
+ Say Y here to compile LoRaWAN support into the kernel or say M to
+ compile it as a module.
diff --git a/net/lorawan/Makefile b/net/lorawan/Makefile
new file mode 100644
index 000000000000..8c923ca6541a
--- /dev/null
+++ b/net/lorawan/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_LORAWAN) += lorawan.o
+lorawan-objs := socket.o
diff --git a/net/lorawan/socket.c b/net/lorawan/socket.c
new file mode 100644
index 000000000000..f0bd391c4137
--- /dev/null
+++ b/net/lorawan/socket.c
@@ -0,0 +1,688 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN stack related definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/net.h>
+#include <linux/if_arp.h>
+#include <linux/termios.h> /* For TIOCOUTQ/INQ */
+#include <net/sock.h>
+#include <linux/lora/lorawan_netdev.h>
+
+#define LORAWAN_MODULE_NAME "lorawan"
+
+#define LRW_DBG_STR(fmt) LORAWAN_MODULE_NAME": "fmt
+#define lrw_info(fmt, ...) (pr_info(LRW_DBG_STR(fmt), ##__VA_ARGS__))
+#define lrw_dbg(fmt, ...) (pr_debug(LRW_DBG_STR(fmt), ##__VA_ARGS__))
+
+/**
+ * dgram_sock - This structure holds the states of Datagram socket
+ *
+ * @sk: network layer representation of the socket
+ * sk must be the first member of dgram_sock
+ * @src_devaddr: the LoRaWAN device address for this connection
+ * @bound: this socket is bound or not
+ * @connected: this socket is connected to the destination or not
+ * @want_ack: this socket needs to ack for the connection or not
+ */
+struct dgram_sock {
+ struct sock sk;
+ u32 src_devaddr;
+
+ u8 bound:1;
+ u8 connected:1;
+};
+
+static HLIST_HEAD(dgram_head);
+static DEFINE_RWLOCK(dgram_lock);
+
+static struct dgram_sock *
+dgram_sk(const struct sock *sk)
+{
+ return container_of(sk, struct dgram_sock, sk);
+}
+
+static struct net_device *
+lrw_get_dev_by_addr(struct net *net, u32 devaddr)
+{
+ __be32 be_addr = cpu_to_be32(devaddr);
+ struct net_device *ndev = NULL;
+
+ rcu_read_lock();
+ ndev = dev_getbyhwaddr_rcu(net, ARPHRD_LORAWAN, (char *)&be_addr);
+ if (ndev)
+ dev_hold(ndev);
+ rcu_read_unlock();
+
+ return ndev;
+}
+
+static int
+dgram_init(struct sock *sk)
+{
+ return 0;
+}
+
+static void
+dgram_close(struct sock *sk, long timeout)
+{
+ sk_common_release(sk);
+}
+
+static int
+dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
+ struct dgram_sock *ro = dgram_sk(sk);
+ struct net_device *ndev;
+ int ret;
+
+ lock_sock(sk);
+ ro->bound = 0;
+
+ ret = -EINVAL;
+ if (len < sizeof(*addr))
+ goto dgram_bind_end;
+
+ if (addr->family != AF_LORAWAN)
+ goto dgram_bind_end;
+
+ if (addr->addr_in.addr_type != LRW_ADDR_DEVADDR)
+ goto dgram_bind_end;
+
+ lrw_dbg("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
+ ndev = lrw_get_dev_by_addr(sock_net(sk), addr->addr_in.devaddr);
+ if (!ndev) {
+ ret = -ENODEV;
+ goto dgram_bind_end;
+ }
+ netdev_dbg(ndev, "%s: get ndev\n", __func__);
+
+ if (ndev->type != ARPHRD_LORAWAN) {
+ ret = -ENODEV;
+ goto dgram_bind_end;
+ }
+
+ ro->src_devaddr = addr->addr_in.devaddr;
+ ro->bound = 1;
+ ret = 0;
+ dev_put(ndev);
+ lrw_dbg("%s: bound address %X\n", __func__, ro->src_devaddr);
+
+dgram_bind_end:
+ release_sock(sk);
+ return ret;
+}
+
+static int
+lrw_dev_hard_header(struct sk_buff *skb, struct net_device *ndev,
+ const u32 src_devaddr, size_t len)
+{
+ /* TODO: Prepare the LoRaWAN sending header here */
+ return 0;
+}
+
+static int
+dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ size_t hlen;
+ size_t tlen;
+ int ret;
+
+ lrw_dbg("%s: going to send %zu bytes", __func__, size);
+ if (msg->msg_flags & MSG_OOB) {
+ lrw_dbg("msg->msg_flags = 0x%x\n", msg->msg_flags);
+ return -EOPNOTSUPP;
+ }
+
+ lrw_dbg("%s: check msg_name\n", __func__);
+ if (!ro->connected && !msg->msg_name)
+ return -EDESTADDRREQ;
+ else if (ro->connected && msg->msg_name)
+ return -EISCONN;
+
+ lrw_dbg("%s: check bound\n", __func__);
+ if (!ro->bound)
+ ndev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_LORAWAN);
+ else
+ ndev = lrw_get_dev_by_addr(sock_net(sk), ro->src_devaddr);
+
+ if (!ndev) {
+ lrw_dbg("no dev\n");
+ ret = -ENXIO;
+ goto dgram_sendmsg_end;
+ }
+
+ if (size > ndev->mtu){
+ netdev_dbg(ndev, "size = %zu, mtu = %u\n", size, ndev->mtu);
+ ret = -EMSGSIZE;
+ goto dgram_sendmsg_end;
+ }
+
+ netdev_dbg(ndev, "%s: create skb\n", __func__);
+ hlen = LL_RESERVED_SPACE(ndev);
+ tlen = ndev->needed_tailroom;
+ skb = sock_alloc_send_skb(sk, hlen + tlen + size,
+ msg->msg_flags & MSG_DONTWAIT,
+ &ret);
+
+ if (!skb)
+ goto dgram_sendmsg_no_skb;
+
+ skb_reserve(skb, hlen);
+ skb_reset_network_header(skb);
+
+ ret = lrw_dev_hard_header(skb, ndev, 0, size);
+ if (ret < 0)
+ goto dgram_sendmsg_no_skb;
+
+ ret = memcpy_from_msg(skb_put(skb, size), msg, size);
+ if (ret > 0)
+ goto dgram_sendmsg_err_skb;
+
+ skb->dev = ndev;
+ skb->protocol = htons(ETH_P_LORAWAN);
+
+ netdev_dbg(ndev, "%s: push skb to xmit queue\n", __func__);
+ ret = dev_queue_xmit(skb);
+ if (ret > 0)
+ ret = net_xmit_errno(ret);
+ netdev_dbg(ndev, "%s: pushed skb to xmit queue with ret=%d\n",
+ __func__, ret);
+ dev_put(ndev);
+
+ return ret ?: size;
+
+dgram_sendmsg_err_skb:
+ kfree_skb(skb);
+dgram_sendmsg_no_skb:
+ dev_put(ndev);
+
+dgram_sendmsg_end:
+ return ret;
+}
+
+static int
+dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+ int noblock, int flags, int *addr_len)
+{
+ DECLARE_SOCKADDR(struct sockaddr_lorawan *, saddr, msg->msg_name);
+ struct sk_buff *skb;
+ size_t copied = 0;
+ int err;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto dgram_recvmsg_end;
+
+ copied = skb->len;
+ if (len < copied) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
+ if (err)
+ goto dgram_recvmsg_done;
+
+ sock_recv_ts_and_drops(msg, sk, skb);
+ if(saddr) {
+ memset(saddr, 0, sizeof(*saddr));
+ saddr->family = AF_LORAWAN;
+ saddr->addr_in.devaddr = lrw_get_mac_cb(skb)->devaddr;
+ *addr_len = sizeof(*saddr);
+ }
+
+ if (flags & MSG_TRUNC)
+ copied = skb->len;
+
+dgram_recvmsg_done:
+ skb_free_datagram(sk, skb);
+
+dgram_recvmsg_end:
+ if (err)
+ return err;
+ return copied;
+}
+
+static int
+dgram_hash(struct sock *sk)
+{
+ lrw_dbg("%s\n", __func__);
+ write_lock_bh(&dgram_lock);
+ sk_add_node(sk, &dgram_head);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ write_unlock_bh(&dgram_lock);
+
+ return 0;
+}
+
+static void
+dgram_unhash(struct sock *sk)
+{
+ lrw_dbg("%s\n", __func__);
+ write_lock_bh(&dgram_lock);
+ if (sk_del_node_init(sk))
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ write_unlock_bh(&dgram_lock);
+}
+
+static int
+dgram_connect(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ /* Nodes of LoRaWAN send data to a gateway only, then data is received
+ * and transferred to servers with the gateway's policy.
+ * So, the destination address is not used by nodes.
+ */
+ lock_sock(sk);
+ ro->connected = 1;
+ release_sock(sk);
+
+ return 0;
+}
+
+static int
+dgram_disconnect(struct sock *sk, int flags)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ lock_sock(sk);
+ ro->connected = 0;
+ release_sock(sk);
+
+ return 0;
+}
+
+static int
+dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ struct net_device *ndev = sk->sk_dst_cache->dev;
+ struct sk_buff *skb;
+ int amount;
+ int err;
+
+ netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
+ switch (cmd) {
+ case SIOCOUTQ:
+ amount = sk_wmem_alloc_get(sk);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ case SIOCINQ:
+ amount = 0;
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb) {
+ /* We will only return the amount of this packet
+ * since that is all that will be read.
+ */
+ amount = skb->len;
+ }
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ default:
+ err = -ENOIOCTLCMD;
+ }
+
+ return err;
+}
+
+static int
+dgram_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ int val, len;
+
+ if (level != SOL_LORAWAN)
+ return -EOPNOTSUPP;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ len = min_t(unsigned int, len, sizeof(int));
+
+ switch (optname) {
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+dgram_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ int val;
+ int err;
+
+ err = 0;
+
+ if (optlen < sizeof(int))
+ return -EINVAL;
+
+ if (get_user(val, (int __user *)optval))
+ return -EFAULT;
+
+ lock_sock(sk);
+
+ switch (optname) {
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ release_sock(sk);
+
+ return err;
+}
+
+static struct proto lrw_dgram_prot = {
+ .name = "LoRaWAN",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct dgram_sock),
+ .init = dgram_init,
+ .close = dgram_close,
+ .bind = dgram_bind,
+ .sendmsg = dgram_sendmsg,
+ .recvmsg = dgram_recvmsg,
+ .hash = dgram_hash,
+ .unhash = dgram_unhash,
+ .connect = dgram_connect,
+ .disconnect = dgram_disconnect,
+ .ioctl = dgram_ioctl,
+ .getsockopt = dgram_getsockopt,
+ .setsockopt = dgram_setsockopt,
+};
+
+static int
+lrw_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk) {
+ sock->sk = NULL;
+ sk->sk_prot->close(sk, 0);
+ }
+
+ return 0;
+}
+
+static int
+lrw_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
+ struct sock *sk = sock->sk;
+
+ lrw_dbg("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
+ if (sk->sk_prot->bind)
+ return sk->sk_prot->bind(sk, uaddr, addr_len);
+
+ return sock_no_bind(sock, uaddr, addr_len);
+}
+
+static int
+lrw_sock_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ if (addr_len < sizeof(uaddr->sa_family))
+ return -EINVAL;
+
+ return sk->sk_prot->connect(sk, uaddr, addr_len);
+}
+
+static int
+lrw_ndev_ioctl(struct sock *sk, struct ifreq __user *arg, unsigned int cmd)
+{
+ struct net_device *ndev;
+ struct ifreq ifr;
+ int ret;
+
+ lrw_dbg("%s: cmd %ud\n", __func__, cmd);
+ ret = -ENOIOCTLCMD;
+
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
+
+ ifr.ifr_name[IFNAMSIZ-1] = 0;
+
+ dev_load(sock_net(sk), ifr.ifr_name);
+ ndev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
+
+ netdev_dbg(ndev, "%s: cmd %ud\n", __func__, cmd);
+ if (!ndev)
+ return -ENODEV;
+
+ if (ndev->type == ARPHRD_LORAWAN && ndev->netdev_ops->ndo_do_ioctl)
+ ret = ndev->netdev_ops->ndo_do_ioctl(ndev, &ifr, cmd);
+
+ if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ ret = -EFAULT;
+ dev_put(ndev);
+
+ return ret;
+}
+
+static int
+lrw_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+
+ lrw_dbg("%s: cmd %ud\n", __func__, cmd);
+ switch (cmd) {
+ case SIOCGSTAMP:
+ return sock_get_timestamp(sk, (struct timeval __user *)arg);
+ case SIOCGSTAMPNS:
+ return sock_get_timestampns(sk, (struct timespec __user *)arg);
+ case SIOCOUTQ:
+ case SIOCINQ:
+ if (!sk->sk_prot->ioctl)
+ return -ENOIOCTLCMD;
+ return sk->sk_prot->ioctl(sk, cmd, arg);
+ default:
+ return lrw_ndev_ioctl(sk, (struct ifreq __user *)arg, cmd);
+ }
+}
+
+static int
+lrw_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+
+ lrw_dbg("%s: going to send %zu bytes\n", __func__, len);
+ return sk->sk_prot->sendmsg(sk, msg, len);
+}
+
+static const struct proto_ops lrw_dgram_ops = {
+ .family = PF_LORAWAN,
+ .owner = THIS_MODULE,
+ .release = lrw_sock_release,
+ .bind = lrw_sock_bind,
+ .connect = lrw_sock_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = lrw_sock_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = lrw_sock_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static int
+lorawan_creat(struct net *net, struct socket *sock, int protocol, int kern)
+{
+ struct sock *sk;
+ int ret;
+
+ if (!net_eq(net, &init_net))
+ return -EAFNOSUPPORT;
+
+ if (sock->type != SOCK_DGRAM)
+ return -EAFNOSUPPORT;
+
+ /* Allocates enough memory for dgram_sock whose first member is sk */
+ sk = sk_alloc(net, PF_LORAWAN, GFP_KERNEL, &lrw_dgram_prot, kern);
+ if (!sk)
+ return -ENOMEM;
+
+ sock->ops = &lrw_dgram_ops;
+ sock_init_data(sock, sk);
+ sk->sk_family = PF_LORAWAN;
+ sock_set_flag(sk, SOCK_ZAPPED);
+
+ if (sk->sk_prot->hash) {
+ ret = sk->sk_prot->hash(sk);
+ if (ret) {
+ sk_common_release(sk);
+ goto lorawan_creat_end;
+ }
+ }
+
+ if (sk->sk_prot->init) {
+ ret = sk->sk_prot->init(sk);
+ if (ret)
+ sk_common_release(sk);
+ }
+
+lorawan_creat_end:
+ return ret;
+}
+
+static const struct net_proto_family lorawan_family_ops = {
+ .owner = THIS_MODULE,
+ .family = PF_LORAWAN,
+ .create = lorawan_creat,
+};
+
+static int
+lrw_dgram_deliver(struct net_device *ndev, struct sk_buff *skb)
+{
+ struct dgram_sock *ro;
+ struct sock *sk;
+ bool found;
+ int ret;
+
+ ret = NET_RX_SUCCESS;
+ found = false;
+
+ read_lock(&dgram_lock);
+ sk_for_each(sk, &dgram_head) {
+ ro = dgram_sk(sk);
+ if(cpu_to_le32(ro->src_devaddr) == *(__le32 *)ndev->dev_addr) {
+ found = true;
+ break;
+ }
+ }
+ read_unlock(&dgram_lock);
+
+ if (!found)
+ goto lrw_dgram_deliver_err;
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NET_RX_DROP;
+
+ if (sock_queue_rcv_skb(sk, skb) < 0)
+ goto lrw_dgram_deliver_err;
+
+ return ret;
+
+lrw_dgram_deliver_err:
+ kfree_skb(skb);
+ ret = NET_RX_DROP;
+ return ret;
+}
+
+static int
+lorawan_rcv(struct sk_buff *skb, struct net_device *ndev,
+ struct packet_type *pt, struct net_device *orig_ndev)
+{
+ if (!netif_running(ndev))
+ goto lorawan_rcv_drop;
+
+ if (!net_eq(dev_net(ndev), &init_net))
+ goto lorawan_rcv_drop;
+
+ if (ndev->type != ARPHRD_LORAWAN)
+ goto lorawan_rcv_drop;
+
+ if (skb->pkt_type != PACKET_OTHERHOST)
+ return lrw_dgram_deliver(ndev, skb);
+
+lorawan_rcv_drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static struct packet_type lorawan_packet_type = {
+ .type = htons(ETH_P_LORAWAN),
+ .func = lorawan_rcv,
+};
+
+static int __init
+lrw_sock_init(void)
+{
+ int ret;
+
+ lrw_info("module inserted\n");
+ ret = proto_register(&lrw_dgram_prot, 1);
+ if(ret)
+ goto lrw_sock_init_end;
+
+ /* Tell SOCKET that we are alive */
+ ret = sock_register(&lorawan_family_ops);
+ if(ret)
+ goto lrw_sock_init_err;
+
+ dev_add_pack(&lorawan_packet_type);
+ ret = 0;
+ goto lrw_sock_init_end;
+
+lrw_sock_init_err:
+ proto_unregister(&lrw_dgram_prot);
+
+lrw_sock_init_end:
+ return 0;
+}
+
+static void __exit
+lrw_sock_exit(void)
+{
+ dev_remove_pack(&lorawan_packet_type);
+ sock_unregister(PF_LORAWAN);
+ proto_unregister(&lrw_dgram_prot);
+ lrw_info("module removed\n");
+}
+
+module_init(lrw_sock_init);
+module_exit(lrw_sock_exit);
+
+MODULE_AUTHOR("Jian-Hong Pan, <[email protected]>");
+MODULE_DESCRIPTION("LoRaWAN socket kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS_NETPROTO(PF_LORAWAN);
--
2.19.1
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 <[email protected]>
---
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
net/maclorawan/Kconfig | 14 +
net/maclorawan/Makefile | 2 +
net/maclorawan/mac.c | 520 ++++++++++++++++++++++++++++++++++
net/maclorawan/main.c | 605 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 1141 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..177537d5f59f
--- /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..2d9f322c364e
--- /dev/null
+++ b/net/maclorawan/mac.c
@@ -0,0 +1,520 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/poll.h>
+#include <asm/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(struct lrw_session), 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, mic[4];
+ __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, 2), &le_fcnt_up, 2);
+
+ /* Push FCtrl */
+ fctrl = 0;
+ if (lrw_st->rx_should_ack) {
+ fctrl |= 0x20;
+ lrw_st->rx_should_ack = false;
+ }
+ memcpy(skb_push(skb, 1), &fctrl, 1);
+
+ /* 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);
+}
+
+void
+lrw_parse_frame(struct lrw_session *ss, struct sk_buff *skb)
+{
+ 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, 4);
+
+ /* Get frame control */
+ fhdr->fctrl = skb->data[0];
+ skb_pull(skb, 1);
+
+ /* 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, 2);
+
+ /* Get frame options */
+ fhdr->fopts_len = fhdr->fctrl & 0xF;
+ if (fhdr->fopts_len > 0) {
+ memcpy(fhdr->fopts, skb->data, fhdr->fopts_len);
+ skb_pull(skb, fhdr->fopts_len);
+ }
+
+ /* TODO: Parse frame options */
+
+ /* Remove message integrity code */
+ skb_trim(skb, skb->len - LRW_MIC_LEN);
+}
+
+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;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ p_fcnt = (__le16 *)(rx_skb->data + 6);
+ 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 = lrw_st->rx_skb_list.next;
+ 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;
+
+ lrw_parse_frame(ss, skb);
+
+ /* 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;
+
+ if (ss->rx_skb->len > 0) {
+ spin_lock_bh(&ss->state_lock);
+ ss->state = LRW_RXRECEIVED_SS;
+ spin_unlock_bh(&ss->state_lock);
+
+ lrw_get_mac_cb(skb)->devaddr = lrw_st->devaddr;
+ 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 */
+ kfree_skb(skb);
+}
+
+int
+lrw_check_mic(struct crypto_shash *tfm, struct sk_buff *skb)
+{
+ u32 devaddr;
+ size_t len;
+ u8 cks[4];
+ u16 fcnt;
+ u8 *buf;
+ u8 *mic;
+
+ buf = skb->data;
+ devaddr = le32_to_cpu(*((__le32 *)(buf + LRW_MHDR_LEN)));
+ fcnt = le16_to_cpu(*(__le16 *)(buf + 6));
+ len = skb->len - 4;
+ mic = skb->data + len;
+
+ lrw_calc_mic(tfm, LRW_DOWNLINK, devaddr, fcnt, buf, len, cks);
+
+ return (!memcmp(cks, mic, 4));
+}
+
+/**
+ * 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__);
+
+ 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)) {
+ skb_queue_tail(&lrw_st->rx_skb_list, skb);
+ schedule_work(&lrw_st->rx_work);
+ }
+ else {
+ 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 - 20 * HZ / 1000;
+ 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..9ca778706880
--- /dev/null
+++ b/net/maclorawan/main.c
@@ -0,0 +1,605 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#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 = (void *) lrw_st + sizeof(struct lrw_struct);
+
+ 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);
+
+ return;
+}
+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, <[email protected]>");
+MODULE_DESCRIPTION("LoRaWAN soft MAC kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
--
2.19.1
Add the maclorawan header file for common APIs in the module.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Use SPDX license identifiers
net/maclorawan/maclorawan.h | 199 ++++++++++++++++++++++++++++++++++++
1 file changed, 199 insertions(+)
create mode 100644 net/maclorawan/maclorawan.h
diff --git a/net/maclorawan/maclorawan.h b/net/maclorawan/maclorawan.h
new file mode 100644
index 000000000000..66b87f051d51
--- /dev/null
+++ b/net/maclorawan/maclorawan.h
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __MAC_LORAWAN_H__
+#define __MAC_LORAWAN_H__
+
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <uapi/linux/if_arp.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include <linux/lora/lorawan.h>
+
+#define LORAWAN_MODULE_NAME "maclorawan"
+
+/* List the message types of LoRaWAN */
+enum {
+ LRW_JOIN_REQUEST,
+ LRW_JOIN_ACCEPT,
+ LRW_UNCONFIRMED_DATA_UP,
+ LRW_UNCONFIRMED_DATA_DOWN,
+ LRW_CONFIRMED_DATA_UP,
+ LRW_CONFIRMED_DATA_DOWN,
+ LRW_RFU,
+ LRW_PROPRIETARY,
+};
+
+/* List the communication directions */
+enum {
+ LRW_UPLINK,
+ LRW_DOWNLINK,
+};
+
+/* States of LoRaWAN slot timing */
+enum {
+ LRW_INIT_SS,
+ LRW_XMITTING_SS,
+ LRW_XMITTED,
+ LRW_RX1_SS,
+ LRW_RX2_SS,
+ LRW_RXTIMEOUT_SS,
+ LRW_RXRECEIVED_SS,
+ LRW_RETRANSMIT_SS,
+};
+
+#define LRW_MHDR_LEN 1
+#define LRW_FHDR_MAX_LEN 22
+#define LRW_FOPS_MAX_LEN 15
+#define LRW_FPORT_LEN 1
+#define LRW_MIC_LEN 4
+
+/**
+ * lrw_fhdr - Hold the message's basic information of the frame
+ *
+ * @mtype: this message's type
+ * @fctrl: the frame control byte
+ * @fcnt: this message's frame counter value
+ * @fopts: this frame's options field
+ * @fopts_len: the length of the fopts
+ */
+struct lrw_fhdr {
+ u8 mtype;
+ u8 fctrl;
+ u16 fcnt;
+ u8 fopts[LRW_FPORT_LEN];
+ u8 fopts_len;
+};
+
+/**
+ * lrw_session - LoRaWAN session for Class A end device
+ *
+ * @lrw_st: points to the belonging lrw_st
+ * @entry: the entry of the ss_list in lrw_struct
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ * @fcnt_up: uplink frame counter
+ * @fcnt_down: downlink frame counter
+ * @fport: the LoRaWAN data message's port field
+ * @tx_skb: points to the TX skb, the frame
+ * @rx_skb: points to the RX skb, the frame
+ * @tx_fhdr: hold the message's basic information of the TX frame
+ * @rx_fhdr: hold the message's basic information of the RX frame
+ * @tx_should_ack: flag for determining the TX which should be acked or not
+ * @retry: retry times for xmitting failed
+ * @state: this session's current state
+ * @state_lock: lock of the session's state
+ * @timer: timing for this session and the state transition
+ * @timeout_work: work if waiting acknowledge time out
+ * @rx_delay1: RX1 delay time in seconds
+ * @rx_delay2: RX2 delay time in seconds
+ * @rx1_window: RX1 window opening time in mini-seconds
+ * @rx2_window: RX2 window opening time in mini-seconds
+ * @ack_timeout: time out time for waiting acknowledge in seconds
+ */
+struct lrw_session {
+ struct lrw_struct *lrw_st;
+ struct list_head entry;
+
+ u32 devaddr;
+ u16 fcnt_up;
+ u16 fcnt_down;
+ u8 fport;
+ struct sk_buff *tx_skb;
+ struct sk_buff *rx_skb;
+ struct lrw_fhdr tx_fhdr;
+ struct lrw_fhdr rx_fhdr;
+
+ bool tx_should_ack;
+ u8 retry;
+ u8 state;
+ spinlock_t state_lock;
+
+ struct timer_list timer;
+ struct work_struct timeout_work;
+ unsigned long rx_delay1;
+ unsigned long rx_delay2;
+ unsigned long rx1_window;
+ unsigned long rx2_window;
+ unsigned long ack_timeout;
+};
+
+/**
+ * lrw_struct - The full LoRaWAN hardware to the LoRa device.
+ *
+ * @dev: this LoRa device registed in system
+ * @hw: the LoRa device of this LoRaWAN hardware
+ * @ops: handle of LoRa operations interfaces
+ * @rx_skb_list: the list of received frames
+ * @ss_list: LoRaWAN session list of this LoRaWAN hardware
+ * @_cur_ss: pointer of the current processing session
+ * @rx_should_ack: represent the current session should be acked or not
+ * @state: the state of this LoRaWAN hardware
+ * @app_eui: the LoRaWAN application EUI
+ * @dev_eui: the LoRaWAN device EUI
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ * @appky: the Application key
+ * @nwkskey: the Network session key
+ * @appskey: the Application session key
+ * @nwks_shash_tfm: the hash handler for LoRaWAN network session
+ * @nwks_skc_tfm: the crypto handler for LoRaWAN network session
+ * @apps_skc_tfm: the crypto handler for LoRaWAN application session
+ * @fcnt_up: the counter of this LoRaWAN hardware's up frame
+ * @fcnt_down: the counter of this LoRaWAN hardware's down frame
+ * @xmit_task: the xmit task for the current LoRaWAN session
+ * @rx_work: the RX work in workqueue for the current LoRaWAN session
+ * @ndev: points to the emulating network device
+ * @_net: the current network namespace of this LoRaWAN hardware
+ */
+struct lrw_struct {
+ struct device dev;
+ struct lrw_hw hw;
+ struct lrw_operations *ops;
+
+ struct sk_buff_head rx_skb_list;
+ struct list_head ss_list;
+ struct mutex ss_list_lock;
+ struct lrw_session *_cur_ss;
+ bool rx_should_ack;
+ u8 state;
+
+ u64 app_eui;
+ u64 dev_eui;
+ u32 devaddr;
+ u8 appkey[LRW_KEY_LEN];
+ u8 nwkskey[LRW_KEY_LEN];
+ u8 appskey[LRW_KEY_LEN];
+ struct crypto_shash *nwks_shash_tfm;
+ struct crypto_skcipher *nwks_skc_tfm;
+ struct crypto_skcipher *apps_skc_tfm;
+
+ u16 fcnt_up;
+ u16 fcnt_down;
+
+ struct tasklet_struct xmit_task;
+ struct work_struct rx_work;
+
+ struct net_device *ndev;
+ possible_net_t _net;
+};
+
+#define NETDEV_2_LRW(ndev) ((struct lrw_struct *)netdev_priv(ndev))
+
+struct lrw_session * lrw_alloc_ss(struct lrw_struct *);
+void lrw_free_ss(struct lrw_session *);
+void lrw_del_ss(struct lrw_session *);
+int lrw_start_hw(struct lrw_struct *);
+void lrw_stop_hw(struct lrw_struct *);
+void lrw_prepare_tx_frame(struct lrw_session *);
+void lrw_xmit(unsigned long);
+void lrw_rx_work(struct work_struct *);
+
+#endif
--
2.19.1
Am 14.11.18 um 17:01 schrieb Jian-Hong Pan:
> This patch adds the macro and definition for the implementation of
> LoRaWAN protocol.
>
> Signed-off-by: Jian-Hong Pan <[email protected]>
> ---
> V2:
> - Modify the commit message
Note that both v2 and v3 don't seem to be based on the latest lora-next
branch, as your constants are already queued and used there.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
From: Jian-Hong Pan <[email protected]>
Date: Thu, 15 Nov 2018 00:01:23 +0800
> +#define lrw_get_mac_cb(skb) ((struct lrw_mac_cb *)skb->cb)
Please make this a static inline function. If the identifier is all lowercase
programmers expect it to be real code not a CPP macro.
> +#define LORAWAN_MODULE_NAME "lorawan"
> +
> +#define LRW_DBG_STR(fmt) LORAWAN_MODULE_NAME": "fmt
> +#define lrw_info(fmt, ...) (pr_info(LRW_DBG_STR(fmt), ##__VA_ARGS__))
> +#define lrw_dbg(fmt, ...) (pr_debug(LRW_DBG_STR(fmt), ##__VA_ARGS__))
Just define "pr_fmt()" appropriately and you don't need to play these kinds
of games.
Set pr_fmt() and call pr_info() and pr_debug() directly.
From: Jian-Hong Pan <[email protected]>
Date: Thu, 15 Nov 2018 00:01:25 +0800
> + * @rx1_window: RX1 window opening time in mini-seconds
> + * @rx2_window: RX2 window opening time in mini-seconds
What is a "mini-second"?
David Miller <[email protected]> 於 2018年11月17日 週六 下午12:32寫道:
>
> From: Jian-Hong Pan <[email protected]>
> Date: Thu, 15 Nov 2018 00:01:25 +0800
>
> > + * @rx1_window: RX1 window opening time in mini-seconds
> > + * @rx2_window: RX2 window opening time in mini-seconds
>
> What is a "mini-second"?
Spelling mistake. It should be millisecond.
Thanks for pointing out
Andreas Färber <[email protected]> 於 2018年11月15日 週四 上午12:12寫道:
>
> Am 14.11.18 um 17:01 schrieb Jian-Hong Pan:
> > This patch adds the macro and definition for the implementation of
> > LoRaWAN protocol.
> >
> > Signed-off-by: Jian-Hong Pan <[email protected]>
> > ---
> > V2:
> > - Modify the commit message
>
> Note that both v2 and v3 don't seem to be based on the latest lora-next
> branch, as your constants are already queued and used there.
Oh! I see the commits now. V4 patch set will follow the latest branch.
Thanks,
Jian-Hong Pan
David Miller <[email protected]> 於 2018年11月17日 週六 下午12:32寫道:
>
> From: Jian-Hong Pan <[email protected]>
> Date: Thu, 15 Nov 2018 00:01:23 +0800
>
> > +#define lrw_get_mac_cb(skb) ((struct lrw_mac_cb *)skb->cb)
>
> Please make this a static inline function. If the identifier is all lowercase
> programmers expect it to be real code not a CPP macro.
Okay! Sure
> > +#define LORAWAN_MODULE_NAME "lorawan"
> > +
> > +#define LRW_DBG_STR(fmt) LORAWAN_MODULE_NAME": "fmt
> > +#define lrw_info(fmt, ...) (pr_info(LRW_DBG_STR(fmt), ##__VA_ARGS__))
> > +#define lrw_dbg(fmt, ...) (pr_debug(LRW_DBG_STR(fmt), ##__VA_ARGS__))
>
> Just define "pr_fmt()" appropriately and you don't need to play these kinds
> of games.
>
> Set pr_fmt() and call pr_info() and pr_debug() directly.
After checking printk.h again, I get the idea of the comment.
"pr_fmt()" is more convenient!
Thanks for the reviewing. I will make new patches.
Jian-Hong Pan
LoRaWAN(TM) is the MAC layer defined by LoRa Alliance(TM) over LoRa
devices. LoRa is one of Low-Power Wide-Area Network (LPWAN) technology.
LoRaWAN networks typically are laid out in a star-of-stars topology in
which gateways relay messages between end-devices and a central network
server at the backend. Gateways are connected to the network server via
standard IP connections while end-devices use single hop LoRa(TM) or FSK
communication to one or many gateways.
A LoRa network distinguishes between a basic LoRaWAN (named Class A) and
optional features (Class B, Class C ...):
* Bi-directional end-devices (Class A)
* Bi-directional end-devices with scheduled receive slots (Class B)
* Bi-directional end-devices with maximal receive slots (Class C)
This patch set add LoRaWAN class module implementing the stack,
especially the soft MAC, between socket APIs and LoRa device drivers.
socket APIs:
send and receive the data
------------------------------------------------------------------------
LoRaWAN class module implements soft MAC:
append the header/footer, encryption/decryption, timing slot and MAC
commands
------------------------------------------------------------------------
LoRa device drivers:
send and receive the messages for MAC layer
------------------------------------------------------------------------
LoRa devices
This module starts from simple and implements partial Class A
end-devices features defined in LoRaWAN(TM) Specification Ver. 1.0.2.
More features and complexity, for example regional parameters, confirmed
data messages, join request/accept messages for Over-The-Air Activation,
MAC commands ... will be added in the future.
Jian-Hong Pan (6):
net: lorawan: Add LoRaWAN socket module
net: lorawan: Add LoRaWAN API declaration for LoRa devices
net: maclorawan: Add maclorawan module declaration
net: maclorawan: Implement the crypto of maclorawan module
net: maclorawan: Implement maclorawan class module
net: lorawan: List LORAWAN in menuconfig
include/linux/lora/lorawan.h | 131 ++++++
include/linux/lora/lorawan_netdev.h | 52 +++
net/Kconfig | 2 +
net/Makefile | 2 +
net/lorawan/Kconfig | 10 +
net/lorawan/Makefile | 2 +
net/lorawan/socket.c | 686 ++++++++++++++++++++++++++++
net/maclorawan/Kconfig | 14 +
net/maclorawan/Makefile | 2 +
net/maclorawan/crypto.c | 212 +++++++++
net/maclorawan/crypto.h | 27 ++
net/maclorawan/mac.c | 518 +++++++++++++++++++++
net/maclorawan/maclorawan.h | 200 ++++++++
net/maclorawan/main.c | 605 ++++++++++++++++++++++++
14 files changed, 2463 insertions(+)
create mode 100644 include/linux/lora/lorawan.h
create mode 100644 include/linux/lora/lorawan_netdev.h
create mode 100644 net/lorawan/Kconfig
create mode 100644 net/lorawan/Makefile
create mode 100644 net/lorawan/socket.c
create mode 100644 net/maclorawan/Kconfig
create mode 100644 net/maclorawan/Makefile
create mode 100644 net/maclorawan/crypto.c
create mode 100644 net/maclorawan/crypto.h
create mode 100644 net/maclorawan/mac.c
create mode 100644 net/maclorawan/maclorawan.h
create mode 100644 net/maclorawan/main.c
--
2.19.2
List LORAWAN and MACLORAWAN in menuconfig and make they can be built.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
net/Kconfig | 2 ++
net/Makefile | 2 ++
2 files changed, 4 insertions(+)
diff --git a/net/Kconfig b/net/Kconfig
index cf2e651ee31d..03b3ff306778 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -224,6 +224,8 @@ source "net/6lowpan/Kconfig"
source "net/ieee802154/Kconfig"
source "net/mac802154/Kconfig"
source "net/lora/Kconfig"
+source "net/lorawan/Kconfig"
+source "net/maclorawan/Kconfig"
source "net/sched/Kconfig"
source "net/dcb/Kconfig"
source "net/dns_resolver/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index e80b84313851..9d5515965a8f 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -63,6 +63,8 @@ obj-$(CONFIG_6LOWPAN) += 6lowpan/
obj-$(CONFIG_IEEE802154) += ieee802154/
obj-$(CONFIG_MAC802154) += mac802154/
obj-$(CONFIG_LORA) += lora/
+obj-$(CONFIG_LORAWAN) += lorawan/
+obj-$(CONFIG_MACLORAWAN) += maclorawan/
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
--
2.19.2
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 <[email protected]>
---
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
net/maclorawan/Kconfig | 14 +
net/maclorawan/Makefile | 2 +
net/maclorawan/mac.c | 518 ++++++++++++++++++++++++++++++++++
net/maclorawan/main.c | 605 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 1139 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..70e6923bf63e
--- /dev/null
+++ b/net/maclorawan/mac.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#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, mic[4];
+ __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, 2), &le_fcnt_up, 2);
+
+ /* Push FCtrl */
+ fctrl = 0;
+ if (lrw_st->rx_should_ack) {
+ fctrl |= 0x20;
+ lrw_st->rx_should_ack = false;
+ }
+ memcpy(skb_push(skb, 1), &fctrl, 1);
+
+ /* 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);
+}
+
+void
+lrw_parse_frame(struct lrw_session *ss, struct sk_buff *skb)
+{
+ 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, 4);
+
+ /* Get frame control */
+ fhdr->fctrl = skb->data[0];
+ skb_pull(skb, 1);
+
+ /* 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, 2);
+
+ /* Get frame options */
+ fhdr->fopts_len = fhdr->fctrl & 0xF;
+ if (fhdr->fopts_len > 0) {
+ memcpy(fhdr->fopts, skb->data, fhdr->fopts_len);
+ skb_pull(skb, fhdr->fopts_len);
+ }
+
+ /* TODO: Parse frame options */
+
+ /* Remove message integrity code */
+ skb_trim(skb, skb->len - LRW_MIC_LEN);
+}
+
+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;
+
+ netdev_dbg(lrw_st->ndev, "%s\n", __func__);
+
+ p_fcnt = (__le16 *)(rx_skb->data + 6);
+ 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 = lrw_st->rx_skb_list.next;
+ 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;
+
+ lrw_parse_frame(ss, skb);
+
+ /* 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;
+
+ if (ss->rx_skb->len > 0) {
+ spin_lock_bh(&ss->state_lock);
+ ss->state = LRW_RXRECEIVED_SS;
+ spin_unlock_bh(&ss->state_lock);
+
+ lrw_get_mac_cb(skb)->devaddr = lrw_st->devaddr;
+ 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 */
+ kfree_skb(skb);
+}
+
+int
+lrw_check_mic(struct crypto_shash *tfm, struct sk_buff *skb)
+{
+ u32 devaddr;
+ size_t len;
+ u8 cks[4];
+ u16 fcnt;
+ u8 *buf;
+ u8 *mic;
+
+ buf = skb->data;
+ devaddr = le32_to_cpu(*((__le32 *)(buf + LRW_MHDR_LEN)));
+ fcnt = le16_to_cpu(*(__le16 *)(buf + 6));
+ len = skb->len - 4;
+ mic = skb->data + len;
+
+ lrw_calc_mic(tfm, LRW_DOWNLINK, devaddr, fcnt, buf, len, cks);
+
+ return (!memcmp(cks, mic, 4));
+}
+
+/**
+ * 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__);
+
+ 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)) {
+ skb_queue_tail(&lrw_st->rx_skb_list, skb);
+ schedule_work(&lrw_st->rx_work);
+ } else {
+ 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..49beed5111b8
--- /dev/null
+++ b/net/maclorawan/main.c
@@ -0,0 +1,605 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#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);
+
+ 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, <[email protected]>");
+MODULE_DESCRIPTION("LoRaWAN soft MAC kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
--
2.19.2
Add public LoRaWAN API for compatible LoRa device drivers.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Merge the lrw_operations: set_bw, set_mod, set_sf into set_dr
- Use SPDX license identifiers
V3:
- Remove the unused lrw_random_addr function
V4:
- Fix by coding style report from scripts/checkpatch.pl
include/linux/lora/lorawan.h | 131 +++++++++++++++++++++++++++++++++++
1 file changed, 131 insertions(+)
create mode 100644 include/linux/lora/lorawan.h
diff --git a/include/linux/lora/lorawan.h b/include/linux/lora/lorawan.h
new file mode 100644
index 000000000000..201f27521655
--- /dev/null
+++ b/include/linux/lora/lorawan.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN compatible hardware's definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_H__
+#define __LORAWAN_H__
+
+#include <linux/skbuff.h>
+
+/* List the role of the LoRaWAN hardware */
+enum {
+ LRW_GATEWAY,
+ LRW_CLASS_A_NODE,
+ LRW_CLASS_B_NODE,
+ LRW_CLASS_C_NODE,
+};
+
+/* List the RF modes */
+enum {
+ LRW_LORA,
+ LRW_FSK,
+};
+
+/**
+ * lrw_dr - This structure holds the RF related configuration of the data rate.
+ * @bw:
+ * Bandwidth in Hz
+ *
+ * @sf:
+ * Spread factor of CSS modulation used by LoRa mode
+ *
+ * @mode:
+ * LoRa or FSK mode
+ */
+struct lrw_dr {
+ u32 bw;
+ u8 sf;
+ u8 mode;
+};
+
+#define LRW_DEVADDR_LEN (sizeof(__le32))
+
+/* List the LoRa device's states of LoRaWAN hardware */
+enum {
+ LRW_STOP,
+ LRW_START,
+ LRW_STATE_IDLE,
+ LRW_STATE_TX,
+ LRW_STATE_RX,
+};
+
+/**
+ * lrw_hw - This structure holds the LoRa device of LoRaWAN hardware.
+ * @priv:
+ * points to the private data of the LoRa device
+ */
+struct lrw_hw {
+ void *priv;
+};
+
+/**
+ * lrw_operations - Lists the LoRaWAN device/interface's operations.
+ * These are callback functions for the LoRaWAN module. Compatible LoRa device
+ * driver should implement some of them according to the usage. The
+ * unimplemented callback functions must be assigned as NULL.
+ *
+ * @start:
+ * called when the interface is being up state
+ *
+ * @stop:
+ * called when the interface is being down state
+ *
+ * @xmit_async:
+ * called to xmit the data through the interface asynchronously
+ *
+ * @set_txpower:
+ * called to set xmitting RF power in mBm of the interface
+ *
+ * @set_frq:
+ * called to set carrier frequency in Hz of the interface
+ *
+ * @set_dr:
+ * called to set related RF configuration of the LoRaWAN data rate
+ *
+ * @start_rx_window:
+ * called to ask the LoRa device open a receiving window
+ *
+ * @set_state:
+ * called to set the LoRa device's working state
+ */
+struct lrw_operations {
+ int (*start)(struct lrw_hw *hw);
+ void (*stop)(struct lrw_hw *hw);
+
+ int (*xmit_async)(struct lrw_hw *hw, struct sk_buff *skb);
+ int (*set_txpower)(struct lrw_hw *hw, s32 pwr);
+ int (*set_frq)(struct lrw_hw *hw, u32 frq);
+ int (*set_dr)(struct lrw_hw *hw, struct lrw_dr *dr);
+ int (*start_rx_window)(struct lrw_hw *hw, u32 delay);
+ int (*set_state)(struct lrw_hw *hw, u8 state);
+};
+
+struct lrw_hw *lrw_alloc_hw(size_t priv_data_len, struct lrw_operations *ops);
+void lrw_free_hw(struct lrw_hw *hw);
+int lrw_register_hw(struct lrw_hw *hw);
+void lrw_unregister_hw(struct lrw_hw *hw);
+void lrw_rx_irqsave(struct lrw_hw *hw, struct sk_buff *skb);
+void lrw_xmit_complete(struct lrw_hw *hw, struct sk_buff *skb);
+
+void lrw_set_deveui(struct lrw_hw *hw, u64 eui);
+u64 lrw_get_deveui(struct lrw_hw *hw);
+void lrw_set_appeui(struct lrw_hw *hw, u64 eui);
+u64 lrw_get_appeui(struct lrw_hw *hw);
+void lrw_set_devaddr(struct lrw_hw *hw, u32 eui);
+u32 lrw_get_devaddr(struct lrw_hw *hw);
+
+enum {
+ LRW_APPKEY,
+ LRW_NWKSKEY,
+ LRW_APPSKEY,
+};
+
+#define LRW_KEY_LEN 16
+
+int lrw_set_key(struct lrw_hw *hw, u8 type, u8 *key, size_t key_len);
+
+#endif
--
2.19.2
This patch adds a new address/protocol family for LoRaWAN network.
It also implements the the functions and maps to Datagram socket for
LoRaWAN unconfirmed data messages.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Add lorawan_netdev.h header file for network address related
declaration
- Use SPDX license identifiers
V3:
- Change the inline functions to a single line macro or just remove the
decoration word - inline
- Order local variables from longest to shortest line in the functions
- Change mac_cb inline function to lrw_get_mac_cb macro
V4:
- Change lrw_get_mac_cb macro to an inline function
- Use pr_fmt() instead of defining new printing log macros
- Fix by coding style report from scripts/checkpatch.pl
include/linux/lora/lorawan_netdev.h | 52 +++
net/lorawan/Kconfig | 10 +
net/lorawan/Makefile | 2 +
net/lorawan/socket.c | 686 ++++++++++++++++++++++++++++
4 files changed, 750 insertions(+)
create mode 100644 include/linux/lora/lorawan_netdev.h
create mode 100644 net/lorawan/Kconfig
create mode 100644 net/lorawan/Makefile
create mode 100644 net/lorawan/socket.c
diff --git a/include/linux/lora/lorawan_netdev.h b/include/linux/lora/lorawan_netdev.h
new file mode 100644
index 000000000000..5bffb5164f95
--- /dev/null
+++ b/include/linux/lora/lorawan_netdev.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN stack related definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_NET_DEVICE_H__
+#define __LORAWAN_NET_DEVICE_H__
+
+enum {
+ LRW_ADDR_APPEUI,
+ LRW_ADDR_DEVEUI,
+ LRW_ADDR_DEVADDR,
+};
+
+struct lrw_addr_in {
+ int addr_type;
+ union {
+ u64 app_eui;
+ u64 dev_eui;
+ u32 devaddr;
+ };
+};
+
+struct sockaddr_lorawan {
+ sa_family_t family; /* AF_LORAWAN */
+ struct lrw_addr_in addr_in;
+};
+
+/**
+ * lrw_mac_cb - This structure holds the control buffer (cb) of sk_buff
+ *
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ */
+struct lrw_mac_cb {
+ u32 devaddr;
+};
+
+/**
+ * lrw_get_mac_cb - Get the LoRaWAN MAC control buffer of the sk_buff
+ * @skb: the exchanging sk_buff
+ *
+ * Return: the pointer of LoRaWAN MAC control buffer
+ */
+static inline struct lrw_mac_cb *lrw_get_mac_cb(struct sk_buff *skb)
+{
+ return (struct lrw_mac_cb *)skb->cb;
+}
+
+#endif
diff --git a/net/lorawan/Kconfig b/net/lorawan/Kconfig
new file mode 100644
index 000000000000..bf6c9b77573b
--- /dev/null
+++ b/net/lorawan/Kconfig
@@ -0,0 +1,10 @@
+config LORAWAN
+ tristate "LoRaWAN Network support"
+ help
+ LoRaWAN defines low data rate, low power and long range wireless
+ wide area networks. It was designed to organize networks of automation
+ devices, such as sensors, switches and actuators. It can operate
+ multiple kilometers wide.
+
+ Say Y here to compile LoRaWAN support into the kernel or say M to
+ compile it as a module.
diff --git a/net/lorawan/Makefile b/net/lorawan/Makefile
new file mode 100644
index 000000000000..8c923ca6541a
--- /dev/null
+++ b/net/lorawan/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_LORAWAN) += lorawan.o
+lorawan-objs := socket.o
diff --git a/net/lorawan/socket.c b/net/lorawan/socket.c
new file mode 100644
index 000000000000..c1981ef5b067
--- /dev/null
+++ b/net/lorawan/socket.c
@@ -0,0 +1,686 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*-
+ * LoRaWAN stack related definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#define LORAWAN_MODULE_NAME "lorawan"
+
+#define pr_fmt(fmt) LORAWAN_MODULE_NAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/net.h>
+#include <linux/if_arp.h>
+#include <linux/termios.h> /* For TIOCOUTQ/INQ */
+#include <net/sock.h>
+#include <linux/lora/lorawan_netdev.h>
+
+/**
+ * dgram_sock - This structure holds the states of Datagram socket
+ *
+ * @sk: network layer representation of the socket
+ * sk must be the first member of dgram_sock
+ * @src_devaddr: the LoRaWAN device address for this connection
+ * @bound: this socket is bound or not
+ * @connected: this socket is connected to the destination or not
+ * @want_ack: this socket needs to ack for the connection or not
+ */
+struct dgram_sock {
+ struct sock sk;
+ u32 src_devaddr;
+
+ u8 bound:1;
+ u8 connected:1;
+};
+
+static HLIST_HEAD(dgram_head);
+static DEFINE_RWLOCK(dgram_lock);
+
+static struct dgram_sock *
+dgram_sk(const struct sock *sk)
+{
+ return container_of(sk, struct dgram_sock, sk);
+}
+
+static struct net_device *
+lrw_get_dev_by_addr(struct net *net, u32 devaddr)
+{
+ __be32 be_addr = cpu_to_be32(devaddr);
+ struct net_device *ndev = NULL;
+
+ rcu_read_lock();
+ ndev = dev_getbyhwaddr_rcu(net, ARPHRD_LORAWAN, (char *)&be_addr);
+ if (ndev)
+ dev_hold(ndev);
+ rcu_read_unlock();
+
+ return ndev;
+}
+
+static int
+dgram_init(struct sock *sk)
+{
+ return 0;
+}
+
+static void
+dgram_close(struct sock *sk, long timeout)
+{
+ sk_common_release(sk);
+}
+
+static int
+dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
+ struct dgram_sock *ro = dgram_sk(sk);
+ struct net_device *ndev;
+ int ret;
+
+ lock_sock(sk);
+ ro->bound = 0;
+
+ ret = -EINVAL;
+ if (len < sizeof(*addr))
+ goto dgram_bind_end;
+
+ if (addr->family != AF_LORAWAN)
+ goto dgram_bind_end;
+
+ if (addr->addr_in.addr_type != LRW_ADDR_DEVADDR)
+ goto dgram_bind_end;
+
+ pr_debug("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
+ ndev = lrw_get_dev_by_addr(sock_net(sk), addr->addr_in.devaddr);
+ if (!ndev) {
+ ret = -ENODEV;
+ goto dgram_bind_end;
+ }
+ netdev_dbg(ndev, "%s: get ndev\n", __func__);
+
+ if (ndev->type != ARPHRD_LORAWAN) {
+ ret = -ENODEV;
+ goto dgram_bind_end;
+ }
+
+ ro->src_devaddr = addr->addr_in.devaddr;
+ ro->bound = 1;
+ ret = 0;
+ dev_put(ndev);
+ pr_debug("%s: bound address %X\n", __func__, ro->src_devaddr);
+
+dgram_bind_end:
+ release_sock(sk);
+ return ret;
+}
+
+static int
+lrw_dev_hard_header(struct sk_buff *skb, struct net_device *ndev,
+ const u32 src_devaddr, size_t len)
+{
+ /* TODO: Prepare the LoRaWAN sending header here */
+ return 0;
+}
+
+static int
+dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ size_t hlen;
+ size_t tlen;
+ int ret;
+
+ pr_debug("%s: going to send %zu bytes", __func__, size);
+ if (msg->msg_flags & MSG_OOB) {
+ pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
+ return -EOPNOTSUPP;
+ }
+
+ pr_debug("%s: check msg_name\n", __func__);
+ if (!ro->connected && !msg->msg_name)
+ return -EDESTADDRREQ;
+ else if (ro->connected && msg->msg_name)
+ return -EISCONN;
+
+ pr_debug("%s: check bound\n", __func__);
+ if (!ro->bound)
+ ndev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_LORAWAN);
+ else
+ ndev = lrw_get_dev_by_addr(sock_net(sk), ro->src_devaddr);
+
+ if (!ndev) {
+ pr_debug("no dev\n");
+ ret = -ENXIO;
+ goto dgram_sendmsg_end;
+ }
+
+ if (size > ndev->mtu) {
+ netdev_dbg(ndev, "size = %zu, mtu = %u\n", size, ndev->mtu);
+ ret = -EMSGSIZE;
+ goto dgram_sendmsg_end;
+ }
+
+ netdev_dbg(ndev, "%s: create skb\n", __func__);
+ hlen = LL_RESERVED_SPACE(ndev);
+ tlen = ndev->needed_tailroom;
+ skb = sock_alloc_send_skb(sk, hlen + tlen + size,
+ msg->msg_flags & MSG_DONTWAIT,
+ &ret);
+
+ if (!skb)
+ goto dgram_sendmsg_no_skb;
+
+ skb_reserve(skb, hlen);
+ skb_reset_network_header(skb);
+
+ ret = lrw_dev_hard_header(skb, ndev, 0, size);
+ if (ret < 0)
+ goto dgram_sendmsg_no_skb;
+
+ ret = memcpy_from_msg(skb_put(skb, size), msg, size);
+ if (ret > 0)
+ goto dgram_sendmsg_err_skb;
+
+ skb->dev = ndev;
+ skb->protocol = htons(ETH_P_LORAWAN);
+
+ netdev_dbg(ndev, "%s: push skb to xmit queue\n", __func__);
+ ret = dev_queue_xmit(skb);
+ if (ret > 0)
+ ret = net_xmit_errno(ret);
+ netdev_dbg(ndev, "%s: pushed skb to xmit queue with ret=%d\n",
+ __func__, ret);
+ dev_put(ndev);
+
+ return ret ?: size;
+
+dgram_sendmsg_err_skb:
+ kfree_skb(skb);
+dgram_sendmsg_no_skb:
+ dev_put(ndev);
+
+dgram_sendmsg_end:
+ return ret;
+}
+
+static int
+dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+ int noblock, int flags, int *addr_len)
+{
+ DECLARE_SOCKADDR(struct sockaddr_lorawan *, saddr, msg->msg_name);
+ struct sk_buff *skb;
+ size_t copied = 0;
+ int err;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto dgram_recvmsg_end;
+
+ copied = skb->len;
+ if (len < copied) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
+ if (err)
+ goto dgram_recvmsg_done;
+
+ sock_recv_ts_and_drops(msg, sk, skb);
+ if (saddr) {
+ memset(saddr, 0, sizeof(*saddr));
+ saddr->family = AF_LORAWAN;
+ saddr->addr_in.devaddr = lrw_get_mac_cb(skb)->devaddr;
+ *addr_len = sizeof(*saddr);
+ }
+
+ if (flags & MSG_TRUNC)
+ copied = skb->len;
+
+dgram_recvmsg_done:
+ skb_free_datagram(sk, skb);
+
+dgram_recvmsg_end:
+ if (err)
+ return err;
+ return copied;
+}
+
+static int
+dgram_hash(struct sock *sk)
+{
+ pr_debug("%s\n", __func__);
+ write_lock_bh(&dgram_lock);
+ sk_add_node(sk, &dgram_head);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ write_unlock_bh(&dgram_lock);
+
+ return 0;
+}
+
+static void
+dgram_unhash(struct sock *sk)
+{
+ pr_debug("%s\n", __func__);
+ write_lock_bh(&dgram_lock);
+ if (sk_del_node_init(sk))
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ write_unlock_bh(&dgram_lock);
+}
+
+static int
+dgram_connect(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ /* Nodes of LoRaWAN send data to a gateway only, then data is received
+ * and transferred to servers with the gateway's policy.
+ * So, the destination address is not used by nodes.
+ */
+ lock_sock(sk);
+ ro->connected = 1;
+ release_sock(sk);
+
+ return 0;
+}
+
+static int
+dgram_disconnect(struct sock *sk, int flags)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ lock_sock(sk);
+ ro->connected = 0;
+ release_sock(sk);
+
+ return 0;
+}
+
+static int
+dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ struct net_device *ndev = sk->sk_dst_cache->dev;
+ struct sk_buff *skb;
+ int amount;
+ int err;
+
+ netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
+ switch (cmd) {
+ case SIOCOUTQ:
+ amount = sk_wmem_alloc_get(sk);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ case SIOCINQ:
+ amount = 0;
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb) {
+ /* We will only return the amount of this packet
+ * since that is all that will be read.
+ */
+ amount = skb->len;
+ }
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ default:
+ err = -ENOIOCTLCMD;
+ }
+
+ return err;
+}
+
+static int
+dgram_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ int val, len;
+
+ if (level != SOL_LORAWAN)
+ return -EOPNOTSUPP;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ len = min_t(unsigned int, len, sizeof(int));
+
+ switch (optname) {
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+dgram_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ int val;
+ int err;
+
+ err = 0;
+
+ if (optlen < sizeof(int))
+ return -EINVAL;
+
+ if (get_user(val, (int __user *)optval))
+ return -EFAULT;
+
+ lock_sock(sk);
+
+ switch (optname) {
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ release_sock(sk);
+
+ return err;
+}
+
+static struct proto lrw_dgram_prot = {
+ .name = "LoRaWAN",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct dgram_sock),
+ .init = dgram_init,
+ .close = dgram_close,
+ .bind = dgram_bind,
+ .sendmsg = dgram_sendmsg,
+ .recvmsg = dgram_recvmsg,
+ .hash = dgram_hash,
+ .unhash = dgram_unhash,
+ .connect = dgram_connect,
+ .disconnect = dgram_disconnect,
+ .ioctl = dgram_ioctl,
+ .getsockopt = dgram_getsockopt,
+ .setsockopt = dgram_setsockopt,
+};
+
+static int
+lrw_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk) {
+ sock->sk = NULL;
+ sk->sk_prot->close(sk, 0);
+ }
+
+ return 0;
+}
+
+static int
+lrw_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
+ struct sock *sk = sock->sk;
+
+ pr_debug("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
+ if (sk->sk_prot->bind)
+ return sk->sk_prot->bind(sk, uaddr, addr_len);
+
+ return sock_no_bind(sock, uaddr, addr_len);
+}
+
+static int
+lrw_sock_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ if (addr_len < sizeof(uaddr->sa_family))
+ return -EINVAL;
+
+ return sk->sk_prot->connect(sk, uaddr, addr_len);
+}
+
+static int
+lrw_ndev_ioctl(struct sock *sk, struct ifreq __user *arg, unsigned int cmd)
+{
+ struct net_device *ndev;
+ struct ifreq ifr;
+ int ret;
+
+ pr_debug("%s: cmd %ud\n", __func__, cmd);
+ ret = -ENOIOCTLCMD;
+
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
+
+ ifr.ifr_name[IFNAMSIZ - 1] = 0;
+
+ dev_load(sock_net(sk), ifr.ifr_name);
+ ndev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
+
+ netdev_dbg(ndev, "%s: cmd %ud\n", __func__, cmd);
+ if (!ndev)
+ return -ENODEV;
+
+ if (ndev->type == ARPHRD_LORAWAN && ndev->netdev_ops->ndo_do_ioctl)
+ ret = ndev->netdev_ops->ndo_do_ioctl(ndev, &ifr, cmd);
+
+ if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ ret = -EFAULT;
+ dev_put(ndev);
+
+ return ret;
+}
+
+static int
+lrw_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+
+ pr_debug("%s: cmd %ud\n", __func__, cmd);
+ switch (cmd) {
+ case SIOCGSTAMP:
+ return sock_get_timestamp(sk, (struct timeval __user *)arg);
+ case SIOCGSTAMPNS:
+ return sock_get_timestampns(sk, (struct timespec __user *)arg);
+ case SIOCOUTQ:
+ case SIOCINQ:
+ if (!sk->sk_prot->ioctl)
+ return -ENOIOCTLCMD;
+ return sk->sk_prot->ioctl(sk, cmd, arg);
+ default:
+ return lrw_ndev_ioctl(sk, (struct ifreq __user *)arg, cmd);
+ }
+}
+
+static int
+lrw_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+
+ pr_debug("%s: going to send %zu bytes\n", __func__, len);
+ return sk->sk_prot->sendmsg(sk, msg, len);
+}
+
+static const struct proto_ops lrw_dgram_ops = {
+ .family = PF_LORAWAN,
+ .owner = THIS_MODULE,
+ .release = lrw_sock_release,
+ .bind = lrw_sock_bind,
+ .connect = lrw_sock_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = lrw_sock_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = lrw_sock_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static int
+lorawan_creat(struct net *net, struct socket *sock, int protocol, int kern)
+{
+ struct sock *sk;
+ int ret;
+
+ if (!net_eq(net, &init_net))
+ return -EAFNOSUPPORT;
+
+ if (sock->type != SOCK_DGRAM)
+ return -EAFNOSUPPORT;
+
+ /* Allocates enough memory for dgram_sock whose first member is sk */
+ sk = sk_alloc(net, PF_LORAWAN, GFP_KERNEL, &lrw_dgram_prot, kern);
+ if (!sk)
+ return -ENOMEM;
+
+ sock->ops = &lrw_dgram_ops;
+ sock_init_data(sock, sk);
+ sk->sk_family = PF_LORAWAN;
+ sock_set_flag(sk, SOCK_ZAPPED);
+
+ if (sk->sk_prot->hash) {
+ ret = sk->sk_prot->hash(sk);
+ if (ret) {
+ sk_common_release(sk);
+ goto lorawan_creat_end;
+ }
+ }
+
+ if (sk->sk_prot->init) {
+ ret = sk->sk_prot->init(sk);
+ if (ret)
+ sk_common_release(sk);
+ }
+
+lorawan_creat_end:
+ return ret;
+}
+
+static const struct net_proto_family lorawan_family_ops = {
+ .owner = THIS_MODULE,
+ .family = PF_LORAWAN,
+ .create = lorawan_creat,
+};
+
+static int
+lrw_dgram_deliver(struct net_device *ndev, struct sk_buff *skb)
+{
+ struct dgram_sock *ro;
+ struct sock *sk;
+ bool found;
+ int ret;
+
+ ret = NET_RX_SUCCESS;
+ found = false;
+
+ read_lock(&dgram_lock);
+ sk_for_each(sk, &dgram_head) {
+ ro = dgram_sk(sk);
+ if (cpu_to_le32(ro->src_devaddr) == *(__le32 *)ndev->dev_addr) {
+ found = true;
+ break;
+ }
+ }
+ read_unlock(&dgram_lock);
+
+ if (!found)
+ goto lrw_dgram_deliver_err;
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NET_RX_DROP;
+
+ if (sock_queue_rcv_skb(sk, skb) < 0)
+ goto lrw_dgram_deliver_err;
+
+ return ret;
+
+lrw_dgram_deliver_err:
+ kfree_skb(skb);
+ ret = NET_RX_DROP;
+ return ret;
+}
+
+static int
+lorawan_rcv(struct sk_buff *skb, struct net_device *ndev,
+ struct packet_type *pt, struct net_device *orig_ndev)
+{
+ if (!netif_running(ndev))
+ goto lorawan_rcv_drop;
+
+ if (!net_eq(dev_net(ndev), &init_net))
+ goto lorawan_rcv_drop;
+
+ if (ndev->type != ARPHRD_LORAWAN)
+ goto lorawan_rcv_drop;
+
+ if (skb->pkt_type != PACKET_OTHERHOST)
+ return lrw_dgram_deliver(ndev, skb);
+
+lorawan_rcv_drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static struct packet_type lorawan_packet_type = {
+ .type = htons(ETH_P_LORAWAN),
+ .func = lorawan_rcv,
+};
+
+static int __init
+lrw_sock_init(void)
+{
+ int ret;
+
+ pr_info("module inserted\n");
+ ret = proto_register(&lrw_dgram_prot, 1);
+ if (ret)
+ goto lrw_sock_init_end;
+
+ /* Tell SOCKET that we are alive */
+ ret = sock_register(&lorawan_family_ops);
+ if (ret)
+ goto lrw_sock_init_err;
+
+ dev_add_pack(&lorawan_packet_type);
+ ret = 0;
+ goto lrw_sock_init_end;
+
+lrw_sock_init_err:
+ proto_unregister(&lrw_dgram_prot);
+
+lrw_sock_init_end:
+ return 0;
+}
+
+static void __exit
+lrw_sock_exit(void)
+{
+ dev_remove_pack(&lorawan_packet_type);
+ sock_unregister(PF_LORAWAN);
+ proto_unregister(&lrw_dgram_prot);
+ pr_info("module removed\n");
+}
+
+module_init(lrw_sock_init);
+module_exit(lrw_sock_exit);
+
+MODULE_AUTHOR("Jian-Hong Pan, <[email protected]>");
+MODULE_DESCRIPTION("LoRaWAN socket kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS_NETPROTO(PF_LORAWAN);
--
2.19.2
Implement the crypto for encryption/decryption and message integrity
code (MIC) according to LoRaWAN(TM) Specification Ver. 1.0.2.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Rename the lrwsec files to crypto files
- Modify for Big/Little-Endian
- Use SPDX license identifiers
V3:
- Order local variables from longest to shortest line in the functions
V4:
- Fix by coding style report from scripts/checkpatch.pl
net/maclorawan/crypto.c | 212 ++++++++++++++++++++++++++++++++++++++++
net/maclorawan/crypto.h | 27 +++++
2 files changed, 239 insertions(+)
create mode 100644 net/maclorawan/crypto.c
create mode 100644 net/maclorawan/crypto.h
diff --git a/net/maclorawan/crypto.c b/net/maclorawan/crypto.c
new file mode 100644
index 000000000000..0ae9d211cd14
--- /dev/null
+++ b/net/maclorawan/crypto.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#include <linux/scatterlist.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include "crypto.h"
+
+struct crypto_shash *
+lrw_mic_key_setup(u8 *k, size_t k_len)
+{
+ struct crypto_shash *tfm;
+ char *algo = "cmac(aes)";
+ int err;
+
+ tfm = crypto_alloc_shash(algo, 0, 0);
+ if (!IS_ERR(tfm)) {
+ err = crypto_shash_setkey(tfm, k, k_len);
+ if (err) {
+ crypto_free_shash(tfm);
+ tfm = NULL;
+ }
+ }
+
+ return tfm;
+}
+
+int
+lrw_aes_cmac(struct crypto_shash *tfm, u8 *bz, u8 *data, size_t len, u8 *out)
+{
+ SHASH_DESC_ON_STACK(desc, tfm);
+ int err;
+
+ desc->tfm = tfm;
+
+ err = crypto_shash_init(desc);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_update(desc, bz, 16);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_update(desc, data, len);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_final(desc, out);
+
+lrw_aes_cmac_end:
+ return err;
+}
+
+int
+lrw_set_bzero(u8 dir, u32 devaddr, u32 fcnt, u8 len, u8 *bz)
+{
+ __le32 le_devaddr = cpu_to_le32(devaddr);
+ __le32 le_fcnt = cpu_to_le32(fcnt);
+
+ bz[0] = 0x49;
+ memset(bz + 1, 0x00, 4);
+ bz[5] = dir;
+ memcpy(bz + 6, &le_devaddr, 4);
+ memcpy(bz + 10, &le_fcnt, 4);
+ bz[14] = 0x00;
+ bz[15] = len;
+
+ return 0;
+}
+
+int
+lrw_calc_mic(struct crypto_shash *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len, u8 *mic4)
+{
+ u8 mic[16];
+ u8 bz[16];
+ int err;
+
+ /* According to LoRaWAN Specification Version 1.0.2
+ * - 4.4 Massege Integrity Code (MIC)
+ */
+ lrw_set_bzero(dir, devaddr, fcnt, len, bz);
+ err = lrw_aes_cmac(tfm, bz, buf, len, mic);
+ if (!err)
+ memcpy(mic4, mic, 4);
+
+ return err;
+}
+
+void
+lrw_mic_key_free(struct crypto_shash *tfm)
+{
+ crypto_free_shash(tfm);
+}
+
+struct crypto_sync_skcipher *
+lrw_aes_enc_key_setup(char *algo, u8 *k, size_t k_len)
+{
+ struct crypto_sync_skcipher *tfm;
+ int err;
+
+ tfm = crypto_alloc_sync_skcipher(algo, 0, CRYPTO_ALG_ASYNC);
+ if (!IS_ERR(tfm)) {
+ err = crypto_sync_skcipher_setkey(tfm, k, k_len);
+ if (err) {
+ crypto_free_sync_skcipher(tfm);
+ tfm = NULL;
+ }
+ }
+
+ return tfm;
+}
+
+struct crypto_sync_skcipher *
+lrw_encrypt_key_setup(u8 *k, size_t k_len)
+{
+ return lrw_aes_enc_key_setup("cbc(aes)", k, k_len);
+}
+
+int
+lrw_aes_enc(struct crypto_sync_skcipher *tfm, u8 *in, size_t len, u8 *out)
+{
+ SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
+ struct scatterlist src, dst;
+ u8 iv[16];
+ int err;
+
+ memset(iv, 0, 16);
+ /* The buffer for sg_init_one cannot be a global or const local
+ * (will confuse the scatterlist)
+ */
+ sg_init_one(&src, in, len);
+ sg_init_one(&dst, out, len);
+
+ skcipher_request_set_sync_tfm(req, tfm);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+ skcipher_request_set_crypt(req, &src, &dst, len, iv);
+ err = crypto_skcipher_encrypt(req);
+ skcipher_request_zero(req);
+
+ return err;
+}
+
+#define LRW_SEQUENCE_OF_BLOCK_LEN 16
+
+int
+lrw_set_sob(u8 dir, u32 devaddr, u32 fcnt, u8 index, u8 *sob)
+{
+ __le32 le_devaddr = cpu_to_le32(devaddr);
+ __le32 _fcnt = cpu_to_le32(fcnt);
+
+ sob[0] = 0x01;
+ memset(sob + 1, 0x00, 4);
+ sob[5] = dir;
+ memcpy(sob + 6, &le_devaddr, 4);
+ memcpy(sob + 10, &_fcnt, 4);
+ sob[14] = 0x00;
+ sob[15] = index;
+
+ return 0;
+}
+
+int
+lrw_encrypt_sob(struct crypto_sync_skcipher *tfm, u8 *sob)
+{
+ return lrw_aes_enc(tfm, sob, LRW_SEQUENCE_OF_BLOCK_LEN, sob);
+}
+
+int
+lrw_encrypt_buf(struct crypto_sync_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len)
+{
+ u8 sob[LRW_SEQUENCE_OF_BLOCK_LEN];
+ u8 i, j;
+
+ /* According to LoRaWAN Specification Version 1.0.2
+ * - 4.3.3 MAC Frame Payload Encryption (FRMPayload)
+ */
+ for (i = 0; (i * LRW_SEQUENCE_OF_BLOCK_LEN) < len; i++) {
+ lrw_set_sob(dir, devaddr, fcnt, i, sob);
+ lrw_encrypt_sob(tfm, sob);
+ for (j = 0; (i * LRW_SEQUENCE_OF_BLOCK_LEN + j) < len; j++)
+ buf[i * LRW_SEQUENCE_OF_BLOCK_LEN + j] ^= sob[j];
+ }
+
+ return 0;
+}
+
+int
+lrw_decrypt_buf(struct crypto_sync_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len)
+{
+ /* According to XOR swap algorithm */
+ return lrw_encrypt_buf(tfm, dir, devaddr, fcnt, buf, len);
+}
+
+void
+lrw_aes_enc_key_free(struct crypto_sync_skcipher *tfm)
+{
+ crypto_free_sync_skcipher(tfm);
+}
+
+void
+lrw_encrypt_key_free(struct crypto_sync_skcipher *tfm)
+{
+ lrw_aes_enc_key_free(tfm);
+}
diff --git a/net/maclorawan/crypto.h b/net/maclorawan/crypto.h
new file mode 100644
index 000000000000..22dd810040c0
--- /dev/null
+++ b/net/maclorawan/crypto.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_CRYPTO_H__
+#define __LORAWAN_CRYPTO_H__
+
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+
+struct crypto_shash *lrw_mic_key_setup(u8 *k, size_t k_len);
+int lrw_calc_mic(struct crypto_shash *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len, u8 *mic4);
+void lrw_mic_key_free(struct crypto_shash *tfm);
+
+struct crypto_sync_skcipher *lrw_encrypt_key_setup(u8 *k, size_t k_len);
+int lrw_encrypt_buf(struct crypto_sync_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len);
+int lrw_decrypt_buf(struct crypto_sync_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len);
+void lrw_encrypt_key_free(struct crypto_sync_skcipher *tfm);
+
+#endif
--
2.19.2
Add the maclorawan header file for common APIs in the module.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Use SPDX license identifiers
V4:
- Fix typo in comments
- Fix by coding style report from scripts/checkpatch.pl
net/maclorawan/maclorawan.h | 200 ++++++++++++++++++++++++++++++++++++
1 file changed, 200 insertions(+)
create mode 100644 net/maclorawan/maclorawan.h
diff --git a/net/maclorawan/maclorawan.h b/net/maclorawan/maclorawan.h
new file mode 100644
index 000000000000..572c26c7cf40
--- /dev/null
+++ b/net/maclorawan/maclorawan.h
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __MAC_LORAWAN_H__
+#define __MAC_LORAWAN_H__
+
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <uapi/linux/if_arp.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include <linux/lora/lorawan.h>
+
+#define LORAWAN_MODULE_NAME "maclorawan"
+
+/* List the message types of LoRaWAN */
+enum {
+ LRW_JOIN_REQUEST,
+ LRW_JOIN_ACCEPT,
+ LRW_UNCONFIRMED_DATA_UP,
+ LRW_UNCONFIRMED_DATA_DOWN,
+ LRW_CONFIRMED_DATA_UP,
+ LRW_CONFIRMED_DATA_DOWN,
+ LRW_RFU,
+ LRW_PROPRIETARY,
+};
+
+/* List the communication directions */
+enum {
+ LRW_UPLINK,
+ LRW_DOWNLINK,
+};
+
+/* States of LoRaWAN slot timing */
+enum {
+ LRW_INIT_SS,
+ LRW_XMITTING_SS,
+ LRW_XMITTED,
+ LRW_RX1_SS,
+ LRW_RX2_SS,
+ LRW_RXTIMEOUT_SS,
+ LRW_RXRECEIVED_SS,
+ LRW_RETRANSMIT_SS,
+};
+
+#define LRW_MHDR_LEN 1
+#define LRW_FHDR_MAX_LEN 22
+#define LRW_FOPS_MAX_LEN 15
+#define LRW_FPORT_LEN 1
+#define LRW_MIC_LEN 4
+
+/**
+ * lrw_fhdr - Hold the message's basic information of the frame
+ *
+ * @mtype: this message's type
+ * @fctrl: the frame control byte
+ * @fcnt: this message's frame counter value
+ * @fopts: this frame's options field
+ * @fopts_len: the length of the fopts
+ */
+struct lrw_fhdr {
+ u8 mtype;
+ u8 fctrl;
+ u16 fcnt;
+ u8 fopts[LRW_FPORT_LEN];
+ u8 fopts_len;
+};
+
+/**
+ * lrw_session - LoRaWAN session for Class A end device
+ *
+ * @lrw_st: points to the belonging lrw_st
+ * @entry: the entry of the ss_list in lrw_struct
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ * @fcnt_up: uplink frame counter
+ * @fcnt_down: downlink frame counter
+ * @fport: the LoRaWAN data message's port field
+ * @tx_skb: points to the TX skb, the frame
+ * @rx_skb: points to the RX skb, the frame
+ * @tx_fhdr: hold the message's basic information of the TX frame
+ * @rx_fhdr: hold the message's basic information of the RX frame
+ * @tx_should_ack: flag for determining the TX which should be acked or not
+ * @retry: retry times for xmitting failed
+ * @state: this session's current state
+ * @state_lock: lock of the session's state
+ * @timer: timing for this session and the state transition
+ * @timeout_work: work if waiting acknowledge time out
+ * @rx_delay1: RX1 delay time in seconds
+ * @rx_delay2: RX2 delay time in seconds
+ * @rx1_window: RX1 window opening time in milliseconds
+ * @rx2_window: RX2 window opening time in milliseconds
+ * @ack_timeout: time out time for waiting acknowledge in seconds
+ */
+struct lrw_session {
+ struct lrw_struct *lrw_st;
+ struct list_head entry;
+
+ u32 devaddr;
+ u16 fcnt_up;
+ u16 fcnt_down;
+ u8 fport;
+ struct sk_buff *tx_skb;
+ struct sk_buff *rx_skb;
+ struct lrw_fhdr tx_fhdr;
+ struct lrw_fhdr rx_fhdr;
+
+ u8 tx_should_ack;
+ u8 retry;
+ u8 state;
+ spinlock_t state_lock; /* lock of the session's state */
+
+ struct timer_list timer;
+ struct work_struct timeout_work;
+ unsigned long rx_delay1;
+ unsigned long rx_delay2;
+ unsigned long rx1_window;
+ unsigned long rx2_window;
+ unsigned long ack_timeout;
+};
+
+/**
+ * lrw_struct - The full LoRaWAN hardware to the LoRa device.
+ *
+ * @dev: this LoRa device registed in system
+ * @hw: the LoRa device of this LoRaWAN hardware
+ * @ops: handle of LoRa operations interfaces
+ * @rx_skb_list: the list of received frames
+ * @ss_list: LoRaWAN session list of this LoRaWAN hardware
+ * @ss_list_lock: lock of the session list
+ * @_cur_ss: pointer of the current processing session
+ * @rx_should_ack: represent the current session should be acked or not
+ * @state: the state of this LoRaWAN hardware
+ * @app_eui: the LoRaWAN application EUI
+ * @dev_eui: the LoRaWAN device EUI
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ * @appky: the Application key
+ * @nwkskey: the Network session key
+ * @appskey: the Application session key
+ * @nwks_shash_tfm: the hash handler for LoRaWAN network session
+ * @nwks_skc_tfm: the crypto handler for LoRaWAN network session
+ * @apps_skc_tfm: the crypto handler for LoRaWAN application session
+ * @fcnt_up: the counter of this LoRaWAN hardware's up frame
+ * @fcnt_down: the counter of this LoRaWAN hardware's down frame
+ * @xmit_task: the xmit task for the current LoRaWAN session
+ * @rx_work: the RX work in workqueue for the current LoRaWAN session
+ * @ndev: points to the emulating network device
+ * @_net: the current network namespace of this LoRaWAN hardware
+ */
+struct lrw_struct {
+ struct device dev;
+ struct lrw_hw hw;
+ struct lrw_operations *ops;
+
+ struct sk_buff_head rx_skb_list;
+ struct list_head ss_list;
+ struct mutex ss_list_lock; /* lock of the session list */
+ struct lrw_session *_cur_ss;
+ u8 rx_should_ack;
+ u8 state;
+
+ u64 app_eui;
+ u64 dev_eui;
+ u32 devaddr;
+ u8 appkey[LRW_KEY_LEN];
+ u8 nwkskey[LRW_KEY_LEN];
+ u8 appskey[LRW_KEY_LEN];
+ struct crypto_shash *nwks_shash_tfm;
+ struct crypto_sync_skcipher *nwks_skc_tfm;
+ struct crypto_sync_skcipher *apps_skc_tfm;
+
+ u16 fcnt_up;
+ u16 fcnt_down;
+
+ struct tasklet_struct xmit_task;
+ struct work_struct rx_work;
+
+ struct net_device *ndev;
+ possible_net_t _net;
+};
+
+#define NETDEV_2_LRW(ndev) ((struct lrw_struct *)netdev_priv(ndev))
+
+struct lrw_session *lrw_alloc_ss(struct lrw_struct *lrw_st);
+void lrw_free_ss(struct lrw_session *ss);
+void lrw_del_ss(struct lrw_session *ss);
+int lrw_start_hw(struct lrw_struct *lrw_st);
+void lrw_stop_hw(struct lrw_struct *lrw_st);
+void lrw_prepare_tx_frame(struct lrw_session *ss);
+void lrw_xmit(unsigned long data);
+void lrw_rx_work(struct work_struct *work);
+
+#endif
--
2.19.2
> +void
> +lrw_parse_frame(struct lrw_session *ss, struct sk_buff *skb)
> +{
> + 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);
This does not seem robust. There is no point at which you actually check
the message size is valid etc
> + fhdr->fopts_len = fhdr->fctrl & 0xF;
> + if (fhdr->fopts_len > 0) {
> + memcpy(fhdr->fopts, skb->data, fhdr->fopts_len);
> + skb_pull(skb, fhdr->fopts_len);
> + }
In fact you appear to copy random kernel memory into a buffer
> +
> + /* TODO: Parse frame options */
> +
> + /* Remove message integrity code */
> + skb_trim(skb, skb->len - LRW_MIC_LEN);
and then try and trim the buffer to a negative size ?
Alan
I made a fake skb and passed it to lrw_parse_frame() function for
testing. I use print_hex_dump() function to show the skb's content.
Here is the original content in the skb->data and the length is 20 bytes.
[ 33.732033] 00000000: 40 04 03 02 01 00 00 00 00 27 76 d3 2d 1b 79
a0 @........'v.-.y.
[ 33.732065] 00000010: 18 38 fb a6 .8..
Byte 0: MHDR field, value is 0x40.
Byte 1 ~ 4: DevAddr field, value is 0x04 0x03 0x02 0x01.
Byte 5: FCtrl field, value is 0x00.
Byte 6 ~ 7: FCnt field, value is 0x00 0x00.
Byte 8: FPort field, value is 0x00.
Byte 9 ~ 15: Encrypted payload
Byte 16 ~ 19: MIC field value is 0x18 0x38 0xfb 0xa6.
> > +void
> > +lrw_parse_frame(struct lrw_session *ss, struct sk_buff *skb)
> > +{
> > + 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);
print_hex_dump skb here:
[ 33.732202] 00000000: 04 03 02 01 00 00 00 00 27 76 d3 2d 1b 79 a0
18 ........'v.-.y..
[ 33.732204] 00000010: 38 fb a6
> This does not seem robust. There is no point at which you actually check
> the message size is valid etc
Thanks! It is a potential bug.
It should check skb->len >= length of MHDR + DevAddr + FCtrl + FCnt + MIC.
These are required fields for (Un)confirmed Data Up/Down messages.
print_hex_dump skb here:
[ 33.732211] 00000000: 00 27 76 d3 2d 1b 79 a0 18 38 fb a6
.'v.-.y..8..
> > + fhdr->fopts_len = fhdr->fctrl & 0xF;
> > + if (fhdr->fopts_len > 0) {
> > + memcpy(fhdr->fopts, skb->data, fhdr->fopts_len);
> > + skb_pull(skb, fhdr->fopts_len);
> > + }
print_hex_dump skb here:
[ 33.732213] 00000000: 00 27 76 d3 2d 1b 79 a0 18 38 fb a6
.'v.-.y..8..
> In fact you appear to copy random kernel memory into a buffer
It copied fhdr->fopts_len bytes from skb->data to fhdr->fopts if
fhdr->fopts_len > 0.
https://www.kernel.org/doc/html/latest/core-api/kernel-api.html?highlight=memcpy#c.memcpy
> > +
> > + /* TODO: Parse frame options */
> > +
> > + /* Remove message integrity code */
> > + skb_trim(skb, skb->len - LRW_MIC_LEN);
print_hex_dump skb here:
[ 33.732216] 00000000: 00 27 76 d3 2d 1b 79 a0
.'v.-.y.
> and then try and trim the buffer to a negative size ?
It removed 4 tail bytes (MIC). (skb->len - LRW_MIC_LEN) is the final
new length as skb_trim()'s 2nd argument len.
https://www.kernel.org/doc/html/latest/networking/kapi.html?highlight=skb_trim#c.skb_trim
I found another bug which did not initialize rx_skb_list. So,
lrw_parse_frame() may be passed a mystery skb.
Please keep reviewing. That is appreciated.
Thank you,
Jian-Hong Pan
LoRaWAN(TM) is the MAC layer defined by LoRa Alliance(TM) over LoRa
devices. LoRa is one of Low-Power Wide-Area Network (LPWAN) technology.
LoRaWAN networks typically are laid out in a star-of-stars topology in
which gateways relay messages between end-devices and a central network
server at the backend. Gateways are connected to the network server via
standard IP connections while end-devices use single hop LoRa(TM) or FSK
communication to one or many gateways.
A LoRa network distinguishes between a basic LoRaWAN (named Class A) and
optional features (Class B, Class C ...):
* Bi-directional end-devices (Class A)
* Bi-directional end-devices with scheduled receive slots (Class B)
* Bi-directional end-devices with maximal receive slots (Class C)
This patch set add LoRaWAN class module implementing the stack,
especially the soft MAC, between socket APIs and LoRa device drivers.
socket APIs:
send and receive the data
------------------------------------------------------------------------
LoRaWAN class module implements soft MAC:
append the header/footer, encryption/decryption, timing slot and MAC
commands
------------------------------------------------------------------------
LoRa device drivers:
send and receive the messages for MAC layer
------------------------------------------------------------------------
LoRa devices
This module starts from simple and implements partial Class A
end-devices features defined in LoRaWAN(TM) Specification Ver. 1.0.2.
More features and complexity, for example regional parameters, confirmed
data messages, join request/accept messages for Over-The-Air Activation,
MAC commands ... will be added in the future.
Jian-Hong Pan (6):
net: lorawan: Add LoRaWAN socket module
net: lorawan: Add LoRaWAN API declaration for LoRa devices
net: maclorawan: Add maclorawan module declaration
net: maclorawan: Implement the crypto of maclorawan module
net: maclorawan: Implement maclorawan class module
net: lorawan: List LORAWAN in menuconfig
include/linux/lora/lorawan.h | 131 ++++++
include/linux/lora/lorawan_netdev.h | 52 +++
net/Kconfig | 2 +
net/Makefile | 2 +
net/lorawan/Kconfig | 10 +
net/lorawan/Makefile | 2 +
net/lorawan/socket.c | 686 ++++++++++++++++++++++++++++
net/maclorawan/Kconfig | 14 +
net/maclorawan/Makefile | 2 +
net/maclorawan/crypto.c | 212 +++++++++
net/maclorawan/crypto.h | 27 ++
net/maclorawan/mac.c | 555 ++++++++++++++++++++++
net/maclorawan/maclorawan.h | 202 ++++++++
net/maclorawan/main.c | 606 ++++++++++++++++++++++++
14 files changed, 2503 insertions(+)
create mode 100644 include/linux/lora/lorawan.h
create mode 100644 include/linux/lora/lorawan_netdev.h
create mode 100644 net/lorawan/Kconfig
create mode 100644 net/lorawan/Makefile
create mode 100644 net/lorawan/socket.c
create mode 100644 net/maclorawan/Kconfig
create mode 100644 net/maclorawan/Makefile
create mode 100644 net/maclorawan/crypto.c
create mode 100644 net/maclorawan/crypto.h
create mode 100644 net/maclorawan/mac.c
create mode 100644 net/maclorawan/maclorawan.h
create mode 100644 net/maclorawan/main.c
--
2.20.0
This patch adds a new address/protocol family for LoRaWAN network.
It also implements the the functions and maps to Datagram socket for
LoRaWAN unconfirmed data messages.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Add lorawan_netdev.h header file for network address related
declaration
- Use SPDX license identifiers
V3:
- Change the inline functions to a single line macro or just remove the
decoration word - inline
- Order local variables from longest to shortest line in the functions
- Change mac_cb inline function to lrw_get_mac_cb macro
V4:
- Change lrw_get_mac_cb macro to an inline function
- Use pr_fmt() instead of defining new printing log macros
- Fix by coding style report from scripts/checkpatch.pl
V5:
- Fix the device address comparing bug which is caused by endianness
include/linux/lora/lorawan_netdev.h | 52 +++
net/lorawan/Kconfig | 10 +
net/lorawan/Makefile | 2 +
net/lorawan/socket.c | 686 ++++++++++++++++++++++++++++
4 files changed, 750 insertions(+)
create mode 100644 include/linux/lora/lorawan_netdev.h
create mode 100644 net/lorawan/Kconfig
create mode 100644 net/lorawan/Makefile
create mode 100644 net/lorawan/socket.c
diff --git a/include/linux/lora/lorawan_netdev.h b/include/linux/lora/lorawan_netdev.h
new file mode 100644
index 000000000000..5bffb5164f95
--- /dev/null
+++ b/include/linux/lora/lorawan_netdev.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN stack related definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_NET_DEVICE_H__
+#define __LORAWAN_NET_DEVICE_H__
+
+enum {
+ LRW_ADDR_APPEUI,
+ LRW_ADDR_DEVEUI,
+ LRW_ADDR_DEVADDR,
+};
+
+struct lrw_addr_in {
+ int addr_type;
+ union {
+ u64 app_eui;
+ u64 dev_eui;
+ u32 devaddr;
+ };
+};
+
+struct sockaddr_lorawan {
+ sa_family_t family; /* AF_LORAWAN */
+ struct lrw_addr_in addr_in;
+};
+
+/**
+ * lrw_mac_cb - This structure holds the control buffer (cb) of sk_buff
+ *
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ */
+struct lrw_mac_cb {
+ u32 devaddr;
+};
+
+/**
+ * lrw_get_mac_cb - Get the LoRaWAN MAC control buffer of the sk_buff
+ * @skb: the exchanging sk_buff
+ *
+ * Return: the pointer of LoRaWAN MAC control buffer
+ */
+static inline struct lrw_mac_cb *lrw_get_mac_cb(struct sk_buff *skb)
+{
+ return (struct lrw_mac_cb *)skb->cb;
+}
+
+#endif
diff --git a/net/lorawan/Kconfig b/net/lorawan/Kconfig
new file mode 100644
index 000000000000..bf6c9b77573b
--- /dev/null
+++ b/net/lorawan/Kconfig
@@ -0,0 +1,10 @@
+config LORAWAN
+ tristate "LoRaWAN Network support"
+ help
+ LoRaWAN defines low data rate, low power and long range wireless
+ wide area networks. It was designed to organize networks of automation
+ devices, such as sensors, switches and actuators. It can operate
+ multiple kilometers wide.
+
+ Say Y here to compile LoRaWAN support into the kernel or say M to
+ compile it as a module.
diff --git a/net/lorawan/Makefile b/net/lorawan/Makefile
new file mode 100644
index 000000000000..8c923ca6541a
--- /dev/null
+++ b/net/lorawan/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_LORAWAN) += lorawan.o
+lorawan-objs := socket.o
diff --git a/net/lorawan/socket.c b/net/lorawan/socket.c
new file mode 100644
index 000000000000..7ef106b877ca
--- /dev/null
+++ b/net/lorawan/socket.c
@@ -0,0 +1,686 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*-
+ * LoRaWAN stack related definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#define LORAWAN_MODULE_NAME "lorawan"
+
+#define pr_fmt(fmt) LORAWAN_MODULE_NAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/net.h>
+#include <linux/if_arp.h>
+#include <linux/termios.h> /* For TIOCOUTQ/INQ */
+#include <net/sock.h>
+#include <linux/lora/lorawan_netdev.h>
+
+/**
+ * dgram_sock - This structure holds the states of Datagram socket
+ *
+ * @sk: network layer representation of the socket
+ * sk must be the first member of dgram_sock
+ * @src_devaddr: the LoRaWAN device address for this connection
+ * @bound: this socket is bound or not
+ * @connected: this socket is connected to the destination or not
+ * @want_ack: this socket needs to ack for the connection or not
+ */
+struct dgram_sock {
+ struct sock sk;
+ u32 src_devaddr;
+
+ u8 bound:1;
+ u8 connected:1;
+};
+
+static HLIST_HEAD(dgram_head);
+static DEFINE_RWLOCK(dgram_lock);
+
+static struct dgram_sock *
+dgram_sk(const struct sock *sk)
+{
+ return container_of(sk, struct dgram_sock, sk);
+}
+
+static struct net_device *
+lrw_get_dev_by_addr(struct net *net, u32 devaddr)
+{
+ __be32 be_addr = cpu_to_be32(devaddr);
+ struct net_device *ndev = NULL;
+
+ rcu_read_lock();
+ ndev = dev_getbyhwaddr_rcu(net, ARPHRD_LORAWAN, (char *)&be_addr);
+ if (ndev)
+ dev_hold(ndev);
+ rcu_read_unlock();
+
+ return ndev;
+}
+
+static int
+dgram_init(struct sock *sk)
+{
+ return 0;
+}
+
+static void
+dgram_close(struct sock *sk, long timeout)
+{
+ sk_common_release(sk);
+}
+
+static int
+dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
+ struct dgram_sock *ro = dgram_sk(sk);
+ struct net_device *ndev;
+ int ret;
+
+ lock_sock(sk);
+ ro->bound = 0;
+
+ ret = -EINVAL;
+ if (len < sizeof(*addr))
+ goto dgram_bind_end;
+
+ if (addr->family != AF_LORAWAN)
+ goto dgram_bind_end;
+
+ if (addr->addr_in.addr_type != LRW_ADDR_DEVADDR)
+ goto dgram_bind_end;
+
+ pr_debug("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
+ ndev = lrw_get_dev_by_addr(sock_net(sk), addr->addr_in.devaddr);
+ if (!ndev) {
+ ret = -ENODEV;
+ goto dgram_bind_end;
+ }
+ netdev_dbg(ndev, "%s: get ndev\n", __func__);
+
+ if (ndev->type != ARPHRD_LORAWAN) {
+ ret = -ENODEV;
+ goto dgram_bind_end;
+ }
+
+ ro->src_devaddr = addr->addr_in.devaddr;
+ ro->bound = 1;
+ ret = 0;
+ dev_put(ndev);
+ pr_debug("%s: bound address %X\n", __func__, ro->src_devaddr);
+
+dgram_bind_end:
+ release_sock(sk);
+ return ret;
+}
+
+static int
+lrw_dev_hard_header(struct sk_buff *skb, struct net_device *ndev,
+ const u32 src_devaddr, size_t len)
+{
+ /* TODO: Prepare the LoRaWAN sending header here */
+ return 0;
+}
+
+static int
+dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ size_t hlen;
+ size_t tlen;
+ int ret;
+
+ pr_debug("%s: going to send %zu bytes", __func__, size);
+ if (msg->msg_flags & MSG_OOB) {
+ pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
+ return -EOPNOTSUPP;
+ }
+
+ pr_debug("%s: check msg_name\n", __func__);
+ if (!ro->connected && !msg->msg_name)
+ return -EDESTADDRREQ;
+ else if (ro->connected && msg->msg_name)
+ return -EISCONN;
+
+ pr_debug("%s: check bound\n", __func__);
+ if (!ro->bound)
+ ndev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_LORAWAN);
+ else
+ ndev = lrw_get_dev_by_addr(sock_net(sk), ro->src_devaddr);
+
+ if (!ndev) {
+ pr_debug("no dev\n");
+ ret = -ENXIO;
+ goto dgram_sendmsg_end;
+ }
+
+ if (size > ndev->mtu) {
+ netdev_dbg(ndev, "size = %zu, mtu = %u\n", size, ndev->mtu);
+ ret = -EMSGSIZE;
+ goto dgram_sendmsg_end;
+ }
+
+ netdev_dbg(ndev, "%s: create skb\n", __func__);
+ hlen = LL_RESERVED_SPACE(ndev);
+ tlen = ndev->needed_tailroom;
+ skb = sock_alloc_send_skb(sk, hlen + tlen + size,
+ msg->msg_flags & MSG_DONTWAIT,
+ &ret);
+
+ if (!skb)
+ goto dgram_sendmsg_no_skb;
+
+ skb_reserve(skb, hlen);
+ skb_reset_network_header(skb);
+
+ ret = lrw_dev_hard_header(skb, ndev, 0, size);
+ if (ret < 0)
+ goto dgram_sendmsg_no_skb;
+
+ ret = memcpy_from_msg(skb_put(skb, size), msg, size);
+ if (ret > 0)
+ goto dgram_sendmsg_err_skb;
+
+ skb->dev = ndev;
+ skb->protocol = htons(ETH_P_LORAWAN);
+
+ netdev_dbg(ndev, "%s: push skb to xmit queue\n", __func__);
+ ret = dev_queue_xmit(skb);
+ if (ret > 0)
+ ret = net_xmit_errno(ret);
+ netdev_dbg(ndev, "%s: pushed skb to xmit queue with ret=%d\n",
+ __func__, ret);
+ dev_put(ndev);
+
+ return ret ?: size;
+
+dgram_sendmsg_err_skb:
+ kfree_skb(skb);
+dgram_sendmsg_no_skb:
+ dev_put(ndev);
+
+dgram_sendmsg_end:
+ return ret;
+}
+
+static int
+dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+ int noblock, int flags, int *addr_len)
+{
+ DECLARE_SOCKADDR(struct sockaddr_lorawan *, saddr, msg->msg_name);
+ struct sk_buff *skb;
+ size_t copied = 0;
+ int err;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto dgram_recvmsg_end;
+
+ copied = skb->len;
+ if (len < copied) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
+ if (err)
+ goto dgram_recvmsg_done;
+
+ sock_recv_ts_and_drops(msg, sk, skb);
+ if (saddr) {
+ memset(saddr, 0, sizeof(*saddr));
+ saddr->family = AF_LORAWAN;
+ saddr->addr_in.devaddr = lrw_get_mac_cb(skb)->devaddr;
+ *addr_len = sizeof(*saddr);
+ }
+
+ if (flags & MSG_TRUNC)
+ copied = skb->len;
+
+dgram_recvmsg_done:
+ skb_free_datagram(sk, skb);
+
+dgram_recvmsg_end:
+ if (err)
+ return err;
+ return copied;
+}
+
+static int
+dgram_hash(struct sock *sk)
+{
+ pr_debug("%s\n", __func__);
+ write_lock_bh(&dgram_lock);
+ sk_add_node(sk, &dgram_head);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ write_unlock_bh(&dgram_lock);
+
+ return 0;
+}
+
+static void
+dgram_unhash(struct sock *sk)
+{
+ pr_debug("%s\n", __func__);
+ write_lock_bh(&dgram_lock);
+ if (sk_del_node_init(sk))
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ write_unlock_bh(&dgram_lock);
+}
+
+static int
+dgram_connect(struct sock *sk, struct sockaddr *uaddr, int len)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ /* Nodes of LoRaWAN send data to a gateway only, then data is received
+ * and transferred to servers with the gateway's policy.
+ * So, the destination address is not used by nodes.
+ */
+ lock_sock(sk);
+ ro->connected = 1;
+ release_sock(sk);
+
+ return 0;
+}
+
+static int
+dgram_disconnect(struct sock *sk, int flags)
+{
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ lock_sock(sk);
+ ro->connected = 0;
+ release_sock(sk);
+
+ return 0;
+}
+
+static int
+dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ struct net_device *ndev = sk->sk_dst_cache->dev;
+ struct sk_buff *skb;
+ int amount;
+ int err;
+
+ netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
+ switch (cmd) {
+ case SIOCOUTQ:
+ amount = sk_wmem_alloc_get(sk);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ case SIOCINQ:
+ amount = 0;
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb) {
+ /* We will only return the amount of this packet
+ * since that is all that will be read.
+ */
+ amount = skb->len;
+ }
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ default:
+ err = -ENOIOCTLCMD;
+ }
+
+ return err;
+}
+
+static int
+dgram_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ int val, len;
+
+ if (level != SOL_LORAWAN)
+ return -EOPNOTSUPP;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ len = min_t(unsigned int, len, sizeof(int));
+
+ switch (optname) {
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+dgram_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ int val;
+ int err;
+
+ err = 0;
+
+ if (optlen < sizeof(int))
+ return -EINVAL;
+
+ if (get_user(val, (int __user *)optval))
+ return -EFAULT;
+
+ lock_sock(sk);
+
+ switch (optname) {
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ release_sock(sk);
+
+ return err;
+}
+
+static struct proto lrw_dgram_prot = {
+ .name = "LoRaWAN",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct dgram_sock),
+ .init = dgram_init,
+ .close = dgram_close,
+ .bind = dgram_bind,
+ .sendmsg = dgram_sendmsg,
+ .recvmsg = dgram_recvmsg,
+ .hash = dgram_hash,
+ .unhash = dgram_unhash,
+ .connect = dgram_connect,
+ .disconnect = dgram_disconnect,
+ .ioctl = dgram_ioctl,
+ .getsockopt = dgram_getsockopt,
+ .setsockopt = dgram_setsockopt,
+};
+
+static int
+lrw_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk) {
+ sock->sk = NULL;
+ sk->sk_prot->close(sk, 0);
+ }
+
+ return 0;
+}
+
+static int
+lrw_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
+ struct sock *sk = sock->sk;
+
+ pr_debug("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
+ if (sk->sk_prot->bind)
+ return sk->sk_prot->bind(sk, uaddr, addr_len);
+
+ return sock_no_bind(sock, uaddr, addr_len);
+}
+
+static int
+lrw_sock_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ if (addr_len < sizeof(uaddr->sa_family))
+ return -EINVAL;
+
+ return sk->sk_prot->connect(sk, uaddr, addr_len);
+}
+
+static int
+lrw_ndev_ioctl(struct sock *sk, struct ifreq __user *arg, unsigned int cmd)
+{
+ struct net_device *ndev;
+ struct ifreq ifr;
+ int ret;
+
+ pr_debug("%s: cmd %ud\n", __func__, cmd);
+ ret = -ENOIOCTLCMD;
+
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
+
+ ifr.ifr_name[IFNAMSIZ - 1] = 0;
+
+ dev_load(sock_net(sk), ifr.ifr_name);
+ ndev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
+
+ netdev_dbg(ndev, "%s: cmd %ud\n", __func__, cmd);
+ if (!ndev)
+ return -ENODEV;
+
+ if (ndev->type == ARPHRD_LORAWAN && ndev->netdev_ops->ndo_do_ioctl)
+ ret = ndev->netdev_ops->ndo_do_ioctl(ndev, &ifr, cmd);
+
+ if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ ret = -EFAULT;
+ dev_put(ndev);
+
+ return ret;
+}
+
+static int
+lrw_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+
+ pr_debug("%s: cmd %ud\n", __func__, cmd);
+ switch (cmd) {
+ case SIOCGSTAMP:
+ return sock_get_timestamp(sk, (struct timeval __user *)arg);
+ case SIOCGSTAMPNS:
+ return sock_get_timestampns(sk, (struct timespec __user *)arg);
+ case SIOCOUTQ:
+ case SIOCINQ:
+ if (!sk->sk_prot->ioctl)
+ return -ENOIOCTLCMD;
+ return sk->sk_prot->ioctl(sk, cmd, arg);
+ default:
+ return lrw_ndev_ioctl(sk, (struct ifreq __user *)arg, cmd);
+ }
+}
+
+static int
+lrw_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+
+ pr_debug("%s: going to send %zu bytes\n", __func__, len);
+ return sk->sk_prot->sendmsg(sk, msg, len);
+}
+
+static const struct proto_ops lrw_dgram_ops = {
+ .family = PF_LORAWAN,
+ .owner = THIS_MODULE,
+ .release = lrw_sock_release,
+ .bind = lrw_sock_bind,
+ .connect = lrw_sock_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = datagram_poll,
+ .ioctl = lrw_sock_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = lrw_sock_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static int
+lorawan_creat(struct net *net, struct socket *sock, int protocol, int kern)
+{
+ struct sock *sk;
+ int ret;
+
+ if (!net_eq(net, &init_net))
+ return -EAFNOSUPPORT;
+
+ if (sock->type != SOCK_DGRAM)
+ return -EAFNOSUPPORT;
+
+ /* Allocates enough memory for dgram_sock whose first member is sk */
+ sk = sk_alloc(net, PF_LORAWAN, GFP_KERNEL, &lrw_dgram_prot, kern);
+ if (!sk)
+ return -ENOMEM;
+
+ sock->ops = &lrw_dgram_ops;
+ sock_init_data(sock, sk);
+ sk->sk_family = PF_LORAWAN;
+ sock_set_flag(sk, SOCK_ZAPPED);
+
+ if (sk->sk_prot->hash) {
+ ret = sk->sk_prot->hash(sk);
+ if (ret) {
+ sk_common_release(sk);
+ goto lorawan_creat_end;
+ }
+ }
+
+ if (sk->sk_prot->init) {
+ ret = sk->sk_prot->init(sk);
+ if (ret)
+ sk_common_release(sk);
+ }
+
+lorawan_creat_end:
+ return ret;
+}
+
+static const struct net_proto_family lorawan_family_ops = {
+ .owner = THIS_MODULE,
+ .family = PF_LORAWAN,
+ .create = lorawan_creat,
+};
+
+static int
+lrw_dgram_deliver(struct net_device *ndev, struct sk_buff *skb)
+{
+ struct dgram_sock *ro;
+ struct sock *sk;
+ bool found;
+ int ret;
+
+ ret = NET_RX_SUCCESS;
+ found = false;
+
+ read_lock(&dgram_lock);
+ sk_for_each(sk, &dgram_head) {
+ ro = dgram_sk(sk);
+ if (cpu_to_be32(ro->src_devaddr) == *(__be32 *)ndev->dev_addr) {
+ found = true;
+ break;
+ }
+ }
+ read_unlock(&dgram_lock);
+
+ if (!found)
+ goto lrw_dgram_deliver_err;
+
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NET_RX_DROP;
+
+ if (sock_queue_rcv_skb(sk, skb) < 0)
+ goto lrw_dgram_deliver_err;
+
+ return ret;
+
+lrw_dgram_deliver_err:
+ kfree_skb(skb);
+ ret = NET_RX_DROP;
+ return ret;
+}
+
+static int
+lorawan_rcv(struct sk_buff *skb, struct net_device *ndev,
+ struct packet_type *pt, struct net_device *orig_ndev)
+{
+ if (!netif_running(ndev))
+ goto lorawan_rcv_drop;
+
+ if (!net_eq(dev_net(ndev), &init_net))
+ goto lorawan_rcv_drop;
+
+ if (ndev->type != ARPHRD_LORAWAN)
+ goto lorawan_rcv_drop;
+
+ if (skb->pkt_type != PACKET_OTHERHOST)
+ return lrw_dgram_deliver(ndev, skb);
+
+lorawan_rcv_drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+}
+
+static struct packet_type lorawan_packet_type = {
+ .type = htons(ETH_P_LORAWAN),
+ .func = lorawan_rcv,
+};
+
+static int __init
+lrw_sock_init(void)
+{
+ int ret;
+
+ pr_info("module inserted\n");
+ ret = proto_register(&lrw_dgram_prot, 1);
+ if (ret)
+ goto lrw_sock_init_end;
+
+ /* Tell SOCKET that we are alive */
+ ret = sock_register(&lorawan_family_ops);
+ if (ret)
+ goto lrw_sock_init_err;
+
+ dev_add_pack(&lorawan_packet_type);
+ ret = 0;
+ goto lrw_sock_init_end;
+
+lrw_sock_init_err:
+ proto_unregister(&lrw_dgram_prot);
+
+lrw_sock_init_end:
+ return 0;
+}
+
+static void __exit
+lrw_sock_exit(void)
+{
+ dev_remove_pack(&lorawan_packet_type);
+ sock_unregister(PF_LORAWAN);
+ proto_unregister(&lrw_dgram_prot);
+ pr_info("module removed\n");
+}
+
+module_init(lrw_sock_init);
+module_exit(lrw_sock_exit);
+
+MODULE_AUTHOR("Jian-Hong Pan, <[email protected]>");
+MODULE_DESCRIPTION("LoRaWAN socket kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS_NETPROTO(PF_LORAWAN);
--
2.20.0
Add public LoRaWAN API for compatible LoRa device drivers.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Merge the lrw_operations: set_bw, set_mod, set_sf into set_dr
- Use SPDX license identifiers
V3:
- Remove the unused lrw_random_addr function
V4:
- Fix by coding style report from scripts/checkpatch.pl
include/linux/lora/lorawan.h | 131 +++++++++++++++++++++++++++++++++++
1 file changed, 131 insertions(+)
create mode 100644 include/linux/lora/lorawan.h
diff --git a/include/linux/lora/lorawan.h b/include/linux/lora/lorawan.h
new file mode 100644
index 000000000000..201f27521655
--- /dev/null
+++ b/include/linux/lora/lorawan.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN compatible hardware's definitions
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_H__
+#define __LORAWAN_H__
+
+#include <linux/skbuff.h>
+
+/* List the role of the LoRaWAN hardware */
+enum {
+ LRW_GATEWAY,
+ LRW_CLASS_A_NODE,
+ LRW_CLASS_B_NODE,
+ LRW_CLASS_C_NODE,
+};
+
+/* List the RF modes */
+enum {
+ LRW_LORA,
+ LRW_FSK,
+};
+
+/**
+ * lrw_dr - This structure holds the RF related configuration of the data rate.
+ * @bw:
+ * Bandwidth in Hz
+ *
+ * @sf:
+ * Spread factor of CSS modulation used by LoRa mode
+ *
+ * @mode:
+ * LoRa or FSK mode
+ */
+struct lrw_dr {
+ u32 bw;
+ u8 sf;
+ u8 mode;
+};
+
+#define LRW_DEVADDR_LEN (sizeof(__le32))
+
+/* List the LoRa device's states of LoRaWAN hardware */
+enum {
+ LRW_STOP,
+ LRW_START,
+ LRW_STATE_IDLE,
+ LRW_STATE_TX,
+ LRW_STATE_RX,
+};
+
+/**
+ * lrw_hw - This structure holds the LoRa device of LoRaWAN hardware.
+ * @priv:
+ * points to the private data of the LoRa device
+ */
+struct lrw_hw {
+ void *priv;
+};
+
+/**
+ * lrw_operations - Lists the LoRaWAN device/interface's operations.
+ * These are callback functions for the LoRaWAN module. Compatible LoRa device
+ * driver should implement some of them according to the usage. The
+ * unimplemented callback functions must be assigned as NULL.
+ *
+ * @start:
+ * called when the interface is being up state
+ *
+ * @stop:
+ * called when the interface is being down state
+ *
+ * @xmit_async:
+ * called to xmit the data through the interface asynchronously
+ *
+ * @set_txpower:
+ * called to set xmitting RF power in mBm of the interface
+ *
+ * @set_frq:
+ * called to set carrier frequency in Hz of the interface
+ *
+ * @set_dr:
+ * called to set related RF configuration of the LoRaWAN data rate
+ *
+ * @start_rx_window:
+ * called to ask the LoRa device open a receiving window
+ *
+ * @set_state:
+ * called to set the LoRa device's working state
+ */
+struct lrw_operations {
+ int (*start)(struct lrw_hw *hw);
+ void (*stop)(struct lrw_hw *hw);
+
+ int (*xmit_async)(struct lrw_hw *hw, struct sk_buff *skb);
+ int (*set_txpower)(struct lrw_hw *hw, s32 pwr);
+ int (*set_frq)(struct lrw_hw *hw, u32 frq);
+ int (*set_dr)(struct lrw_hw *hw, struct lrw_dr *dr);
+ int (*start_rx_window)(struct lrw_hw *hw, u32 delay);
+ int (*set_state)(struct lrw_hw *hw, u8 state);
+};
+
+struct lrw_hw *lrw_alloc_hw(size_t priv_data_len, struct lrw_operations *ops);
+void lrw_free_hw(struct lrw_hw *hw);
+int lrw_register_hw(struct lrw_hw *hw);
+void lrw_unregister_hw(struct lrw_hw *hw);
+void lrw_rx_irqsave(struct lrw_hw *hw, struct sk_buff *skb);
+void lrw_xmit_complete(struct lrw_hw *hw, struct sk_buff *skb);
+
+void lrw_set_deveui(struct lrw_hw *hw, u64 eui);
+u64 lrw_get_deveui(struct lrw_hw *hw);
+void lrw_set_appeui(struct lrw_hw *hw, u64 eui);
+u64 lrw_get_appeui(struct lrw_hw *hw);
+void lrw_set_devaddr(struct lrw_hw *hw, u32 eui);
+u32 lrw_get_devaddr(struct lrw_hw *hw);
+
+enum {
+ LRW_APPKEY,
+ LRW_NWKSKEY,
+ LRW_APPSKEY,
+};
+
+#define LRW_KEY_LEN 16
+
+int lrw_set_key(struct lrw_hw *hw, u8 type, u8 *key, size_t key_len);
+
+#endif
--
2.20.0
Implement the crypto for encryption/decryption and message integrity
code (MIC) according to LoRaWAN(TM) Specification Ver. 1.0.2.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Rename the lrwsec files to crypto files
- Modify for Big/Little-Endian
- Use SPDX license identifiers
V3:
- Order local variables from longest to shortest line in the functions
V4:
- Fix by coding style report from scripts/checkpatch.pl
net/maclorawan/crypto.c | 212 ++++++++++++++++++++++++++++++++++++++++
net/maclorawan/crypto.h | 27 +++++
2 files changed, 239 insertions(+)
create mode 100644 net/maclorawan/crypto.c
create mode 100644 net/maclorawan/crypto.h
diff --git a/net/maclorawan/crypto.c b/net/maclorawan/crypto.c
new file mode 100644
index 000000000000..0ae9d211cd14
--- /dev/null
+++ b/net/maclorawan/crypto.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#include <linux/scatterlist.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include "crypto.h"
+
+struct crypto_shash *
+lrw_mic_key_setup(u8 *k, size_t k_len)
+{
+ struct crypto_shash *tfm;
+ char *algo = "cmac(aes)";
+ int err;
+
+ tfm = crypto_alloc_shash(algo, 0, 0);
+ if (!IS_ERR(tfm)) {
+ err = crypto_shash_setkey(tfm, k, k_len);
+ if (err) {
+ crypto_free_shash(tfm);
+ tfm = NULL;
+ }
+ }
+
+ return tfm;
+}
+
+int
+lrw_aes_cmac(struct crypto_shash *tfm, u8 *bz, u8 *data, size_t len, u8 *out)
+{
+ SHASH_DESC_ON_STACK(desc, tfm);
+ int err;
+
+ desc->tfm = tfm;
+
+ err = crypto_shash_init(desc);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_update(desc, bz, 16);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_update(desc, data, len);
+ if (err)
+ goto lrw_aes_cmac_end;
+
+ err = crypto_shash_final(desc, out);
+
+lrw_aes_cmac_end:
+ return err;
+}
+
+int
+lrw_set_bzero(u8 dir, u32 devaddr, u32 fcnt, u8 len, u8 *bz)
+{
+ __le32 le_devaddr = cpu_to_le32(devaddr);
+ __le32 le_fcnt = cpu_to_le32(fcnt);
+
+ bz[0] = 0x49;
+ memset(bz + 1, 0x00, 4);
+ bz[5] = dir;
+ memcpy(bz + 6, &le_devaddr, 4);
+ memcpy(bz + 10, &le_fcnt, 4);
+ bz[14] = 0x00;
+ bz[15] = len;
+
+ return 0;
+}
+
+int
+lrw_calc_mic(struct crypto_shash *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len, u8 *mic4)
+{
+ u8 mic[16];
+ u8 bz[16];
+ int err;
+
+ /* According to LoRaWAN Specification Version 1.0.2
+ * - 4.4 Massege Integrity Code (MIC)
+ */
+ lrw_set_bzero(dir, devaddr, fcnt, len, bz);
+ err = lrw_aes_cmac(tfm, bz, buf, len, mic);
+ if (!err)
+ memcpy(mic4, mic, 4);
+
+ return err;
+}
+
+void
+lrw_mic_key_free(struct crypto_shash *tfm)
+{
+ crypto_free_shash(tfm);
+}
+
+struct crypto_sync_skcipher *
+lrw_aes_enc_key_setup(char *algo, u8 *k, size_t k_len)
+{
+ struct crypto_sync_skcipher *tfm;
+ int err;
+
+ tfm = crypto_alloc_sync_skcipher(algo, 0, CRYPTO_ALG_ASYNC);
+ if (!IS_ERR(tfm)) {
+ err = crypto_sync_skcipher_setkey(tfm, k, k_len);
+ if (err) {
+ crypto_free_sync_skcipher(tfm);
+ tfm = NULL;
+ }
+ }
+
+ return tfm;
+}
+
+struct crypto_sync_skcipher *
+lrw_encrypt_key_setup(u8 *k, size_t k_len)
+{
+ return lrw_aes_enc_key_setup("cbc(aes)", k, k_len);
+}
+
+int
+lrw_aes_enc(struct crypto_sync_skcipher *tfm, u8 *in, size_t len, u8 *out)
+{
+ SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
+ struct scatterlist src, dst;
+ u8 iv[16];
+ int err;
+
+ memset(iv, 0, 16);
+ /* The buffer for sg_init_one cannot be a global or const local
+ * (will confuse the scatterlist)
+ */
+ sg_init_one(&src, in, len);
+ sg_init_one(&dst, out, len);
+
+ skcipher_request_set_sync_tfm(req, tfm);
+ skcipher_request_set_callback(req, 0, NULL, NULL);
+ skcipher_request_set_crypt(req, &src, &dst, len, iv);
+ err = crypto_skcipher_encrypt(req);
+ skcipher_request_zero(req);
+
+ return err;
+}
+
+#define LRW_SEQUENCE_OF_BLOCK_LEN 16
+
+int
+lrw_set_sob(u8 dir, u32 devaddr, u32 fcnt, u8 index, u8 *sob)
+{
+ __le32 le_devaddr = cpu_to_le32(devaddr);
+ __le32 _fcnt = cpu_to_le32(fcnt);
+
+ sob[0] = 0x01;
+ memset(sob + 1, 0x00, 4);
+ sob[5] = dir;
+ memcpy(sob + 6, &le_devaddr, 4);
+ memcpy(sob + 10, &_fcnt, 4);
+ sob[14] = 0x00;
+ sob[15] = index;
+
+ return 0;
+}
+
+int
+lrw_encrypt_sob(struct crypto_sync_skcipher *tfm, u8 *sob)
+{
+ return lrw_aes_enc(tfm, sob, LRW_SEQUENCE_OF_BLOCK_LEN, sob);
+}
+
+int
+lrw_encrypt_buf(struct crypto_sync_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len)
+{
+ u8 sob[LRW_SEQUENCE_OF_BLOCK_LEN];
+ u8 i, j;
+
+ /* According to LoRaWAN Specification Version 1.0.2
+ * - 4.3.3 MAC Frame Payload Encryption (FRMPayload)
+ */
+ for (i = 0; (i * LRW_SEQUENCE_OF_BLOCK_LEN) < len; i++) {
+ lrw_set_sob(dir, devaddr, fcnt, i, sob);
+ lrw_encrypt_sob(tfm, sob);
+ for (j = 0; (i * LRW_SEQUENCE_OF_BLOCK_LEN + j) < len; j++)
+ buf[i * LRW_SEQUENCE_OF_BLOCK_LEN + j] ^= sob[j];
+ }
+
+ return 0;
+}
+
+int
+lrw_decrypt_buf(struct crypto_sync_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len)
+{
+ /* According to XOR swap algorithm */
+ return lrw_encrypt_buf(tfm, dir, devaddr, fcnt, buf, len);
+}
+
+void
+lrw_aes_enc_key_free(struct crypto_sync_skcipher *tfm)
+{
+ crypto_free_sync_skcipher(tfm);
+}
+
+void
+lrw_encrypt_key_free(struct crypto_sync_skcipher *tfm)
+{
+ lrw_aes_enc_key_free(tfm);
+}
diff --git a/net/maclorawan/crypto.h b/net/maclorawan/crypto.h
new file mode 100644
index 000000000000..22dd810040c0
--- /dev/null
+++ b/net/maclorawan/crypto.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __LORAWAN_CRYPTO_H__
+#define __LORAWAN_CRYPTO_H__
+
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+
+struct crypto_shash *lrw_mic_key_setup(u8 *k, size_t k_len);
+int lrw_calc_mic(struct crypto_shash *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len, u8 *mic4);
+void lrw_mic_key_free(struct crypto_shash *tfm);
+
+struct crypto_sync_skcipher *lrw_encrypt_key_setup(u8 *k, size_t k_len);
+int lrw_encrypt_buf(struct crypto_sync_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len);
+int lrw_decrypt_buf(struct crypto_sync_skcipher *tfm,
+ u8 dir, u32 devaddr, u32 fcnt, u8 *buf, size_t len);
+void lrw_encrypt_key_free(struct crypto_sync_skcipher *tfm);
+
+#endif
--
2.20.0
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 <[email protected]>
---
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 <[email protected]>
+ *
+ */
+
+#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 <[email protected]>
+ *
+ */
+
+#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, <[email protected]>");
+MODULE_DESCRIPTION("LoRaWAN soft MAC kernel module");
+MODULE_LICENSE("Dual BSD/GPL");
--
2.20.0
List LORAWAN and MACLORAWAN in menuconfig and make they can be built.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
net/Kconfig | 2 ++
net/Makefile | 2 ++
2 files changed, 4 insertions(+)
diff --git a/net/Kconfig b/net/Kconfig
index cf2e651ee31d..03b3ff306778 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -224,6 +224,8 @@ source "net/6lowpan/Kconfig"
source "net/ieee802154/Kconfig"
source "net/mac802154/Kconfig"
source "net/lora/Kconfig"
+source "net/lorawan/Kconfig"
+source "net/maclorawan/Kconfig"
source "net/sched/Kconfig"
source "net/dcb/Kconfig"
source "net/dns_resolver/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index e80b84313851..9d5515965a8f 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -63,6 +63,8 @@ obj-$(CONFIG_6LOWPAN) += 6lowpan/
obj-$(CONFIG_IEEE802154) += ieee802154/
obj-$(CONFIG_MAC802154) += mac802154/
obj-$(CONFIG_LORA) += lora/
+obj-$(CONFIG_LORAWAN) += lorawan/
+obj-$(CONFIG_MACLORAWAN) += maclorawan/
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
--
2.20.0
Add the maclorawan header file for common APIs in the module.
Signed-off-by: Jian-Hong Pan <[email protected]>
---
V2:
- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
LoRaWAN Soft MAC modules
- Use SPDX license identifiers
V4:
- Fix typo in comments
- Fix by coding style report from scripts/checkpatch.pl
V5:
- Add the frame control and frame count fields' length
net/maclorawan/maclorawan.h | 202 ++++++++++++++++++++++++++++++++++++
1 file changed, 202 insertions(+)
create mode 100644 net/maclorawan/maclorawan.h
diff --git a/net/maclorawan/maclorawan.h b/net/maclorawan/maclorawan.h
new file mode 100644
index 000000000000..024c394bebca
--- /dev/null
+++ b/net/maclorawan/maclorawan.h
@@ -0,0 +1,202 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
+/*-
+ * LoRaWAN soft MAC
+ *
+ * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
+ *
+ */
+
+#ifndef __MAC_LORAWAN_H__
+#define __MAC_LORAWAN_H__
+
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <uapi/linux/if_arp.h>
+#include <crypto/hash.h>
+#include <crypto/skcipher.h>
+#include <linux/lora/lorawan.h>
+
+#define LORAWAN_MODULE_NAME "maclorawan"
+
+/* List the message types of LoRaWAN */
+enum {
+ LRW_JOIN_REQUEST,
+ LRW_JOIN_ACCEPT,
+ LRW_UNCONFIRMED_DATA_UP,
+ LRW_UNCONFIRMED_DATA_DOWN,
+ LRW_CONFIRMED_DATA_UP,
+ LRW_CONFIRMED_DATA_DOWN,
+ LRW_RFU,
+ LRW_PROPRIETARY,
+};
+
+/* List the communication directions */
+enum {
+ LRW_UPLINK,
+ LRW_DOWNLINK,
+};
+
+/* States of LoRaWAN slot timing */
+enum {
+ LRW_INIT_SS,
+ LRW_XMITTING_SS,
+ LRW_XMITTED,
+ LRW_RX1_SS,
+ LRW_RX2_SS,
+ LRW_RXTIMEOUT_SS,
+ LRW_RXRECEIVED_SS,
+ LRW_RETRANSMIT_SS,
+};
+
+#define LRW_MHDR_LEN 1
+#define LRW_FHDR_MAX_LEN 22
+#define LRW_FCTRL_LEN 1
+#define LRW_FCNT_LEN 2
+#define LRW_FOPS_MAX_LEN 15
+#define LRW_FPORT_LEN 1
+#define LRW_MIC_LEN 4
+
+/**
+ * lrw_fhdr - Hold the message's basic information of the frame
+ *
+ * @mtype: this message's type
+ * @fctrl: the frame control byte
+ * @fcnt: this message's frame counter value
+ * @fopts: this frame's options field
+ * @fopts_len: the length of the fopts
+ */
+struct lrw_fhdr {
+ u8 mtype;
+ u8 fctrl;
+ u16 fcnt;
+ u8 fopts[LRW_FPORT_LEN];
+ u8 fopts_len;
+};
+
+/**
+ * lrw_session - LoRaWAN session for Class A end device
+ *
+ * @lrw_st: points to the belonging lrw_st
+ * @entry: the entry of the ss_list in lrw_struct
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ * @fcnt_up: uplink frame counter
+ * @fcnt_down: downlink frame counter
+ * @fport: the LoRaWAN data message's port field
+ * @tx_skb: points to the TX skb, the frame
+ * @rx_skb: points to the RX skb, the frame
+ * @tx_fhdr: hold the message's basic information of the TX frame
+ * @rx_fhdr: hold the message's basic information of the RX frame
+ * @tx_should_ack: flag for determining the TX which should be acked or not
+ * @retry: retry times for xmitting failed
+ * @state: this session's current state
+ * @state_lock: lock of the session's state
+ * @timer: timing for this session and the state transition
+ * @timeout_work: work if waiting acknowledge time out
+ * @rx_delay1: RX1 delay time in seconds
+ * @rx_delay2: RX2 delay time in seconds
+ * @rx1_window: RX1 window opening time in milliseconds
+ * @rx2_window: RX2 window opening time in milliseconds
+ * @ack_timeout: time out time for waiting acknowledge in seconds
+ */
+struct lrw_session {
+ struct lrw_struct *lrw_st;
+ struct list_head entry;
+
+ u32 devaddr;
+ u16 fcnt_up;
+ u16 fcnt_down;
+ u8 fport;
+ struct sk_buff *tx_skb;
+ struct sk_buff *rx_skb;
+ struct lrw_fhdr tx_fhdr;
+ struct lrw_fhdr rx_fhdr;
+
+ u8 tx_should_ack;
+ u8 retry;
+ u8 state;
+ spinlock_t state_lock; /* lock of the session's state */
+
+ struct timer_list timer;
+ struct work_struct timeout_work;
+ unsigned long rx_delay1;
+ unsigned long rx_delay2;
+ unsigned long rx1_window;
+ unsigned long rx2_window;
+ unsigned long ack_timeout;
+};
+
+/**
+ * lrw_struct - The full LoRaWAN hardware to the LoRa device.
+ *
+ * @dev: this LoRa device registed in system
+ * @hw: the LoRa device of this LoRaWAN hardware
+ * @ops: handle of LoRa operations interfaces
+ * @rx_skb_list: the list of received frames
+ * @ss_list: LoRaWAN session list of this LoRaWAN hardware
+ * @ss_list_lock: lock of the session list
+ * @_cur_ss: pointer of the current processing session
+ * @rx_should_ack: represent the current session should be acked or not
+ * @state: the state of this LoRaWAN hardware
+ * @app_eui: the LoRaWAN application EUI
+ * @dev_eui: the LoRaWAN device EUI
+ * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
+ * @appky: the Application key
+ * @nwkskey: the Network session key
+ * @appskey: the Application session key
+ * @nwks_shash_tfm: the hash handler for LoRaWAN network session
+ * @nwks_skc_tfm: the crypto handler for LoRaWAN network session
+ * @apps_skc_tfm: the crypto handler for LoRaWAN application session
+ * @fcnt_up: the counter of this LoRaWAN hardware's up frame
+ * @fcnt_down: the counter of this LoRaWAN hardware's down frame
+ * @xmit_task: the xmit task for the current LoRaWAN session
+ * @rx_work: the RX work in workqueue for the current LoRaWAN session
+ * @ndev: points to the emulating network device
+ * @_net: the current network namespace of this LoRaWAN hardware
+ */
+struct lrw_struct {
+ struct device dev;
+ struct lrw_hw hw;
+ struct lrw_operations *ops;
+
+ struct sk_buff_head rx_skb_list;
+ struct list_head ss_list;
+ struct mutex ss_list_lock; /* lock of the session list */
+ struct lrw_session *_cur_ss;
+ u8 rx_should_ack;
+ u8 state;
+
+ u64 app_eui;
+ u64 dev_eui;
+ u32 devaddr;
+ u8 appkey[LRW_KEY_LEN];
+ u8 nwkskey[LRW_KEY_LEN];
+ u8 appskey[LRW_KEY_LEN];
+ struct crypto_shash *nwks_shash_tfm;
+ struct crypto_sync_skcipher *nwks_skc_tfm;
+ struct crypto_sync_skcipher *apps_skc_tfm;
+
+ u16 fcnt_up;
+ u16 fcnt_down;
+
+ struct tasklet_struct xmit_task;
+ struct work_struct rx_work;
+
+ struct net_device *ndev;
+ possible_net_t _net;
+};
+
+#define NETDEV_2_LRW(ndev) ((struct lrw_struct *)netdev_priv(ndev))
+
+struct lrw_session *lrw_alloc_ss(struct lrw_struct *lrw_st);
+void lrw_free_ss(struct lrw_session *ss);
+void lrw_del_ss(struct lrw_session *ss);
+int lrw_start_hw(struct lrw_struct *lrw_st);
+void lrw_stop_hw(struct lrw_struct *lrw_st);
+void lrw_prepare_tx_frame(struct lrw_session *ss);
+void lrw_xmit(unsigned long data);
+void lrw_rx_work(struct work_struct *work);
+
+#endif
--
2.20.0
Hello Jian-Hong,
Thanks for your work in LoRaWAN.
I have a question about the architecture of your module. AFAIK LoRaWAN
is already the MAC Layer above the LoRa technology. Why do you want to
make a new layer called "maclorawan" ?
Regards,
Xue Liu
On Sun, 16 Dec 2018 at 11:20, Jian-Hong Pan <[email protected]> wrote:
>
> List LORAWAN and MACLORAWAN in menuconfig and make they can be built.
>
> Signed-off-by: Jian-Hong Pan <[email protected]>
> ---
> V2:
> - Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
> LoRaWAN Soft MAC modules
>
> net/Kconfig | 2 ++
> net/Makefile | 2 ++
> 2 files changed, 4 insertions(+)
>
> diff --git a/net/Kconfig b/net/Kconfig
> index cf2e651ee31d..03b3ff306778 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -224,6 +224,8 @@ source "net/6lowpan/Kconfig"
> source "net/ieee802154/Kconfig"
> source "net/mac802154/Kconfig"
> source "net/lora/Kconfig"
> +source "net/lorawan/Kconfig"
> +source "net/maclorawan/Kconfig"
> source "net/sched/Kconfig"
> source "net/dcb/Kconfig"
> source "net/dns_resolver/Kconfig"
> diff --git a/net/Makefile b/net/Makefile
> index e80b84313851..9d5515965a8f 100644
> --- a/net/Makefile
> +++ b/net/Makefile
> @@ -63,6 +63,8 @@ obj-$(CONFIG_6LOWPAN) += 6lowpan/
> obj-$(CONFIG_IEEE802154) += ieee802154/
> obj-$(CONFIG_MAC802154) += mac802154/
> obj-$(CONFIG_LORA) += lora/
> +obj-$(CONFIG_LORAWAN) += lorawan/
> +obj-$(CONFIG_MACLORAWAN) += maclorawan/
>
> ifeq ($(CONFIG_NET),y)
> obj-$(CONFIG_SYSCTL) += sysctl_net.o
> --
> 2.20.0
>
--
Sun, Dec 16, 2018 at 11:18:53AM CET, [email protected] wrote:
>LoRaWAN(TM) is the MAC layer defined by LoRa Alliance(TM) over LoRa
>devices. LoRa is one of Low-Power Wide-Area Network (LPWAN) technology.
>
>LoRaWAN networks typically are laid out in a star-of-stars topology in
>which gateways relay messages between end-devices and a central network
>server at the backend. Gateways are connected to the network server via
>standard IP connections while end-devices use single hop LoRa(TM) or FSK
>communication to one or many gateways.
>
>A LoRa network distinguishes between a basic LoRaWAN (named Class A) and
>optional features (Class B, Class C ...):
>* Bi-directional end-devices (Class A)
>* Bi-directional end-devices with scheduled receive slots (Class B)
>* Bi-directional end-devices with maximal receive slots (Class C)
>
>This patch set add LoRaWAN class module implementing the stack,
>especially the soft MAC, between socket APIs and LoRa device drivers.
>
>socket APIs:
>send and receive the data
>------------------------------------------------------------------------
>LoRaWAN class module implements soft MAC:
>append the header/footer, encryption/decryption, timing slot and MAC
>commands
>------------------------------------------------------------------------
>LoRa device drivers:
>send and receive the messages for MAC layer
>------------------------------------------------------------------------
>LoRa devices
>
>This module starts from simple and implements partial Class A
>end-devices features defined in LoRaWAN(TM) Specification Ver. 1.0.2.
>More features and complexity, for example regional parameters, confirmed
>data messages, join request/accept messages for Over-The-Air Activation,
>MAC commands ... will be added in the future.
Could you please send new patchset versions as separate patchsets? It is
messy to have all versions in one thread. Also, please send it so the
patches are in-reply-to the cover letter (git-send-email default).
Thanks!
Sun, Dec 16, 2018 at 11:18:59AM CET, [email protected] wrote:
>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 <[email protected]>
>---
>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
I don't get it. In patch "Add LoRaWAN API declaration for LoRa devices"
you add headers for "API" and here you implement functions. That is just
weird. Does it mean you can have other implementations?
Also, you don't really have any user of this API in the set. Please
introduce at least 1 driver, preferably more (I see that Andreas has
multiple ones in his patchset). You cannot push kernel infrastructure
without kernel user.
Hello Xue Liu,
Am 17.12.18 um 09:50 schrieb Xue Liu:
> I have a question about the architecture of your module. AFAIK LoRaWAN
> is already the MAC Layer above the LoRa technology. Why do you want to
> make a new layer called "maclorawan" ?
I had asked Jian-Hong to separate between his soft-MAC implementation
and the common bits needed to drive hard-MAC implementations found on
several of the hardware modules made available to me.
The prefix "mac" was copied from mac80211 and mac802154:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net
If you have better ideas for how to structure this, just let us know,
ideally as inline comment where you see it (or on the cover letter).
Only comment I have for this patch at the moment is that I would prefer
to have the Kconfig bits be in the patches adding the code, so that we
can actually build-test them before 6/6.
Been updating my lab to 4.20-rcX with some hiccups. Ben's
regmap_noinc_write support made it into 4.20, so I expect to have Ben's
pending branch for sx1301 merged into rebased lora-next before Christmas
and my sx1276 conversion to follow, leaving the PF_PACKET vs. PF_LORA
discussion from ELCE - haven't assessed yet how much this series would
be affected by the underlying changes, but if the abstraction was done
right then only maclorawan implementation should be affected.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Hello Andreas,
On Mon, 17 Dec 2018 at 15:19, Andreas Färber <[email protected]> wrote:
>
> Hello Xue Liu,
>
> Am 17.12.18 um 09:50 schrieb Xue Liu:
> > I have a question about the architecture of your module. AFAIK LoRaWAN
> > is already the MAC Layer above the LoRa technology. Why do you want to
> > make a new layer called "maclorawan" ?
>
> I had asked Jian-Hong to separate between his soft-MAC implementation
> and the common bits needed to drive hard-MAC implementations found on
> several of the hardware modules made available to me.
>
As a reference Linux 802.11 uses cfg80211 to talk with hard-MAC devices.
We may also use the name “cfglora” for hard-MAC implementation.
> The prefix "mac" was copied from mac80211 and mac802154:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net
>
OK. Understood. I guess they use mac80211 and mac802154 to distinguish
physical layer and MAC layer. Since LoRa and LoRaWAN are already
separate I think
"maclorawan" is not necessary.
> If you have better ideas for how to structure this, just let us know,
> ideally as inline comment where you see it (or on the cover letter).
>
> Only comment I have for this patch at the moment is that I would prefer
> to have the Kconfig bits be in the patches adding the code, so that we
> can actually build-test them before 6/6.
>
> Been updating my lab to 4.20-rcX with some hiccups. Ben's
> regmap_noinc_write support made it into 4.20, so I expect to have Ben's
> pending branch for sx1301 merged into rebased lora-next before Christmas
> and my sx1276 conversion to follow, leaving the PF_PACKET vs. PF_LORA
> discussion from ELCE - haven't assessed yet how much this series would
> be affected by the underlying changes, but if the abstraction was done
> right then only maclorawan implementation should be affected.
>
> Regards,
> Andreas
>
> --
> SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Felix Imendörffer, Jane Smithard, Graham Norton
> HRB 21284 (AG Nürnberg)
Regards,
Xue Liu
--
> Sun, Dec 16, 2018 at 11:18:59AM CET, [email protected] wrote:
> >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 <[email protected]>
> >---
> >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
>
>
> I don't get it. In patch "Add LoRaWAN API declaration for LoRa devices"
> you add headers for "API" and here you implement functions. That is just
> weird. Does it mean you can have other implementations?
LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa PHY.
This part is soft-MAC as Andreas mentioned
http://lists.infradead.org/pipermail/linux-lpwan/2018-December/000010.html
> Also, you don't really have any user of this API in the set. Please
> introduce at least 1 driver, preferably more (I see that Andreas has
> multiple ones in his patchset). You cannot push kernel infrastructure
> without kernel user.
The soft-MAC is suitable for the LoRa chips' device drivers, like
sx1276/77/78/79, RFM95/96/97/98W ...
Still waiting for Andreas' sx1276 version 2 patch and more discussion.
For example, how to make PF_LORA and PF_LORAWAN like Ethernet, PF_INET
and PF_INET6 don't need separate devices either, both use eth0.
https://lkml.org/lkml/2018/8/3/266
Jian-Hong Pan
Tue, Dec 18, 2018 at 03:27:09PM CET, [email protected] wrote:
>> Sun, Dec 16, 2018 at 11:18:59AM CET, [email protected] wrote:
>> >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 <[email protected]>
>> >---
>> >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
>>
>>
>> I don't get it. In patch "Add LoRaWAN API declaration for LoRa devices"
>> you add headers for "API" and here you implement functions. That is just
>> weird. Does it mean you can have other implementations?
>
>LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa PHY.
>This part is soft-MAC as Andreas mentioned
>http://lists.infradead.org/pipermail/linux-lpwan/2018-December/000010.html
Okay, that does not answer my concern about header file in one patch and
the actual implementation of functions in another one.
>
>> Also, you don't really have any user of this API in the set. Please
>> introduce at least 1 driver, preferably more (I see that Andreas has
>> multiple ones in his patchset). You cannot push kernel infrastructure
>> without kernel user.
>
>The soft-MAC is suitable for the LoRa chips' device drivers, like
>sx1276/77/78/79, RFM95/96/97/98W ...
>Still waiting for Andreas' sx1276 version 2 patch and more discussion.
>For example, how to make PF_LORA and PF_LORAWAN like Ethernet, PF_INET
>and PF_INET6 don't need separate devices either, both use eth0.
>https://lkml.org/lkml/2018/8/3/266
Then you should push this is RFC or together with Andreases work in a
single patchset. Infra without users cannot be merged.
>
>Jian-Hong Pan
> Tue, Dec 18, 2018 at 03:27:09PM CET, [email protected] wrote:
> >> Sun, Dec 16, 2018 at 11:18:59AM CET, [email protected] wrote:
> >> >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 <[email protected]>
> >> >---
> >> >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
> >>
> >>
> >> I don't get it. In patch "Add LoRaWAN API declaration for LoRa devices"
> >> you add headers for "API" and here you implement functions. That is just
> >> weird. Does it mean you can have other implementations?
> >
> >LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa PHY.
> >This part is soft-MAC as Andreas mentioned
> >http://lists.infradead.org/pipermail/linux-lpwan/2018-December/000010.html
>
> Okay, that does not answer my concern about header file in one patch and
> the actual implementation of functions in another one.
Just for clarification:
- Patch "net: lorawan: Add LoRaWAN socket module" is for lorawan module
- Patch "net: lorawan: Add LoRaWAN API declaration for LoRa devices"
containes the header file "include/linux/lora/lorawan.h" which will be
included by LoRa device drivers or other kernel modules.
- Patches "net: maclorawan: Add maclorawan module declaration", "net:
maclorawan: Implement the crypto of maclorawan module" and "net:
maclorawan: Implement maclorawan class module" are for maclorawan
module.
Question 1:
Should I marge "net: maclorawan: Add maclorawan module declaration",
"net: maclorawan: Implement the crypto of maclorawan module" and "net:
maclorawan: Implement maclorawan class module" into a single patch
named "net: maclorawan: Add maclorawan as the soft-MAC module"?
Then:
For example, after a LoRa device driver includes the header
"linux/lora/lorawan.h", the device driver will call "lrw_alloc_hw()"
and pass with a "struct lrw_operations" type of variable's pointer.
It gets a type of "struct lrw_hw *" pointer. Then, it will call
"lrw_register_hw()" to register the device. The device driver
implements the callback functions for the "struct lrw_operations" type
of variable by it self before calls "lrw_alloc_hw()".
Question 2:
Should the patch "net: lorawan: Add LoRaWAN API declaration for LoRa
devices" also be merged into "net: maclorawan: Add maclorawan as the
soft-MAC module" or "net: maclorawan: Implement maclorawan class
module"? Or, just leave it as a single patch?
Am 18.12.18 um 15:27 schrieb Jian-Hong Pan:
>> Sun, Dec 16, 2018 at 11:18:59AM CET, [email protected] wrote:
>>> 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 <[email protected]>
[...]
>>> 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
>>
>> I don't get it. In patch "Add LoRaWAN API declaration for LoRa devices"
>> you add headers for "API" and here you implement functions. That is just
>> weird. Does it mean you can have other implementations?
>
> LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa PHY.
> This part is soft-MAC as Andreas mentioned
> http://lists.infradead.org/pipermail/linux-lpwan/2018-December/000010.html
>
>> Also, you don't really have any user of this API in the set. Please
>> introduce at least 1 driver, preferably more (I see that Andreas has
>> multiple ones in his patchset). You cannot push kernel infrastructure
>> without kernel user.
>
> The soft-MAC is suitable for the LoRa chips' device drivers, like
> sx1276/77/78/79, RFM95/96/97/98W ...
> Still waiting for Andreas' sx1276 version 2 patch and more discussion.
sx1276 regmap conversion was pushed to my staging tree together with
Ben's sx1301 final conversion last night, lightly tested.
https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-lora.git/log/?h=lora-next
TBD: rename to sx127x, implement regmap fields, only auto-detect reset
when no OF node available (all low priority atm, patches welcome)
(and for sx1301 I still need to update my DT overlays with the new clk)
> For example, how to make PF_LORA and PF_LORAWAN like Ethernet, PF_INET
> and PF_INET6 don't need separate devices either, both use eth0.
> https://lkml.org/lkml/2018/8/3/266
Jiri, I am expecting the maclorawan driver to lower packets from
ETH_P_LORAWAN to ETH_P_LORA in a generic way, so that any of the LoRa
device drivers can benefit of it, with maclorawan using the LoRa netlink
commands that the individual drivers implement.
Not sure what if anything is missing for that in the current revision?
Still dealing with the lower-level infrastructure and my test setup ...
progressing slowly.
I'll probably need to queue the remaining generic LoRaWAN part 1/6 in my
tree to resolve this circular dependency between Jian-Hong and me, so
that only the soft-MAC implementation remains a separate patch series.
The hard-MAC implementations will be on my plate mostly, as both SX1276
and SX1301 need the soft-MAC.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
> Am 18.12.18 um 15:27 schrieb Jian-Hong Pan:
> >> Sun, Dec 16, 2018 at 11:18:59AM CET, [email protected] wrote:
> >>> 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 <[email protected]>
> [...]
> >>> 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
> >>
> >> I don't get it. In patch "Add LoRaWAN API declaration for LoRa devices"
> >> you add headers for "API" and here you implement functions. That is just
> >> weird. Does it mean you can have other implementations?
> >
> > LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa PHY.
> > This part is soft-MAC as Andreas mentioned
> > http://lists.infradead.org/pipermail/linux-lpwan/2018-
> December/000010.html
> >
> >> Also, you don't really have any user of this API in the set. Please
> >> introduce at least 1 driver, preferably more (I see that Andreas has
> >> multiple ones in his patchset). You cannot push kernel infrastructure
> >> without kernel user.
> >
> > The soft-MAC is suitable for the LoRa chips' device drivers, like
> > sx1276/77/78/79, RFM95/96/97/98W ...
> > Still waiting for Andreas' sx1276 version 2 patch and more discussion.
>
> sx1276 regmap conversion was pushed to my staging tree together with
> Ben's sx1301 final conversion last night, lightly tested.
>
> https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-
> lora.git/log/?h=lora-next
>
> TBD: rename to sx127x, implement regmap fields, only auto-detect reset
> when no OF node available (all low priority atm, patches welcome)
>
> (and for sx1301 I still need to update my DT overlays with the new clk)
>
> > For example, how to make PF_LORA and PF_LORAWAN like Ethernet,
> PF_INET
> > and PF_INET6 don't need separate devices either, both use eth0.
> > https://lkml.org/lkml/2018/8/3/266
>
> Jiri, I am expecting the maclorawan driver to lower packets from
> ETH_P_LORAWAN to ETH_P_LORA in a generic way, so that any of the LoRa
> device drivers can benefit of it, with maclorawan using the LoRa netlink
> commands that the individual drivers implement.
> Not sure what if anything is missing for that in the current revision?
> Still dealing with the lower-level infrastructure and my test setup ...
> progressing slowly.
>
> I'll probably need to queue the remaining generic LoRaWAN part 1/6 in my
> tree to resolve this circular dependency between Jian-Hong and me, so
> that only the soft-MAC implementation remains a separate patch series.
> The hard-MAC implementations will be on my plate mostly, as both SX1276
> and SX1301 need the soft-MAC.
On the SX1301 side of things, the ability to send messages as a LoRaWAN
node device is a niche use case, the majority if not all people will use the
concentrator card as the pass through gateway to the node.
In this mode of operation the parameters for transmission such as; frequency,
spreading factor / data rate, power, are given by a remote server and passed
in from the userspace application which received it.
Eventually in the kernel these need to be checked locally to ensure regulatory
compliance.
To that end I have experimented with framing, as CAN does, so that this
metadata can be provided on a write from userspace to the SX1301 driver.
Sounds like we need different protocols for framing within the protocol family.
Raw in the case of nodes and framed with metadata in the case of concentrator
cards, thoughts?
I will send my experiment RFC to the lpwan mailing list.
Regards,
Ben Whitten
> > Am 18.12.18 um 15:27 schrieb Jian-Hong Pan:
> > >> Sun, Dec 16, 2018 at 11:18:59AM CET, [email protected] wrote:
> > >>> 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 <[email protected]>
> > [...]
> > >>> 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
> > >>
> > >> I don't get it. In patch "Add LoRaWAN API declaration for LoRa devices"
> > >> you add headers for "API" and here you implement functions. That is just
> > >> weird. Does it mean you can have other implementations?
> > >
> > > LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa PHY.
> > > This part is soft-MAC as Andreas mentioned
> > > http://lists.infradead.org/pipermail/linux-lpwan/2018-
> > December/000010.html
> > >
> > >> Also, you don't really have any user of this API in the set. Please
> > >> introduce at least 1 driver, preferably more (I see that Andreas has
> > >> multiple ones in his patchset). You cannot push kernel infrastructure
> > >> without kernel user.
> > >
> > > The soft-MAC is suitable for the LoRa chips' device drivers, like
> > > sx1276/77/78/79, RFM95/96/97/98W ...
> > > Still waiting for Andreas' sx1276 version 2 patch and more discussion.
> >
> > sx1276 regmap conversion was pushed to my staging tree together with
> > Ben's sx1301 final conversion last night, lightly tested.
> >
> > https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-
> > lora.git/log/?h=lora-next
> >
> > TBD: rename to sx127x, implement regmap fields, only auto-detect reset
> > when no OF node available (all low priority atm, patches welcome)
> >
> > (and for sx1301 I still need to update my DT overlays with the new clk)
> >
> > > For example, how to make PF_LORA and PF_LORAWAN like Ethernet,
> > PF_INET
> > > and PF_INET6 don't need separate devices either, both use eth0.
> > > https://lkml.org/lkml/2018/8/3/266
> >
> > Jiri, I am expecting the maclorawan driver to lower packets from
> > ETH_P_LORAWAN to ETH_P_LORA in a generic way, so that any of the LoRa
> > device drivers can benefit of it, with maclorawan using the LoRa netlink
> > commands that the individual drivers implement.
> > Not sure what if anything is missing for that in the current revision?
> > Still dealing with the lower-level infrastructure and my test setup ...
> > progressing slowly.
> >
> > I'll probably need to queue the remaining generic LoRaWAN part 1/6 in my
> > tree to resolve this circular dependency between Jian-Hong and me, so
> > that only the soft-MAC implementation remains a separate patch series.
> > The hard-MAC implementations will be on my plate mostly, as both SX1276
> > and SX1301 need the soft-MAC.
>
> On the SX1301 side of things, the ability to send messages as a LoRaWAN
> node device is a niche use case, the majority if not all people will use the
> concentrator card as the pass through gateway to the node.
>
> In this mode of operation the parameters for transmission such as; frequency,
> spreading factor / data rate, power, are given by a remote server and passed
> in from the userspace application which received it.
> Eventually in the kernel these need to be checked locally to ensure regulatory
> compliance.
> To that end I have experimented with framing, as CAN does, so that this
> metadata can be provided on a write from userspace to the SX1301 driver.
>
> Sounds like we need different protocols for framing within the protocol family.
> Raw in the case of nodes and framed with metadata in the case of concentrator
> cards, thoughts?
Yes, I have thought the roles of node and gateway. They may have
different skb passing paths.
As you mentioned, many things of the gateway is controlled by the
remote server. So, I only implement the path for nodes right now.
Maybe, we can have a role flag: node, gateway which can be assigned by
some way. Then, the skb can be decode, checked and passed according
to the role flag. And module also checks the integrity (MIC, length
...) and filter out the bad skb before sends to next stop.
> I will send my experiment RFC to the lpwan mailing list.
Or you can send the RFC first. Then we can have the skb passing path
for gateway and figure out how to put them together.
Does this sound reasonable?
Regards,
Jian-Hong Pan
> > > Am 18.12.18 um 15:27 schrieb Jian-Hong Pan:
> > > >> Sun, Dec 16, 2018 at 11:18:59AM CET, [email protected] wrote:
> > > >>> 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 <[email protected]>
> > > [...]
> > > >>> 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
> > > >>
> > > >> I don't get it. In patch "Add LoRaWAN API declaration for LoRa devices"
> > > >> you add headers for "API" and here you implement functions. That is
> just
> > > >> weird. Does it mean you can have other implementations?
> > > >
> > > > LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa PHY.
> > > > This part is soft-MAC as Andreas mentioned
> > > > http://lists.infradead.org/pipermail/linux-lpwan/2018-
> > > December/000010.html
> > > >
> > > >> Also, you don't really have any user of this API in the set. Please
> > > >> introduce at least 1 driver, preferably more (I see that Andreas has
> > > >> multiple ones in his patchset). You cannot push kernel infrastructure
> > > >> without kernel user.
> > > >
> > > > The soft-MAC is suitable for the LoRa chips' device drivers, like
> > > > sx1276/77/78/79, RFM95/96/97/98W ...
> > > > Still waiting for Andreas' sx1276 version 2 patch and more discussion.
> > >
> > > sx1276 regmap conversion was pushed to my staging tree together with
> > > Ben's sx1301 final conversion last night, lightly tested.
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-
> > > lora.git/log/?h=lora-next
> > >
> > > TBD: rename to sx127x, implement regmap fields, only auto-detect reset
> > > when no OF node available (all low priority atm, patches welcome)
> > >
> > > (and for sx1301 I still need to update my DT overlays with the new clk)
> > >
> > > > For example, how to make PF_LORA and PF_LORAWAN like Ethernet,
> > > PF_INET
> > > > and PF_INET6 don't need separate devices either, both use eth0.
> > > > https://lkml.org/lkml/2018/8/3/266
> > >
> > > Jiri, I am expecting the maclorawan driver to lower packets from
> > > ETH_P_LORAWAN to ETH_P_LORA in a generic way, so that any of the LoRa
> > > device drivers can benefit of it, with maclorawan using the LoRa netlink
> > > commands that the individual drivers implement.
> > > Not sure what if anything is missing for that in the current revision?
> > > Still dealing with the lower-level infrastructure and my test setup ...
> > > progressing slowly.
> > >
> > > I'll probably need to queue the remaining generic LoRaWAN part 1/6 in my
> > > tree to resolve this circular dependency between Jian-Hong and me, so
> > > that only the soft-MAC implementation remains a separate patch series.
> > > The hard-MAC implementations will be on my plate mostly, as both SX1276
> > > and SX1301 need the soft-MAC.
> >
> > On the SX1301 side of things, the ability to send messages as a LoRaWAN
> > node device is a niche use case, the majority if not all people will use the
> > concentrator card as the pass through gateway to the node.
> >
> > In this mode of operation the parameters for transmission such as;
> frequency,
> > spreading factor / data rate, power, are given by a remote server and passed
> > in from the userspace application which received it.
> > Eventually in the kernel these need to be checked locally to ensure
> regulatory
> > compliance.
> > To that end I have experimented with framing, as CAN does, so that this
> > metadata can be provided on a write from userspace to the SX1301 driver.
> >
> > Sounds like we need different protocols for framing within the protocol
> family.
> > Raw in the case of nodes and framed with metadata in the case of
> concentrator
> > cards, thoughts?
>
> Yes, I have thought the roles of node and gateway. They may have
> different skb passing paths.
> As you mentioned, many things of the gateway is controlled by the
> remote server. So, I only implement the path for nodes right now.
> Maybe, we can have a role flag: node, gateway which can be assigned by
> some way. Then, the skb can be decode, checked and passed according
> to the role flag. And module also checks the integrity (MIC, length
> ...) and filter out the bad skb before sends to next stop.
As IP have IPPROTO_TCP, IPPROTO_UDP, etc maybe we can have
LORA_PROTO_MODULE, LORA_PROTO_GATEWAY which dictates the
pathway and skb format.
> > I will send my experiment RFC to the lpwan mailing list.
>
> Or you can send the RFC first. Then we can have the skb passing path
> for gateway and figure out how to put them together.
>
> Does this sound reasonable?
Sent, it's being held at mailing list for moderator approval but should be in
your inboxes currently.
Regards,
Ben
Am 20.12.18 um 11:19 schrieb Ben Whitten:
>>>> The hard-MAC implementations will be on my plate mostly, as both SX1276
>>>> and SX1301 need the soft-MAC.
>>>
>>> On the SX1301 side of things, the ability to send messages as a LoRaWAN
>>> node device is a niche use case, the majority if not all people will use the
>>> concentrator card as the pass through gateway to the node.
>>>
>>> In this mode of operation the parameters for transmission such as;
>> frequency,
>>> spreading factor / data rate, power, are given by a remote server and passed
>>> in from the userspace application which received it.
>>> Eventually in the kernel these need to be checked locally to ensure
>> regulatory
>>> compliance.
>>> To that end I have experimented with framing, as CAN does, so that this
>>> metadata can be provided on a write from userspace to the SX1301 driver.
>>>
>>> Sounds like we need different protocols for framing within the protocol
>> family.
>>> Raw in the case of nodes and framed with metadata in the case of
>> concentrator
>>> cards, thoughts?
>>
>> Yes, I have thought the roles of node and gateway. They may have
>> different skb passing paths.
>> As you mentioned, many things of the gateway is controlled by the
>> remote server. So, I only implement the path for nodes right now.
>> Maybe, we can have a role flag: node, gateway which can be assigned by
>> some way. Then, the skb can be decode, checked and passed according
>> to the role flag. And module also checks the integrity (MIC, length
>> ...) and filter out the bad skb before sends to next stop.
>
> As IP have IPPROTO_TCP, IPPROTO_UDP, etc maybe we can have
> LORA_PROTO_MODULE, LORA_PROTO_GATEWAY which dictates the
> pathway and skb format.
No, please don't. You were on track with your earlier comment: The user
decides what he wants to do by selecting PF_LORAWAN with
SOCK_DGRAM/_SEQPACKET vs. SOCK_RAW (or PF_PACKET with ETH_P_LORA[WAN]).
The terminology "gateway" has little to do with it. I've heard of packet
forwarders using JSON format for communication with their backend, so it
appears to highly depend on what exactly the gateway wants to do and how
the user wants to access the needed data.
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Hi,
On Tue, Dec 18, 2018 at 02:50:58PM +0100, Xue Liu wrote:
> Hello Andreas,
>
> On Mon, 17 Dec 2018 at 15:19, Andreas Färber <[email protected]> wrote:
> >
> > Hello Xue Liu,
> >
> > Am 17.12.18 um 09:50 schrieb Xue Liu:
> > > I have a question about the architecture of your module. AFAIK LoRaWAN
> > > is already the MAC Layer above the LoRa technology. Why do you want to
> > > make a new layer called "maclorawan" ?
> >
> > I had asked Jian-Hong to separate between his soft-MAC implementation
> > and the common bits needed to drive hard-MAC implementations found on
> > several of the hardware modules made available to me.
> >
> As a reference Linux 802.11 uses cfg80211 to talk with hard-MAC devices.
> We may also use the name “cfglora” for hard-MAC implementation.
>
There exists also a cfg802154. :-)
Note that cfg80211 is also for providing a backwardscompatibility to the
wireless ioctl() interface.
In theory it's simple:
netlink API -> SoftMAC (macFOOBAR layer) -> cfgFOOBAR implementation -> driver layer
\-> HardMAC (driver layer) -> cfgFOOBAR implementation
The "difficult" part is providing one userspace interface for both
either SoftMAC layer and all HardMAC drivers.
There both maintainers need to be synchronized.
As a side note I want to mention that the 802.15.4 subsystem supports
currently a HardMAC transceiver in the SoftMAC layer. We don't have
quite HardMAC support yet but are somehow prepared according the
wireless architecture above.
The people who did that didn't wanted to investigate time to implement a
whole HardMAC layer, as I told them when we support more in the SoftMAC
layer it will break their transceiver... so they was fine with that as
warning it seems. :-)
- Alex
Hi Alexander and Xue Liu,
Am 24.12.18 um 16:32 schrieb Alexander Aring:
> On Tue, Dec 18, 2018 at 02:50:58PM +0100, Xue Liu wrote:
>> On Mon, 17 Dec 2018 at 15:19, Andreas Färber <[email protected]> wrote:
>>> Am 17.12.18 um 09:50 schrieb Xue Liu:
>>>> I have a question about the architecture of your module. AFAIK LoRaWAN
>>>> is already the MAC Layer above the LoRa technology. Why do you want to
>>>> make a new layer called "maclorawan" ?
>>>
>>> I had asked Jian-Hong to separate between his soft-MAC implementation
>>> and the common bits needed to drive hard-MAC implementations found on
>>> several of the hardware modules made available to me.
>>>
>> As a reference Linux 802.11 uses cfg80211 to talk with hard-MAC devices.
>> We may also use the name “cfglora” for hard-MAC implementation.
>
> There exists also a cfg802154. :-)
>
> Note that cfg80211 is also for providing a backwardscompatibility to the
> wireless ioctl() interface.
>
> In theory it's simple:
>
> netlink API -> SoftMAC (macFOOBAR layer) -> cfgFOOBAR implementation -> driver layer
> \-> HardMAC (driver layer) -> cfgFOOBAR implementation
So how does cfgFOOBAR relate to nlFOOBAR now? Given that we were told to
use netlink and pointed to some nl802whatever, I am confused about two
people now calling for cfg. We have an nllora stubbed in linux-lora.git,
and I was expecting to see an nllorawan¹ either in this series or on
top. If you're suggesting to rename them technology-neutral, then please
say so clearly - otherwise it sounds to me like you didn't actually look
at the staged code yet or didn't read our previous discussions and lead
our contributors to reinvent things we already have...
We really need to complete the layers from the ground up before we get
lost in more nice-to-have upper layers: For LoRaWAN that means we need
to have TX and RX working for LoRa _and_ FSK. sx1276 still has lots of
hardcoded stuff from my own testing that needs to hook into nllora, and
FSK exists only as ETH_P_FSK constant so far, with no concept for
switching modes yet (which as mentioned in my presentation¹ needs to go
via sleep mode, losing most register settings) nor any netlink support.
Not all drivers need to be at the same implementation level, of course,
but we need at least one that's far enough to validate such patches.
And seeing that I just found a major bug in sx1276 driver's TX path,
apparently no one apart from me is testing that driver - sx128x and
sx1301 were not yet complete enough to transmit, and due to the open
socket address/protocol discussions none can receive yet, so as Jiri
hinted, this LoRaWAN soft-MAC patch series can't have been
runtime-tested against any staged driver at all! => [RFC lora-next v5 6/6]
Therefore I thought in our case some hard-MAC may be easier to validate
LoRaWAN sockets (patch 1/6), to avoid a dependency on completing the MAC
implementation first. For example, iM880, RF1276TS and 32001353 are pure
LoRaWAN modules without raw LoRa support. (Whereas many others support
both and I'm still looking for input on how to best deal with that -
currently exposing them as LoRa devices for maximal flexibility.)
Regards,
Andreas
¹
https://events.linuxfoundation.org/wp-content/uploads/2017/12/ELCE2018_LoRa_final_Andreas-Farber.pdf
https://www.youtube.com/watch?v=Jjel65sZO9M
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
On Fri, Dec 28, 2018 at 05:57:53AM +0100, Andreas Färber wrote:
> Hi Alexander and Xue Liu,
>
> Am 24.12.18 um 16:32 schrieb Alexander Aring:
> > On Tue, Dec 18, 2018 at 02:50:58PM +0100, Xue Liu wrote:
> >> On Mon, 17 Dec 2018 at 15:19, Andreas Färber <[email protected]> wrote:
> >>> Am 17.12.18 um 09:50 schrieb Xue Liu:
> >>>> I have a question about the architecture of your module. AFAIK LoRaWAN
> >>>> is already the MAC Layer above the LoRa technology. Why do you want to
> >>>> make a new layer called "maclorawan" ?
> >>>
> >>> I had asked Jian-Hong to separate between his soft-MAC implementation
> >>> and the common bits needed to drive hard-MAC implementations found on
> >>> several of the hardware modules made available to me.
> >>>
> >> As a reference Linux 802.11 uses cfg80211 to talk with hard-MAC devices.
> >> We may also use the name “cfglora” for hard-MAC implementation.
> >
> > There exists also a cfg802154. :-)
> >
> > Note that cfg80211 is also for providing a backwardscompatibility to the
> > wireless ioctl() interface.
> >
> > In theory it's simple:
> >
> > netlink API -> SoftMAC (macFOOBAR layer) -> cfgFOOBAR implementation -> driver layer
> > \-> HardMAC (driver layer) -> cfgFOOBAR implementation
>
> So how does cfgFOOBAR relate to nlFOOBAR now? Given that we were told to
> use netlink and pointed to some nl802whatever, I am confused about two
> people now calling for cfg. We have an nllora stubbed in linux-lora.git,
> and I was expecting to see an nllorawan¹ either in this series or on
Why there is a different between two lora technologies? This sounds you
driving into two lora subsystems without one userspace api to access them,
this getting worse.
> top. If you're suggesting to rename them technology-neutral, then please
I am sorry, I actually meant that... People tell me that I can't explain
things all the time.
> say so clearly - otherwise it sounds to me like you didn't actually look
> at the staged code yet or didn't read our previous discussions and lead
> our contributors to reinvent things we already have...
>
As example for 802.15.4:
nl802154 (which is one netlink interface for doesn't matter what
kind 802.15.4 device is behind) -> callback structure of cfg802154 which
goes to a somehow 802.15.4 device as SoftMAC layer or HardMAC driver.
> We really need to complete the layers from the ground up before we get
> lost in more nice-to-have upper layers: For LoRaWAN that means we need
> to have TX and RX working for LoRa _and_ FSK. sx1276 still has lots of
> hardcoded stuff from my own testing that needs to hook into nllora, and
> FSK exists only as ETH_P_FSK constant so far, with no concept for
> switching modes yet (which as mentioned in my presentation¹ needs to go
> via sleep mode, losing most register settings) nor any netlink support.
> Not all drivers need to be at the same implementation level, of course,
> but we need at least one that's far enough to validate such patches.
>
Your register behaviour sounds for me like a feature for regmap. Or
either a feature to handle in your subsystem.
> And seeing that I just found a major bug in sx1276 driver's TX path,
> apparently no one apart from me is testing that driver - sx128x and
> sx1301 were not yet complete enough to transmit, and due to the open
> socket address/protocol discussions none can receive yet, so as Jiri
> hinted, this LoRaWAN soft-MAC patch series can't have been
> runtime-tested against any staged driver at all! => [RFC lora-next v5 6/6]
>
aha. When I started working on ieee802154 many times I thought nobody
had really tested it. That's somehow the process of upstream
programming, it's growing over the time. The first implementation is
always somehow crappy, but people working on it and get experience over
the time, you cannot have perfect code.
> Therefore I thought in our case some hard-MAC may be easier to validate
> LoRaWAN sockets (patch 1/6), to avoid a dependency on completing the MAC
> implementation first. For example, iM880, RF1276TS and 32001353 are pure
> LoRaWAN modules without raw LoRa support. (Whereas many others support
> both and I'm still looking for input on how to best deal with that -
> currently exposing them as LoRa devices for maximal flexibility.)
>
So that means you ignore SoftMAC because HardMAC is easier? We actually
go the opposite way to say SoftMAC introduce the most infrastructure and
then say that we will bind HardMAC to it.
Of course binding a socket interface to a datapath is easy.
- Alex
Hi Jian-Hong,
Am 16.12.18 um 11:18 schrieb Jian-Hong Pan:
> This patch adds a new address/protocol family for LoRaWAN network.
> It also implements the the functions and maps to Datagram socket for
> LoRaWAN unconfirmed data messages.
>
> Signed-off-by: Jian-Hong Pan <[email protected]>
[...]
> include/linux/lora/lorawan_netdev.h | 52 +++
> net/lorawan/Kconfig | 10 +
> net/lorawan/Makefile | 2 +
> net/lorawan/socket.c | 686 ++++++++++++++++++++++++++++
> 4 files changed, 750 insertions(+)
> create mode 100644 include/linux/lora/lorawan_netdev.h
> create mode 100644 net/lorawan/Kconfig
> create mode 100644 net/lorawan/Makefile
> create mode 100644 net/lorawan/socket.c
I'm not 100% happy with this yet, but to decouple it from the soft-MAC
discussion (patches 2-6/6) and to allow reuse by Ben, I've staged it in
linux-lora.git.
We can clean it up with incremental patches there (and squash later).
>
> diff --git a/include/linux/lora/lorawan_netdev.h b/include/linux/lora/lorawan_netdev.h
> new file mode 100644
> index 000000000000..5bffb5164f95
> --- /dev/null
> +++ b/include/linux/lora/lorawan_netdev.h
> @@ -0,0 +1,52 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
Is there any practical reason you dual-license your code? My LoRa code
is only GPL - should I reconsider that?
> +/*-
I assume the dash is a typo?
> + * LoRaWAN stack related definitions
> + *
> + * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
> + *
Leftover white line from old license header?
> + */
> +
> +#ifndef __LORAWAN_NET_DEVICE_H__
> +#define __LORAWAN_NET_DEVICE_H__
> +
> +enum {
> + LRW_ADDR_APPEUI,
> + LRW_ADDR_DEVEUI,
> + LRW_ADDR_DEVADDR,
> +};
> +
> +struct lrw_addr_in {
> + int addr_type;
> + union {
> + u64 app_eui;
> + u64 dev_eui;
In my RFC and in linux-lora.git I have a lora_eui typedef - any reason
you're not using it here?
> + u32 devaddr;
> + };
> +};
> +
> +struct sockaddr_lorawan {
> + sa_family_t family; /* AF_LORAWAN */
> + struct lrw_addr_in addr_in;
> +};
> +
> +/**
> + * lrw_mac_cb - This structure holds the control buffer (cb) of sk_buff
> + *
> + * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
> + */
> +struct lrw_mac_cb {
> + u32 devaddr;
> +};
> +
> +/**
> + * lrw_get_mac_cb - Get the LoRaWAN MAC control buffer of the sk_buff
> + * @skb: the exchanging sk_buff
> + *
> + * Return: the pointer of LoRaWAN MAC control buffer
> + */
> +static inline struct lrw_mac_cb *lrw_get_mac_cb(struct sk_buff *skb)
> +{
> + return (struct lrw_mac_cb *)skb->cb;
> +}
For LoRa I have a separate lora/skb.h - suggest to split this off into
its own header consistently.
> +
> +#endif
> diff --git a/net/lorawan/Kconfig b/net/lorawan/Kconfig
> new file mode 100644
> index 000000000000..bf6c9b77573b
> --- /dev/null
> +++ b/net/lorawan/Kconfig
> @@ -0,0 +1,10 @@
> +config LORAWAN
> + tristate "LoRaWAN Network support"
The N in LoRaWAN is already for Network. :)
> + help
> + LoRaWAN defines low data rate, low power and long range wireless
> + wide area networks. It was designed to organize networks of automation
> + devices, such as sensors, switches and actuators. It can operate
> + multiple kilometers wide.
The missing information here to distinguish it from LoRa would be that
it's a client/server technology centered around gateways. In particular
gateways that anyone can run, distinguishing it from (Sigfox or) NB-IoT.
> +
> + Say Y here to compile LoRaWAN support into the kernel or say M to
> + compile it as a module.
> diff --git a/net/lorawan/Makefile b/net/lorawan/Makefile
> new file mode 100644
> index 000000000000..8c923ca6541a
> --- /dev/null
> +++ b/net/lorawan/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_LORAWAN) += lorawan.o
> +lorawan-objs := socket.o
Both Kconfig and Makefile are not integrated into net/ here. That
happens only in 6/6.
> diff --git a/net/lorawan/socket.c b/net/lorawan/socket.c
> new file mode 100644
> index 000000000000..7ef106b877ca
> --- /dev/null
> +++ b/net/lorawan/socket.c
> @@ -0,0 +1,686 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
> +/*-
?
> + * LoRaWAN stack related definitions
> + *
> + * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
> + *
?
> + */
> +
> +#define LORAWAN_MODULE_NAME "lorawan"
> +
> +#define pr_fmt(fmt) LORAWAN_MODULE_NAME ": " fmt
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/list.h>
> +#include <linux/net.h>
> +#include <linux/if_arp.h>
> +#include <linux/termios.h> /* For TIOCOUTQ/INQ */
> +#include <net/sock.h>
> +#include <linux/lora/lorawan_netdev.h>
Please sort headers alphabetically.
> +
> +/**
> + * dgram_sock - This structure holds the states of Datagram socket
> + *
> + * @sk: network layer representation of the socket
> + * sk must be the first member of dgram_sock
Might that sentence be more useful as inline comment below?
> + * @src_devaddr: the LoRaWAN device address for this connection
> + * @bound: this socket is bound or not
> + * @connected: this socket is connected to the destination or not
> + * @want_ack: this socket needs to ack for the connection or not
Doesn't exist below?
> + */
> +struct dgram_sock {
> + struct sock sk;
> + u32 src_devaddr;
> +
> + u8 bound:1;
> + u8 connected:1;
> +};
> +
> +static HLIST_HEAD(dgram_head);
> +static DEFINE_RWLOCK(dgram_lock);
> +
> +static struct dgram_sock *
> +dgram_sk(const struct sock *sk)
> +{
> + return container_of(sk, struct dgram_sock, sk);
> +}
> +
> +static struct net_device *
> +lrw_get_dev_by_addr(struct net *net, u32 devaddr)
> +{
> + __be32 be_addr = cpu_to_be32(devaddr);
> + struct net_device *ndev = NULL;
> +
> + rcu_read_lock();
> + ndev = dev_getbyhwaddr_rcu(net, ARPHRD_LORAWAN, (char *)&be_addr);
> + if (ndev)
> + dev_hold(ndev);
> + rcu_read_unlock();
> +
> + return ndev;
> +}
> +
> +static int
> +dgram_init(struct sock *sk)
> +{
> + return 0;
> +}
> +
> +static void
> +dgram_close(struct sock *sk, long timeout)
> +{
> + sk_common_release(sk);
> +}
> +
> +static int
> +dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
> +{
> + struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
> + struct dgram_sock *ro = dgram_sk(sk);
> + struct net_device *ndev;
> + int ret;
> +
> + lock_sock(sk);
> + ro->bound = 0;
> +
> + ret = -EINVAL;
> + if (len < sizeof(*addr))
> + goto dgram_bind_end;
> +
> + if (addr->family != AF_LORAWAN)
> + goto dgram_bind_end;
> +
> + if (addr->addr_in.addr_type != LRW_ADDR_DEVADDR)
> + goto dgram_bind_end;
> +
> + pr_debug("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
> + ndev = lrw_get_dev_by_addr(sock_net(sk), addr->addr_in.devaddr);
> + if (!ndev) {
> + ret = -ENODEV;
> + goto dgram_bind_end;
> + }
> + netdev_dbg(ndev, "%s: get ndev\n", __func__);
> +
> + if (ndev->type != ARPHRD_LORAWAN) {
> + ret = -ENODEV;
> + goto dgram_bind_end;
This is leaking ndev.
> + }
> +
> + ro->src_devaddr = addr->addr_in.devaddr;
> + ro->bound = 1;
> + ret = 0;
> + dev_put(ndev);
> + pr_debug("%s: bound address %X\n", __func__, ro->src_devaddr);
> +
> +dgram_bind_end:
> + release_sock(sk);
> + return ret;
> +}
> +
> +static int
> +lrw_dev_hard_header(struct sk_buff *skb, struct net_device *ndev,
> + const u32 src_devaddr, size_t len)
> +{
> + /* TODO: Prepare the LoRaWAN sending header here */
I wonder, is your idea that you would always write headers here but have
me ignore them in hard-MAC drivers by accessing data and not head?
I.e., does this dgram (and a later seqpacket) implementation give us not
just the buffer to pass to hard-MAC or soft-MAC but actually LoRa, too,
so that maclorawan needs to further post-processing of header/checksum?
> + return 0;
> +}
> +
> +static int
> +dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
> +{
> + struct dgram_sock *ro = dgram_sk(sk);
> + struct net_device *ndev;
> + struct sk_buff *skb;
> + size_t hlen;
> + size_t tlen;
> + int ret;
> +
> + pr_debug("%s: going to send %zu bytes", __func__, size);
\n
> + if (msg->msg_flags & MSG_OOB) {
> + pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
> + return -EOPNOTSUPP;
> + }
> +
> + pr_debug("%s: check msg_name\n", __func__);
> + if (!ro->connected && !msg->msg_name)
> + return -EDESTADDRREQ;
> + else if (ro->connected && msg->msg_name)
> + return -EISCONN;
> +
> + pr_debug("%s: check bound\n", __func__);
> + if (!ro->bound)
> + ndev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_LORAWAN);
> + else
> + ndev = lrw_get_dev_by_addr(sock_net(sk), ro->src_devaddr);
> +
> + if (!ndev) {
> + pr_debug("no dev\n");
> + ret = -ENXIO;
> + goto dgram_sendmsg_end;
> + }
> +
> + if (size > ndev->mtu) {
> + netdev_dbg(ndev, "size = %zu, mtu = %u\n", size, ndev->mtu);
> + ret = -EMSGSIZE;
> + goto dgram_sendmsg_end;
Leaks at least ndev from lrw_get_dev_by_addr.
> + }
> +
> + netdev_dbg(ndev, "%s: create skb\n", __func__);
> + hlen = LL_RESERVED_SPACE(ndev);
> + tlen = ndev->needed_tailroom;
> + skb = sock_alloc_send_skb(sk, hlen + tlen + size,
> + msg->msg_flags & MSG_DONTWAIT,
> + &ret);
> +
> + if (!skb)
> + goto dgram_sendmsg_no_skb;
> +
> + skb_reserve(skb, hlen);
> + skb_reset_network_header(skb);
> +
> + ret = lrw_dev_hard_header(skb, ndev, 0, size);
> + if (ret < 0)
> + goto dgram_sendmsg_no_skb;
> +
> + ret = memcpy_from_msg(skb_put(skb, size), msg, size);
> + if (ret > 0)
> + goto dgram_sendmsg_err_skb;
> +
> + skb->dev = ndev;
> + skb->protocol = htons(ETH_P_LORAWAN);
> +
> + netdev_dbg(ndev, "%s: push skb to xmit queue\n", __func__);
> + ret = dev_queue_xmit(skb);
> + if (ret > 0)
> + ret = net_xmit_errno(ret);
> + netdev_dbg(ndev, "%s: pushed skb to xmit queue with ret=%d\n",
> + __func__, ret);
> + dev_put(ndev);
> +
> + return ret ?: size;
> +
> +dgram_sendmsg_err_skb:
> + kfree_skb(skb);
> +dgram_sendmsg_no_skb:
> + dev_put(ndev);
> +
> +dgram_sendmsg_end:
> + return ret;
> +}
> +
> +static int
> +dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
> + int noblock, int flags, int *addr_len)
> +{
> + DECLARE_SOCKADDR(struct sockaddr_lorawan *, saddr, msg->msg_name);
> + struct sk_buff *skb;
> + size_t copied = 0;
> + int err;
> +
> + skb = skb_recv_datagram(sk, flags, noblock, &err);
> + if (!skb)
> + goto dgram_recvmsg_end;
> +
> + copied = skb->len;
> + if (len < copied) {
> + msg->msg_flags |= MSG_TRUNC;
> + copied = len;
> + }
> +
> + err = skb_copy_datagram_msg(skb, 0, msg, copied);
> + if (err)
> + goto dgram_recvmsg_done;
> +
> + sock_recv_ts_and_drops(msg, sk, skb);
> + if (saddr) {
> + memset(saddr, 0, sizeof(*saddr));
> + saddr->family = AF_LORAWAN;
> + saddr->addr_in.devaddr = lrw_get_mac_cb(skb)->devaddr;
> + *addr_len = sizeof(*saddr);
> + }
> +
> + if (flags & MSG_TRUNC)
> + copied = skb->len;
> +
> +dgram_recvmsg_done:
> + skb_free_datagram(sk, skb);
> +
> +dgram_recvmsg_end:
> + if (err)
> + return err;
> + return copied;
> +}
> +
> +static int
> +dgram_hash(struct sock *sk)
> +{
> + pr_debug("%s\n", __func__);
> + write_lock_bh(&dgram_lock);
> + sk_add_node(sk, &dgram_head);
> + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
> + write_unlock_bh(&dgram_lock);
> +
> + return 0;
> +}
> +
> +static void
> +dgram_unhash(struct sock *sk)
> +{
> + pr_debug("%s\n", __func__);
> + write_lock_bh(&dgram_lock);
> + if (sk_del_node_init(sk))
> + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
> + write_unlock_bh(&dgram_lock);
> +}
> +
> +static int
> +dgram_connect(struct sock *sk, struct sockaddr *uaddr, int len)
> +{
> + struct dgram_sock *ro = dgram_sk(sk);
> +
> + /* Nodes of LoRaWAN send data to a gateway only, then data is received
> + * and transferred to servers with the gateway's policy.
> + * So, the destination address is not used by nodes.
> + */
> + lock_sock(sk);
> + ro->connected = 1;
> + release_sock(sk);
> +
> + return 0;
> +}
> +
> +static int
> +dgram_disconnect(struct sock *sk, int flags)
> +{
> + struct dgram_sock *ro = dgram_sk(sk);
> +
> + lock_sock(sk);
> + ro->connected = 0;
> + release_sock(sk);
> +
> + return 0;
> +}
> +
> +static int
> +dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
> +{
> + struct net_device *ndev = sk->sk_dst_cache->dev;
> + struct sk_buff *skb;
> + int amount;
> + int err;
> +
> + netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
> + switch (cmd) {
> + case SIOCOUTQ:
> + amount = sk_wmem_alloc_get(sk);
> + err = put_user(amount, (int __user *)arg);
> + break;
> + case SIOCINQ:
> + amount = 0;
> + spin_lock_bh(&sk->sk_receive_queue.lock);
> + skb = skb_peek(&sk->sk_receive_queue);
> + if (skb) {
> + /* We will only return the amount of this packet
> + * since that is all that will be read.
> + */
> + amount = skb->len;
> + }
> + spin_unlock_bh(&sk->sk_receive_queue.lock);
> + err = put_user(amount, (int __user *)arg);
> + break;
> + default:
> + err = -ENOIOCTLCMD;
> + }
> +
> + return err;
> +}
> +
> +static int
> +dgram_getsockopt(struct sock *sk, int level, int optname,
> + char __user *optval, int __user *optlen)
> +{
> + int val, len;
> +
> + if (level != SOL_LORAWAN)
> + return -EOPNOTSUPP;
> +
> + if (get_user(len, optlen))
> + return -EFAULT;
> +
> + len = min_t(unsigned int, len, sizeof(int));
> +
> + switch (optname) {
> + default:
> + return -ENOPROTOOPT;
> + }
> +
> + if (put_user(len, optlen))
> + return -EFAULT;
> +
> + if (copy_to_user(optval, &val, len))
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> +static int
> +dgram_setsockopt(struct sock *sk, int level, int optname,
> + char __user *optval, unsigned int optlen)
> +{
> + int val;
> + int err;
> +
> + err = 0;
> +
> + if (optlen < sizeof(int))
> + return -EINVAL;
> +
> + if (get_user(val, (int __user *)optval))
> + return -EFAULT;
> +
> + lock_sock(sk);
> +
> + switch (optname) {
> + default:
> + err = -ENOPROTOOPT;
> + break;
> + }
> +
> + release_sock(sk);
> +
> + return err;
> +}
> +
> +static struct proto lrw_dgram_prot = {
> + .name = "LoRaWAN",
> + .owner = THIS_MODULE,
> + .obj_size = sizeof(struct dgram_sock),
> + .init = dgram_init,
> + .close = dgram_close,
> + .bind = dgram_bind,
> + .sendmsg = dgram_sendmsg,
> + .recvmsg = dgram_recvmsg,
> + .hash = dgram_hash,
> + .unhash = dgram_unhash,
> + .connect = dgram_connect,
> + .disconnect = dgram_disconnect,
> + .ioctl = dgram_ioctl,
> + .getsockopt = dgram_getsockopt,
> + .setsockopt = dgram_setsockopt,
> +};
> +
> +static int
> +lrw_sock_release(struct socket *sock)
> +{
> + struct sock *sk = sock->sk;
> +
> + if (sk) {
> + sock->sk = NULL;
> + sk->sk_prot->close(sk, 0);
> + }
> +
> + return 0;
> +}
> +
> +static int
> +lrw_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
> +{
> + struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
> + struct sock *sk = sock->sk;
> +
> + pr_debug("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
> + if (sk->sk_prot->bind)
> + return sk->sk_prot->bind(sk, uaddr, addr_len);
> +
> + return sock_no_bind(sock, uaddr, addr_len);
> +}
> +
> +static int
> +lrw_sock_connect(struct socket *sock, struct sockaddr *uaddr,
> + int addr_len, int flags)
> +{
> + struct sock *sk = sock->sk;
> +
> + if (addr_len < sizeof(uaddr->sa_family))
> + return -EINVAL;
> +
> + return sk->sk_prot->connect(sk, uaddr, addr_len);
> +}
> +
> +static int
> +lrw_ndev_ioctl(struct sock *sk, struct ifreq __user *arg, unsigned int cmd)
> +{
> + struct net_device *ndev;
> + struct ifreq ifr;
> + int ret;
> +
> + pr_debug("%s: cmd %ud\n", __func__, cmd);
> + ret = -ENOIOCTLCMD;
> +
> + if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
> + return -EFAULT;
> +
> + ifr.ifr_name[IFNAMSIZ - 1] = 0;
> +
> + dev_load(sock_net(sk), ifr.ifr_name);
> + ndev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
> +
> + netdev_dbg(ndev, "%s: cmd %ud\n", __func__, cmd);
> + if (!ndev)
> + return -ENODEV;
> +
> + if (ndev->type == ARPHRD_LORAWAN && ndev->netdev_ops->ndo_do_ioctl)
> + ret = ndev->netdev_ops->ndo_do_ioctl(ndev, &ifr, cmd);
> +
> + if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
> + ret = -EFAULT;
> + dev_put(ndev);
> +
> + return ret;
> +}
> +
> +static int
> +lrw_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
> +{
> + struct sock *sk = sock->sk;
> +
> + pr_debug("%s: cmd %ud\n", __func__, cmd);
> + switch (cmd) {
> + case SIOCGSTAMP:
> + return sock_get_timestamp(sk, (struct timeval __user *)arg);
> + case SIOCGSTAMPNS:
> + return sock_get_timestampns(sk, (struct timespec __user *)arg);
> + case SIOCOUTQ:
> + case SIOCINQ:
> + if (!sk->sk_prot->ioctl)
> + return -ENOIOCTLCMD;
> + return sk->sk_prot->ioctl(sk, cmd, arg);
> + default:
> + return lrw_ndev_ioctl(sk, (struct ifreq __user *)arg, cmd);
> + }
> +}
> +
> +static int
> +lrw_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
> +{
> + struct sock *sk = sock->sk;
> +
> + pr_debug("%s: going to send %zu bytes\n", __func__, len);
> + return sk->sk_prot->sendmsg(sk, msg, len);
> +}
> +
> +static const struct proto_ops lrw_dgram_ops = {
> + .family = PF_LORAWAN,
> + .owner = THIS_MODULE,
> + .release = lrw_sock_release,
> + .bind = lrw_sock_bind,
> + .connect = lrw_sock_connect,
> + .socketpair = sock_no_socketpair,
> + .accept = sock_no_accept,
> + .getname = sock_no_getname,
> + .poll = datagram_poll,
> + .ioctl = lrw_sock_ioctl,
> + .listen = sock_no_listen,
> + .shutdown = sock_no_shutdown,
> + .setsockopt = sock_common_setsockopt,
> + .getsockopt = sock_common_getsockopt,
> + .sendmsg = lrw_sock_sendmsg,
> + .recvmsg = sock_common_recvmsg,
> + .mmap = sock_no_mmap,
> + .sendpage = sock_no_sendpage,
> +};
> +
> +static int
> +lorawan_creat(struct net *net, struct socket *sock, int protocol, int kern)
create
Also, why lorawan_ here and lrw_ elsewhere?
> +{
> + struct sock *sk;
> + int ret;
> +
> + if (!net_eq(net, &init_net))
> + return -EAFNOSUPPORT;
> +
> + if (sock->type != SOCK_DGRAM)
> + return -EAFNOSUPPORT;
> +
> + /* Allocates enough memory for dgram_sock whose first member is sk */
> + sk = sk_alloc(net, PF_LORAWAN, GFP_KERNEL, &lrw_dgram_prot, kern);
> + if (!sk)
> + return -ENOMEM;
> +
> + sock->ops = &lrw_dgram_ops;
> + sock_init_data(sock, sk);
> + sk->sk_family = PF_LORAWAN;
> + sock_set_flag(sk, SOCK_ZAPPED);
> +
> + if (sk->sk_prot->hash) {
> + ret = sk->sk_prot->hash(sk);
> + if (ret) {
> + sk_common_release(sk);
> + goto lorawan_creat_end;
> + }
> + }
> +
> + if (sk->sk_prot->init) {
> + ret = sk->sk_prot->init(sk);
> + if (ret)
> + sk_common_release(sk);
> + }
> +
> +lorawan_creat_end:
create
> + return ret;
> +}
> +
> +static const struct net_proto_family lorawan_family_ops = {
> + .owner = THIS_MODULE,
> + .family = PF_LORAWAN,
> + .create = lorawan_creat,
> +};
> +
> +static int
> +lrw_dgram_deliver(struct net_device *ndev, struct sk_buff *skb)
> +{
> + struct dgram_sock *ro;
> + struct sock *sk;
> + bool found;
> + int ret;
> +
> + ret = NET_RX_SUCCESS;
> + found = false;
In times of C99 you could probably fold that into the declarations.
> +
> + read_lock(&dgram_lock);
> + sk_for_each(sk, &dgram_head) {
> + ro = dgram_sk(sk);
> + if (cpu_to_be32(ro->src_devaddr) == *(__be32 *)ndev->dev_addr) {
> + found = true;
> + break;
> + }
> + }
> + read_unlock(&dgram_lock);
> +
> + if (!found)
> + goto lrw_dgram_deliver_err;
> +
> + skb = skb_share_check(skb, GFP_ATOMIC);
> + if (!skb)
> + return NET_RX_DROP;
> +
> + if (sock_queue_rcv_skb(sk, skb) < 0)
> + goto lrw_dgram_deliver_err;
> +
> + return ret;
> +
> +lrw_dgram_deliver_err:
> + kfree_skb(skb);
> + ret = NET_RX_DROP;
> + return ret;
> +}
> +
> +static int
> +lorawan_rcv(struct sk_buff *skb, struct net_device *ndev,
> + struct packet_type *pt, struct net_device *orig_ndev)
> +{
> + if (!netif_running(ndev))
> + goto lorawan_rcv_drop;
> +
> + if (!net_eq(dev_net(ndev), &init_net))
> + goto lorawan_rcv_drop;
> +
> + if (ndev->type != ARPHRD_LORAWAN)
> + goto lorawan_rcv_drop;
> +
> + if (skb->pkt_type != PACKET_OTHERHOST)
> + return lrw_dgram_deliver(ndev, skb);
> +
> +lorawan_rcv_drop:
> + kfree_skb(skb);
> + return NET_RX_DROP;
> +}
> +
> +static struct packet_type lorawan_packet_type = {
> + .type = htons(ETH_P_LORAWAN),
> + .func = lorawan_rcv,
> +};
> +
> +static int __init
> +lrw_sock_init(void)
> +{
> + int ret;
> +
> + pr_info("module inserted\n");
> + ret = proto_register(&lrw_dgram_prot, 1);
> + if (ret)
> + goto lrw_sock_init_end;
> +
> + /* Tell SOCKET that we are alive */
Drop?
> + ret = sock_register(&lorawan_family_ops);
> + if (ret)
> + goto lrw_sock_init_err;
> +
> + dev_add_pack(&lorawan_packet_type);
> + ret = 0;
> + goto lrw_sock_init_end;
> +
> +lrw_sock_init_err:
> + proto_unregister(&lrw_dgram_prot);
> +
> +lrw_sock_init_end:
> + return 0;
return ret;?
> +}
> +
> +static void __exit
> +lrw_sock_exit(void)
> +{
> + dev_remove_pack(&lorawan_packet_type);
> + sock_unregister(PF_LORAWAN);
> + proto_unregister(&lrw_dgram_prot);
> + pr_info("module removed\n");
> +}
> +
> +module_init(lrw_sock_init);
> +module_exit(lrw_sock_exit);
> +
> +MODULE_AUTHOR("Jian-Hong Pan, <[email protected]>");
Drop the comma?
> +MODULE_DESCRIPTION("LoRaWAN socket kernel module");
Aren't they all kernel modules? :)
> +MODULE_LICENSE("Dual BSD/GPL");
> +MODULE_ALIAS_NETPROTO(PF_LORAWAN);
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Am 28.12.18 um 16:43 schrieb Alexander Aring:
> On Fri, Dec 28, 2018 at 05:57:53AM +0100, Andreas Färber wrote:
>> Am 24.12.18 um 16:32 schrieb Alexander Aring:
>>> On Tue, Dec 18, 2018 at 02:50:58PM +0100, Xue Liu wrote:
>>>> On Mon, 17 Dec 2018 at 15:19, Andreas Färber <[email protected]> wrote:
>>>>> Am 17.12.18 um 09:50 schrieb Xue Liu:
>>>>>> I have a question about the architecture of your module. AFAIK LoRaWAN
>>>>>> is already the MAC Layer above the LoRa technology. Why do you want to
>>>>>> make a new layer called "maclorawan" ?
>>>>>
>>>>> I had asked Jian-Hong to separate between his soft-MAC implementation
>>>>> and the common bits needed to drive hard-MAC implementations found on
>>>>> several of the hardware modules made available to me.
>>>>>
>>>> As a reference Linux 802.11 uses cfg80211 to talk with hard-MAC devices.
>>>> We may also use the name “cfglora” for hard-MAC implementation.
>>>
>>> There exists also a cfg802154. :-)
>>>
>>> Note that cfg80211 is also for providing a backwardscompatibility to the
>>> wireless ioctl() interface.
>>>
>>> In theory it's simple:
>>>
>>> netlink API -> SoftMAC (macFOOBAR layer) -> cfgFOOBAR implementation -> driver layer
>>> \-> HardMAC (driver layer) -> cfgFOOBAR implementation
>>
>> So how does cfgFOOBAR relate to nlFOOBAR now? Given that we were told to
>> use netlink and pointed to some nl802whatever, I am confused about two
>> people now calling for cfg. We have an nllora stubbed in linux-lora.git,
>> and I was expecting to see an nllorawan¹ either in this series or on
>
> Why there is a different between two lora technologies? This sounds you
> driving into two lora subsystems without one userspace api to access them,
> this getting worse.
This had just been explained to Jiri: LoRa PHY vs. LoRaWAN MAC. Similar
splits exist for other technologies; and how the LoRaWAN soft-MAC can
reuse underlying LoRa facilities is the very subject of this sub-thread!
It entirely depends on what the user wants to do - connect to a LoRaWAN
network as client, forward LoRaWAN packets as a gateway, communicate
peer-to-peer, implement alternative MACs in userspace, ...
So if a user is working on LoRaWAN MAC layer I expect him to deal with
PF_LORAWAN and nllorawan and not need direct access to nllora, as that
would be an implementation detail specific to the soft-MAC; a hard-MAC
should not need to implement nllora, as it may not have that control.
>> top. If you're suggesting to rename them technology-neutral, then please
>
> I am sorry, I actually meant that... People tell me that I can't explain
> things all the time.
>
>> say so clearly - otherwise it sounds to me like you didn't actually look
>> at the staged code yet or didn't read our previous discussions and lead
>> our contributors to reinvent things we already have...
>
> As example for 802.15.4:
>
> nl802154 (which is one netlink interface for doesn't matter what
> kind 802.15.4 device is behind) -> callback structure of cfg802154 which
> goes to a somehow 802.15.4 device as SoftMAC layer or HardMAC driver.
Okay, I took a shortcut there for LoRa and assume we always have a LoRa
netdev (i.e., no theoretical SDR netdev), so that I could postpone
figuring out how to register extra per-netdev cfg structs for quicker
PoC of the netlink interface. :) My get_freq callback will need to be
moved out from struct lora_dev_priv (lora/netdev.h) into its own struct.
>> We really need to complete the layers from the ground up before we get
>> lost in more nice-to-have upper layers: For LoRaWAN that means we need
>> to have TX and RX working for LoRa _and_ FSK. sx1276 still has lots of
>> hardcoded stuff from my own testing that needs to hook into nllora, and
>> FSK exists only as ETH_P_FSK constant so far, with no concept for
>> switching modes yet (which as mentioned in my presentation¹ needs to go
>> via sleep mode, losing most register settings) nor any netlink support.
>> Not all drivers need to be at the same implementation level, of course,
>> but we need at least one that's far enough to validate such patches.
>
> Your register behaviour sounds for me like a feature for regmap. Or
> either a feature to handle in your subsystem.
We don't have regmap caching enabled. sx1276 has no paging implemented,
as it's slightly more complicated than sx1301 (and the "youngest" of 3).
I assume instead of just writing from netlink/cfg callbacks to regmap
we'll need to also save the values in the driver for writing them back
after a mode switch or suspend.
However it's also a question of who initiates the mode switch and how,
presumably via netlink.
>> And seeing that I just found a major bug in sx1276 driver's TX path,
>> apparently no one apart from me is testing that driver - sx128x and
>> sx1301 were not yet complete enough to transmit, and due to the open
>> socket address/protocol discussions none can receive yet, so as Jiri
>> hinted, this LoRaWAN soft-MAC patch series can't have been
>> runtime-tested against any staged driver at all! => [RFC lora-next v5 6/6]
>
> aha. When I started working on ieee802154 many times I thought nobody
> had really tested it. That's somehow the process of upstream
> programming, it's growing over the time. The first implementation is
> always somehow crappy, but people working on it and get experience over
> the time, you cannot have perfect code.
This is not about iterative development, it's about discussing about a
high-level cfg interface on top of nl on top of a PHY that didn't work;
not just some optional offload feature being broken. ;) We may never
have perfect code, but we can't merge a driver that doesn't work at all,
and as long as I know it's not working sufficiently I am holding back on
sending out a v2, in favor of queuing more fixes and cleanups that'll
make it worth people's time to review.
Having the soft-MAC be further implemented and actually using the
functions it implements may avoid some of the declarations Jiri disliked
by just making them static locally where they're needed.
Earlier reviewers were already getting deep into coding style reviews,
but it seems this is not yet a "PATCH" ready for merging after all and
should therefore by its author be labeled "RFC" or at least "RFT" if it
couldn't be tested yet. If it's a "PATCH", I expect to be able to queue
it on my tree if I spot no major design problems or nits the author
could help fix upfront. Compare below.
>> Therefore I thought in our case some hard-MAC may be easier to validate
>> LoRaWAN sockets (patch 1/6), to avoid a dependency on completing the MAC
>> implementation first. For example, iM880, RF1276TS and 32001353 are pure
>> LoRaWAN modules without raw LoRa support. (Whereas many others support
>> both and I'm still looking for input on how to best deal with that -
>> currently exposing them as LoRa devices for maximal flexibility.)
>
> So that means you ignore SoftMAC because HardMAC is easier?
No, I don't ignore it, I'm missing infrastructure to evaluate it!
As Jiri has pointed out earlier, this series is doing two things, 1)
introduce LoRaWAN sockets (that's great, needs review/discussion) and 2)
prepare a soft-MAC implementation with a number of functions that are
not yet called from anywhere. Plus the series omits to extend my
lower-level netlink interfaces and driver(s) with the facilities it'll
need to actually work. The default Sync Word on sx1276 is 0x12, for
instance.
Basically Jian-Hong and I are at odds in which direction the layers
should plug together, with me vehemently against coding LoRaWAN stuff in
LoRa drivers (layering violation!) and instead wanting that centrally in
an optional LoRaWAN soft-MAC module (=maclorawan) on top. Compare 2/6
still reading "driver should implement some of them according to the
usage" - for me that should be a cfg struct at the nllorawan layer,
which he should implement in maclorawan and I only for hard-MAC drivers,
instead of exporting an ABI that's not called by anyone and would
seemingly need to be reimplemented in each and every of my LoRa drivers!
Note that duplicate copies and forks of LoRaWAN soft-MAC userspace
implementations on GitHub were one of the reasons for me to start this
kernel project, so I really, really don't want that here. And 5/6 main.c
appears to still be based around the idea that he gets his own loraX
netdevs, seemingly conflicting by name with mine if they would actually
get created by the functions getting called anywhere...
Maybe you can now grasp my annoyance with this non-RFC v5 making only
cosmetic changes since v2? (and completely lacking any changelog in 0/6)
> We actually
> go the opposite way to say SoftMAC introduce the most infrastructure and
> then say that we will bind HardMAC to it.
The very definition of a hard-MAC is that it does not need a soft-MAC.
So that doesn't make much sense to me. The netlink interfaces for LoRa
(e.g., Join operations for authentication) will be constrained by what
ops the hard-MACs need, whereas we have much more freedoms with our own
soft-MAC. LoRaWAN provides a meta set of operations compared to LoRa+FSK
(e.g., setting the data rate on MAC layer would involve regional
awareness and translates to setting frequency+bandwidth+SF on PHY layer,
and a LoRa transmission/reception will need to set a Sync Word of 0x34);
therefore my saying that for the soft-MAC we need interfaces and
implementations on my LoRa PHY layer that are still incomplete today.
Once the PHY layer is working, the soft-MAC would just need to properly
dispatch - and ideally I would have it provide packets in an ETH_P_LORA
format, so that I don't need duplicate (ETH_P_LORA + ETH_P_LORAWAN)
implementations in each soft-MAC capable LoRa driver. Thus, if the PHY
doesn't transmit (yet), LoRaWAN can't send either.
And that brings us back to the big question circling since months of how
to design these socket layers: Currently I'm using a PF_LORA and was
hoping to extend its sock_addr to cope with the Sync Word, frequency,
etc. and set that via skb for each TX (which raised problems for RX). At
ELCE 2018 it was suggested that we should rather take the easy route of
not defining a PF_LORA and just use PF_PACKET with htons(ETH_P_LORA) and
apply every global setting via netlink; that however appears to obsolete
my lora/dgram.c code dealing with private ifindex field then, so that we
could no longer pass any LoRa-specific per-packet metadata from LoRaWAN
layer down to LoRa either. And since I'm unfamiliar with PF_PACKET, I'm
focusing on making the basics work first before diving into refactorings
that give us no functional benefits.
Anyway, I assume we'll need to translate from LoRaWAN to LoRa skbs
somewhere in this patch series to add the necessary LoRaWAN headers for
SOCK_DGRAM or SOCK_SEQPACKET packets - it's neither in 1/6 nor in 5/6.
Additionally, operations such as Join that the user would request via
netlink command will need to create LoRa skbs in the soft-MAC directly.
So in that way LoRaWAN may be comparable to Wifi connecting to an AP,
whereas LoRa is more like shoveling packets into/out an Ethernet cable.
Hope that explains better from my side as well.
Most of it had been brought up in replies to the cover letter of my
original RFC on netdev list: https://patchwork.ozlabs.org/cover/937545/
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
Andreas Färber <[email protected]> 於 2018年12月29日 週六 下午3:27寫道:
>
> Hi Jian-Hong,
>
> Am 16.12.18 um 11:18 schrieb Jian-Hong Pan:
> > This patch adds a new address/protocol family for LoRaWAN network.
> > It also implements the the functions and maps to Datagram socket for
> > LoRaWAN unconfirmed data messages.
> >
> > Signed-off-by: Jian-Hong Pan <[email protected]>
> [...]
> > include/linux/lora/lorawan_netdev.h | 52 +++
> > net/lorawan/Kconfig | 10 +
> > net/lorawan/Makefile | 2 +
> > net/lorawan/socket.c | 686 ++++++++++++++++++++++++++++
> > 4 files changed, 750 insertions(+)
> > create mode 100644 include/linux/lora/lorawan_netdev.h
> > create mode 100644 net/lorawan/Kconfig
> > create mode 100644 net/lorawan/Makefile
> > create mode 100644 net/lorawan/socket.c
>
> I'm not 100% happy with this yet, but to decouple it from the soft-MAC
> discussion (patches 2-6/6) and to allow reuse by Ben, I've staged it in
> linux-lora.git.
>
> We can clean it up with incremental patches there (and squash later).
>
> >
> > diff --git a/include/linux/lora/lorawan_netdev.h b/include/linux/lora/lorawan_netdev.h
> > new file mode 100644
> > index 000000000000..5bffb5164f95
> > --- /dev/null
> > +++ b/include/linux/lora/lorawan_netdev.h
> > @@ -0,0 +1,52 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
>
> Is there any practical reason you dual-license your code? My LoRa code
> is only GPL - should I reconsider that?
I use dual-license due to the event "[Ksummit-discuss] [CORE TOPIC]
GPL defense issues"
https://lists.linuxfoundation.org/pipermail/ksummit-discuss/2016-August/003580.html
> > +/*-
>
> I assume the dash is a typo?
>
> > + * LoRaWAN stack related definitions
> > + *
> > + * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
> > + *
>
> Leftover white line from old license header?
>
> > + */
> > +
> > +#ifndef __LORAWAN_NET_DEVICE_H__
> > +#define __LORAWAN_NET_DEVICE_H__
> > +
> > +enum {
> > + LRW_ADDR_APPEUI,
> > + LRW_ADDR_DEVEUI,
> > + LRW_ADDR_DEVADDR,
> > +};
> > +
> > +struct lrw_addr_in {
> > + int addr_type;
> > + union {
> > + u64 app_eui;
> > + u64 dev_eui;
>
> In my RFC and in linux-lora.git I have a lora_eui typedef - any reason
> you're not using it here?
lora_eui is defined as a type of u8 array with 8 bytes in
include/linux/lora/dev.h
typedef u8 lora_eui[8];
It is not a basic nor simple data type.
1. There is the endian issue. LoRaWAN uses little endian for
transmission. If u64 data type is used, we can call functions like
cpu_to_le64 and le64_to_cpu to deal the endian jobs directly.
2. We can compare the EUIs with relational operators directly if the
EUI is not a type of array.
> > + u32 devaddr;
> > + };
> > +};
> > +
> > +struct sockaddr_lorawan {
> > + sa_family_t family; /* AF_LORAWAN */
> > + struct lrw_addr_in addr_in;
> > +};
> > +
> > +/**
> > + * lrw_mac_cb - This structure holds the control buffer (cb) of sk_buff
> > + *
> > + * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
> > + */
> > +struct lrw_mac_cb {
> > + u32 devaddr;
> > +};
> > +
> > +/**
> > + * lrw_get_mac_cb - Get the LoRaWAN MAC control buffer of the sk_buff
> > + * @skb: the exchanging sk_buff
> > + *
> > + * Return: the pointer of LoRaWAN MAC control buffer
> > + */
> > +static inline struct lrw_mac_cb *lrw_get_mac_cb(struct sk_buff *skb)
> > +{
> > + return (struct lrw_mac_cb *)skb->cb;
> > +}
>
> For LoRa I have a separate lora/skb.h - suggest to split this off into
> its own header consistently.
Sounds good.
Do you prefer separate them into lora/skb.h or lora/lorawan_skb.h?
> > +
> > +#endif
> > diff --git a/net/lorawan/Kconfig b/net/lorawan/Kconfig
> > new file mode 100644
> > index 000000000000..bf6c9b77573b
> > --- /dev/null
> > +++ b/net/lorawan/Kconfig
> > @@ -0,0 +1,10 @@
> > +config LORAWAN
> > + tristate "LoRaWAN Network support"
>
> The N in LoRaWAN is already for Network. :)
>
> > + help
> > + LoRaWAN defines low data rate, low power and long range wireless
> > + wide area networks. It was designed to organize networks of automation
> > + devices, such as sensors, switches and actuators. It can operate
> > + multiple kilometers wide.
>
> The missing information here to distinguish it from LoRa would be that
> it's a client/server technology centered around gateways. In particular
> gateways that anyone can run, distinguishing it from (Sigfox or) NB-IoT.
>
> > +
> > + Say Y here to compile LoRaWAN support into the kernel or say M to
> > + compile it as a module.
> > diff --git a/net/lorawan/Makefile b/net/lorawan/Makefile
> > new file mode 100644
> > index 000000000000..8c923ca6541a
> > --- /dev/null
> > +++ b/net/lorawan/Makefile
> > @@ -0,0 +1,2 @@
> > +obj-$(CONFIG_LORAWAN) += lorawan.o
> > +lorawan-objs := socket.o
>
> Both Kconfig and Makefile are not integrated into net/ here. That
> happens only in 6/6.
>
> > diff --git a/net/lorawan/socket.c b/net/lorawan/socket.c
> > new file mode 100644
> > index 000000000000..7ef106b877ca
> > --- /dev/null
> > +++ b/net/lorawan/socket.c
> > @@ -0,0 +1,686 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
> > +/*-
>
> ?
>
> > + * LoRaWAN stack related definitions
> > + *
> > + * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
> > + *
>
> ?
>
> > + */
> > +
> > +#define LORAWAN_MODULE_NAME "lorawan"
> > +
> > +#define pr_fmt(fmt) LORAWAN_MODULE_NAME ": " fmt
> > +
> > +#include <linux/init.h>
> > +#include <linux/module.h>
> > +#include <linux/list.h>
> > +#include <linux/net.h>
> > +#include <linux/if_arp.h>
> > +#include <linux/termios.h> /* For TIOCOUTQ/INQ */
> > +#include <net/sock.h>
> > +#include <linux/lora/lorawan_netdev.h>
>
> Please sort headers alphabetically.
>
> > +
> > +/**
> > + * dgram_sock - This structure holds the states of Datagram socket
> > + *
> > + * @sk: network layer representation of the socket
> > + * sk must be the first member of dgram_sock
>
> Might that sentence be more useful as inline comment below?
>
> > + * @src_devaddr: the LoRaWAN device address for this connection
> > + * @bound: this socket is bound or not
> > + * @connected: this socket is connected to the destination or not
> > + * @want_ack: this socket needs to ack for the connection or not
>
> Doesn't exist below?
>
> > + */
> > +struct dgram_sock {
> > + struct sock sk;
> > + u32 src_devaddr;
> > +
> > + u8 bound:1;
> > + u8 connected:1;
> > +};
> > +
> > +static HLIST_HEAD(dgram_head);
> > +static DEFINE_RWLOCK(dgram_lock);
> > +
> > +static struct dgram_sock *
> > +dgram_sk(const struct sock *sk)
> > +{
> > + return container_of(sk, struct dgram_sock, sk);
> > +}
> > +
> > +static struct net_device *
> > +lrw_get_dev_by_addr(struct net *net, u32 devaddr)
> > +{
> > + __be32 be_addr = cpu_to_be32(devaddr);
> > + struct net_device *ndev = NULL;
> > +
> > + rcu_read_lock();
> > + ndev = dev_getbyhwaddr_rcu(net, ARPHRD_LORAWAN, (char *)&be_addr);
> > + if (ndev)
> > + dev_hold(ndev);
> > + rcu_read_unlock();
> > +
> > + return ndev;
> > +}
> > +
> > +static int
> > +dgram_init(struct sock *sk)
> > +{
> > + return 0;
> > +}
> > +
> > +static void
> > +dgram_close(struct sock *sk, long timeout)
> > +{
> > + sk_common_release(sk);
> > +}
> > +
> > +static int
> > +dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
> > +{
> > + struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
> > + struct dgram_sock *ro = dgram_sk(sk);
> > + struct net_device *ndev;
> > + int ret;
> > +
> > + lock_sock(sk);
> > + ro->bound = 0;
> > +
> > + ret = -EINVAL;
> > + if (len < sizeof(*addr))
> > + goto dgram_bind_end;
> > +
> > + if (addr->family != AF_LORAWAN)
> > + goto dgram_bind_end;
> > +
> > + if (addr->addr_in.addr_type != LRW_ADDR_DEVADDR)
> > + goto dgram_bind_end;
> > +
> > + pr_debug("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
> > + ndev = lrw_get_dev_by_addr(sock_net(sk), addr->addr_in.devaddr);
> > + if (!ndev) {
> > + ret = -ENODEV;
> > + goto dgram_bind_end;
> > + }
> > + netdev_dbg(ndev, "%s: get ndev\n", __func__);
> > +
> > + if (ndev->type != ARPHRD_LORAWAN) {
> > + ret = -ENODEV;
> > + goto dgram_bind_end;
>
> This is leaking ndev.
>
> > + }
> > +
> > + ro->src_devaddr = addr->addr_in.devaddr;
> > + ro->bound = 1;
> > + ret = 0;
> > + dev_put(ndev);
> > + pr_debug("%s: bound address %X\n", __func__, ro->src_devaddr);
> > +
> > +dgram_bind_end:
> > + release_sock(sk);
> > + return ret;
> > +}
> > +
> > +static int
> > +lrw_dev_hard_header(struct sk_buff *skb, struct net_device *ndev,
> > + const u32 src_devaddr, size_t len)
> > +{
> > + /* TODO: Prepare the LoRaWAN sending header here */
>
> I wonder, is your idea that you would always write headers here but have
> me ignore them in hard-MAC drivers by accessing data and not head?
>
> I.e., does this dgram (and a later seqpacket) implementation give us not
> just the buffer to pass to hard-MAC or soft-MAC but actually LoRa, too,
> so that maclorawan needs to further post-processing of header/checksum?
I put lrw_dev_hard_header function here for the header processing nnd
thought it might be called in the future originally. However, as you
mentioned, we already have soft and hard-MAC and this abstraction
layer only pass the buffer. So, it can be removed now.
> > + return 0;
> > +}
> > +
> > +static int
> > +dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
> > +{
> > + struct dgram_sock *ro = dgram_sk(sk);
> > + struct net_device *ndev;
> > + struct sk_buff *skb;
> > + size_t hlen;
> > + size_t tlen;
> > + int ret;
> > +
> > + pr_debug("%s: going to send %zu bytes", __func__, size);
>
> \n
>
> > + if (msg->msg_flags & MSG_OOB) {
> > + pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + pr_debug("%s: check msg_name\n", __func__);
> > + if (!ro->connected && !msg->msg_name)
> > + return -EDESTADDRREQ;
> > + else if (ro->connected && msg->msg_name)
> > + return -EISCONN;
> > +
> > + pr_debug("%s: check bound\n", __func__);
> > + if (!ro->bound)
> > + ndev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_LORAWAN);
> > + else
> > + ndev = lrw_get_dev_by_addr(sock_net(sk), ro->src_devaddr);
> > +
> > + if (!ndev) {
> > + pr_debug("no dev\n");
> > + ret = -ENXIO;
> > + goto dgram_sendmsg_end;
> > + }
> > +
> > + if (size > ndev->mtu) {
> > + netdev_dbg(ndev, "size = %zu, mtu = %u\n", size, ndev->mtu);
> > + ret = -EMSGSIZE;
> > + goto dgram_sendmsg_end;
>
> Leaks at least ndev from lrw_get_dev_by_addr.
>
> > + }
> > +
> > + netdev_dbg(ndev, "%s: create skb\n", __func__);
> > + hlen = LL_RESERVED_SPACE(ndev);
> > + tlen = ndev->needed_tailroom;
> > + skb = sock_alloc_send_skb(sk, hlen + tlen + size,
> > + msg->msg_flags & MSG_DONTWAIT,
> > + &ret);
> > +
> > + if (!skb)
> > + goto dgram_sendmsg_no_skb;
> > +
> > + skb_reserve(skb, hlen);
> > + skb_reset_network_header(skb);
> > +
> > + ret = lrw_dev_hard_header(skb, ndev, 0, size);
> > + if (ret < 0)
> > + goto dgram_sendmsg_no_skb;
> > +
> > + ret = memcpy_from_msg(skb_put(skb, size), msg, size);
> > + if (ret > 0)
> > + goto dgram_sendmsg_err_skb;
> > +
> > + skb->dev = ndev;
> > + skb->protocol = htons(ETH_P_LORAWAN);
> > +
> > + netdev_dbg(ndev, "%s: push skb to xmit queue\n", __func__);
> > + ret = dev_queue_xmit(skb);
> > + if (ret > 0)
> > + ret = net_xmit_errno(ret);
> > + netdev_dbg(ndev, "%s: pushed skb to xmit queue with ret=%d\n",
> > + __func__, ret);
> > + dev_put(ndev);
> > +
> > + return ret ?: size;
> > +
> > +dgram_sendmsg_err_skb:
> > + kfree_skb(skb);
> > +dgram_sendmsg_no_skb:
> > + dev_put(ndev);
> > +
> > +dgram_sendmsg_end:
> > + return ret;
> > +}
> > +
> > +static int
> > +dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
> > + int noblock, int flags, int *addr_len)
> > +{
> > + DECLARE_SOCKADDR(struct sockaddr_lorawan *, saddr, msg->msg_name);
> > + struct sk_buff *skb;
> > + size_t copied = 0;
> > + int err;
> > +
> > + skb = skb_recv_datagram(sk, flags, noblock, &err);
> > + if (!skb)
> > + goto dgram_recvmsg_end;
> > +
> > + copied = skb->len;
> > + if (len < copied) {
> > + msg->msg_flags |= MSG_TRUNC;
> > + copied = len;
> > + }
> > +
> > + err = skb_copy_datagram_msg(skb, 0, msg, copied);
> > + if (err)
> > + goto dgram_recvmsg_done;
> > +
> > + sock_recv_ts_and_drops(msg, sk, skb);
> > + if (saddr) {
> > + memset(saddr, 0, sizeof(*saddr));
> > + saddr->family = AF_LORAWAN;
> > + saddr->addr_in.devaddr = lrw_get_mac_cb(skb)->devaddr;
> > + *addr_len = sizeof(*saddr);
> > + }
> > +
> > + if (flags & MSG_TRUNC)
> > + copied = skb->len;
> > +
> > +dgram_recvmsg_done:
> > + skb_free_datagram(sk, skb);
> > +
> > +dgram_recvmsg_end:
> > + if (err)
> > + return err;
> > + return copied;
> > +}
> > +
> > +static int
> > +dgram_hash(struct sock *sk)
> > +{
> > + pr_debug("%s\n", __func__);
> > + write_lock_bh(&dgram_lock);
> > + sk_add_node(sk, &dgram_head);
> > + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
> > + write_unlock_bh(&dgram_lock);
> > +
> > + return 0;
> > +}
> > +
> > +static void
> > +dgram_unhash(struct sock *sk)
> > +{
> > + pr_debug("%s\n", __func__);
> > + write_lock_bh(&dgram_lock);
> > + if (sk_del_node_init(sk))
> > + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
> > + write_unlock_bh(&dgram_lock);
> > +}
> > +
> > +static int
> > +dgram_connect(struct sock *sk, struct sockaddr *uaddr, int len)
> > +{
> > + struct dgram_sock *ro = dgram_sk(sk);
> > +
> > + /* Nodes of LoRaWAN send data to a gateway only, then data is received
> > + * and transferred to servers with the gateway's policy.
> > + * So, the destination address is not used by nodes.
> > + */
> > + lock_sock(sk);
> > + ro->connected = 1;
> > + release_sock(sk);
> > +
> > + return 0;
> > +}
> > +
> > +static int
> > +dgram_disconnect(struct sock *sk, int flags)
> > +{
> > + struct dgram_sock *ro = dgram_sk(sk);
> > +
> > + lock_sock(sk);
> > + ro->connected = 0;
> > + release_sock(sk);
> > +
> > + return 0;
> > +}
> > +
> > +static int
> > +dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
> > +{
> > + struct net_device *ndev = sk->sk_dst_cache->dev;
> > + struct sk_buff *skb;
> > + int amount;
> > + int err;
> > +
> > + netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
> > + switch (cmd) {
> > + case SIOCOUTQ:
> > + amount = sk_wmem_alloc_get(sk);
> > + err = put_user(amount, (int __user *)arg);
> > + break;
> > + case SIOCINQ:
> > + amount = 0;
> > + spin_lock_bh(&sk->sk_receive_queue.lock);
> > + skb = skb_peek(&sk->sk_receive_queue);
> > + if (skb) {
> > + /* We will only return the amount of this packet
> > + * since that is all that will be read.
> > + */
> > + amount = skb->len;
> > + }
> > + spin_unlock_bh(&sk->sk_receive_queue.lock);
> > + err = put_user(amount, (int __user *)arg);
> > + break;
> > + default:
> > + err = -ENOIOCTLCMD;
> > + }
> > +
> > + return err;
> > +}
> > +
> > +static int
> > +dgram_getsockopt(struct sock *sk, int level, int optname,
> > + char __user *optval, int __user *optlen)
> > +{
> > + int val, len;
> > +
> > + if (level != SOL_LORAWAN)
> > + return -EOPNOTSUPP;
> > +
> > + if (get_user(len, optlen))
> > + return -EFAULT;
> > +
> > + len = min_t(unsigned int, len, sizeof(int));
> > +
> > + switch (optname) {
> > + default:
> > + return -ENOPROTOOPT;
> > + }
> > +
> > + if (put_user(len, optlen))
> > + return -EFAULT;
> > +
> > + if (copy_to_user(optval, &val, len))
> > + return -EFAULT;
> > +
> > + return 0;
> > +}
> > +
> > +static int
> > +dgram_setsockopt(struct sock *sk, int level, int optname,
> > + char __user *optval, unsigned int optlen)
> > +{
> > + int val;
> > + int err;
> > +
> > + err = 0;
> > +
> > + if (optlen < sizeof(int))
> > + return -EINVAL;
> > +
> > + if (get_user(val, (int __user *)optval))
> > + return -EFAULT;
> > +
> > + lock_sock(sk);
> > +
> > + switch (optname) {
> > + default:
> > + err = -ENOPROTOOPT;
> > + break;
> > + }
> > +
> > + release_sock(sk);
> > +
> > + return err;
> > +}
> > +
> > +static struct proto lrw_dgram_prot = {
> > + .name = "LoRaWAN",
> > + .owner = THIS_MODULE,
> > + .obj_size = sizeof(struct dgram_sock),
> > + .init = dgram_init,
> > + .close = dgram_close,
> > + .bind = dgram_bind,
> > + .sendmsg = dgram_sendmsg,
> > + .recvmsg = dgram_recvmsg,
> > + .hash = dgram_hash,
> > + .unhash = dgram_unhash,
> > + .connect = dgram_connect,
> > + .disconnect = dgram_disconnect,
> > + .ioctl = dgram_ioctl,
> > + .getsockopt = dgram_getsockopt,
> > + .setsockopt = dgram_setsockopt,
> > +};
> > +
> > +static int
> > +lrw_sock_release(struct socket *sock)
> > +{
> > + struct sock *sk = sock->sk;
> > +
> > + if (sk) {
> > + sock->sk = NULL;
> > + sk->sk_prot->close(sk, 0);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int
> > +lrw_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
> > +{
> > + struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
> > + struct sock *sk = sock->sk;
> > +
> > + pr_debug("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
> > + if (sk->sk_prot->bind)
> > + return sk->sk_prot->bind(sk, uaddr, addr_len);
> > +
> > + return sock_no_bind(sock, uaddr, addr_len);
> > +}
> > +
> > +static int
> > +lrw_sock_connect(struct socket *sock, struct sockaddr *uaddr,
> > + int addr_len, int flags)
> > +{
> > + struct sock *sk = sock->sk;
> > +
> > + if (addr_len < sizeof(uaddr->sa_family))
> > + return -EINVAL;
> > +
> > + return sk->sk_prot->connect(sk, uaddr, addr_len);
> > +}
> > +
> > +static int
> > +lrw_ndev_ioctl(struct sock *sk, struct ifreq __user *arg, unsigned int cmd)
> > +{
> > + struct net_device *ndev;
> > + struct ifreq ifr;
> > + int ret;
> > +
> > + pr_debug("%s: cmd %ud\n", __func__, cmd);
> > + ret = -ENOIOCTLCMD;
> > +
> > + if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
> > + return -EFAULT;
> > +
> > + ifr.ifr_name[IFNAMSIZ - 1] = 0;
> > +
> > + dev_load(sock_net(sk), ifr.ifr_name);
> > + ndev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
> > +
> > + netdev_dbg(ndev, "%s: cmd %ud\n", __func__, cmd);
> > + if (!ndev)
> > + return -ENODEV;
> > +
> > + if (ndev->type == ARPHRD_LORAWAN && ndev->netdev_ops->ndo_do_ioctl)
> > + ret = ndev->netdev_ops->ndo_do_ioctl(ndev, &ifr, cmd);
> > +
> > + if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
> > + ret = -EFAULT;
> > + dev_put(ndev);
> > +
> > + return ret;
> > +}
> > +
> > +static int
> > +lrw_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
> > +{
> > + struct sock *sk = sock->sk;
> > +
> > + pr_debug("%s: cmd %ud\n", __func__, cmd);
> > + switch (cmd) {
> > + case SIOCGSTAMP:
> > + return sock_get_timestamp(sk, (struct timeval __user *)arg);
> > + case SIOCGSTAMPNS:
> > + return sock_get_timestampns(sk, (struct timespec __user *)arg);
> > + case SIOCOUTQ:
> > + case SIOCINQ:
> > + if (!sk->sk_prot->ioctl)
> > + return -ENOIOCTLCMD;
> > + return sk->sk_prot->ioctl(sk, cmd, arg);
> > + default:
> > + return lrw_ndev_ioctl(sk, (struct ifreq __user *)arg, cmd);
> > + }
> > +}
> > +
> > +static int
> > +lrw_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
> > +{
> > + struct sock *sk = sock->sk;
> > +
> > + pr_debug("%s: going to send %zu bytes\n", __func__, len);
> > + return sk->sk_prot->sendmsg(sk, msg, len);
> > +}
> > +
> > +static const struct proto_ops lrw_dgram_ops = {
> > + .family = PF_LORAWAN,
> > + .owner = THIS_MODULE,
> > + .release = lrw_sock_release,
> > + .bind = lrw_sock_bind,
> > + .connect = lrw_sock_connect,
> > + .socketpair = sock_no_socketpair,
> > + .accept = sock_no_accept,
> > + .getname = sock_no_getname,
> > + .poll = datagram_poll,
> > + .ioctl = lrw_sock_ioctl,
> > + .listen = sock_no_listen,
> > + .shutdown = sock_no_shutdown,
> > + .setsockopt = sock_common_setsockopt,
> > + .getsockopt = sock_common_getsockopt,
> > + .sendmsg = lrw_sock_sendmsg,
> > + .recvmsg = sock_common_recvmsg,
> > + .mmap = sock_no_mmap,
> > + .sendpage = sock_no_sendpage,
> > +};
> > +
> > +static int
> > +lorawan_creat(struct net *net, struct socket *sock, int protocol, int kern)
>
> create
> Also, why lorawan_ here and lrw_ elsewhere?
>
> > +{
> > + struct sock *sk;
> > + int ret;
> > +
> > + if (!net_eq(net, &init_net))
> > + return -EAFNOSUPPORT;
> > +
> > + if (sock->type != SOCK_DGRAM)
> > + return -EAFNOSUPPORT;
> > +
> > + /* Allocates enough memory for dgram_sock whose first member is sk */
> > + sk = sk_alloc(net, PF_LORAWAN, GFP_KERNEL, &lrw_dgram_prot, kern);
> > + if (!sk)
> > + return -ENOMEM;
> > +
> > + sock->ops = &lrw_dgram_ops;
> > + sock_init_data(sock, sk);
> > + sk->sk_family = PF_LORAWAN;
> > + sock_set_flag(sk, SOCK_ZAPPED);
> > +
> > + if (sk->sk_prot->hash) {
> > + ret = sk->sk_prot->hash(sk);
> > + if (ret) {
> > + sk_common_release(sk);
> > + goto lorawan_creat_end;
> > + }
> > + }
> > +
> > + if (sk->sk_prot->init) {
> > + ret = sk->sk_prot->init(sk);
> > + if (ret)
> > + sk_common_release(sk);
> > + }
> > +
> > +lorawan_creat_end:
>
> create
>
> > + return ret;
> > +}
> > +
> > +static const struct net_proto_family lorawan_family_ops = {
> > + .owner = THIS_MODULE,
> > + .family = PF_LORAWAN,
> > + .create = lorawan_creat,
> > +};
> > +
> > +static int
> > +lrw_dgram_deliver(struct net_device *ndev, struct sk_buff *skb)
> > +{
> > + struct dgram_sock *ro;
> > + struct sock *sk;
> > + bool found;
> > + int ret;
> > +
> > + ret = NET_RX_SUCCESS;
> > + found = false;
>
> In times of C99 you could probably fold that into the declarations.
>
> > +
> > + read_lock(&dgram_lock);
> > + sk_for_each(sk, &dgram_head) {
> > + ro = dgram_sk(sk);
> > + if (cpu_to_be32(ro->src_devaddr) == *(__be32 *)ndev->dev_addr) {
> > + found = true;
> > + break;
> > + }
> > + }
> > + read_unlock(&dgram_lock);
> > +
> > + if (!found)
> > + goto lrw_dgram_deliver_err;
> > +
> > + skb = skb_share_check(skb, GFP_ATOMIC);
> > + if (!skb)
> > + return NET_RX_DROP;
> > +
> > + if (sock_queue_rcv_skb(sk, skb) < 0)
> > + goto lrw_dgram_deliver_err;
> > +
> > + return ret;
> > +
> > +lrw_dgram_deliver_err:
> > + kfree_skb(skb);
> > + ret = NET_RX_DROP;
> > + return ret;
> > +}
> > +
> > +static int
> > +lorawan_rcv(struct sk_buff *skb, struct net_device *ndev,
> > + struct packet_type *pt, struct net_device *orig_ndev)
> > +{
> > + if (!netif_running(ndev))
> > + goto lorawan_rcv_drop;
> > +
> > + if (!net_eq(dev_net(ndev), &init_net))
> > + goto lorawan_rcv_drop;
> > +
> > + if (ndev->type != ARPHRD_LORAWAN)
> > + goto lorawan_rcv_drop;
> > +
> > + if (skb->pkt_type != PACKET_OTHERHOST)
> > + return lrw_dgram_deliver(ndev, skb);
> > +
> > +lorawan_rcv_drop:
> > + kfree_skb(skb);
> > + return NET_RX_DROP;
> > +}
> > +
> > +static struct packet_type lorawan_packet_type = {
> > + .type = htons(ETH_P_LORAWAN),
> > + .func = lorawan_rcv,
> > +};
> > +
> > +static int __init
> > +lrw_sock_init(void)
> > +{
> > + int ret;
> > +
> > + pr_info("module inserted\n");
> > + ret = proto_register(&lrw_dgram_prot, 1);
> > + if (ret)
> > + goto lrw_sock_init_end;
> > +
> > + /* Tell SOCKET that we are alive */
>
> Drop?
>
> > + ret = sock_register(&lorawan_family_ops);
> > + if (ret)
> > + goto lrw_sock_init_err;
> > +
> > + dev_add_pack(&lorawan_packet_type);
> > + ret = 0;
> > + goto lrw_sock_init_end;
> > +
> > +lrw_sock_init_err:
> > + proto_unregister(&lrw_dgram_prot);
> > +
> > +lrw_sock_init_end:
> > + return 0;
>
> return ret;?
>
> > +}
> > +
> > +static void __exit
> > +lrw_sock_exit(void)
> > +{
> > + dev_remove_pack(&lorawan_packet_type);
> > + sock_unregister(PF_LORAWAN);
> > + proto_unregister(&lrw_dgram_prot);
> > + pr_info("module removed\n");
> > +}
> > +
> > +module_init(lrw_sock_init);
> > +module_exit(lrw_sock_exit);
> > +
> > +MODULE_AUTHOR("Jian-Hong Pan, <[email protected]>");
>
> Drop the comma?
>
> > +MODULE_DESCRIPTION("LoRaWAN socket kernel module");
>
> Aren't they all kernel modules? :)
>
> > +MODULE_LICENSE("Dual BSD/GPL");
> > +MODULE_ALIAS_NETPROTO(PF_LORAWAN);
Thanks for the reviewing. Preparing the fixing patches now. :)
Jain-Hong Pan
Jian-Hong Pan <[email protected]> 於 2019年1月7日 週一 下午10:47寫道:
>
> Andreas Färber <[email protected]> 於 2018年12月29日 週六 下午3:27寫道:
> >
> > Hi Jian-Hong,
> >
> > Am 16.12.18 um 11:18 schrieb Jian-Hong Pan:
> > > This patch adds a new address/protocol family for LoRaWAN network.
> > > It also implements the the functions and maps to Datagram socket for
> > > LoRaWAN unconfirmed data messages.
> > >
> > > Signed-off-by: Jian-Hong Pan <[email protected]>
> > [...]
> > > include/linux/lora/lorawan_netdev.h | 52 +++
> > > net/lorawan/Kconfig | 10 +
> > > net/lorawan/Makefile | 2 +
> > > net/lorawan/socket.c | 686 ++++++++++++++++++++++++++++
> > > 4 files changed, 750 insertions(+)
> > > create mode 100644 include/linux/lora/lorawan_netdev.h
> > > create mode 100644 net/lorawan/Kconfig
> > > create mode 100644 net/lorawan/Makefile
> > > create mode 100644 net/lorawan/socket.c
> >
> > I'm not 100% happy with this yet, but to decouple it from the soft-MAC
> > discussion (patches 2-6/6) and to allow reuse by Ben, I've staged it in
> > linux-lora.git.
> >
> > We can clean it up with incremental patches there (and squash later).
> >
> > >
> > > diff --git a/include/linux/lora/lorawan_netdev.h b/include/linux/lora/lorawan_netdev.h
> > > new file mode 100644
> > > index 000000000000..5bffb5164f95
> > > --- /dev/null
> > > +++ b/include/linux/lora/lorawan_netdev.h
> > > @@ -0,0 +1,52 @@
> > > +/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */
> >
> > Is there any practical reason you dual-license your code? My LoRa code
> > is only GPL - should I reconsider that?
>
> I use dual-license due to the event "[Ksummit-discuss] [CORE TOPIC]
> GPL defense issues"
> https://lists.linuxfoundation.org/pipermail/ksummit-discuss/2016-August/003580.html
>
> > > +/*-
> >
> > I assume the dash is a typo?
> >
> > > + * LoRaWAN stack related definitions
> > > + *
> > > + * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
> > > + *
> >
> > Leftover white line from old license header?
> >
> > > + */
> > > +
> > > +#ifndef __LORAWAN_NET_DEVICE_H__
> > > +#define __LORAWAN_NET_DEVICE_H__
> > > +
> > > +enum {
> > > + LRW_ADDR_APPEUI,
> > > + LRW_ADDR_DEVEUI,
> > > + LRW_ADDR_DEVADDR,
> > > +};
> > > +
> > > +struct lrw_addr_in {
> > > + int addr_type;
> > > + union {
> > > + u64 app_eui;
> > > + u64 dev_eui;
> >
> > In my RFC and in linux-lora.git I have a lora_eui typedef - any reason
> > you're not using it here?
>
> lora_eui is defined as a type of u8 array with 8 bytes in
> include/linux/lora/dev.h
> typedef u8 lora_eui[8];
> It is not a basic nor simple data type.
>
> 1. There is the endian issue. LoRaWAN uses little endian for
> transmission. If u64 data type is used, we can call functions like
> cpu_to_le64 and le64_to_cpu to deal the endian jobs directly.
> 2. We can compare the EUIs with relational operators directly if the
> EUI is not a type of array.
>
> > > + u32 devaddr;
> > > + };
> > > +};
> > > +
> > > +struct sockaddr_lorawan {
> > > + sa_family_t family; /* AF_LORAWAN */
> > > + struct lrw_addr_in addr_in;
> > > +};
> > > +
> > > +/**
> > > + * lrw_mac_cb - This structure holds the control buffer (cb) of sk_buff
> > > + *
> > > + * @devaddr: the LoRaWAN device address of this LoRaWAN hardware
> > > + */
> > > +struct lrw_mac_cb {
> > > + u32 devaddr;
> > > +};
> > > +
> > > +/**
> > > + * lrw_get_mac_cb - Get the LoRaWAN MAC control buffer of the sk_buff
> > > + * @skb: the exchanging sk_buff
> > > + *
> > > + * Return: the pointer of LoRaWAN MAC control buffer
> > > + */
> > > +static inline struct lrw_mac_cb *lrw_get_mac_cb(struct sk_buff *skb)
> > > +{
> > > + return (struct lrw_mac_cb *)skb->cb;
> > > +}
> >
> > For LoRa I have a separate lora/skb.h - suggest to split this off into
> > its own header consistently.
>
> Sounds good.
> Do you prefer separate them into lora/skb.h or lora/lorawan_skb.h?
>
> > > +
> > > +#endif
> > > diff --git a/net/lorawan/Kconfig b/net/lorawan/Kconfig
> > > new file mode 100644
> > > index 000000000000..bf6c9b77573b
> > > --- /dev/null
> > > +++ b/net/lorawan/Kconfig
> > > @@ -0,0 +1,10 @@
> > > +config LORAWAN
> > > + tristate "LoRaWAN Network support"
> >
> > The N in LoRaWAN is already for Network. :)
> >
> > > + help
> > > + LoRaWAN defines low data rate, low power and long range wireless
> > > + wide area networks. It was designed to organize networks of automation
> > > + devices, such as sensors, switches and actuators. It can operate
> > > + multiple kilometers wide.
> >
> > The missing information here to distinguish it from LoRa would be that
> > it's a client/server technology centered around gateways. In particular
> > gateways that anyone can run, distinguishing it from (Sigfox or) NB-IoT.
> >
> > > +
> > > + Say Y here to compile LoRaWAN support into the kernel or say M to
> > > + compile it as a module.
> > > diff --git a/net/lorawan/Makefile b/net/lorawan/Makefile
> > > new file mode 100644
> > > index 000000000000..8c923ca6541a
> > > --- /dev/null
> > > +++ b/net/lorawan/Makefile
> > > @@ -0,0 +1,2 @@
> > > +obj-$(CONFIG_LORAWAN) += lorawan.o
> > > +lorawan-objs := socket.o
> >
> > Both Kconfig and Makefile are not integrated into net/ here. That
> > happens only in 6/6.
> >
> > > diff --git a/net/lorawan/socket.c b/net/lorawan/socket.c
> > > new file mode 100644
> > > index 000000000000..7ef106b877ca
> > > --- /dev/null
> > > +++ b/net/lorawan/socket.c
> > > @@ -0,0 +1,686 @@
> > > +// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
> > > +/*-
> >
> > ?
> >
> > > + * LoRaWAN stack related definitions
> > > + *
> > > + * Copyright (c) 2018 Jian-Hong, Pan <[email protected]>
> > > + *
> >
> > ?
> >
> > > + */
> > > +
> > > +#define LORAWAN_MODULE_NAME "lorawan"
> > > +
> > > +#define pr_fmt(fmt) LORAWAN_MODULE_NAME ": " fmt
> > > +
> > > +#include <linux/init.h>
> > > +#include <linux/module.h>
> > > +#include <linux/list.h>
> > > +#include <linux/net.h>
> > > +#include <linux/if_arp.h>
> > > +#include <linux/termios.h> /* For TIOCOUTQ/INQ */
> > > +#include <net/sock.h>
> > > +#include <linux/lora/lorawan_netdev.h>
> >
> > Please sort headers alphabetically.
> >
> > > +
> > > +/**
> > > + * dgram_sock - This structure holds the states of Datagram socket
> > > + *
> > > + * @sk: network layer representation of the socket
> > > + * sk must be the first member of dgram_sock
> >
> > Might that sentence be more useful as inline comment below?
> >
> > > + * @src_devaddr: the LoRaWAN device address for this connection
> > > + * @bound: this socket is bound or not
> > > + * @connected: this socket is connected to the destination or not
> > > + * @want_ack: this socket needs to ack for the connection or not
> >
> > Doesn't exist below?
> >
> > > + */
> > > +struct dgram_sock {
> > > + struct sock sk;
> > > + u32 src_devaddr;
> > > +
> > > + u8 bound:1;
> > > + u8 connected:1;
> > > +};
> > > +
> > > +static HLIST_HEAD(dgram_head);
> > > +static DEFINE_RWLOCK(dgram_lock);
> > > +
> > > +static struct dgram_sock *
> > > +dgram_sk(const struct sock *sk)
> > > +{
> > > + return container_of(sk, struct dgram_sock, sk);
> > > +}
> > > +
> > > +static struct net_device *
> > > +lrw_get_dev_by_addr(struct net *net, u32 devaddr)
> > > +{
> > > + __be32 be_addr = cpu_to_be32(devaddr);
> > > + struct net_device *ndev = NULL;
> > > +
> > > + rcu_read_lock();
> > > + ndev = dev_getbyhwaddr_rcu(net, ARPHRD_LORAWAN, (char *)&be_addr);
> > > + if (ndev)
> > > + dev_hold(ndev);
> > > + rcu_read_unlock();
> > > +
> > > + return ndev;
> > > +}
> > > +
> > > +static int
> > > +dgram_init(struct sock *sk)
> > > +{
> > > + return 0;
> > > +}
> > > +
> > > +static void
> > > +dgram_close(struct sock *sk, long timeout)
> > > +{
> > > + sk_common_release(sk);
> > > +}
> > > +
> > > +static int
> > > +dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
> > > +{
> > > + struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
> > > + struct dgram_sock *ro = dgram_sk(sk);
> > > + struct net_device *ndev;
> > > + int ret;
> > > +
> > > + lock_sock(sk);
> > > + ro->bound = 0;
> > > +
> > > + ret = -EINVAL;
> > > + if (len < sizeof(*addr))
> > > + goto dgram_bind_end;
> > > +
> > > + if (addr->family != AF_LORAWAN)
> > > + goto dgram_bind_end;
> > > +
> > > + if (addr->addr_in.addr_type != LRW_ADDR_DEVADDR)
> > > + goto dgram_bind_end;
> > > +
> > > + pr_debug("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
> > > + ndev = lrw_get_dev_by_addr(sock_net(sk), addr->addr_in.devaddr);
> > > + if (!ndev) {
> > > + ret = -ENODEV;
> > > + goto dgram_bind_end;
> > > + }
> > > + netdev_dbg(ndev, "%s: get ndev\n", __func__);
> > > +
> > > + if (ndev->type != ARPHRD_LORAWAN) {
> > > + ret = -ENODEV;
> > > + goto dgram_bind_end;
> >
> > This is leaking ndev.
> >
> > > + }
> > > +
> > > + ro->src_devaddr = addr->addr_in.devaddr;
> > > + ro->bound = 1;
> > > + ret = 0;
> > > + dev_put(ndev);
> > > + pr_debug("%s: bound address %X\n", __func__, ro->src_devaddr);
> > > +
> > > +dgram_bind_end:
> > > + release_sock(sk);
> > > + return ret;
> > > +}
> > > +
> > > +static int
> > > +lrw_dev_hard_header(struct sk_buff *skb, struct net_device *ndev,
> > > + const u32 src_devaddr, size_t len)
> > > +{
> > > + /* TODO: Prepare the LoRaWAN sending header here */
> >
> > I wonder, is your idea that you would always write headers here but have
> > me ignore them in hard-MAC drivers by accessing data and not head?
> >
> > I.e., does this dgram (and a later seqpacket) implementation give us not
> > just the buffer to pass to hard-MAC or soft-MAC but actually LoRa, too,
> > so that maclorawan needs to further post-processing of header/checksum?
>
> I put lrw_dev_hard_header function here for the header processing nnd
> thought it might be called in the future originally. However, as you
> mentioned, we already have soft and hard-MAC and this abstraction
> layer only pass the buffer. So, it can be removed now.
>
> > > + return 0;
> > > +}
> > > +
> > > +static int
> > > +dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
> > > +{
> > > + struct dgram_sock *ro = dgram_sk(sk);
> > > + struct net_device *ndev;
> > > + struct sk_buff *skb;
> > > + size_t hlen;
> > > + size_t tlen;
> > > + int ret;
> > > +
> > > + pr_debug("%s: going to send %zu bytes", __func__, size);
> >
> > \n
> >
> > > + if (msg->msg_flags & MSG_OOB) {
> > > + pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags);
> > > + return -EOPNOTSUPP;
> > > + }
> > > +
> > > + pr_debug("%s: check msg_name\n", __func__);
> > > + if (!ro->connected && !msg->msg_name)
> > > + return -EDESTADDRREQ;
> > > + else if (ro->connected && msg->msg_name)
> > > + return -EISCONN;
> > > +
> > > + pr_debug("%s: check bound\n", __func__);
> > > + if (!ro->bound)
> > > + ndev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_LORAWAN);
> > > + else
> > > + ndev = lrw_get_dev_by_addr(sock_net(sk), ro->src_devaddr);
> > > +
> > > + if (!ndev) {
> > > + pr_debug("no dev\n");
> > > + ret = -ENXIO;
> > > + goto dgram_sendmsg_end;
> > > + }
> > > +
> > > + if (size > ndev->mtu) {
> > > + netdev_dbg(ndev, "size = %zu, mtu = %u\n", size, ndev->mtu);
> > > + ret = -EMSGSIZE;
> > > + goto dgram_sendmsg_end;
> >
> > Leaks at least ndev from lrw_get_dev_by_addr.
> >
> > > + }
> > > +
> > > + netdev_dbg(ndev, "%s: create skb\n", __func__);
> > > + hlen = LL_RESERVED_SPACE(ndev);
> > > + tlen = ndev->needed_tailroom;
> > > + skb = sock_alloc_send_skb(sk, hlen + tlen + size,
> > > + msg->msg_flags & MSG_DONTWAIT,
> > > + &ret);
> > > +
> > > + if (!skb)
> > > + goto dgram_sendmsg_no_skb;
> > > +
> > > + skb_reserve(skb, hlen);
> > > + skb_reset_network_header(skb);
> > > +
> > > + ret = lrw_dev_hard_header(skb, ndev, 0, size);
> > > + if (ret < 0)
> > > + goto dgram_sendmsg_no_skb;
> > > +
> > > + ret = memcpy_from_msg(skb_put(skb, size), msg, size);
> > > + if (ret > 0)
> > > + goto dgram_sendmsg_err_skb;
> > > +
> > > + skb->dev = ndev;
> > > + skb->protocol = htons(ETH_P_LORAWAN);
> > > +
> > > + netdev_dbg(ndev, "%s: push skb to xmit queue\n", __func__);
> > > + ret = dev_queue_xmit(skb);
> > > + if (ret > 0)
> > > + ret = net_xmit_errno(ret);
> > > + netdev_dbg(ndev, "%s: pushed skb to xmit queue with ret=%d\n",
> > > + __func__, ret);
> > > + dev_put(ndev);
> > > +
> > > + return ret ?: size;
> > > +
> > > +dgram_sendmsg_err_skb:
> > > + kfree_skb(skb);
> > > +dgram_sendmsg_no_skb:
> > > + dev_put(ndev);
> > > +
> > > +dgram_sendmsg_end:
> > > + return ret;
> > > +}
> > > +
> > > +static int
> > > +dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
> > > + int noblock, int flags, int *addr_len)
> > > +{
> > > + DECLARE_SOCKADDR(struct sockaddr_lorawan *, saddr, msg->msg_name);
> > > + struct sk_buff *skb;
> > > + size_t copied = 0;
> > > + int err;
> > > +
> > > + skb = skb_recv_datagram(sk, flags, noblock, &err);
> > > + if (!skb)
> > > + goto dgram_recvmsg_end;
> > > +
> > > + copied = skb->len;
> > > + if (len < copied) {
> > > + msg->msg_flags |= MSG_TRUNC;
> > > + copied = len;
> > > + }
> > > +
> > > + err = skb_copy_datagram_msg(skb, 0, msg, copied);
> > > + if (err)
> > > + goto dgram_recvmsg_done;
> > > +
> > > + sock_recv_ts_and_drops(msg, sk, skb);
> > > + if (saddr) {
> > > + memset(saddr, 0, sizeof(*saddr));
> > > + saddr->family = AF_LORAWAN;
> > > + saddr->addr_in.devaddr = lrw_get_mac_cb(skb)->devaddr;
> > > + *addr_len = sizeof(*saddr);
> > > + }
> > > +
> > > + if (flags & MSG_TRUNC)
> > > + copied = skb->len;
> > > +
> > > +dgram_recvmsg_done:
> > > + skb_free_datagram(sk, skb);
> > > +
> > > +dgram_recvmsg_end:
> > > + if (err)
> > > + return err;
> > > + return copied;
> > > +}
> > > +
> > > +static int
> > > +dgram_hash(struct sock *sk)
> > > +{
> > > + pr_debug("%s\n", __func__);
> > > + write_lock_bh(&dgram_lock);
> > > + sk_add_node(sk, &dgram_head);
> > > + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
> > > + write_unlock_bh(&dgram_lock);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static void
> > > +dgram_unhash(struct sock *sk)
> > > +{
> > > + pr_debug("%s\n", __func__);
> > > + write_lock_bh(&dgram_lock);
> > > + if (sk_del_node_init(sk))
> > > + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
> > > + write_unlock_bh(&dgram_lock);
> > > +}
> > > +
> > > +static int
> > > +dgram_connect(struct sock *sk, struct sockaddr *uaddr, int len)
> > > +{
> > > + struct dgram_sock *ro = dgram_sk(sk);
> > > +
> > > + /* Nodes of LoRaWAN send data to a gateway only, then data is received
> > > + * and transferred to servers with the gateway's policy.
> > > + * So, the destination address is not used by nodes.
> > > + */
> > > + lock_sock(sk);
> > > + ro->connected = 1;
> > > + release_sock(sk);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int
> > > +dgram_disconnect(struct sock *sk, int flags)
> > > +{
> > > + struct dgram_sock *ro = dgram_sk(sk);
> > > +
> > > + lock_sock(sk);
> > > + ro->connected = 0;
> > > + release_sock(sk);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int
> > > +dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
> > > +{
> > > + struct net_device *ndev = sk->sk_dst_cache->dev;
> > > + struct sk_buff *skb;
> > > + int amount;
> > > + int err;
> > > +
> > > + netdev_dbg(ndev, "%s: ioctl file (cmd=0x%X)\n", __func__, cmd);
> > > + switch (cmd) {
> > > + case SIOCOUTQ:
> > > + amount = sk_wmem_alloc_get(sk);
> > > + err = put_user(amount, (int __user *)arg);
> > > + break;
> > > + case SIOCINQ:
> > > + amount = 0;
> > > + spin_lock_bh(&sk->sk_receive_queue.lock);
> > > + skb = skb_peek(&sk->sk_receive_queue);
> > > + if (skb) {
> > > + /* We will only return the amount of this packet
> > > + * since that is all that will be read.
> > > + */
> > > + amount = skb->len;
> > > + }
> > > + spin_unlock_bh(&sk->sk_receive_queue.lock);
> > > + err = put_user(amount, (int __user *)arg);
> > > + break;
> > > + default:
> > > + err = -ENOIOCTLCMD;
> > > + }
> > > +
> > > + return err;
> > > +}
> > > +
> > > +static int
> > > +dgram_getsockopt(struct sock *sk, int level, int optname,
> > > + char __user *optval, int __user *optlen)
> > > +{
> > > + int val, len;
> > > +
> > > + if (level != SOL_LORAWAN)
> > > + return -EOPNOTSUPP;
> > > +
> > > + if (get_user(len, optlen))
> > > + return -EFAULT;
> > > +
> > > + len = min_t(unsigned int, len, sizeof(int));
> > > +
> > > + switch (optname) {
> > > + default:
> > > + return -ENOPROTOOPT;
> > > + }
> > > +
> > > + if (put_user(len, optlen))
> > > + return -EFAULT;
> > > +
> > > + if (copy_to_user(optval, &val, len))
> > > + return -EFAULT;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int
> > > +dgram_setsockopt(struct sock *sk, int level, int optname,
> > > + char __user *optval, unsigned int optlen)
> > > +{
> > > + int val;
> > > + int err;
> > > +
> > > + err = 0;
> > > +
> > > + if (optlen < sizeof(int))
> > > + return -EINVAL;
> > > +
> > > + if (get_user(val, (int __user *)optval))
> > > + return -EFAULT;
> > > +
> > > + lock_sock(sk);
> > > +
> > > + switch (optname) {
> > > + default:
> > > + err = -ENOPROTOOPT;
> > > + break;
> > > + }
> > > +
> > > + release_sock(sk);
> > > +
> > > + return err;
> > > +}
> > > +
> > > +static struct proto lrw_dgram_prot = {
> > > + .name = "LoRaWAN",
> > > + .owner = THIS_MODULE,
> > > + .obj_size = sizeof(struct dgram_sock),
> > > + .init = dgram_init,
> > > + .close = dgram_close,
> > > + .bind = dgram_bind,
> > > + .sendmsg = dgram_sendmsg,
> > > + .recvmsg = dgram_recvmsg,
> > > + .hash = dgram_hash,
> > > + .unhash = dgram_unhash,
> > > + .connect = dgram_connect,
> > > + .disconnect = dgram_disconnect,
> > > + .ioctl = dgram_ioctl,
> > > + .getsockopt = dgram_getsockopt,
> > > + .setsockopt = dgram_setsockopt,
> > > +};
> > > +
> > > +static int
> > > +lrw_sock_release(struct socket *sock)
> > > +{
> > > + struct sock *sk = sock->sk;
> > > +
> > > + if (sk) {
> > > + sock->sk = NULL;
> > > + sk->sk_prot->close(sk, 0);
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int
> > > +lrw_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
> > > +{
> > > + struct sockaddr_lorawan *addr = (struct sockaddr_lorawan *)uaddr;
> > > + struct sock *sk = sock->sk;
> > > +
> > > + pr_debug("%s: bind address %X\n", __func__, addr->addr_in.devaddr);
> > > + if (sk->sk_prot->bind)
> > > + return sk->sk_prot->bind(sk, uaddr, addr_len);
> > > +
> > > + return sock_no_bind(sock, uaddr, addr_len);
> > > +}
> > > +
> > > +static int
> > > +lrw_sock_connect(struct socket *sock, struct sockaddr *uaddr,
> > > + int addr_len, int flags)
> > > +{
> > > + struct sock *sk = sock->sk;
> > > +
> > > + if (addr_len < sizeof(uaddr->sa_family))
> > > + return -EINVAL;
> > > +
> > > + return sk->sk_prot->connect(sk, uaddr, addr_len);
> > > +}
> > > +
> > > +static int
> > > +lrw_ndev_ioctl(struct sock *sk, struct ifreq __user *arg, unsigned int cmd)
> > > +{
> > > + struct net_device *ndev;
> > > + struct ifreq ifr;
> > > + int ret;
> > > +
> > > + pr_debug("%s: cmd %ud\n", __func__, cmd);
> > > + ret = -ENOIOCTLCMD;
> > > +
> > > + if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
> > > + return -EFAULT;
> > > +
> > > + ifr.ifr_name[IFNAMSIZ - 1] = 0;
> > > +
> > > + dev_load(sock_net(sk), ifr.ifr_name);
> > > + ndev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
> > > +
> > > + netdev_dbg(ndev, "%s: cmd %ud\n", __func__, cmd);
> > > + if (!ndev)
> > > + return -ENODEV;
> > > +
> > > + if (ndev->type == ARPHRD_LORAWAN && ndev->netdev_ops->ndo_do_ioctl)
> > > + ret = ndev->netdev_ops->ndo_do_ioctl(ndev, &ifr, cmd);
> > > +
> > > + if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
> > > + ret = -EFAULT;
> > > + dev_put(ndev);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static int
> > > +lrw_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
> > > +{
> > > + struct sock *sk = sock->sk;
> > > +
> > > + pr_debug("%s: cmd %ud\n", __func__, cmd);
> > > + switch (cmd) {
> > > + case SIOCGSTAMP:
> > > + return sock_get_timestamp(sk, (struct timeval __user *)arg);
> > > + case SIOCGSTAMPNS:
> > > + return sock_get_timestampns(sk, (struct timespec __user *)arg);
> > > + case SIOCOUTQ:
> > > + case SIOCINQ:
> > > + if (!sk->sk_prot->ioctl)
> > > + return -ENOIOCTLCMD;
> > > + return sk->sk_prot->ioctl(sk, cmd, arg);
> > > + default:
> > > + return lrw_ndev_ioctl(sk, (struct ifreq __user *)arg, cmd);
> > > + }
> > > +}
> > > +
> > > +static int
> > > +lrw_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
> > > +{
> > > + struct sock *sk = sock->sk;
> > > +
> > > + pr_debug("%s: going to send %zu bytes\n", __func__, len);
> > > + return sk->sk_prot->sendmsg(sk, msg, len);
> > > +}
> > > +
> > > +static const struct proto_ops lrw_dgram_ops = {
> > > + .family = PF_LORAWAN,
> > > + .owner = THIS_MODULE,
> > > + .release = lrw_sock_release,
> > > + .bind = lrw_sock_bind,
> > > + .connect = lrw_sock_connect,
> > > + .socketpair = sock_no_socketpair,
> > > + .accept = sock_no_accept,
> > > + .getname = sock_no_getname,
> > > + .poll = datagram_poll,
> > > + .ioctl = lrw_sock_ioctl,
> > > + .listen = sock_no_listen,
> > > + .shutdown = sock_no_shutdown,
> > > + .setsockopt = sock_common_setsockopt,
> > > + .getsockopt = sock_common_getsockopt,
> > > + .sendmsg = lrw_sock_sendmsg,
> > > + .recvmsg = sock_common_recvmsg,
> > > + .mmap = sock_no_mmap,
> > > + .sendpage = sock_no_sendpage,
> > > +};
> > > +
> > > +static int
> > > +lorawan_creat(struct net *net, struct socket *sock, int protocol, int kern)
> >
> > create
> > Also, why lorawan_ here and lrw_ elsewhere?
> >
> > > +{
> > > + struct sock *sk;
> > > + int ret;
> > > +
> > > + if (!net_eq(net, &init_net))
> > > + return -EAFNOSUPPORT;
> > > +
> > > + if (sock->type != SOCK_DGRAM)
> > > + return -EAFNOSUPPORT;
> > > +
> > > + /* Allocates enough memory for dgram_sock whose first member is sk */
> > > + sk = sk_alloc(net, PF_LORAWAN, GFP_KERNEL, &lrw_dgram_prot, kern);
> > > + if (!sk)
> > > + return -ENOMEM;
> > > +
> > > + sock->ops = &lrw_dgram_ops;
> > > + sock_init_data(sock, sk);
> > > + sk->sk_family = PF_LORAWAN;
> > > + sock_set_flag(sk, SOCK_ZAPPED);
> > > +
> > > + if (sk->sk_prot->hash) {
> > > + ret = sk->sk_prot->hash(sk);
> > > + if (ret) {
> > > + sk_common_release(sk);
> > > + goto lorawan_creat_end;
> > > + }
> > > + }
> > > +
> > > + if (sk->sk_prot->init) {
> > > + ret = sk->sk_prot->init(sk);
> > > + if (ret)
> > > + sk_common_release(sk);
> > > + }
> > > +
> > > +lorawan_creat_end:
> >
> > create
> >
> > > + return ret;
> > > +}
> > > +
> > > +static const struct net_proto_family lorawan_family_ops = {
> > > + .owner = THIS_MODULE,
> > > + .family = PF_LORAWAN,
> > > + .create = lorawan_creat,
> > > +};
> > > +
> > > +static int
> > > +lrw_dgram_deliver(struct net_device *ndev, struct sk_buff *skb)
> > > +{
> > > + struct dgram_sock *ro;
> > > + struct sock *sk;
> > > + bool found;
> > > + int ret;
> > > +
> > > + ret = NET_RX_SUCCESS;
> > > + found = false;
> >
> > In times of C99 you could probably fold that into the declarations.
I have thought this issue many times, but give up and just follow
"Reverse XMAS tree declarations" rule in net subsystem. [1]
David also gave the related review comment. [2]
[1] https://lore.kernel.org/patchwork/patch/732076/
[2] https://lkml.org/lkml/2018/11/5/916
Regards,
Jian-Hong Pan
> > > +
> > > + read_lock(&dgram_lock);
> > > + sk_for_each(sk, &dgram_head) {
> > > + ro = dgram_sk(sk);
> > > + if (cpu_to_be32(ro->src_devaddr) == *(__be32 *)ndev->dev_addr) {
> > > + found = true;
> > > + break;
> > > + }
> > > + }
> > > + read_unlock(&dgram_lock);
> > > +
> > > + if (!found)
> > > + goto lrw_dgram_deliver_err;
> > > +
> > > + skb = skb_share_check(skb, GFP_ATOMIC);
> > > + if (!skb)
> > > + return NET_RX_DROP;
> > > +
> > > + if (sock_queue_rcv_skb(sk, skb) < 0)
> > > + goto lrw_dgram_deliver_err;
> > > +
> > > + return ret;
> > > +
> > > +lrw_dgram_deliver_err:
> > > + kfree_skb(skb);
> > > + ret = NET_RX_DROP;
> > > + return ret;
> > > +}
> > > +
> > > +static int
> > > +lorawan_rcv(struct sk_buff *skb, struct net_device *ndev,
> > > + struct packet_type *pt, struct net_device *orig_ndev)
> > > +{
> > > + if (!netif_running(ndev))
> > > + goto lorawan_rcv_drop;
> > > +
> > > + if (!net_eq(dev_net(ndev), &init_net))
> > > + goto lorawan_rcv_drop;
> > > +
> > > + if (ndev->type != ARPHRD_LORAWAN)
> > > + goto lorawan_rcv_drop;
> > > +
> > > + if (skb->pkt_type != PACKET_OTHERHOST)
> > > + return lrw_dgram_deliver(ndev, skb);
> > > +
> > > +lorawan_rcv_drop:
> > > + kfree_skb(skb);
> > > + return NET_RX_DROP;
> > > +}
> > > +
> > > +static struct packet_type lorawan_packet_type = {
> > > + .type = htons(ETH_P_LORAWAN),
> > > + .func = lorawan_rcv,
> > > +};
> > > +
> > > +static int __init
> > > +lrw_sock_init(void)
> > > +{
> > > + int ret;
> > > +
> > > + pr_info("module inserted\n");
> > > + ret = proto_register(&lrw_dgram_prot, 1);
> > > + if (ret)
> > > + goto lrw_sock_init_end;
> > > +
> > > + /* Tell SOCKET that we are alive */
> >
> > Drop?
> >
> > > + ret = sock_register(&lorawan_family_ops);
> > > + if (ret)
> > > + goto lrw_sock_init_err;
> > > +
> > > + dev_add_pack(&lorawan_packet_type);
> > > + ret = 0;
> > > + goto lrw_sock_init_end;
> > > +
> > > +lrw_sock_init_err:
> > > + proto_unregister(&lrw_dgram_prot);
> > > +
> > > +lrw_sock_init_end:
> > > + return 0;
> >
> > return ret;?
> >
> > > +}
> > > +
> > > +static void __exit
> > > +lrw_sock_exit(void)
> > > +{
> > > + dev_remove_pack(&lorawan_packet_type);
> > > + sock_unregister(PF_LORAWAN);
> > > + proto_unregister(&lrw_dgram_prot);
> > > + pr_info("module removed\n");
> > > +}
> > > +
> > > +module_init(lrw_sock_init);
> > > +module_exit(lrw_sock_exit);
> > > +
> > > +MODULE_AUTHOR("Jian-Hong Pan, <[email protected]>");
> >
> > Drop the comma?
> >
> > > +MODULE_DESCRIPTION("LoRaWAN socket kernel module");
> >
> > Aren't they all kernel modules? :)
> >
> > > +MODULE_LICENSE("Dual BSD/GPL");
> > > +MODULE_ALIAS_NETPROTO(PF_LORAWAN);
>
> Thanks for the reviewing. Preparing the fixing patches now. :)
>
> Jain-Hong Pan