2019-01-29 05:02:04

by Andreas Färber

[permalink] [raw]
Subject: [RFC net-next 0/4] net: EnOcean prototype driver

Hello,

This series implements a serdev driver for the EnOcean Serial Protocol (ESP).

It follows previous weekend's Z-Wave serdev driver [1], exploring various
wireless technologies surrounding LoRa/FSK/OOK in quest of a solid PHY layer.

The EnOcean Radio Protocol 1 (ERP1) is based on ASK; ERP2 on FSK for sub-GHz
and on IEEE 802.15.4 for 2.4 GHz. The company EnOcean also manufactures some
BLE based devices unrelated to ERP or this patchset.

ESP3 has been tested with basic host -> module -> host communication and
radio transmission via PF_PACKET datagram sockets.
ESP2 has only been tested for telegram checksums and served for abstraction.
Both handle the same radio telegrams, with transparent translation for ESP2.

Note how much further this implementation got on one weekend plus an evening
in comparison with [1], thanks to solid, open protocol documentation.

Unlike Z-Wave's Command Classes, EnOcean relies on a Learn-in process to
interpret incoming telegram data from a certain device ID correctly, i.e.,
the receiver needs to maintain a mapping from device ID to Equipment Profile.

By exposing the radio (sub)telegrams on a socket, userspace still gets to
handle all the gory Equipment Profile dependent data, while abstracting the
ESP-specific frames and checksumming algorithms. So far no netlink layer was
required; BaseID and receiver sensitivity would in theory be ESP2 options,
and ESP3 offers several more. The selection of ESP2 vs. ESP3 is done via DT.

Again, these have been tested as external modules and lack Kbuild/Makefile
integration with the net subsystem for now. A test program is at [2].

The receive path could not yet be tested for lack of devices and is left out.

Have a lot of fun!

Cheers,
Andreas

[1] https://patchwork.ozlabs.org/patch/1028209/
[2] https://github.com/afaerber/lora-modules/blob/master/txenocean.c

P.S. My conclusion here is that PF_PACKET is a viable alternative to my
current PF_LORA, if we extend my recent cfglora patches with more commands:
https://lists.infradead.org/pipermail/linux-lpwan/2019-January/000154.html
Only downside was that for lack of an include/uapi/linux/enocean.h header
file I had to resort to a patch not included here to define ETH_P_ERP2 etc.
for lack of obvious ways to prepend my own if_{ether,arp}.h files.

Andreas Färber (4):
net: Reserve protocol identifiers for EnOcean
net: Prepare EnOcean device drivers
net: enocean: Add ESP3 driver
net: enocean: Prepare ESP2 support

drivers/net/enocean/Makefile | 7 +
drivers/net/enocean/enocean.c | 124 +++++++++++++
drivers/net/enocean/enocean_esp.c | 287 ++++++++++++++++++++++++++++
drivers/net/enocean/enocean_esp.h | 46 +++++
drivers/net/enocean/enocean_esp2.c | 276 +++++++++++++++++++++++++++
drivers/net/enocean/enocean_esp3.c | 372 +++++++++++++++++++++++++++++++++++++
include/linux/enocean/dev.h | 23 +++
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 2 +
9 files changed, 1138 insertions(+)
create mode 100644 drivers/net/enocean/Makefile
create mode 100644 drivers/net/enocean/enocean.c
create mode 100644 drivers/net/enocean/enocean_esp.c
create mode 100644 drivers/net/enocean/enocean_esp.h
create mode 100644 drivers/net/enocean/enocean_esp2.c
create mode 100644 drivers/net/enocean/enocean_esp3.c
create mode 100644 include/linux/enocean/dev.h

--
2.16.4



2019-01-29 05:02:18

by Andreas Färber

[permalink] [raw]
Subject: [RFC net-next 4/4] net: enocean: Prepare ESP2 support

The EnOcean Serial Protocol 2 used subtelegram frames different from ESP3.
It also uses ORG identifiers differing from RORG identifiers in ERP.

Only checksumming has been tested.

Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/enocean/Makefile | 1 +
drivers/net/enocean/enocean_esp.c | 10 ++
drivers/net/enocean/enocean_esp.h | 8 ++
drivers/net/enocean/enocean_esp2.c | 276 +++++++++++++++++++++++++++++++++++++
4 files changed, 295 insertions(+)
create mode 100644 drivers/net/enocean/enocean_esp2.c

diff --git a/drivers/net/enocean/Makefile b/drivers/net/enocean/Makefile
index 4492e3d48c0a..f34b098997e4 100644
--- a/drivers/net/enocean/Makefile
+++ b/drivers/net/enocean/Makefile
@@ -3,4 +3,5 @@ enocean-dev-y := enocean.o

obj-m += enocean-esp.o
enocean-esp-y := enocean_esp.o
+enocean-esp-y += enocean_esp2.o
enocean-esp-y += enocean_esp3.o
diff --git a/drivers/net/enocean/enocean_esp.c b/drivers/net/enocean/enocean_esp.c
index 61bddb77762d..74720da49369 100644
--- a/drivers/net/enocean/enocean_esp.c
+++ b/drivers/net/enocean/enocean_esp.c
@@ -150,6 +150,15 @@ static void enocean_esp_cleanup(struct enocean_device *edev)
edev->version->cleanup(edev);
}

