diff mbox series

[v5,5/6] net: maclorawan: Implement maclorawan class module

Message ID 20181216101858.9585-6-starnight@g.ncu.edu.tw
State Changes Requested, archived
Delegated to: David Miller
Headers show
Series net: lorawan: Add LoRaWAN soft MAC module | expand

Commit Message

Jian-Hong Pan Dec. 16, 2018, 10:18 a.m. UTC
LoRaWAN defined by LoRa Alliance(TM) is the MAC layer over LoRa devices.

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

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

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

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

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

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

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

Comments

Jiri Pirko Dec. 17, 2018, 2:02 p.m. UTC | #1
Sun, Dec 16, 2018 at 11:18:59AM CET, starnight@g.ncu.edu.tw 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 <starnight@g.ncu.edu.tw>
>---
>V2:
>- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
>  LoRaWAN Soft MAC modules
>- Modify for Big/Little-Endian
>- Use SPDX license identifiers
>
>V3:
>- Remove the decoration word - inline of the functions
>- Order local variables from longest to shortest line in the functions
>- Change the calling mac_cb function to lrw_get_mac_cb macro
>
>V4:
>- Fix the delay period between RX window#1 and window#2
>- Fix by coding style report from scripts/checkpatch.pl
>
>V5:
>- Initial rx_skb_list when it is allocated with LoRa hardware
>- Check the sk_buff's data length before access it
>- Deal FPort field and decrypt payload in lrw_parse_frame function
>- Drop the recieved frame if parse failed
>- Fix the bug which passes wrong skb properties from maclorawan to lorawan module
>
> net/maclorawan/Kconfig  |  14 +
> net/maclorawan/Makefile |   2 +
> net/maclorawan/mac.c    | 555 ++++++++++++++++++++++++++++++++++++
> net/maclorawan/main.c   | 606 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 1177 insertions(+)
> create mode 100644 net/maclorawan/Kconfig
> create mode 100644 net/maclorawan/Makefile
> create mode 100644 net/maclorawan/mac.c
> create mode 100644 net/maclorawan/main.c


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.
Jian-Hong Pan Dec. 18, 2018, 2:27 p.m. UTC | #2
> Sun, Dec 16, 2018 at 11:18:59AM CET, starnight@g.ncu.edu.tw 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 <starnight@g.ncu.edu.tw>
> >---
> >V2:
> >- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
> >  LoRaWAN Soft MAC modules
> >- Modify for Big/Little-Endian
> >- Use SPDX license identifiers
> >
> >V3:
> >- Remove the decoration word - inline of the functions
> >- Order local variables from longest to shortest line in the functions
> >- Change the calling mac_cb function to lrw_get_mac_cb macro
> >
> >V4:
> >- Fix the delay period between RX window#1 and window#2
> >- Fix by coding style report from scripts/checkpatch.pl
> >
> >V5:
> >- Initial rx_skb_list when it is allocated with LoRa hardware
> >- Check the sk_buff's data length before access it
> >- Deal FPort field and decrypt payload in lrw_parse_frame function
> >- Drop the recieved frame if parse failed
> >- Fix the bug which passes wrong skb properties from maclorawan to lorawan module
> >
> > net/maclorawan/Kconfig  |  14 +
> > net/maclorawan/Makefile |   2 +
> > net/maclorawan/mac.c    | 555 ++++++++++++++++++++++++++++++++++++
> > net/maclorawan/main.c   | 606 ++++++++++++++++++++++++++++++++++++++++
> > 4 files changed, 1177 insertions(+)
> > create mode 100644 net/maclorawan/Kconfig
> > create mode 100644 net/maclorawan/Makefile
> > create mode 100644 net/maclorawan/mac.c
> > create mode 100644 net/maclorawan/main.c
>
>
> 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
Jiri Pirko Dec. 18, 2018, 2:27 p.m. UTC | #3
Tue, Dec 18, 2018 at 03:27:09PM CET, starnight@g.ncu.edu.tw wrote:
>> Sun, Dec 16, 2018 at 11:18:59AM CET, starnight@g.ncu.edu.tw 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 <starnight@g.ncu.edu.tw>
>> >---
>> >V2:
>> >- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
>> >  LoRaWAN Soft MAC modules
>> >- Modify for Big/Little-Endian
>> >- Use SPDX license identifiers
>> >
>> >V3:
>> >- Remove the decoration word - inline of the functions
>> >- Order local variables from longest to shortest line in the functions
>> >- Change the calling mac_cb function to lrw_get_mac_cb macro
>> >
>> >V4:
>> >- Fix the delay period between RX window#1 and window#2
>> >- Fix by coding style report from scripts/checkpatch.pl
>> >
>> >V5:
>> >- Initial rx_skb_list when it is allocated with LoRa hardware
>> >- Check the sk_buff's data length before access it
>> >- Deal FPort field and decrypt payload in lrw_parse_frame function
>> >- Drop the recieved frame if parse failed
>> >- Fix the bug which passes wrong skb properties from maclorawan to lorawan module
>> >
>> > net/maclorawan/Kconfig  |  14 +
>> > net/maclorawan/Makefile |   2 +
>> > net/maclorawan/mac.c    | 555 ++++++++++++++++++++++++++++++++++++
>> > net/maclorawan/main.c   | 606 ++++++++++++++++++++++++++++++++++++++++
>> > 4 files changed, 1177 insertions(+)
>> > create mode 100644 net/maclorawan/Kconfig
>> > create mode 100644 net/maclorawan/Makefile
>> > create mode 100644 net/maclorawan/mac.c
>> > create mode 100644 net/maclorawan/main.c
>>
>>
>> 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
Jian-Hong Pan Dec. 18, 2018, 3:34 p.m. UTC | #4
> Tue, Dec 18, 2018 at 03:27:09PM CET, starnight@g.ncu.edu.tw wrote:
> >> Sun, Dec 16, 2018 at 11:18:59AM CET, starnight@g.ncu.edu.tw 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 <starnight@g.ncu.edu.tw>
> >> >---
> >> >V2:
> >> >- Split the LoRaWAN class module patch in V1 into LoRaWAN socket and
> >> >  LoRaWAN Soft MAC modules
> >> >- Modify for Big/Little-Endian
> >> >- Use SPDX license identifiers
> >> >
> >> >V3:
> >> >- Remove the decoration word - inline of the functions
> >> >- Order local variables from longest to shortest line in the functions
> >> >- Change the calling mac_cb function to lrw_get_mac_cb macro
> >> >
> >> >V4:
> >> >- Fix the delay period between RX window#1 and window#2
> >> >- Fix by coding style report from scripts/checkpatch.pl
> >> >
> >> >V5:
> >> >- Initial rx_skb_list when it is allocated with LoRa hardware
> >> >- Check the sk_buff's data length before access it
> >> >- Deal FPort field and decrypt payload in lrw_parse_frame function
> >> >- Drop the recieved frame if parse failed
> >> >- Fix the bug which passes wrong skb properties from maclorawan to lorawan module
> >> >
> >> > net/maclorawan/Kconfig  |  14 +
> >> > net/maclorawan/Makefile |   2 +
> >> > net/maclorawan/mac.c    | 555 ++++++++++++++++++++++++++++++++++++
> >> > net/maclorawan/main.c   | 606 ++++++++++++++++++++++++++++++++++++++++
> >> > 4 files changed, 1177 insertions(+)
> >> > create mode 100644 net/maclorawan/Kconfig
> >> > create mode 100644 net/maclorawan/Makefile
> >> > create mode 100644 net/maclorawan/mac.c
> >> > create mode 100644 net/maclorawan/main.c
> >>
> >>
> >> 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?
Andreas Färber Dec. 18, 2018, 6:49 p.m. UTC | #5
Am 18.12.18 um 15:27 schrieb Jian-Hong Pan:
>> Sun, Dec 16, 2018 at 11:18:59AM CET, starnight@g.ncu.edu.tw 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 <starnight@g.ncu.edu.tw>
[...]
>>> 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
Ben Whitten Dec. 19, 2018, 11:27 a.m. UTC | #6
> Am 18.12.18 um 15:27 schrieb Jian-Hong Pan:
> >> Sun, Dec 16, 2018 at 11:18:59AM CET, starnight@g.ncu.edu.tw 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 <starnight@g.ncu.edu.tw>
> [...]
> >>> 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
Jian-Hong Pan Dec. 19, 2018, 4:26 p.m. UTC | #7
> > Am 18.12.18 um 15:27 schrieb Jian-Hong Pan:
> > >> Sun, Dec 16, 2018 at 11:18:59AM CET, starnight@g.ncu.edu.tw 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 <starnight@g.ncu.edu.tw>
> > [...]
> > >>> 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
Ben Whitten Dec. 20, 2018, 10:19 a.m. UTC | #8
> > > Am 18.12.18 um 15:27 schrieb Jian-Hong Pan:
> > > >> Sun, Dec 16, 2018 at 11:18:59AM CET, starnight@g.ncu.edu.tw 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 <starnight@g.ncu.edu.tw>
> > > [...]
> > > >>> 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
Andreas Färber Dec. 20, 2018, 3:31 p.m. UTC | #9
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
diff mbox series

Patch

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