+static const struct enocean_esp_version enocean_esp2 = {
+ .version = 2,
+ .baudrate = 9600,
+ .serdev_client_ops = &enocean_esp2_serdev_client_ops,
+ .init = enocean_esp2_init,
+ .send = enocean_esp2_send,
+ .cleanup = enocean_esp2_cleanup,
+};
+
static const struct enocean_esp_version enocean_esp3 = {
.version = 3,
.baudrate = 57600,
@@ -160,6 +169,7 @@ static const struct enocean_esp_version enocean_esp3 = {
};

static const struct of_device_id enocean_of_match[] = {
+ { .compatible = "enocean,esp2", .data = &enocean_esp2 },
{ .compatible = "enocean,esp3", .data = &enocean_esp3 },
{}
};
diff --git a/drivers/net/enocean/enocean_esp.h b/drivers/net/enocean/enocean_esp.h
index e02bf5352d61..44660238043a 100644
--- a/drivers/net/enocean/enocean_esp.h
+++ b/drivers/net/enocean/enocean_esp.h
@@ -24,15 +24,23 @@ struct enocean_device {
void *priv;
};

+extern const struct serdev_device_ops enocean_esp2_serdev_client_ops;
extern const struct serdev_device_ops enocean_esp3_serdev_client_ops;

void enocean_esp3_crc8_populate(void);

+int enocean_esp2_init(struct enocean_device *edev);
int enocean_esp3_init(struct enocean_device *edev);

+int enocean_esp2_send(struct enocean_device *edev, u32 dest, const void *data, int data_len);
int enocean_esp3_send(struct enocean_device *edev, u32 dest, const void *data, int data_len);
void enocean_esp_tx_done(struct enocean_device *edev);

+void enocean_esp2_cleanup(struct enocean_device *edev);
void enocean_esp3_cleanup(struct enocean_device *edev);

+#define ENOCEAN_RORG_RPS 0xF6
+#define ENOCEAN_RORG_1BS 0xD5
+#define ENOCEAN_RORG_4BS 0xA5
+
#endif
diff --git a/drivers/net/enocean/enocean_esp2.c b/drivers/net/enocean/enocean_esp2.c
new file mode 100644
index 000000000000..d4cb787de22e
--- /dev/null
+++ b/drivers/net/enocean/enocean_esp2.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * EnOcean Serial Protocol 2
+ *
+ * Copyright (c) 2019 Andreas Färber
+ */
+
+#include <linux/serdev.h>
+
+#include "enocean_esp.h"
+
+#define ESP2_SYNC_BYTE1 0xA5
+#define ESP2_SYNC_BYTE0 0x5A
+
+struct enocean_esp2_packet {
+ u8 sync[2];
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ u8 h_seq:3;
+ u8 length:5;
+#else
+ u8 length:5;
+ u8 h_seq:3;
+#endif
+ u8 org;
+ u8 data[4];
+ u8 id[4];
+ u8 status;
+ u8 checksum;
+} __packed;
+
+#define ESP2_H_SEQ_TRT 0x3
+#define ESP2_H_SEQ_RCT 0x4
+#define ESP2_H_SEQ_TCT 0x5
+
+#define ESP2_TELEGRAM_RESET 0x0A
+
+#define ENOCEAN_ORG_RPS 0x05
+#define ENOCEAN_ORG_1BS 0x06
+#define ENOCEAN_ORG_4BS 0x07
+
+/* Cf. EnOcean Equipment Profiles, Telegram Types (RORG) */
+static inline u8 enocean_org_to_rorg(u8 org)
+{
+ switch (org) {
+ case ENOCEAN_ORG_RPS:
+ return ENOCEAN_RORG_RPS;
+ case ENOCEAN_ORG_1BS:
+ return ENOCEAN_RORG_1BS;
+ case ENOCEAN_ORG_4BS:
+ return ENOCEAN_RORG_4BS;
+ default:
+ return org;
+ }
+}
+
+static u8 enocean_rorg_to_org(u8 rorg)
+{
+ switch (rorg) {
+ case ENOCEAN_RORG_RPS:
+ return ENOCEAN_ORG_RPS;
+ case ENOCEAN_RORG_1BS:
+ return ENOCEAN_ORG_1BS;
+ case ENOCEAN_RORG_4BS:
+ return ENOCEAN_ORG_4BS;
+ default:
+ return rorg;
+ }
+}
+
+struct enocean_esp2_dispatcher {
+ struct list_head list;
+ u8 h_seq;
+ void (*dispatch)(const u8 *data, u8 data_len, struct enocean_esp2_dispatcher *d);
+};
+
+static void enocean_add_esp2_dispatcher(struct enocean_device *edev,
+ struct enocean_esp2_dispatcher *entry)
+{
+ list_add_tail_rcu(&entry->list, &edev->esp_dispatchers);
+}
+
+static void enocean_remove_esp2_dispatcher(struct enocean_device *edev,
+ struct enocean_esp2_dispatcher *entry)
+{
+ list_del_rcu(&entry->list);
+}
+
+#define ESP2_RESPONSE_ERR_SYNTAX_H_SEQ 0x08
+#define ESP2_RESPONSE_ERR_SYNTAX_LENGTH 0x09
+#define ESP2_RESPONSE_ERR_SYNTAX_CHKSUM 0x0A
+#define ESP2_RESPONSE_ERR_SYNTAX_ORG 0x0B
+#define ESP2_RESPONSE_ERR 0x19
+#define ESP2_RESPONSE_ERR_IDRANGE 0x1A
+#define ESP2_RESPONSE_ERR_TX_IDRANGE 0x22
+#define ESP2_RESPONSE_OK 0x58
+
+struct enocean_esp2_priv {
+ struct enocean_device *edev;
+ struct enocean_esp2_dispatcher tx_telegram_response;
+};
+
+static u8 enocean_esp2_checksum(const u8 *data, int len)
+{
+ u8 chksum = 0;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ chksum += data[i];
+ }
+ return chksum;
+}
+
+static int enocean_esp2_send_telegram(struct enocean_device *edev, u8 h_seq, u8 org,
+ const u8 *data, int data_len, unsigned long timeout)
+{
+ struct enocean_esp2_packet pkt;
+ int ret;
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.sync[0] = ESP2_SYNC_BYTE1;
+ pkt.sync[1] = ESP2_SYNC_BYTE0;
+ pkt.h_seq = h_seq;
+ pkt.length = 11;
+ dev_dbg(&edev->serdev->dev, "H_SEQ | LENGTH = %02x\n", (unsigned int)(((u8*)&pkt)[2]));
+ pkt.org = org;
+ if (data_len > 0)
+ memcpy(pkt.data, data, min(data_len, 9));
+ pkt.checksum = enocean_esp2_checksum(((u8 *)&pkt) + 2, sizeof(pkt) - 2 - 1);
+ dev_dbg(&edev->serdev->dev, "checksum = %02x\n", (unsigned int)pkt.checksum);
+
+ ret = serdev_device_write(edev->serdev, (const u8 *)&pkt, sizeof(pkt), timeout);
+ if (ret < 0)
+ return ret;
+ if (ret > 0 && ret < sizeof(pkt))
+ return -EIO;
+ return 0;
+}
+
+static void enocean_esp2_tx_telegram_response_dispatch(const u8 *data, u8 data_len,
+ struct enocean_esp2_dispatcher *d)
+{
+ struct enocean_esp2_priv *priv = container_of(d, struct enocean_esp2_priv, tx_telegram_response);
+ struct enocean_device *edev = priv->edev;
+
+ enocean_remove_esp2_dispatcher(edev, d);
+
+ if (data_len < 1)
+ return;
+
+ switch (data[0]) {
+ case ESP2_RESPONSE_OK:
+ enocean_esp_tx_done(edev);
+ break;
+ case ESP2_RESPONSE_ERR:
+ case ESP2_RESPONSE_ERR_TX_IDRANGE:
+ default:
+ break;
+ }
+}
+
+static int enocean_esp2_tx_telegram(struct enocean_device *edev, u8 org,
+ const u8 *data, int data_len, unsigned long timeout)
+{
+ struct enocean_esp2_priv *priv = edev->priv;
+ int ret;
+
+ enocean_add_esp2_dispatcher(edev, &priv->tx_telegram_response);
+
+ ret = enocean_esp2_send_telegram(edev, ESP2_H_SEQ_TRT,
+ org, data, data_len, timeout);
+ if (ret) {
+ enocean_remove_esp2_dispatcher(edev, &priv->tx_telegram_response);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int enocean_esp2_reset(struct enocean_device *edev, unsigned long timeout)
+{
+ return enocean_esp2_send_telegram(edev, ESP2_H_SEQ_TCT, ESP2_TELEGRAM_RESET, NULL, 0, timeout);
+ /* no RCT */
+}
+
+static int enocean_esp2_receive_buf(struct serdev_device *sdev, const u8 *data, size_t count)
+{
+ struct enocean_device *edev = serdev_device_get_drvdata(sdev);
+ struct enocean_esp2_dispatcher *e;
+ u8 h_seq, length, chksum;
+
+ dev_dbg(&sdev->dev, "Receive (%zu)\n", count);
+
+ if (data[0] != ESP2_SYNC_BYTE1) {
+ dev_warn(&sdev->dev, "not first Sync Byte (found 0x%02x), skipping\n",
+ (unsigned int)data[0]);
+ return 1;
+ }
+
+ if (count < 2)
+ return 0;
+
+ if (data[1] != ESP2_SYNC_BYTE0) {
+ dev_warn(&sdev->dev, "not second Sync Byte (found 0x%02x 0x%02x), skipping\n",
+ ESP2_SYNC_BYTE1, (unsigned int)data[1]);
+ return 1;
+ }
+
+ if (count < 3)
+ return 0;
+
+ h_seq = data[2] >> 5;
+ length = data[2] & 0x1f;
+
+ if (count < 3 + length)
+ return 0;
+
+ chksum = enocean_esp2_checksum(data + 2, 1 + length - 1);
+ if (data[3 + length - 1] != chksum) {
+ dev_warn(&sdev->dev, "invalid checksum (expected 0x%02x, found %02x), skipping\n",
+ (unsigned int)chksum, (unsigned int)data[3 + length - 1]);
+ return 2; /* valid second Sync Byte is not a valid first Sync Byte */
+ }
+
+ print_hex_dump_bytes("received: ", DUMP_PREFIX_OFFSET, data, 3 + length);
+
+ list_for_each_entry_rcu(e, &edev->esp_dispatchers, list) {
+ if (e->h_seq == h_seq)
+ e->dispatch(data + 3, length, e);
+ }
+
+ return 3 + length;
+}
+
+const struct serdev_device_ops enocean_esp2_serdev_client_ops = {
+ .receive_buf = enocean_esp2_receive_buf,
+ .write_wakeup = serdev_device_write_wakeup,
+};
+
+int enocean_esp2_send(struct enocean_device *edev, u32 dest, const void *data, int data_len)
+{
+ const u8 *buf = data;
+
+ return enocean_esp2_tx_telegram(edev,
+ enocean_rorg_to_org(buf[0]), buf + 1, data_len - 1, HZ);
+}
+
+int enocean_esp2_init(struct enocean_device *edev)
+{
+ struct enocean_esp2_priv *priv;
+ int ret;
+
+ ret = enocean_esp2_reset(edev, HZ);
+ if (ret)
+ return ret;
+
+ msleep(100); /* XXX */
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->edev = edev;
+ edev->priv = priv;
+
+ priv->tx_telegram_response.h_seq = ESP2_H_SEQ_RCT;
+ priv->tx_telegram_response.dispatch = enocean_esp2_tx_telegram_response_dispatch;
+
+ return 0;
+}
+
+void enocean_esp2_cleanup(struct enocean_device *edev)
+{
+ struct enocean_esp2_priv *priv = edev->priv;
+
+ kfree(priv);
+}
--
2.16.4


2019-01-29 05:02:39

by Andreas Färber

[permalink] [raw]
Subject: [RFC net-next 1/4] net: Reserve protocol identifiers for EnOcean

EnOcean wireless technology is based on ASK (ERP1) and FSK (ERP2) modulations
for sub-GHz and on IEEE 802.15.4 for 2.4 GHz.

ARPHRD_ENOCEAN
ETH_P_ERP{1,2}

Signed-off-by: Andreas Färber <[email protected]>
---
include/uapi/linux/if_arp.h | 1 +
include/uapi/linux/if_ether.h | 2 ++
2 files changed, 3 insertions(+)

diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
index dd7992a441c9..327ef052329f 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -102,6 +102,7 @@
#define ARPHRD_LORAWAN 828 /* LoRaWAN */
#define ARPHRD_OOK 829 /* On/Off Keying modulation */
#define ARPHRD_FSK 830 /* Frequency Shift Keying modulation */
+#define ARPHRD_ENOCEAN 832 /* EnOcean */

#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 0b5c30f78261..3e22948cc329 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -152,6 +152,8 @@
#define ETH_P_OOK 0x00FC /* On/Off Keying modulation */
#define ETH_P_FSK 0x00FD /* Frequency Shift Keying mod. */
#define ETH_P_FLRC 0x00FE /* Fast Long Range Communication */
+#define ETH_P_ERP1 0x00FF /* EnOcean Radio Protocol 1 */
+#define ETH_P_ERP2 0x0100 /* EnOcean Radio Protocol 2 */

/*
* This is an Ethernet frame header.
--
2.16.4


2019-01-29 05:03:06

by Andreas Färber

[permalink] [raw]
Subject: [RFC net-next 2/4] net: Prepare EnOcean device drivers

Add net_device helpers for EnOcean.

Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/enocean/Makefile | 2 +
drivers/net/enocean/enocean.c | 124 ++++++++++++++++++++++++++++++++++++++++++
include/linux/enocean/dev.h | 23 ++++++++
3 files changed, 149 insertions(+)
create mode 100644 drivers/net/enocean/Makefile
create mode 100644 drivers/net/enocean/enocean.c
create mode 100644 include/linux/enocean/dev.h

diff --git a/drivers/net/enocean/Makefile b/drivers/net/enocean/Makefile
new file mode 100644
index 000000000000..efb3cd16c7f2
--- /dev/null
+++ b/drivers/net/enocean/Makefile
@@ -0,0 +1,2 @@
+obj-m += enocean-dev.o
+enocean-dev-y := enocean.o
diff --git a/drivers/net/enocean/enocean.c b/drivers/net/enocean/enocean.c
new file mode 100644
index 000000000000..087a4de274c8
--- /dev/null
+++ b/drivers/net/enocean/enocean.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * EnOcean net device
+ *
+ * Copyright (c) 2019 Andreas Färber
+ */
+
+#include <linux/enocean/dev.h>
+#include <linux/if_arp.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <net/rtnetlink.h>
+
+int open_enocean_dev(struct net_device *dev)
+{
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(open_enocean_dev);
+
+void close_enocean_dev(struct net_device *dev)
+{
+}
+EXPORT_SYMBOL_GPL(close_enocean_dev);
+
+static void enocean_setup(struct net_device *dev)
+{
+ dev->type = ARPHRD_ENOCEAN;
+ dev->mtu = 255; /* XXX */
+ dev->hard_header_len = 0;
+ dev->addr_len = 0; /* XXX 4 */
+ dev->tx_queue_len = 10;
+
+ dev->flags = IFF_NOARP;
+ dev->features = 0;
+}
+
+struct net_device *alloc_enocean_dev(size_t priv_size)
+{
+ struct enocean_dev_priv *priv;
+ struct net_device *netdev;
+
+ netdev = alloc_netdev(priv_size, "enocean%d", NET_NAME_UNKNOWN, enocean_setup);
+ if (!netdev)
+ return NULL;
+
+ priv = netdev_priv(netdev);
+ priv->dev = netdev;
+
+ return netdev;
+}
+EXPORT_SYMBOL_GPL(alloc_enocean_dev);
+
+void free_enocean_dev(struct net_device *netdev)
+{
+ free_netdev(netdev);
+}
+EXPORT_SYMBOL_GPL(free_enocean_dev);
+
+static void devm_free_enocean_dev(struct device *dev, void *res)
+{
+ struct net_device **net = res;
+
+ free_enocean_dev(*net);
+}
+
+struct net_device *devm_alloc_enocean_dev(struct device *dev, size_t priv)
+{
+ struct net_device **ptr;
+ struct net_device *net;
+
+ net = alloc_enocean_dev(priv);
+ if (!net)
+ return NULL;
+
+ ptr = devres_alloc(devm_free_enocean_dev, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr) {
+ free_enocean_dev(net);
+ return NULL;
+ }
+
+ *ptr = net;
+ devres_add(dev, ptr);
+
+ return net;
+}
+EXPORT_SYMBOL_GPL(devm_alloc_enocean_dev);
+
+static struct rtnl_link_ops enocean_link_ops __read_mostly = {
+ .kind = "enocean",
+ .setup = enocean_setup,
+};
+
+int register_enocean_dev(struct net_device *dev)
+{
+ dev->rtnl_link_ops = &enocean_link_ops;
+ return register_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(register_enocean_dev);
+
+void unregister_enocean_dev(struct net_device *dev)
+{
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_enocean_dev);
+
+static int __init enocean_dev_init(void)
+{
+ return rtnl_link_register(&enocean_link_ops);
+}
+module_init(enocean_dev_init);
+
+static void __exit enocean_dev_exit(void)
+{
+ rtnl_link_unregister(&enocean_link_ops);
+}
+module_exit(enocean_dev_exit);
+
+MODULE_DESCRIPTION("EnOcean device driver interface");
+MODULE_ALIAS_RTNL_LINK("enocean");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andreas Färber");
diff --git a/include/linux/enocean/dev.h b/include/linux/enocean/dev.h
new file mode 100644
index 000000000000..be9d37cdde37
--- /dev/null
+++ b/include/linux/enocean/dev.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * linux/enocean/dev.h
+ *
+ * Copyright (c) 2019 Andreas Färber
+ */
+#ifndef _ENOCEAN_DEV_H
+#define _ENOCEAN_DEV_H
+
+#include <linux/netdevice.h>
+
+struct net_device *alloc_enocean_dev(size_t priv_size);
+struct net_device *devm_alloc_enocean_dev(struct device *dev, size_t priv_size);
+int register_enocean_dev(struct net_device *netdev);
+void unregister_enocean_dev(struct net_device *netdev);
+int open_enocean_dev(struct net_device *netdev);
+void close_enocean_dev(struct net_device *netdev);
+
+struct enocean_dev_priv {
+ struct net_device *dev;
+};
+
+#endif
--
2.16.4


2019-01-29 05:03:41

by Andreas Färber

[permalink] [raw]
Subject: [RFC net-next 3/4] net: enocean: Add ESP3 driver

This implements the EnOcean Serial Protocol 3.
Rudimentary sending is prepared. Error handling is lacking and
reception handling is missing.

Tested with EnOcean TCM310 gateway module.

Signed-off-by: Andreas Färber <[email protected]>
---
drivers/net/enocean/Makefile | 4 +
drivers/net/enocean/enocean_esp.c | 277 +++++++++++++++++++++++++++
drivers/net/enocean/enocean_esp.h | 38 ++++
drivers/net/enocean/enocean_esp3.c | 372 +++++++++++++++++++++++++++++++++++++
4 files changed, 691 insertions(+)
create mode 100644 drivers/net/enocean/enocean_esp.c
create mode 100644 drivers/net/enocean/enocean_esp.h
create mode 100644 drivers/net/enocean/enocean_esp3.c

diff --git a/drivers/net/enocean/Makefile b/drivers/net/enocean/Makefile
index efb3cd16c7f2..4492e3d48c0a 100644
--- a/drivers/net/enocean/Makefile
+++ b/drivers/net/enocean/Makefile
@@ -1,2 +1,6 @@
obj-m += enocean-dev.o
enocean-dev-y := enocean.o
+
+obj-m += enocean-esp.o
+enocean-esp-y := enocean_esp.o
+enocean-esp-y += enocean_esp3.o
diff --git a/drivers/net/enocean/enocean_esp.c b/drivers/net/enocean/enocean_esp.c
new file mode 100644
index 000000000000..61bddb77762d
--- /dev/null
+++ b/drivers/net/enocean/enocean_esp.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * EnOcean Serial Protocol
+ *
+ * Copyright (c) 2019 Andreas Färber
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/enocean/dev.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/serdev.h>
+
+#include "enocean_esp.h"
+
+struct enocean_esp_version {
+ int version;
+ int baudrate;
+ const struct serdev_device_ops *serdev_client_ops;
+ int (*init)(struct enocean_device *edev);
+ int (*send)(struct enocean_device *edev, u32 dest, const void *data, int data_len);
+ void (*cleanup)(struct enocean_device *edev);
+};
+
+struct enocean_esp_priv {
+ struct enocean_dev_priv priv;
+
+ struct enocean_device *edev;
+
+ struct sk_buff *tx_skb;
+ int tx_len;
+
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+};
+
+static netdev_tx_t enocean_dev_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct enocean_esp_priv *priv = netdev_priv(netdev);
+
+ netdev_dbg(netdev, "%s\n", __func__);
+
+ if (skb->protocol != htons(ETH_P_ERP1) &&
+ skb->protocol != htons(ETH_P_ERP2)) {
+ kfree_skb(skb);
+ netdev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ netif_stop_queue(netdev);
+ priv->tx_skb = skb;
+ queue_work(priv->wq, &priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int enocean_esp_tx(struct enocean_device *edev, void *data, int data_len)
+{
+ if (!edev->version->send)
+ return -ENOTSUPP;
+ return edev->version->send(edev, 0xffffffff, data, data_len);
+}
+
+static void enocean_esp_tx_work_handler(struct work_struct *ws)
+{
+ struct enocean_esp_priv *priv = container_of(ws, struct enocean_esp_priv, tx_work);
+ struct enocean_device *edev = priv->edev;
+ struct net_device *netdev = edev->netdev;
+
+ netdev_dbg(netdev, "%s\n", __func__);
+
+ if (priv->tx_skb) {
+ enocean_esp_tx(edev, priv->tx_skb->data, priv->tx_skb->len);
+ priv->tx_len = 1 + priv->tx_skb->len;
+ if (!(netdev->flags & IFF_ECHO) ||
+ priv->tx_skb->pkt_type != PACKET_LOOPBACK ||
+ (priv->tx_skb->protocol != htons(ETH_P_ERP1) &&
+ priv->tx_skb->protocol != htons(ETH_P_ERP2)))
+ kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+}
+
+void enocean_esp_tx_done(struct enocean_device *edev)
+{
+ struct net_device *netdev = edev->netdev;
+ struct enocean_esp_priv *priv = netdev_priv(netdev);
+
+ netdev_info(netdev, "TX done.\n");
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes += priv->tx_len - 1;
+ priv->tx_len = 0;
+ netif_wake_queue(netdev);
+}
+
+static int enocean_dev_open(struct net_device *netdev)
+{
+ struct enocean_esp_priv *priv = netdev_priv(netdev);
+ int ret;
+
+ netdev_dbg(netdev, "%s\n", __func__);
+
+ ret = open_enocean_dev(netdev);
+ if (ret)
+ return ret;
+
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+
+ priv->wq = alloc_workqueue("enocean_esp_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, 0);
+ INIT_WORK(&priv->tx_work, enocean_esp_tx_work_handler);
+
+ netif_wake_queue(netdev);
+
+ return 0;
+}
+
+static int enocean_dev_stop(struct net_device *netdev)
+{
+ struct enocean_esp_priv *priv = netdev_priv(netdev);
+
+ netdev_dbg(netdev, "%s\n", __func__);
+
+ close_enocean_dev(netdev);
+
+ destroy_workqueue(priv->wq);
+ priv->wq = NULL;
+
+ if (priv->tx_skb || priv->tx_len)
+ netdev->stats.tx_errors++;
+ if (priv->tx_skb)
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+
+ return 0;
+}
+
+static const struct net_device_ops enocean_esp_netdev_ops = {
+ .ndo_open = enocean_dev_open,
+ .ndo_stop = enocean_dev_stop,
+ .ndo_start_xmit = enocean_dev_start_xmit,
+};
+
+static void enocean_esp_cleanup(struct enocean_device *edev)
+{
+ if (edev->version->cleanup)
+ edev->version->cleanup(edev);
+}
+
+static const struct enocean_esp_version enocean_esp3 = {
+ .version = 3,
+ .baudrate = 57600,
+ .serdev_client_ops = &enocean_esp3_serdev_client_ops,
+ .init = enocean_esp3_init,
+ .send = enocean_esp3_send,
+ .cleanup = enocean_esp3_cleanup,
+};
+
+static const struct of_device_id enocean_of_match[] = {
+ { .compatible = "enocean,esp3", .data = &enocean_esp3 },
+ {}
+};
+MODULE_DEVICE_TABLE(of, enocean_of_match);
+
+static int enocean_probe(struct serdev_device *sdev)
+{
+ struct enocean_esp_priv *priv;
+ struct enocean_device *edev;
+ int ret;
+
+ dev_dbg(&sdev->dev, "Probing");
+
+ edev = devm_kzalloc(&sdev->dev, sizeof(*edev), GFP_KERNEL);
+ if (!edev)
+ return -ENOMEM;
+
+ edev->version = of_device_get_match_data(&sdev->dev);
+ if (!edev->version)
+ return -ENOTSUPP;
+
+ dev_dbg(&sdev->dev, "ESP%d\n", edev->version->version);
+
+ edev->serdev = sdev;
+ INIT_LIST_HEAD(&edev->esp_dispatchers);
+ serdev_device_set_drvdata(sdev, edev);
+
+ ret = serdev_device_open(sdev);
+ if (ret) {
+ dev_err(&sdev->dev, "Failed to open (%d)\n", ret);
+ return ret;
+ }
+
+ serdev_device_set_baudrate(sdev, edev->version->baudrate);
+ serdev_device_set_flow_control(sdev, false);
+ serdev_device_set_client_ops(sdev, edev->version->serdev_client_ops);
+
+ if (edev->version->init) {
+ ret = edev->version->init(edev);
+ if (ret) {
+ serdev_device_close(sdev);
+ return ret;
+ }
+ }
+
+ edev->netdev = devm_alloc_enocean_dev(&sdev->dev, sizeof(struct enocean_esp_priv));
+ if (!edev->netdev) {
+ enocean_esp_cleanup(edev);
+ serdev_device_close(sdev);
+ return -ENOMEM;
+ }
+
+ edev->netdev->netdev_ops = &enocean_esp_netdev_ops;
+ edev->netdev->flags |= IFF_ECHO;
+ SET_NETDEV_DEV(edev->netdev, &sdev->dev);
+
+ priv = netdev_priv(edev->netdev);
+ priv->edev = edev;
+
+ ret = register_enocean_dev(edev->netdev);
+ if (ret) {
+ enocean_esp_cleanup(edev);
+ serdev_device_close(sdev);
+ return ret;
+ }
+
+ dev_dbg(&sdev->dev, "Done.\n");
+
+ return 0;
+}
+
+static void enocean_remove(struct serdev_device *sdev)
+{
+ struct enocean_device *edev = serdev_device_get_drvdata(sdev);
+
+ unregister_enocean_dev(edev->netdev);
+ enocean_esp_cleanup(edev);
+ serdev_device_close(sdev);
+
+ dev_dbg(&sdev->dev, "Removed\n");
+}
+
+static struct serdev_device_driver enocean_serdev_driver = {
+ .probe = enocean_probe,
+ .remove = enocean_remove,
+ .driver = {
+ .name = "enocean-esp",
+ .of_match_table = enocean_of_match,
+ },
+};
+
+static int __init enocean_init(void)
+{
+ int ret;
+
+ enocean_esp3_crc8_populate();
+
+ ret = serdev_device_driver_register(&enocean_serdev_driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void __exit enocean_exit(void)
+{
+ serdev_device_driver_unregister(&enocean_serdev_driver);
+}
+
+module_init(enocean_init);
+module_exit(enocean_exit);
+
+MODULE_DESCRIPTION("EnOcean serdev driver");
+MODULE_AUTHOR("Andreas Färber <[email protected]>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/enocean/enocean_esp.h b/drivers/net/enocean/enocean_esp.h
new file mode 100644
index 000000000000..e02bf5352d61
--- /dev/null
+++ b/drivers/net/enocean/enocean_esp.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * EnOcean Serial Protocol
+ *
+ * Copyright (c) 2019 Andreas Färber
+ */
+#ifndef ENOCEAN_H
+#define ENOCEAN_H
+
+#include <linux/netdevice.h>
+#include <linux/rculist.h>
+#include <linux/serdev.h>
+
+struct enocean_esp_version;
+
+struct enocean_device {
+ struct serdev_device *serdev;
+ const struct enocean_esp_version *version;
+
+ struct net_device *netdev;
+
+ struct list_head esp_dispatchers;
+
+ void *priv;
+};
+
+extern const struct serdev_device_ops enocean_esp3_serdev_client_ops;
+
+void enocean_esp3_crc8_populate(void);
+
+int enocean_esp3_init(struct enocean_device *edev);
+
+int enocean_esp3_send(struct enocean_device *edev, u32 dest, const void *data, int data_len);
+void enocean_esp_tx_done(struct enocean_device *edev);
+
+void enocean_esp3_cleanup(struct enocean_device *edev);
+
+#endif
diff --git a/drivers/net/enocean/enocean_esp3.c b/drivers/net/enocean/enocean_esp3.c
new file mode 100644
index 000000000000..707c4054ac69
--- /dev/null
+++ b/drivers/net/enocean/enocean_esp3.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * EnOcean Serial Protocol 3
+ *
+ * Copyright (c) 2019 Andreas Färber
+ */
+
+#include <asm/unaligned.h>
+#include <linux/completion.h>
+#include <linux/crc8.h>
+#include <linux/rculist.h>
+#include <linux/serdev.h>
+#include <linux/slab.h>
+
+#include "enocean_esp.h"
+
+/* G(x) = x^8 + x^2 + x^1 + x^0 */
+#define ESP3_CRC8_POLY_MSB 0x07
+
+DECLARE_CRC8_TABLE(enocean_esp3_crc8_table);
+
+void enocean_esp3_crc8_populate(void)
+{
+ crc8_populate_msb(enocean_esp3_crc8_table, ESP3_CRC8_POLY_MSB);
+}
+
+static inline u8 enocean_esp3_crc8(u8 *pdata, size_t nbytes)
+{
+ return crc8(enocean_esp3_crc8_table, pdata, nbytes, 0x00);
+}
+
+#define ESP3_SYNC_WORD 0x55
+
+struct enocean_esp3_dispatcher {
+ struct list_head list;
+ u8 packet_type;
+ void (*dispatch)(const u8 *data, u16 data_len, struct enocean_esp3_dispatcher *d);
+};
+
+static void enocean_add_esp3_dispatcher(struct enocean_device *edev,
+ struct enocean_esp3_dispatcher *entry)
+{
+ list_add_tail_rcu(&entry->list, &edev->esp_dispatchers);
+}
+
+static void enocean_remove_esp3_dispatcher(struct enocean_device *edev,
+ struct enocean_esp3_dispatcher *entry)
+{
+ list_del_rcu(&entry->list);
+}
+
+struct enocean_esp3_response {
+ struct enocean_esp3_dispatcher disp;
+
+ u8 code;
+ void *data;
+ u16 data_len;
+
+ struct completion comp;
+};
+
+static void enocean_esp3_response_dispatch(const u8 *data, u16 data_len,
+ struct enocean_esp3_dispatcher *d)
+{
+ struct enocean_esp3_response *resp =
+ container_of(d, struct enocean_esp3_response, disp);
+
+ if (completion_done(&resp->comp))
+ return;
+
+ if (data_len < 1)
+ return;
+
+ resp->code = data[0];
+ if (data_len > 1) {
+ resp->data = kzalloc(data_len - 1, GFP_KERNEL);
+ if (resp->data)
+ memcpy(resp->data, data + 1, data_len - 1);
+ resp->data_len = data_len - 1;
+ } else {
+ resp->data = NULL;
+ resp->data_len = 0;
+ }
+
+ complete(&resp->comp);
+}
+
+struct enocean_esp3_priv {
+ struct enocean_device *edev;
+ struct enocean_esp3_dispatcher radio_erp1_response;
+};
+
+static inline int enocean_esp3_packet_size(u16 data_len, u8 optional_len)
+{
+ return 1 + 4 + 1 + data_len + optional_len + 1;
+}
+
+static int enocean_send_esp3_packet(struct enocean_device *edev, u8 packet_type,
+ const void *data, u16 data_len, const void *optional_data, u8 optional_len,
+ unsigned long timeout)
+{
+ int len = enocean_esp3_packet_size(data_len, optional_len);
+ u8 *buf;
+ int ret;
+
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = ESP3_SYNC_WORD;
+ put_unaligned_be16(data_len, buf + 1);
+ buf[3] = optional_len;
+ buf[4] = packet_type;
+ buf[5] = enocean_esp3_crc8(buf + 1, 4);
+ dev_dbg(&edev->serdev->dev, "CRC8H = %02x\n", (unsigned int)buf[5]);
+ memcpy(buf + 6, data, data_len);
+ memcpy(buf + 6 + data_len, optional_data, optional_len);
+ buf[6 + data_len + optional_len] = enocean_esp3_crc8(buf + 6, data_len + optional_len);
+ dev_dbg(&edev->serdev->dev, "CRC8D = %02x\n", (unsigned int)buf[6 + data_len + optional_len]);
+
+ ret = serdev_device_write(edev->serdev, buf, len, timeout);
+
+ kfree(buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret > 0 && ret < len)
+ return -EIO;
+ return 0;
+}
+
+#define ESP3_RADIO_ERP1 0x1
+#define ESP3_RESPONSE 0x2
+#define ESP3_COMMON_COMMAND 0x5
+
+static void enocean_esp3_radio_erp1_response_dispatch(const u8 *data, u16 data_len,
+ struct enocean_esp3_dispatcher *d)
+{
+ struct enocean_esp3_priv *priv = container_of(d, struct enocean_esp3_priv, radio_erp1_response);
+ struct enocean_device *edev = priv->edev;
+ int ret;
+
+ enocean_remove_esp3_dispatcher(edev, d);
+
+ if (data_len < 1)
+ return;
+
+ switch (data[0]) {
+ case 0:
+ enocean_esp_tx_done(edev);
+ break;
+ case 2:
+ ret = -ENOTSUPP;
+ break;
+ case 3:
+ ret = -EINVAL;
+ break;
+ case 5:
+ ret = -EIO;
+ break;
+ default:
+ ret = -EIO;
+ break;
+ }
+}
+
+static int enocean_esp3_send_radio_erp1(struct enocean_device *edev, u32 dest,
+ const u8 *data, int data_len, unsigned long timeout)
+{
+ struct enocean_esp3_priv *priv = edev->priv;
+ struct esp3_radio_erp1_optional {
+ u8 sub_tel_num;
+ __be32 destination_id;
+ u8 dbm;
+ u8 security_level;
+ } __packed opt = {
+ .sub_tel_num = 3,
+ .destination_id = cpu_to_be32(dest),
+ .dbm = 0xff,
+ .security_level = 0,
+ };
+ int ret;
+
+ enocean_add_esp3_dispatcher(edev, &priv->radio_erp1_response);
+
+ ret = enocean_send_esp3_packet(edev, ESP3_RADIO_ERP1, data, data_len, &opt, sizeof(opt), timeout);
+ if (ret) {
+ enocean_remove_esp3_dispatcher(edev, &priv->radio_erp1_response);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int enocean_esp3_reset(struct enocean_device *edev, unsigned long timeout)
+{
+ struct enocean_esp3_response resp;
+ const u8 buf[1] = { 0x02 };
+ int ret;
+
+ init_completion(&resp.comp);
+ resp.disp.packet_type = ESP3_RESPONSE;
+ resp.disp.dispatch = enocean_esp3_response_dispatch;
+ enocean_add_esp3_dispatcher(edev, &resp.disp);
+
+ ret = enocean_send_esp3_packet(edev, ESP3_COMMON_COMMAND, buf, sizeof(buf), NULL, 0, timeout);
+ if (ret) {
+ enocean_remove_esp3_dispatcher(edev, &resp.disp);
+ return ret;
+ }
+
+ timeout = wait_for_completion_timeout(&resp.comp, timeout);
+ enocean_remove_esp3_dispatcher(edev, &resp.disp);
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ switch (resp.code) {
+ case 0:
+ return 0;
+ case 1:
+ return -EIO;
+ case 2:
+ return -ENOTSUPP;
+ default:
+ return -EIO;
+ }
+}
+
+struct enocean_esp3_version {
+ u8 app_version[4];
+ u8 api_version[4];
+ __be32 chip_id;
+ __be32 chip_version;
+ char app_desc[16];
+} __packed;
+
+static int enocean_esp3_read_version(struct enocean_device *edev, unsigned long timeout)
+{
+ struct enocean_esp3_response resp;
+ struct enocean_esp3_version *ver;
+ const u8 buf[1] = { 0x03 };
+ int ret;
+
+ init_completion(&resp.comp);
+ resp.disp.packet_type = ESP3_RESPONSE;
+ resp.disp.dispatch = enocean_esp3_response_dispatch;
+ enocean_add_esp3_dispatcher(edev, &resp.disp);
+
+ ret = enocean_send_esp3_packet(edev, ESP3_COMMON_COMMAND, buf, sizeof(buf), NULL, 0, timeout);
+ if (ret) {
+ enocean_remove_esp3_dispatcher(edev, &resp.disp);
+ return ret;
+ }
+
+ timeout = wait_for_completion_timeout(&resp.comp, timeout);
+ enocean_remove_esp3_dispatcher(edev, &resp.disp);
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ switch (resp.code) {
+ case 0:
+ if (!resp.data)
+ return -ENOMEM;
+ break;
+ case 2:
+ if (resp.data)
+ kfree(resp.data);
+ return -ENOTSUPP;
+ default:
+ if (resp.data)
+ kfree(resp.data);
+ return -EIO;
+ }
+
+ ver = resp.data;
+ ver->app_desc[15] = '\0';
+ dev_info(&edev->serdev->dev, "'%s'\n", ver->app_desc);
+ kfree(resp.data);
+
+ return 0;
+}
+
+static int enocean_esp3_receive_buf(struct serdev_device *sdev, const u8 *data, size_t count)
+{
+ struct enocean_device *edev = serdev_device_get_drvdata(sdev);
+ struct enocean_esp3_dispatcher *e;
+ u8 crc8h, crc8d, optional_len;
+ u16 data_len;
+
+ dev_dbg(&sdev->dev, "Receive (%zu)\n", count);
+
+ if (data[0] != ESP3_SYNC_WORD) {
+ dev_warn(&sdev->dev, "not Sync Word (found 0x%02x), skipping\n",
+ (unsigned int)data[0]);
+ return 1;
+ }
+
+ if (count < 6)
+ return 0;
+
+ crc8h = enocean_esp3_crc8((u8*)data + 1, 4);
+ if (data[5] != crc8h) {
+ dev_warn(&sdev->dev, "invalid CRC8H (expected 0x%02x, found 0x%02x), skipping\n",
+ (unsigned int)crc8h, (unsigned int)data[5]);
+ return 1;
+ }
+
+ data_len = be16_to_cpup((__be16 *)(data + 1));
+ optional_len = data[3];
+ if (count < enocean_esp3_packet_size(data_len, optional_len))
+ return 0;
+
+ crc8d = enocean_esp3_crc8((u8*)data + 6, data_len + optional_len);
+ if (data[6 + data_len + optional_len] != crc8d) {
+ dev_warn(&sdev->dev, "invalid CRC8D (expected 0x%02x, found 0x%02x), skipping\n",
+ (unsigned int)crc8d, (unsigned int)data[6 + data_len + optional_len]);
+ return 1;
+ }
+
+ print_hex_dump_debug("received: ", DUMP_PREFIX_NONE, 16, 1, data,
+ enocean_esp3_packet_size(data_len, optional_len), false);
+
+ list_for_each_entry_rcu(e, &edev->esp_dispatchers, list) {
+ if (e->packet_type == data[4])
+ e->dispatch(data + 6, data_len, e);
+ }
+
+ return enocean_esp3_packet_size(data_len, optional_len);
+}
+
+const struct serdev_device_ops enocean_esp3_serdev_client_ops = {
+ .receive_buf = enocean_esp3_receive_buf,
+ .write_wakeup = serdev_device_write_wakeup,
+};
+
+int enocean_esp3_send(struct enocean_device *edev, u32 dest, const void *data, int data_len)
+{
+ return enocean_esp3_send_radio_erp1(edev, dest, data, data_len, HZ / 10);
+}
+
+int enocean_esp3_init(struct enocean_device *edev)
+{
+ struct enocean_esp3_priv *priv;
+ int ret;
+
+ ret = enocean_esp3_reset(edev, HZ / 10);
+ if (ret)
+ return ret;
+
+ msleep(100); /* XXX */
+
+ ret = enocean_esp3_read_version(edev, HZ / 10);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->edev = edev;
+ edev->priv = priv;
+
+ priv->radio_erp1_response.packet_type = ESP3_RESPONSE;
+ priv->radio_erp1_response.dispatch = enocean_esp3_radio_erp1_response_dispatch;
+
+ return 0;
+}
+
+void enocean_esp3_cleanup(struct enocean_device *edev)
+{
+ struct enocean_esp3_priv *priv = edev->priv;
+
+ kfree(priv);
+}
--
2.16.4


2019-01-29 12:57:48

by Alexander Aring

[permalink] [raw]
Subject: Re: [RFC net-next 1/4] net: Reserve protocol identifiers for EnOcean

Hi,

On Tue, Jan 29, 2019 at 06:01:27AM +0100, Andreas Färber wrote:
> EnOcean wireless technology is based on ASK (ERP1) and FSK (ERP2) modulations
> for sub-GHz and on IEEE 802.15.4 for 2.4 GHz.
>

I am not sure what you try to do here. If I see that correctly you
want to add for some special protocol vendor specific transceiver which
is underneath an 802.15.4 transceiver a new ARPHRD type and even more
for each modulation what it supports?

If it's a 802.15.4 transceiver why not using the 802.15.4 subsystem?

For me it sounds more like a HardMAC transceiver driver for doing the
vendor protocol. The different modulations is part of a 802.15.4 phy
device class. Similar like in wireless.

- Alex

2019-01-30 01:43:06

by Andreas Färber

[permalink] [raw]
Subject: Re: [RFC net-next 1/4] net: Reserve protocol identifiers for EnOcean

Hi Alex,

Am 29.01.19 um 13:57 schrieb Alexander Aring:
> On Tue, Jan 29, 2019 at 06:01:27AM +0100, Andreas Färber wrote:
>> EnOcean wireless technology is based on ASK (ERP1) and FSK (ERP2) modulations
>> for sub-GHz and on IEEE 802.15.4 for 2.4 GHz.
>>
>
> I am not sure what you try to do here. If I see that correctly you
> want to add for some special protocol vendor specific transceiver which
> is underneath an 802.15.4 transceiver a new ARPHRD type and even more
> for each modulation what it supports?

No. EnOcean uses a 4-byte node ID across PHY layers, which I am using a
single ARPHRD_ENOCEAN for (which you conveniently cut off above).

As indicated above, the 868 MHz transceiver is _not_ using 802.15.4 PHY
or MAC to my knowledge. It does sound like you spotted "IEEE 802.15.4"
and literally blended out all the rest...

>
> If it's a 802.15.4 transceiver why not using the 802.15.4 subsystem?
>
> For me it sounds more like a HardMAC transceiver driver for doing the
> vendor protocol. The different modulations is part of a 802.15.4 phy
> device class. Similar like in wireless.

I've tried to design this exactly so that one _could_ implement it based
on 802.15.4 PHY framework for 2.4 GHz or based on an FSK PHY for sub-GHz
as a soft-MAC, layered similarly to LoRaWAN vs. LoRa, alongside the ESP
serdev driver in this series.

In ESP3 the only 802.15.4 specific operations are getting/setting the
channel (COMMAND_2_4 packet type), and there's a CO_GET_FREQUENCY_INFO
command to discover frequency and protocol, with 802.15.4 having a
different ID than ERP2 (and I spot a value 0x30 for "Long Range" :-)).
So in theory it might be possible to instantiate an 802.15.4 PHY after
discovering that ESP3 value, but neither is this a generic 802.15.4 PHY
nor a generic FSK PHY, and none of that relates to above ARPHRD really.

PF_PACKET with SOCK_DGRAM for ETH_P_ERP2 gives me the subtelegram
contents to transmit via ESP, whereas SOCK_RAW would give the full frame
to transmit via FSK PHY. By avoiding a custom PF_ENOCEAN we seem to lose
the ability to prepend any protocol headers on the skb for SOCK_DGRAM.

Did you actually read my P.S. in the cover letter? I was glad to avoid
much PF_ socket boilerplate code here (as a playground for LoRa), and
now you're complaining about a single ARPHRD constant! :-/
By that standard we could stop implementing anything new... If you're
worried about number space, why has no one commented on the values added
for LoRa and other previous wireless technologies? No one had any such
comments on my LoRa RFC, nor on Jian-Hong's LoRaWAN patches, so I've
been reserving new ARPHRD_ constants for each technology I work on. If
ARPHRD_NONE would be a better value to use for PHY layers, no one
bothered to point it out so far! Nor did anyone suggest to Jian-Hong to
reuse ARPHRD_EUI64! And yet I spot nothing more suitable for EnOcean
addresses than a custom value. Fact is, the net_device wants some value.
Note that you have two ARPHRD constants assigned for 802.15.4 alone, so
please be fair to others.

An 802.15.4 PHY won't help me for 868 MHz FSK - by my reading 802.15.4
is PSK (BPSK/OQPSK), thus incompatible with ASK/OOK and FSK/MSK.

As noted in the cover letter, Semtech chips have FSK and OOK support
alongside LoRa modulation; so I am looking into FSK PHY support, both
for those chips as well as for some pure FSK/OOK transceivers posted to
linux-lpwan list (and potentially more, given time):
https://lists.infradead.org/pipermail/linux-lpwan/2019-January/000116.html
https://lists.infradead.org/pipermail/linux-lpwan/2019-January/000117.html
https://en.opensuse.org/User:A_faerber/LoRa_interop

Therefore an FSK PHY's netlink interface will need to be able to handle
the requirements of upper-layer protocols, such as:
* Wireless M-Bus (which I could not yet find a suitable 868MHz hard-MAC
for to test against, only 169MHz; Si4432 has an Application Note AN451),
* KNX RF (which I have not come across a hard-MAC for either),
* Sigfox downstream (cf. mm002 LoRa driver as hard-MAC; no public docs),
* Z-Wave (not enough docs to implement much more for now), and here
* EnOcean Radio Protocol 2.

In general I want to make sure my implementations can work with both
soft- and hard-MAC hardware out there, as demonstrated for LoRaWAN.
Pointing a user with hard-MAC device to a theoretical generic subsystem
of your preference doesn't help them, nor does it help to split the
community into separate hard-MAC vs. soft-MAC implementation camps that
make it hard for users to switch.
* For example, when looking for how to actually use the Pine64 Z-Wave
adapter, back in the day I merely found an OpenHAB Raspbian(?) image
that as an openSUSE contributor I would surely not block my board with;
no explanations, no instructions, nothing. And when you have a pure Java
application on the one hand and a C/Python/whatever application on the
other, chances are that the kernel is the only common point of reuse. I
surely mentioned that I hate any userspace applications that attempt to
detect hardware on spidev/i2c-dev/tty without using the kernel-provided
facilities such as DT; finally, serdev allows to move any such
hardware-dependent tty code into the kernel - we just need to figure out
how to best expose functionality there (and ideally grow some more
helpers). Just note how patch 3/4 reuses the kernel's crc8
implementation instead of re-implementing it from scratch. Similarly I'd
love for my AT based LoRa drivers to share more serdev code, despite
line ending and response styles differing greatly (think
serdev_device_readline w/args?); binary protocols like ESP here are
luckily not affected as much. It could also use some more/better
documentation, some of the return values are wrong.
* As another example, we seem to be lacking a generic SDR subsystem:
People with SDR hardware seem to use either downstream kernel modules,
possibly application-generated, or closed-source userspace libraries?
Neither seems able to currently reuse the net subsystem for protocols.
And yet I've been asked repeatedly to design drivers in a way they could
be used with SDR, too, but without any way to actually test that today.
Has anyone talked to the SDR chipset/equipment vendors to remedy that?
The one I was in contact with simply chose not to reply again to date...

For ETH_P_ we seem to be far away from 0xffff, so I don't see a problem
there? Not just was it the easiest thing to implement & test short-term,
but as outlined in the cover letter I saw no way here to turn that into
a non-net-subsystem because the data transmitted is not self-describing
(mostly battery-less sensors/actuators with ca. 4 byte data payload).
You must know that your device with id 0x12345678 conforms to profile X.

Is describing remote devices in DT an option? (CC'ing Rob and Jonathan)

/.../uart@foo {
enocean {
compatible = "enocean,esp3";
#address-cells = <1>;
#size-cells = <0>;

window-handle@41424344 {
compatible = "manufacturer1,handle";
reg = <0x41424344>;
enocean,equipment-profile = <1 2 3>;
#io-channel-cells = <1>;
};

light-switch@41424348 {
compatible = "manufacturer2,rocker";
reg = <0x41424348>;
enocean,equipment-profile = <4 5 6>;
#io-channel-cells = <2>;
};
};
};

Pro: This would allow to abstract sensors (iio?) and actuators (gpio?).
Cf. https://patchwork.ozlabs.org/patch/1028209/ for comparison.
Con: How to deal with it on ACPI or on DT platforms without Overlays?
How would the kernel preserve remote device state across reboots?

So no, I don't think we can or should shoehorn non-802.15.4 PHYs into
your ieee802154 PHY layer. If you see ways to share code between the
various wireless PHYs, that would be great, but at present it seems like
mostly boilerplate code with nothing in your phy struct applying to FSK
or LoRa. Compare my cfglora series pointed to and Xue Liu's recent sysfs
patch under discussion. If no more comments turn up on my cfglora series
I'll copy it into a cfgfsk, so that I can integrate both into sx127x as
a base for further discussions at Netdevconf. Thanks.

Regards,
Andreas

--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)

2019-02-01 01:12:36

by Alexander Aring

[permalink] [raw]
Subject: Re: [RFC net-next 1/4] net: Reserve protocol identifiers for EnOcean

Hi,

On Wed, Jan 30, 2019 at 02:42:29AM +0100, Andreas Färber wrote:
> Hi Alex,
>
> Am 29.01.19 um 13:57 schrieb Alexander Aring:
> > On Tue, Jan 29, 2019 at 06:01:27AM +0100, Andreas Färber wrote:
> >> EnOcean wireless technology is based on ASK (ERP1) and FSK (ERP2) modulations
> >> for sub-GHz and on IEEE 802.15.4 for 2.4 GHz.
> >>
> >
> > I am not sure what you try to do here. If I see that correctly you
> > want to add for some special protocol vendor specific transceiver which
> > is underneath an 802.15.4 transceiver a new ARPHRD type and even more
> > for each modulation what it supports?
>
> No. EnOcean uses a 4-byte node ID across PHY layers, which I am using a
> single ARPHRD_ENOCEAN for (which you conveniently cut off above).
>
> As indicated above, the 868 MHz transceiver is _not_ using 802.15.4 PHY
> or MAC to my knowledge. It does sound like you spotted "IEEE 802.15.4"
> and literally blended out all the rest...
>

Ah okay, I am curious about that. As far I undetstood now this has
nothing to do with LoRa? I was not getting that point.

Is the PHY layer open? Do they actually refer to 802.15.4 in their
specs, but the PHY layer is changed by... preamble, phy header, MTU?

> >
> > If it's a 802.15.4 transceiver why not using the 802.15.4 subsystem?
> >
> > For me it sounds more like a HardMAC transceiver driver for doing the
> > vendor protocol. The different modulations is part of a 802.15.4 phy
> > device class. Similar like in wireless.
>
> I've tried to design this exactly so that one _could_ implement it based
> on 802.15.4 PHY framework for 2.4 GHz or based on an FSK PHY for sub-GHz
> as a soft-MAC, layered similarly to LoRaWAN vs. LoRa, alongside the ESP
> serdev driver in this series.
>
> In ESP3 the only 802.15.4 specific operations are getting/setting the
> channel (COMMAND_2_4 packet type), and there's a CO_GET_FREQUENCY_INFO
> command to discover frequency and protocol, with 802.15.4 having a
> different ID than ERP2 (and I spot a value 0x30 for "Long Range" :-)).
> So in theory it might be possible to instantiate an 802.15.4 PHY after
> discovering that ESP3 value, but neither is this a generic 802.15.4 PHY
> nor a generic FSK PHY, and none of that relates to above ARPHRD really.
>

I keep it in mind, thanks.

> PF_PACKET with SOCK_DGRAM for ETH_P_ERP2 gives me the subtelegram
> contents to transmit via ESP, whereas SOCK_RAW would give the full frame
> to transmit via FSK PHY. By avoiding a custom PF_ENOCEAN we seem to lose
> the ability to prepend any protocol headers on the skb for SOCK_DGRAM.
>

I am not quite following here. SOCK_RAW full frame and SOCK_DGRAM
payload sounds like what I suppose it should work.

A switch of protocol will do a switch from ESP to FSK which is a phy layer
behaviour?

> Did you actually read my P.S. in the cover letter? I was glad to avoid
> much PF_ socket boilerplate code here (as a playground for LoRa), and
> now you're complaining about a single ARPHRD constant! :-/
> By that standard we could stop implementing anything new... If you're
> worried about number space, why has no one commented on the values added
> for LoRa and other previous wireless technologies? No one had any such
> comments on my LoRa RFC, nor on Jian-Hong's LoRaWAN patches, so I've
> been reserving new ARPHRD_ constants for each technology I work on. If
> ARPHRD_NONE would be a better value to use for PHY layers, no one
> bothered to point it out so far! Nor did anyone suggest to Jian-Hong to
> reuse ARPHRD_EUI64! And yet I spot nothing more suitable for EnOcean
> addresses than a custom value. Fact is, the net_device wants some value.
> Note that you have two ARPHRD constants assigned for 802.15.4 alone, so
> please be fair to others.
>

Indeed we only need one. :-)

> An 802.15.4 PHY won't help me for 868 MHz FSK - by my reading 802.15.4
> is PSK (BPSK/OQPSK), thus incompatible with ASK/OOK and FSK/MSK.
>
> As noted in the cover letter, Semtech chips have FSK and OOK support
> alongside LoRa modulation; so I am looking into FSK PHY support, both
> for those chips as well as for some pure FSK/OOK transceivers posted to
> linux-lpwan list (and potentially more, given time):
> https://lists.infradead.org/pipermail/linux-lpwan/2019-January/000116.html
> https://lists.infradead.org/pipermail/linux-lpwan/2019-January/000117.html
> https://en.opensuse.org/User:A_faerber/LoRa_interop
>
> Therefore an FSK PHY's netlink interface will need to be able to handle
> the requirements of upper-layer protocols, such as:
> * Wireless M-Bus (which I could not yet find a suitable 868MHz hard-MAC
> for to test against, only 169MHz; Si4432 has an Application Note AN451),
> * KNX RF (which I have not come across a hard-MAC for either),
> * Sigfox downstream (cf. mm002 LoRa driver as hard-MAC; no public docs),
> * Z-Wave (not enough docs to implement much more for now), and here
> * EnOcean Radio Protocol 2.
>
> In general I want to make sure my implementations can work with both
> soft- and hard-MAC hardware out there, as demonstrated for LoRaWAN.
> Pointing a user with hard-MAC device to a theoretical generic subsystem
> of your preference doesn't help them, nor does it help to split the
> community into separate hard-MAC vs. soft-MAC implementation camps that
> make it hard for users to switch.

agree.

> * For example, when looking for how to actually use the Pine64 Z-Wave
> adapter, back in the day I merely found an OpenHAB Raspbian(?) image
> that as an openSUSE contributor I would surely not block my board with;
> no explanations, no instructions, nothing. And when you have a pure Java
> application on the one hand and a C/Python/whatever application on the
> other, chances are that the kernel is the only common point of reuse. I
> surely mentioned that I hate any userspace applications that attempt to
> detect hardware on spidev/i2c-dev/tty without using the kernel-provided
> facilities such as DT; finally, serdev allows to move any such
> hardware-dependent tty code into the kernel - we just need to figure out
> how to best expose functionality there (and ideally grow some more
> helpers). Just note how patch 3/4 reuses the kernel's crc8
> implementation instead of re-implementing it from scratch. Similarly I'd
> love for my AT based LoRa drivers to share more serdev code, despite
> line ending and response styles differing greatly (think
> serdev_device_readline w/args?); binary protocols like ESP here are
> luckily not affected as much. It could also use some more/better
> documentation, some of the return values are wrong.
> * As another example, we seem to be lacking a generic SDR subsystem:
> People with SDR hardware seem to use either downstream kernel modules,
> possibly application-generated, or closed-source userspace libraries?
> Neither seems able to currently reuse the net subsystem for protocols.
> And yet I've been asked repeatedly to design drivers in a way they could
> be used with SDR, too, but without any way to actually test that today.
> Has anyone talked to the SDR chipset/equipment vendors to remedy that?
> The one I was in contact with simply chose not to reply again to date...
>
> For ETH_P_ we seem to be far away from 0xffff, so I don't see a problem
> there? Not just was it the easiest thing to implement & test short-term,
> but as outlined in the cover letter I saw no way here to turn that into
> a non-net-subsystem because the data transmitted is not self-describing
> (mostly battery-less sensors/actuators with ca. 4 byte data payload).
> You must know that your device with id 0x12345678 conforms to profile X.
>
> Is describing remote devices in DT an option? (CC'ing Rob and Jonathan)
>
> /.../uart@foo {
> enocean {
> compatible = "enocean,esp3";
> #address-cells = <1>;
> #size-cells = <0>;
>
> window-handle@41424344 {
> compatible = "manufacturer1,handle";
> reg = <0x41424344>;
> enocean,equipment-profile = <1 2 3>;

What are these profiles? For declaring you actually can support some
"window-handle"? Can this be changed during runtime?

Is this some kind of device class specification by EnOcean which need to
be set into their transceiver that a management layer handle it which is
running by firmware?

> #io-channel-cells = <1>;
> };
>
> light-switch@41424348 {
> compatible = "manufacturer2,rocker";
> reg = <0x41424348>;
> enocean,equipment-profile = <4 5 6>;
> #io-channel-cells = <2>;
> };
> };
> };
>
> Pro: This would allow to abstract sensors (iio?) and actuators (gpio?).
> Cf. https://patchwork.ozlabs.org/patch/1028209/ for comparison.
> Con: How to deal with it on ACPI or on DT platforms without Overlays?
> How would the kernel preserve remote device state across reboots?
>
> So no, I don't think we can or should shoehorn non-802.15.4 PHYs into
> your ieee802154 PHY layer. If you see ways to share code between the
> various wireless PHYs, that would be great, but at present it seems like
> mostly boilerplate code with nothing in your phy struct applying to FSK
> or LoRa. Compare my cfglora series pointed to and Xue Liu's recent sysfs
> patch under discussion. If no more comments turn up on my cfglora series
> I'll copy it into a cfgfsk, so that I can integrate both into sx127x as
> a base for further discussions at Netdevconf. Thanks.
>

Share code always sounds like a good idea.

Thanks.

- Alex

2019-02-18 04:44:02

by Andreas Färber

[permalink] [raw]
Subject: Re: [RFC net-next 1/4] net: Reserve protocol identifiers for EnOcean

Hi Alex,

Am 01.02.19 um 01:58 schrieb Alexander Aring:
> On Wed, Jan 30, 2019 at 02:42:29AM +0100, Andreas Färber wrote:
>> Am 29.01.19 um 13:57 schrieb Alexander Aring:
>>> On Tue, Jan 29, 2019 at 06:01:27AM +0100, Andreas Färber wrote:
>>>> EnOcean wireless technology is based on ASK (ERP1) and FSK (ERP2) modulations
>>>> for sub-GHz and on IEEE 802.15.4 for 2.4 GHz.
>>>
>>> I am not sure what you try to do here. If I see that correctly you
>>> want to add for some special protocol vendor specific transceiver which
>>> is underneath an 802.15.4 transceiver a new ARPHRD type and even more
>>> for each modulation what it supports?
>>
>> No. EnOcean uses a 4-byte node ID across PHY layers, which I am using a
>> single ARPHRD_ENOCEAN for (which you conveniently cut off above).
>>
>> As indicated above, the 868 MHz transceiver is _not_ using 802.15.4 PHY
>> or MAC to my knowledge. It does sound like you spotted "IEEE 802.15.4"
>> and literally blended out all the rest...
>>
>
> Ah okay, I am curious about that. As far I undetstood now this has
> nothing to do with LoRa? I was not getting that point.

Correct, this is about FSK, not LoRa. The two usually come together and
I therefore need to propose a way to make also FSK usable.

> Is the PHY layer open? Do they actually refer to 802.15.4 in their
> specs, but the PHY layer is changed by... preamble, phy header, MTU?

ERP1 PHY layer appears to be ASK, ERP2 PHY layer is described as FSK.

ERP seems a MAC / Data Link protocol (ERP2 also Network layer) on top of
multiple alternative PHYs.

ASK is amplitude-modulated; FSK is frequency-modulated; 802.15.4 is PSK,
i.e. phase-modulated. Therefore from my view they are unrelated
subsystems [*].

Further, as I stated above, 802.15.4 is being used for 2.4 GHz (for
which I currently don't have any EnOcean hard-MAC transceiver), whereas
I am using FSK on 868 MHz.

The ERP layer is openly described, whereas I have not found a lot about
802.15.4 framing except for the below mentioned commands in ESP3 layer.

The EnOcean Alliance depicts ZigBee:
https://www.enocean.com/technology/radio-technology/
If that is the case, I guess we can end the 802.15.4 discussion here? ;)

No concrete documentation on how BLE is used either.

[*] With the exception that both Nemeus and ST have Sigfox DBPSK
implementations based on FSK transceivers.

>>> If it's a 802.15.4 transceiver why not using the 802.15.4 subsystem?
>>>
>>> For me it sounds more like a HardMAC transceiver driver for doing the
>>> vendor protocol. The different modulations is part of a 802.15.4 phy
>>> device class. Similar like in wireless.
>>
>> I've tried to design this exactly so that one _could_ implement it based
>> on 802.15.4 PHY framework for 2.4 GHz or based on an FSK PHY for sub-GHz
>> as a soft-MAC, layered similarly to LoRaWAN vs. LoRa, alongside the ESP
>> serdev driver in this series.
>>
>> In ESP3 the only 802.15.4 specific operations are getting/setting the
>> channel (COMMAND_2_4 packet type), and there's a CO_GET_FREQUENCY_INFO
>> command to discover frequency and protocol, with 802.15.4 having a
>> different ID than ERP2 (and I spot a value 0x30 for "Long Range" :-)).

This appears to refer to 925 MHz FSK in Japan though, not LoRa.

>> So in theory it might be possible to instantiate an 802.15.4 PHY after
>> discovering that ESP3 value, but neither is this a generic 802.15.4 PHY
>> nor a generic FSK PHY, and none of that relates to above ARPHRD really.
>>
>
> I keep it in mind, thanks.
>
>> PF_PACKET with SOCK_DGRAM for ETH_P_ERP2 gives me the subtelegram
>> contents to transmit via ESP, whereas SOCK_RAW would give the full frame
>> to transmit via FSK PHY. By avoiding a custom PF_ENOCEAN we seem to lose
>> the ability to prepend any protocol headers on the skb for SOCK_DGRAM.
>>
>
> I am not quite following here. SOCK_RAW full frame and SOCK_DGRAM
> payload sounds like what I suppose it should work.
>
> A switch of protocol will do a switch from ESP to FSK which is a phy layer
> behaviour?

No. ESP is a serial communication protocol between host and transceiver.
It abstracts the underlying radio protocol. By using SOCK_DGRAM, as
implemented in this patchset, we can pass payload data from userspace
via ESP to ERP. We cannot transmit a full SOCK_RAW ERP frame via ESP though.

My point was that by not implementing a custom PF_ERP2 protocol family
we don't have a place to implement the framing for FSK or 802.15.4
ourselves and rely on the ESP abstraction for now. Or put differently,
userspace will always need to use SOCK_DGRAM for compatibility with ESP,
and each PHY (FSK, 802.15.4, etc.) would need to re-implement framing
that payload.

>> Did you actually read my P.S. in the cover letter? I was glad to avoid
>> much PF_ socket boilerplate code here (as a playground for LoRa), and
>> now you're complaining about a single ARPHRD constant! :-/
>> By that standard we could stop implementing anything new... If you're
>> worried about number space, why has no one commented on the values added
>> for LoRa and other previous wireless technologies? No one had any such
>> comments on my LoRa RFC, nor on Jian-Hong's LoRaWAN patches, so I've
>> been reserving new ARPHRD_ constants for each technology I work on. If
>> ARPHRD_NONE would be a better value to use for PHY layers, no one
>> bothered to point it out so far! Nor did anyone suggest to Jian-Hong to
>> reuse ARPHRD_EUI64! And yet I spot nothing more suitable for EnOcean
>> addresses than a custom value. Fact is, the net_device wants some value.
>> Note that you have two ARPHRD constants assigned for 802.15.4 alone, so
>> please be fair to others.
>>
>
> Indeed we only need one. :-)
>
>> An 802.15.4 PHY won't help me for 868 MHz FSK - by my reading 802.15.4
>> is PSK (BPSK/OQPSK), thus incompatible with ASK/OOK and FSK/MSK.
>>
>> As noted in the cover letter, Semtech chips have FSK and OOK support
>> alongside LoRa modulation; so I am looking into FSK PHY support, both
>> for those chips as well as for some pure FSK/OOK transceivers posted to
>> linux-lpwan list (and potentially more, given time):
>> https://lists.infradead.org/pipermail/linux-lpwan/2019-January/000116.html
>> https://lists.infradead.org/pipermail/linux-lpwan/2019-January/000117.html
>> https://en.opensuse.org/User:A_faerber/LoRa_interop
>>
>> Therefore an FSK PHY's netlink interface will need to be able to handle
>> the requirements of upper-layer protocols, such as:
>> * Wireless M-Bus (which I could not yet find a suitable 868MHz hard-MAC
>> for to test against, only 169MHz; Si4432 has an Application Note AN451),
>> * KNX RF (which I have not come across a hard-MAC for either),
>> * Sigfox downstream (cf. mm002 LoRa driver as hard-MAC; no public docs),
>> * Z-Wave (not enough docs to implement much more for now), and here
>> * EnOcean Radio Protocol 2.
>>
>> In general I want to make sure my implementations can work with both
>> soft- and hard-MAC hardware out there, as demonstrated for LoRaWAN.
>> Pointing a user with hard-MAC device to a theoretical generic subsystem
>> of your preference doesn't help them, nor does it help to split the
>> community into separate hard-MAC vs. soft-MAC implementation camps that
>> make it hard for users to switch.
>
> agree.
>
>> * For example, when looking for how to actually use the Pine64 Z-Wave
>> adapter, back in the day I merely found an OpenHAB Raspbian(?) image
>> that as an openSUSE contributor I would surely not block my board with;
>> no explanations, no instructions, nothing. And when you have a pure Java
>> application on the one hand and a C/Python/whatever application on the
>> other, chances are that the kernel is the only common point of reuse. I
>> surely mentioned that I hate any userspace applications that attempt to
>> detect hardware on spidev/i2c-dev/tty without using the kernel-provided
>> facilities such as DT; finally, serdev allows to move any such
>> hardware-dependent tty code into the kernel - we just need to figure out
>> how to best expose functionality there (and ideally grow some more
>> helpers). Just note how patch 3/4 reuses the kernel's crc8
>> implementation instead of re-implementing it from scratch. Similarly I'd
>> love for my AT based LoRa drivers to share more serdev code, despite
>> line ending and response styles differing greatly (think
>> serdev_device_readline w/args?); binary protocols like ESP here are
>> luckily not affected as much. It could also use some more/better
>> documentation, some of the return values are wrong.
>> * As another example, we seem to be lacking a generic SDR subsystem:
>> People with SDR hardware seem to use either downstream kernel modules,
>> possibly application-generated, or closed-source userspace libraries?
>> Neither seems able to currently reuse the net subsystem for protocols.
>> And yet I've been asked repeatedly to design drivers in a way they could
>> be used with SDR, too, but without any way to actually test that today.
>> Has anyone talked to the SDR chipset/equipment vendors to remedy that?
>> The one I was in contact with simply chose not to reply again to date...
>>
>> For ETH_P_ we seem to be far away from 0xffff, so I don't see a problem
>> there? Not just was it the easiest thing to implement & test short-term,
>> but as outlined in the cover letter I saw no way here to turn that into
>> a non-net-subsystem because the data transmitted is not self-describing
>> (mostly battery-less sensors/actuators with ca. 4 byte data payload).
>> You must know that your device with id 0x12345678 conforms to profile X.
>>
>> Is describing remote devices in DT an option? (CC'ing Rob and Jonathan)
>>
>> /.../uart@foo {
>> enocean {
>> compatible = "enocean,esp3";
>> #address-cells = <1>;
>> #size-cells = <0>;
>>
>> window-handle@41424344 {
>> compatible = "manufacturer1,handle";
>> reg = <0x41424344>;
>> enocean,equipment-profile = <1 2 3>;
>
> What are these profiles? For declaring you actually can support some
> "window-handle"?

Window handles are an example type of EnOcean sensor: Turning the handle
is used for energy harvesting to send such a short radio message, which
then needs to be interpreted as "window tilted" and not as "button 2
pressed" or "temperature is 20°C" etc.

> Can this be changed during runtime?

Someone using this patchset can obviously send any data they want and
interpret incoming data any way they want. I don't think commercial
devices do that. But then again I'm not an expert.

> Is this some kind of device class specification by EnOcean which need to
> be set into their transceiver that a management layer handle it which is
> running by firmware?

The EnOcean Alliance profiles determine the interpretation of the
payload data and status bytes.

Since none of those sensor/actuator devices are likely to run Linux, I
don't think it matters how sensors implement their MCU firmware layers.

This DT example just being a sketch, we might just encode the profile
identifier triple in the compatible string, say "enocean,a5-02-01".
The actual manufacturer and model should then not have much influence.

Each profile could be a separate kernel driver. There's 25 4BS profiles
for temperature sensors, plus 6 profiles for combinations of multiple
sensors (temperature + humidity, light + temperature + occupancy), plus
one VLD profile with three types. The first hex number in the profile
identifier (e.g., A5-02-01) is the transmitted RORG field, the others
are arbitrary identifiers to select how to read the following data bytes.

Whether such a passively read as opposed to actively polled sensor is
suitable for the iio subsystem would be for Jonathan to comment on.
Presumably such an EnOcean profile driver would have a callback that
receives the payload data and its implementation would then store the
data until it is requested via iio?

Note: It seems that wM-Bus metering payload data beyond frame formats is
even less standardized or documented than EnOcean or Z-Wave. And for
home automation there are a number of entirely proprietary/undocumented
ones based on FSK, too, which then need to go into userspace.

But again, my primary focus before Netdevconf 0x13 will be the PHY
subsystems for LoRa and FSK, not application-specific higher-level
protocols like this one, nor driver frameworks on top as discussed here.

>> #io-channel-cells = <1>;
>> };
>>
>> light-switch@41424348 {
>> compatible = "manufacturer2,rocker";
>> reg = <0x41424348>;
>> enocean,equipment-profile = <4 5 6>;
>> #io-channel-cells = <2>;
>> };
>> };
>> };
>>
>> Pro: This would allow to abstract sensors (iio?) and actuators (gpio?).
>> Cf. https://patchwork.ozlabs.org/patch/1028209/ for comparison.
>> Con: How to deal with it on ACPI or on DT platforms without Overlays?
>> How would the kernel preserve remote device state across reboots?
>>
>> So no, I don't think we can or should shoehorn non-802.15.4 PHYs into
>> your ieee802154 PHY layer. If you see ways to share code between the
>> various wireless PHYs, that would be great, but at present it seems like
>> mostly boilerplate code with nothing in your phy struct applying to FSK
>> or LoRa. Compare my cfglora series pointed to and Xue Liu's recent sysfs
>> patch under discussion. If no more comments turn up on my cfglora series
>> I'll copy it into a cfgfsk, so that I can integrate both into sx127x as
>> a base for further discussions at Netdevconf. Thanks.
>>
>
> Share code always sounds like a good idea.

Well, if you have comments on how to go about that, such as on Xue Liu's
PHY device work, do let us know. :)

Thanks,
Andreas

--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)