From: "Andreas Färber" <afaerber@suse.de> To: netdev@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, "Jian-Hong Pan" <starnight@g.ncu.edu.tw>, "Jiri Pirko" <jiri@resnulli.us>, "Marcel Holtmann" <marcel@holtmann.org>, "David S . Miller" <davem@davemloft.net>, "Matthias Brugger" <mbrugger@suse.com>, "Janus Piwek" <jpiwek@arroweurope.com>, "Michael Röder" <michael.roeder@avnet.eu>, "Dollar Chen" <dollar.chen@wtmec.com>, "Ken Yu" <ken.yu@rakwireless.com>, "Andreas Färber" <afaerber@suse.de> Subject: [RFC net-next 10/15] net: lora: Add Microchip RN2483 Date: Sun, 1 Jul 2018 13:07:59 +0200 [thread overview] Message-ID: <20180701110804.32415-11-afaerber@suse.de> (raw) In-Reply-To: <20180701110804.32415-1-afaerber@suse.de> The Microchip RN2483 and RN2903 are UART based modules exposing both LoRaWAN and LoRa. The RN2483 supports switching between 433 and 868 MHz. Signed-off-by: Andreas Färber <afaerber@suse.de> --- drivers/net/lora/Kconfig | 7 + drivers/net/lora/Makefile | 4 + drivers/net/lora/rn2483.c | 344 ++++++++++++++++++++++++++++++++++++++++++ drivers/net/lora/rn2483.h | 40 +++++ drivers/net/lora/rn2483_cmd.c | 130 ++++++++++++++++ 5 files changed, 525 insertions(+) create mode 100644 drivers/net/lora/rn2483.c create mode 100644 drivers/net/lora/rn2483.h create mode 100644 drivers/net/lora/rn2483_cmd.c diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig index 0436f6b09a1c..940bd2cbe106 100644 --- a/drivers/net/lora/Kconfig +++ b/drivers/net/lora/Kconfig @@ -17,6 +17,13 @@ config LORA_DEV if LORA_DEV +config LORA_RN2483 + tristate "Microchip RN2483/RN2903 driver" + default y + depends on SERIAL_DEV_BUS + help + Microchip RN2483/2903 + config LORA_SX1276 tristate "Semtech SX127x SPI driver" default y diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile index 8845542dba50..07839c3ce9f8 100644 --- a/drivers/net/lora/Makefile +++ b/drivers/net/lora/Makefile @@ -9,5 +9,9 @@ lora-dev-y := dev.o # Alphabetically sorted. # +obj-$(CONFIG_LORA_RN2483) += lora-rn2483.o +lora-rn2483-y := rn2483.o +lora-rn2483-y += rn2483_cmd.o + obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o lora-sx1276-y := sx1276.o diff --git a/drivers/net/lora/rn2483.c b/drivers/net/lora/rn2483.c new file mode 100644 index 000000000000..8b9ec2575ee2 --- /dev/null +++ b/drivers/net/lora/rn2483.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Microchip RN2483/RN2903 + * + * Copyright (c) 2017-2018 Andreas Färber + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/lora.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of.h> +#include <linux/serdev.h> +#include <linux/lora/dev.h> + +#include "rn2483.h" + +struct rn2483_priv { + struct lora_priv lora; +}; + +static netdev_tx_t rn2483_loradev_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + if (skb->protocol != htons(ETH_P_LORA)) { + kfree_skb(skb); + netdev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + + netif_stop_queue(netdev); + + /* TODO */ + return NETDEV_TX_OK; +} + +static int rn2483_loradev_open(struct net_device *netdev) +{ + int ret; + + netdev_dbg(netdev, "%s", __func__); + + ret = open_loradev(netdev); + if (ret) + return ret; + + netif_start_queue(netdev); + + return 0; +} + +static int rn2483_loradev_stop(struct net_device *netdev) +{ + netdev_dbg(netdev, "%s", __func__); + + netif_stop_queue(netdev); + close_loradev(netdev); + + return 0; +} + +static const struct net_device_ops rn2483_net_device_ops = { + .ndo_open = rn2483_loradev_open, + .ndo_stop = rn2483_loradev_stop, + .ndo_start_xmit = rn2483_loradev_start_xmit, +}; + +int rn2483_readline_timeout(struct rn2483_device *rndev, char **line, unsigned long timeout) +{ + timeout = wait_for_completion_timeout(&rndev->line_recv_comp, timeout); + if (!timeout) + return -ETIMEDOUT; + + *line = devm_kstrdup(&rndev->serdev->dev, rndev->buf, GFP_KERNEL); + complete(&rndev->line_read_comp); + if (!*line) + return -ENOMEM; + + return 0; +} + +static void rn2483_receive_line(struct rn2483_device *rndev, const char *sz, size_t len) +{ + dev_dbg(&rndev->serdev->dev, "Received line '%s' (%d)", sz, (int)len); + + reinit_completion(&rndev->line_read_comp); + complete(&rndev->line_recv_comp); + wait_for_completion(&rndev->line_read_comp); + reinit_completion(&rndev->line_recv_comp); +} + +static int rn2483_receive_buf(struct serdev_device *serdev, const u8 *data, size_t count) +{ + struct rn2483_device *rndev = serdev_device_get_drvdata(serdev); + size_t i; + + dev_dbg(&serdev->dev, "Receive (%d)", (int)count); + if (!rndev->buf) { + rndev->buf = devm_kmalloc(&serdev->dev, count, GFP_KERNEL); + if (!rndev->buf) + return 0; + rndev->buflen = 0; + } else { + void *tmp = devm_kmalloc(&serdev->dev, rndev->buflen + count, GFP_KERNEL); + if (!tmp) + return 0; + memcpy(tmp, rndev->buf, rndev->buflen); + devm_kfree(&serdev->dev, rndev->buf); + rndev->buf = tmp; + } + + for (i = 0; i < count; i++) { + if (data[i] == '\r') { + rndev->saw_cr = true; + } else if (data[i] == '\n' && rndev->saw_cr) { + if (i > 1) + memcpy(rndev->buf + rndev->buflen, data, i - 1); + ((char *)rndev->buf)[rndev->buflen + i - 1] = 0; + rn2483_receive_line(rndev, rndev->buf, rndev->buflen + i - 1); + rndev->saw_cr = false; + devm_kfree(&serdev->dev, rndev->buf); + rndev->buf = NULL; + rndev->buflen = 0; + return i + 1; + } else + rndev->saw_cr = false; + } + + memcpy(rndev->buf + rndev->buflen, data, count); + rndev->buflen += count; + return count; +} + +static const struct serdev_device_ops rn2483_serdev_client_ops = { + .receive_buf = rn2483_receive_buf, +}; + +static int rn2483_probe(struct serdev_device *sdev) +{ + struct rn2483_device *rndev; + char *line, *cmd; + char sz[5]; + u32 status; + int ret; + + dev_info(&sdev->dev, "Probing"); + + rndev = devm_kzalloc(&sdev->dev, sizeof(struct rn2483_device), GFP_KERNEL); + if (!rndev) + return -ENOMEM; + + rndev->serdev = sdev; + init_completion(&rndev->line_recv_comp); + init_completion(&rndev->line_read_comp); + mutex_init(&rndev->cmd_lock); + serdev_device_set_drvdata(sdev, rndev); + + rndev->reset_gpio = devm_gpiod_get_optional(&sdev->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(rndev->reset_gpio)) + return PTR_ERR(rndev->reset_gpio); + + ret = serdev_device_open(sdev); + if (ret) { + dev_err(&sdev->dev, "Failed to open (%d)", ret); + return ret; + } + + serdev_device_set_baudrate(sdev, 57600); + serdev_device_set_flow_control(sdev, false); + + gpiod_set_value_cansleep(rndev->reset_gpio, 0); + msleep(5); + serdev_device_set_client_ops(sdev, &rn2483_serdev_client_ops); + gpiod_set_value_cansleep(rndev->reset_gpio, 1); + msleep(100); + + ret = rn2483_readline_timeout(rndev, &line, HZ); + if (ret) { + if (ret != -ENOMEM) + dev_err(&sdev->dev, "Timeout waiting for firmware identification"); + goto err_timeout; + } + + if (strlen(line) < strlen("RNxxxx X.Y.Z MMM DD YYYY HH:MM:SS") || line[6] != ' ' || + strncmp(line, "RN", 2) != 0) { + dev_err(&sdev->dev, "Unexpected response '%s'", line); + devm_kfree(&sdev->dev, line); + ret = -EINVAL; + goto err_version; + } + dev_info(&sdev->dev, "Firmware '%s'", line); + strncpy(sz, line + 2, 4); + sz[4] = 0; + devm_kfree(&sdev->dev, line); + ret = kstrtouint(sz, 10, &rndev->model); + if (ret) + goto err_model; + if (!(rndev->model == 2483 || rndev->model == 2903)) { + dev_err(&sdev->dev, "Unknown model %u", rndev->model); + ret = -ENOTSUPP; + goto err_model; + } + dev_info(&sdev->dev, "Detected RN%u", rndev->model); + + ret = rn2483_sys_get_hweui(rndev, &rndev->hweui); + if (ret) { + if (ret != -ENOMEM) + dev_err(&sdev->dev, "Failed to read HWEUI (%d)", ret); + goto err_hweui; + } + dev_info(&sdev->dev, "HWEUI " PRIxLORAEUI, LORA_EUI(rndev->hweui)); + + switch (rndev->model) { + case 2483: + ret = rn2483_mac_get_band(rndev, &rndev->band); + if (ret) { + dev_err(&sdev->dev, "Failed to read band (%d)", ret); + goto err_band; + } + dev_info(&sdev->dev, "Frequency band %u MHz", rndev->band); + + ret = rn2483_mac_reset_band(rndev, 433); + if (ret) { + dev_err(&sdev->dev, "Failed to reset band (%d)", ret); + goto err_band; + } + rndev->band = 433; + + ret = rn2483_mac_get_band(rndev, &rndev->band); + if (!ret) + dev_info(&sdev->dev, "New frequency band: %u MHz", rndev->band); + break; + case 2903: + /* No "mac get band" command available */ + rndev->band = 915; + break; + } + + ret = rn2483_mac_get_status(rndev, &status); + if (!ret) + dev_info(&sdev->dev, "MAC status %08x", status); + + if (true) { + u32 pause; + ret = rn2483_mac_pause(rndev, &pause); + if (!ret) + dev_info(&sdev->dev, "MAC pausing (0x%08x)", pause); + ret = rn2483_mac_resume(rndev); + if (!ret) + dev_info(&sdev->dev, "MAC resuming"); + } + + cmd = "mac get sync"; + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, cmd, &line, HZ); + mutex_unlock(&rndev->cmd_lock); + if (!ret) { + dev_info(&sdev->dev, "%s => '%s'", cmd, line); + devm_kfree(&sdev->dev, line); + } + + rndev->netdev = alloc_loradev(sizeof(struct rn2483_priv)); + if (!rndev->netdev) { + ret = -ENOMEM; + goto err_alloc_netdev; + } + + rndev->netdev->netdev_ops = &rn2483_net_device_ops; + SET_NETDEV_DEV(rndev->netdev, &sdev->dev); + + ret = register_loradev(rndev->netdev); + if (ret) + goto err_register_netdev; + + dev_info(&sdev->dev, "Done."); + + return 0; + +err_register_netdev: + free_loradev(rndev->netdev); +err_alloc_netdev: +err_band: +err_hweui: +err_model: +err_version: +err_timeout: + gpiod_set_value_cansleep(rndev->reset_gpio, 0); + return ret; +} + +static void rn2483_remove(struct serdev_device *sdev) +{ + struct rn2483_device *rndev = serdev_device_get_drvdata(sdev); + + unregister_loradev(rndev->netdev); + free_loradev(rndev->netdev); + + gpiod_set_value_cansleep(rndev->reset_gpio, 0); + + complete(&rndev->line_read_comp); + + serdev_device_close(sdev); + + dev_info(&sdev->dev, "Removed"); +} + +static const struct of_device_id rn2483_of_match[] = { + { .compatible = "microchip,rn2483" }, + { .compatible = "microchip,rn2903" }, + {} +}; +MODULE_DEVICE_TABLE(of, rn2483_of_match); + +static struct serdev_device_driver rn2483_serdev_driver = { + .probe = rn2483_probe, + .remove = rn2483_remove, + .driver = { + .name = "rn2483", + .of_match_table = rn2483_of_match, + }, +}; + +static int __init rn2483_init(void) +{ + int ret; + + ret = serdev_device_driver_register(&rn2483_serdev_driver); + if (ret) + return ret; + + return 0; +} + +static void __exit rn2483_exit(void) +{ + serdev_device_driver_unregister(&rn2483_serdev_driver); +} + +module_init(rn2483_init); +module_exit(rn2483_exit); + +MODULE_DESCRIPTION("RN2483 serdev driver"); +MODULE_AUTHOR("Andreas Färber <afaerber@suse.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/lora/rn2483.h b/drivers/net/lora/rn2483.h new file mode 100644 index 000000000000..f92660286f15 --- /dev/null +++ b/drivers/net/lora/rn2483.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2017-2018 Andreas Färber + */ +#ifndef _RN2483_H +#define _RN2483_H + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/netdevice.h> +#include <linux/serdev.h> +#include <linux/lora/dev.h> + +struct rn2483_device { + struct serdev_device *serdev; + struct gpio_desc *reset_gpio; + struct net_device *netdev; + unsigned model; + lora_eui hweui; + unsigned band; + bool saw_cr; + void *buf; + size_t buflen; + struct completion line_recv_comp; + struct completion line_read_comp; + struct mutex cmd_lock; +}; + +int rn2483_readline_timeout(struct rn2483_device *rndev, char **line, unsigned long timeout); +int rn2483_send_command_timeout(struct rn2483_device *rndev, + const char *cmd, char **resp, unsigned long timeout); + +int rn2483_sys_get_hweui(struct rn2483_device *rndev, lora_eui *val); +int rn2483_mac_get_band(struct rn2483_device *rndev, uint *val); +int rn2483_mac_get_status(struct rn2483_device *rndev, u32 *val); +int rn2483_mac_reset_band(struct rn2483_device *rndev, unsigned band); +int rn2483_mac_pause(struct rn2483_device *rndev, u32 *max_pause); +int rn2483_mac_resume(struct rn2483_device *rndev); + +#endif diff --git a/drivers/net/lora/rn2483_cmd.c b/drivers/net/lora/rn2483_cmd.c new file mode 100644 index 000000000000..6d6fca8fa93c --- /dev/null +++ b/drivers/net/lora/rn2483_cmd.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Microchip RN2483/RN2903 - UART commands + * + * Copyright (c) 2017-2018 Andreas Färber + */ +#include "rn2483.h" + +#define RN2483_CMD_TIMEOUT HZ + +int rn2483_send_command_timeout(struct rn2483_device *rndev, + const char *cmd, char **resp, unsigned long timeout) +{ + int ret; + + ret = serdev_device_write_buf(rndev->serdev, cmd, strlen(cmd)); + if (ret < 0) + return ret; + + ret = serdev_device_write_buf(rndev->serdev, "\r\n", 2); + if (ret < 0) + return ret; + + return rn2483_readline_timeout(rndev, resp, timeout); +} + +int rn2483_sys_get_hweui(struct rn2483_device *rndev, lora_eui *val) +{ + int ret; + char *line; + + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, "sys get hweui", &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + if (ret) + return ret; + + ret = lora_strtoeui(line, val); + devm_kfree(&rndev->serdev->dev, line); + return ret; +} + +int rn2483_mac_get_band(struct rn2483_device *rndev, uint *val) +{ + int ret; + char *line; + + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, "mac get band", &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + if (ret) + return ret; + + ret = kstrtouint(line, 10, val); + devm_kfree(&rndev->serdev->dev, line); + + return ret; +} + +int rn2483_mac_get_status(struct rn2483_device *rndev, u32 *val) +{ + int ret; + char *line; + + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, "mac get status", &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + if (ret) + return ret; + + ret = kstrtou32(line, 16, val); + devm_kfree(&rndev->serdev->dev, line); + return ret; +} + +int rn2483_mac_reset_band(struct rn2483_device *rndev, unsigned band) +{ + int ret; + char *line, *cmd; + + cmd = devm_kasprintf(&rndev->serdev->dev, GFP_KERNEL, "mac reset %u", band); + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, cmd, &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + devm_kfree(&rndev->serdev->dev, cmd); + if (ret) + return ret; + + if (strcmp(line, "ok") == 0) + ret = 0; + else if (strcmp(line, "invalid_param") == 0) + ret = -EINVAL; + else + ret = -EPROTO; + + devm_kfree(&rndev->serdev->dev, line); + return ret; +} + +int rn2483_mac_pause(struct rn2483_device *rndev, u32 *max_pause) +{ + int ret; + char *line; + + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, "mac pause", &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + if (ret) + return ret; + + ret = kstrtou32(line, 10, max_pause); + devm_kfree(&rndev->serdev->dev, line); + return ret; +} + +int rn2483_mac_resume(struct rn2483_device *rndev) +{ + int ret; + char *line; + + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, "mac resume", &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + if (ret) + return ret; + + ret = (strcmp(line, "ok") == 0) ? 0 : -EPROTO; + devm_kfree(&rndev->serdev->dev, line); + return ret; +} -- 2.16.4
WARNING: multiple messages have this Message-ID (diff)
From: afaerber@suse.de (Andreas Färber) To: linux-arm-kernel@lists.infradead.org Subject: [RFC net-next 10/15] net: lora: Add Microchip RN2483 Date: Sun, 1 Jul 2018 13:07:59 +0200 [thread overview] Message-ID: <20180701110804.32415-11-afaerber@suse.de> (raw) In-Reply-To: <20180701110804.32415-1-afaerber@suse.de> The Microchip RN2483 and RN2903 are UART based modules exposing both LoRaWAN and LoRa. The RN2483 supports switching between 433 and 868 MHz. Signed-off-by: Andreas F?rber <afaerber@suse.de> --- drivers/net/lora/Kconfig | 7 + drivers/net/lora/Makefile | 4 + drivers/net/lora/rn2483.c | 344 ++++++++++++++++++++++++++++++++++++++++++ drivers/net/lora/rn2483.h | 40 +++++ drivers/net/lora/rn2483_cmd.c | 130 ++++++++++++++++ 5 files changed, 525 insertions(+) create mode 100644 drivers/net/lora/rn2483.c create mode 100644 drivers/net/lora/rn2483.h create mode 100644 drivers/net/lora/rn2483_cmd.c diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig index 0436f6b09a1c..940bd2cbe106 100644 --- a/drivers/net/lora/Kconfig +++ b/drivers/net/lora/Kconfig @@ -17,6 +17,13 @@ config LORA_DEV if LORA_DEV +config LORA_RN2483 + tristate "Microchip RN2483/RN2903 driver" + default y + depends on SERIAL_DEV_BUS + help + Microchip RN2483/2903 + config LORA_SX1276 tristate "Semtech SX127x SPI driver" default y diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile index 8845542dba50..07839c3ce9f8 100644 --- a/drivers/net/lora/Makefile +++ b/drivers/net/lora/Makefile @@ -9,5 +9,9 @@ lora-dev-y := dev.o # Alphabetically sorted. # +obj-$(CONFIG_LORA_RN2483) += lora-rn2483.o +lora-rn2483-y := rn2483.o +lora-rn2483-y += rn2483_cmd.o + obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o lora-sx1276-y := sx1276.o diff --git a/drivers/net/lora/rn2483.c b/drivers/net/lora/rn2483.c new file mode 100644 index 000000000000..8b9ec2575ee2 --- /dev/null +++ b/drivers/net/lora/rn2483.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Microchip RN2483/RN2903 + * + * Copyright (c) 2017-2018 Andreas F?rber + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/lora.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of.h> +#include <linux/serdev.h> +#include <linux/lora/dev.h> + +#include "rn2483.h" + +struct rn2483_priv { + struct lora_priv lora; +}; + +static netdev_tx_t rn2483_loradev_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + if (skb->protocol != htons(ETH_P_LORA)) { + kfree_skb(skb); + netdev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + + netif_stop_queue(netdev); + + /* TODO */ + return NETDEV_TX_OK; +} + +static int rn2483_loradev_open(struct net_device *netdev) +{ + int ret; + + netdev_dbg(netdev, "%s", __func__); + + ret = open_loradev(netdev); + if (ret) + return ret; + + netif_start_queue(netdev); + + return 0; +} + +static int rn2483_loradev_stop(struct net_device *netdev) +{ + netdev_dbg(netdev, "%s", __func__); + + netif_stop_queue(netdev); + close_loradev(netdev); + + return 0; +} + +static const struct net_device_ops rn2483_net_device_ops = { + .ndo_open = rn2483_loradev_open, + .ndo_stop = rn2483_loradev_stop, + .ndo_start_xmit = rn2483_loradev_start_xmit, +}; + +int rn2483_readline_timeout(struct rn2483_device *rndev, char **line, unsigned long timeout) +{ + timeout = wait_for_completion_timeout(&rndev->line_recv_comp, timeout); + if (!timeout) + return -ETIMEDOUT; + + *line = devm_kstrdup(&rndev->serdev->dev, rndev->buf, GFP_KERNEL); + complete(&rndev->line_read_comp); + if (!*line) + return -ENOMEM; + + return 0; +} + +static void rn2483_receive_line(struct rn2483_device *rndev, const char *sz, size_t len) +{ + dev_dbg(&rndev->serdev->dev, "Received line '%s' (%d)", sz, (int)len); + + reinit_completion(&rndev->line_read_comp); + complete(&rndev->line_recv_comp); + wait_for_completion(&rndev->line_read_comp); + reinit_completion(&rndev->line_recv_comp); +} + +static int rn2483_receive_buf(struct serdev_device *serdev, const u8 *data, size_t count) +{ + struct rn2483_device *rndev = serdev_device_get_drvdata(serdev); + size_t i; + + dev_dbg(&serdev->dev, "Receive (%d)", (int)count); + if (!rndev->buf) { + rndev->buf = devm_kmalloc(&serdev->dev, count, GFP_KERNEL); + if (!rndev->buf) + return 0; + rndev->buflen = 0; + } else { + void *tmp = devm_kmalloc(&serdev->dev, rndev->buflen + count, GFP_KERNEL); + if (!tmp) + return 0; + memcpy(tmp, rndev->buf, rndev->buflen); + devm_kfree(&serdev->dev, rndev->buf); + rndev->buf = tmp; + } + + for (i = 0; i < count; i++) { + if (data[i] == '\r') { + rndev->saw_cr = true; + } else if (data[i] == '\n' && rndev->saw_cr) { + if (i > 1) + memcpy(rndev->buf + rndev->buflen, data, i - 1); + ((char *)rndev->buf)[rndev->buflen + i - 1] = 0; + rn2483_receive_line(rndev, rndev->buf, rndev->buflen + i - 1); + rndev->saw_cr = false; + devm_kfree(&serdev->dev, rndev->buf); + rndev->buf = NULL; + rndev->buflen = 0; + return i + 1; + } else + rndev->saw_cr = false; + } + + memcpy(rndev->buf + rndev->buflen, data, count); + rndev->buflen += count; + return count; +} + +static const struct serdev_device_ops rn2483_serdev_client_ops = { + .receive_buf = rn2483_receive_buf, +}; + +static int rn2483_probe(struct serdev_device *sdev) +{ + struct rn2483_device *rndev; + char *line, *cmd; + char sz[5]; + u32 status; + int ret; + + dev_info(&sdev->dev, "Probing"); + + rndev = devm_kzalloc(&sdev->dev, sizeof(struct rn2483_device), GFP_KERNEL); + if (!rndev) + return -ENOMEM; + + rndev->serdev = sdev; + init_completion(&rndev->line_recv_comp); + init_completion(&rndev->line_read_comp); + mutex_init(&rndev->cmd_lock); + serdev_device_set_drvdata(sdev, rndev); + + rndev->reset_gpio = devm_gpiod_get_optional(&sdev->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(rndev->reset_gpio)) + return PTR_ERR(rndev->reset_gpio); + + ret = serdev_device_open(sdev); + if (ret) { + dev_err(&sdev->dev, "Failed to open (%d)", ret); + return ret; + } + + serdev_device_set_baudrate(sdev, 57600); + serdev_device_set_flow_control(sdev, false); + + gpiod_set_value_cansleep(rndev->reset_gpio, 0); + msleep(5); + serdev_device_set_client_ops(sdev, &rn2483_serdev_client_ops); + gpiod_set_value_cansleep(rndev->reset_gpio, 1); + msleep(100); + + ret = rn2483_readline_timeout(rndev, &line, HZ); + if (ret) { + if (ret != -ENOMEM) + dev_err(&sdev->dev, "Timeout waiting for firmware identification"); + goto err_timeout; + } + + if (strlen(line) < strlen("RNxxxx X.Y.Z MMM DD YYYY HH:MM:SS") || line[6] != ' ' || + strncmp(line, "RN", 2) != 0) { + dev_err(&sdev->dev, "Unexpected response '%s'", line); + devm_kfree(&sdev->dev, line); + ret = -EINVAL; + goto err_version; + } + dev_info(&sdev->dev, "Firmware '%s'", line); + strncpy(sz, line + 2, 4); + sz[4] = 0; + devm_kfree(&sdev->dev, line); + ret = kstrtouint(sz, 10, &rndev->model); + if (ret) + goto err_model; + if (!(rndev->model == 2483 || rndev->model == 2903)) { + dev_err(&sdev->dev, "Unknown model %u", rndev->model); + ret = -ENOTSUPP; + goto err_model; + } + dev_info(&sdev->dev, "Detected RN%u", rndev->model); + + ret = rn2483_sys_get_hweui(rndev, &rndev->hweui); + if (ret) { + if (ret != -ENOMEM) + dev_err(&sdev->dev, "Failed to read HWEUI (%d)", ret); + goto err_hweui; + } + dev_info(&sdev->dev, "HWEUI " PRIxLORAEUI, LORA_EUI(rndev->hweui)); + + switch (rndev->model) { + case 2483: + ret = rn2483_mac_get_band(rndev, &rndev->band); + if (ret) { + dev_err(&sdev->dev, "Failed to read band (%d)", ret); + goto err_band; + } + dev_info(&sdev->dev, "Frequency band %u MHz", rndev->band); + + ret = rn2483_mac_reset_band(rndev, 433); + if (ret) { + dev_err(&sdev->dev, "Failed to reset band (%d)", ret); + goto err_band; + } + rndev->band = 433; + + ret = rn2483_mac_get_band(rndev, &rndev->band); + if (!ret) + dev_info(&sdev->dev, "New frequency band: %u MHz", rndev->band); + break; + case 2903: + /* No "mac get band" command available */ + rndev->band = 915; + break; + } + + ret = rn2483_mac_get_status(rndev, &status); + if (!ret) + dev_info(&sdev->dev, "MAC status %08x", status); + + if (true) { + u32 pause; + ret = rn2483_mac_pause(rndev, &pause); + if (!ret) + dev_info(&sdev->dev, "MAC pausing (0x%08x)", pause); + ret = rn2483_mac_resume(rndev); + if (!ret) + dev_info(&sdev->dev, "MAC resuming"); + } + + cmd = "mac get sync"; + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, cmd, &line, HZ); + mutex_unlock(&rndev->cmd_lock); + if (!ret) { + dev_info(&sdev->dev, "%s => '%s'", cmd, line); + devm_kfree(&sdev->dev, line); + } + + rndev->netdev = alloc_loradev(sizeof(struct rn2483_priv)); + if (!rndev->netdev) { + ret = -ENOMEM; + goto err_alloc_netdev; + } + + rndev->netdev->netdev_ops = &rn2483_net_device_ops; + SET_NETDEV_DEV(rndev->netdev, &sdev->dev); + + ret = register_loradev(rndev->netdev); + if (ret) + goto err_register_netdev; + + dev_info(&sdev->dev, "Done."); + + return 0; + +err_register_netdev: + free_loradev(rndev->netdev); +err_alloc_netdev: +err_band: +err_hweui: +err_model: +err_version: +err_timeout: + gpiod_set_value_cansleep(rndev->reset_gpio, 0); + return ret; +} + +static void rn2483_remove(struct serdev_device *sdev) +{ + struct rn2483_device *rndev = serdev_device_get_drvdata(sdev); + + unregister_loradev(rndev->netdev); + free_loradev(rndev->netdev); + + gpiod_set_value_cansleep(rndev->reset_gpio, 0); + + complete(&rndev->line_read_comp); + + serdev_device_close(sdev); + + dev_info(&sdev->dev, "Removed"); +} + +static const struct of_device_id rn2483_of_match[] = { + { .compatible = "microchip,rn2483" }, + { .compatible = "microchip,rn2903" }, + {} +}; +MODULE_DEVICE_TABLE(of, rn2483_of_match); + +static struct serdev_device_driver rn2483_serdev_driver = { + .probe = rn2483_probe, + .remove = rn2483_remove, + .driver = { + .name = "rn2483", + .of_match_table = rn2483_of_match, + }, +}; + +static int __init rn2483_init(void) +{ + int ret; + + ret = serdev_device_driver_register(&rn2483_serdev_driver); + if (ret) + return ret; + + return 0; +} + +static void __exit rn2483_exit(void) +{ + serdev_device_driver_unregister(&rn2483_serdev_driver); +} + +module_init(rn2483_init); +module_exit(rn2483_exit); + +MODULE_DESCRIPTION("RN2483 serdev driver"); +MODULE_AUTHOR("Andreas F?rber <afaerber@suse.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/lora/rn2483.h b/drivers/net/lora/rn2483.h new file mode 100644 index 000000000000..f92660286f15 --- /dev/null +++ b/drivers/net/lora/rn2483.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2017-2018 Andreas F?rber + */ +#ifndef _RN2483_H +#define _RN2483_H + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/netdevice.h> +#include <linux/serdev.h> +#include <linux/lora/dev.h> + +struct rn2483_device { + struct serdev_device *serdev; + struct gpio_desc *reset_gpio; + struct net_device *netdev; + unsigned model; + lora_eui hweui; + unsigned band; + bool saw_cr; + void *buf; + size_t buflen; + struct completion line_recv_comp; + struct completion line_read_comp; + struct mutex cmd_lock; +}; + +int rn2483_readline_timeout(struct rn2483_device *rndev, char **line, unsigned long timeout); +int rn2483_send_command_timeout(struct rn2483_device *rndev, + const char *cmd, char **resp, unsigned long timeout); + +int rn2483_sys_get_hweui(struct rn2483_device *rndev, lora_eui *val); +int rn2483_mac_get_band(struct rn2483_device *rndev, uint *val); +int rn2483_mac_get_status(struct rn2483_device *rndev, u32 *val); +int rn2483_mac_reset_band(struct rn2483_device *rndev, unsigned band); +int rn2483_mac_pause(struct rn2483_device *rndev, u32 *max_pause); +int rn2483_mac_resume(struct rn2483_device *rndev); + +#endif diff --git a/drivers/net/lora/rn2483_cmd.c b/drivers/net/lora/rn2483_cmd.c new file mode 100644 index 000000000000..6d6fca8fa93c --- /dev/null +++ b/drivers/net/lora/rn2483_cmd.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Microchip RN2483/RN2903 - UART commands + * + * Copyright (c) 2017-2018 Andreas F?rber + */ +#include "rn2483.h" + +#define RN2483_CMD_TIMEOUT HZ + +int rn2483_send_command_timeout(struct rn2483_device *rndev, + const char *cmd, char **resp, unsigned long timeout) +{ + int ret; + + ret = serdev_device_write_buf(rndev->serdev, cmd, strlen(cmd)); + if (ret < 0) + return ret; + + ret = serdev_device_write_buf(rndev->serdev, "\r\n", 2); + if (ret < 0) + return ret; + + return rn2483_readline_timeout(rndev, resp, timeout); +} + +int rn2483_sys_get_hweui(struct rn2483_device *rndev, lora_eui *val) +{ + int ret; + char *line; + + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, "sys get hweui", &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + if (ret) + return ret; + + ret = lora_strtoeui(line, val); + devm_kfree(&rndev->serdev->dev, line); + return ret; +} + +int rn2483_mac_get_band(struct rn2483_device *rndev, uint *val) +{ + int ret; + char *line; + + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, "mac get band", &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + if (ret) + return ret; + + ret = kstrtouint(line, 10, val); + devm_kfree(&rndev->serdev->dev, line); + + return ret; +} + +int rn2483_mac_get_status(struct rn2483_device *rndev, u32 *val) +{ + int ret; + char *line; + + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, "mac get status", &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + if (ret) + return ret; + + ret = kstrtou32(line, 16, val); + devm_kfree(&rndev->serdev->dev, line); + return ret; +} + +int rn2483_mac_reset_band(struct rn2483_device *rndev, unsigned band) +{ + int ret; + char *line, *cmd; + + cmd = devm_kasprintf(&rndev->serdev->dev, GFP_KERNEL, "mac reset %u", band); + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, cmd, &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + devm_kfree(&rndev->serdev->dev, cmd); + if (ret) + return ret; + + if (strcmp(line, "ok") == 0) + ret = 0; + else if (strcmp(line, "invalid_param") == 0) + ret = -EINVAL; + else + ret = -EPROTO; + + devm_kfree(&rndev->serdev->dev, line); + return ret; +} + +int rn2483_mac_pause(struct rn2483_device *rndev, u32 *max_pause) +{ + int ret; + char *line; + + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, "mac pause", &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + if (ret) + return ret; + + ret = kstrtou32(line, 10, max_pause); + devm_kfree(&rndev->serdev->dev, line); + return ret; +} + +int rn2483_mac_resume(struct rn2483_device *rndev) +{ + int ret; + char *line; + + mutex_lock(&rndev->cmd_lock); + ret = rn2483_send_command_timeout(rndev, "mac resume", &line, RN2483_CMD_TIMEOUT); + mutex_unlock(&rndev->cmd_lock); + if (ret) + return ret; + + ret = (strcmp(line, "ok") == 0) ? 0 : -EPROTO; + devm_kfree(&rndev->serdev->dev, line); + return ret; +} -- 2.16.4
next prev parent reply other threads:[~2018-07-01 11:10 UTC|newest] Thread overview: 173+ messages / expand[flat|nested] mbox.gz Atom feed top 2018-07-01 11:07 [RFC net-next 00/15] net: A socket API for LoRa Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` [RFC net-next 01/15] net: Reserve protocol numbers " Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` [RFC net-next 02/15] net: lora: Define sockaddr_lora Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` [RFC net-next 03/15] net: lora: Add protocol numbers Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` [RFC net-next 04/15] net: Add lora subsystem Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` [RFC net-next 05/15] HACK: net: lora: Deal with .poll_mask in 4.18-rc2 Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-02 16:22 ` Jiri Pirko 2018-07-02 16:22 ` Jiri Pirko 2018-07-02 16:59 ` Andreas Färber 2018-07-02 16:59 ` Andreas Färber 2018-07-01 11:07 ` [RFC net-next 06/15] net: lora: Prepare for device drivers Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` [RFC net-next 07/15] net: lora: Add Semtech SX1276 Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 12:02 ` Andreas Färber 2018-07-01 12:02 ` Andreas Färber 2018-07-01 11:07 ` [RFC net-next 08/15] net: lora: sx1276: Add debugfs Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-02 16:26 ` Jiri Pirko 2018-07-02 16:26 ` Jiri Pirko 2018-07-02 17:57 ` Andreas Färber 2018-07-02 17:57 ` Andreas Färber 2018-07-01 11:07 ` [RFC net-next 09/15] net: lora: Prepare EUI helpers Andreas Färber 2018-07-01 11:07 ` Andreas Färber 2018-07-01 11:07 ` Andreas Färber [this message] 2018-07-01 11:07 ` [RFC net-next 10/15] net: lora: Add Microchip RN2483 Andreas Färber 2018-07-01 11:08 ` [RFC net-next 11/15] net: lora: Add IMST WiMOD Andreas Färber 2018-07-01 11:08 ` Andreas Färber 2019-01-06 14:57 ` Heinrich Schuchardt 2019-01-06 14:57 ` Heinrich Schuchardt 2019-01-07 11:29 ` Andreas Färber 2019-01-07 11:29 ` Andreas Färber 2018-07-01 11:08 ` [RFC net-next 12/15] net: lora: Add USI WM-SG-SM-42 Andreas Färber 2018-07-01 11:08 ` Andreas Färber 2018-07-01 11:08 ` [RFC net-next 13/15] net: lora: Prepare RAK RAK811 Andreas Färber 2018-07-01 11:08 ` Andreas Färber 2018-07-01 11:08 ` [RFC net-next 14/15] net: lora: Prepare Semtech SX1257 Andreas Färber 2018-07-01 11:08 ` Andreas Färber 2018-07-01 11:08 ` [RFC net-next 15/15] net: lora: Add Semtech SX1301 Andreas Färber 2018-07-01 11:08 ` Andreas Färber 2018-07-02 11:51 ` Ben Whitten 2018-07-02 11:51 ` Ben Whitten 2018-07-03 3:01 ` Andreas Färber 2018-07-03 3:01 ` Andreas Färber 2018-07-05 8:59 ` Ben Whitten 2018-07-05 8:59 ` Ben Whitten 2018-07-05 8:59 ` Ben Whitten 2018-07-02 16:12 ` Mark Brown 2018-07-02 16:12 ` Mark Brown 2018-07-02 16:12 ` Mark Brown 2018-07-02 17:34 ` Andreas Färber 2018-07-02 17:34 ` Andreas Färber 2018-07-02 20:43 ` Ben Whitten 2018-07-02 20:43 ` Ben Whitten 2018-07-03 3:21 ` Andreas Färber 2018-07-03 3:21 ` Andreas Färber 2018-07-03 3:21 ` Andreas Färber 2018-07-05 8:43 ` Ben Whitten 2018-07-05 8:43 ` Ben Whitten 2018-07-05 8:43 ` Ben Whitten 2018-07-03 14:50 ` Mark Brown 2018-07-03 14:50 ` Mark Brown 2018-07-03 15:09 ` Andreas Färber 2018-07-03 15:09 ` Andreas Färber 2018-07-03 15:09 ` Andreas Färber 2018-07-03 15:31 ` Mark Brown 2018-07-03 15:31 ` Mark Brown 2018-07-03 15:31 ` Mark Brown 2018-07-03 16:40 ` Andreas Färber 2018-07-03 16:40 ` Andreas Färber 2018-07-04 11:43 ` Mark Brown 2018-07-04 11:43 ` Mark Brown 2018-07-04 13:41 ` Ben Whitten 2018-07-04 13:41 ` Ben Whitten 2018-07-04 13:41 ` Ben Whitten 2018-07-04 14:32 ` Mark Brown 2018-07-04 14:32 ` Mark Brown 2018-07-04 14:32 ` Mark Brown 2018-07-03 15:11 ` [RFC net-next 00/15] net: A socket API for LoRa Jian-Hong Pan 2018-07-03 15:11 ` Jian-Hong Pan 2018-07-03 15:11 ` Jian-Hong Pan 2018-08-05 0:11 ` Andreas Färber 2018-08-05 0:11 ` Andreas Färber 2018-08-05 0:11 ` Andreas Färber 2018-08-08 20:36 ` Alan Cox 2018-08-08 20:36 ` Alan Cox 2018-08-08 20:36 ` Alan Cox 2018-08-08 22:42 ` Andreas Färber 2018-08-08 22:42 ` Andreas Färber 2018-08-08 22:42 ` Andreas Färber 2018-08-09 11:59 ` Alan Cox 2018-08-09 11:59 ` Alan Cox 2018-08-09 11:59 ` Alan Cox 2018-08-09 15:02 ` Jian-Hong Pan 2018-08-09 15:02 ` Jian-Hong Pan 2018-08-09 15:02 ` Jian-Hong Pan 2018-08-09 15:21 ` Alexander Aring 2018-08-09 15:21 ` Alexander Aring 2018-08-09 15:21 ` Alexander Aring 2018-08-10 15:57 ` Alan Cox 2018-08-10 15:57 ` Alan Cox 2018-08-10 15:57 ` Alan Cox 2018-08-11 18:30 ` Stefan Schmidt 2018-08-11 18:30 ` Stefan Schmidt 2018-08-11 18:30 ` Stefan Schmidt 2018-08-12 16:49 ` Andreas Färber 2018-08-12 16:49 ` Andreas Färber 2018-08-12 16:49 ` Andreas Färber 2018-08-12 16:37 ` Jian-Hong Pan 2018-08-12 16:37 ` Jian-Hong Pan 2018-08-12 16:37 ` Jian-Hong Pan 2018-08-12 17:59 ` Andreas Färber 2018-08-12 17:59 ` Andreas Färber 2018-08-12 17:59 ` Andreas Färber 2018-08-13 12:36 ` Alan Cox 2018-08-13 12:36 ` Alan Cox 2018-08-13 12:36 ` Alan Cox 2018-08-09 15:12 ` Alexander Aring 2018-08-09 15:12 ` Alexander Aring 2018-08-09 15:12 ` Alexander Aring 2018-08-09 15:12 ` Alexander Aring 2018-08-09 0:50 ` Andreas Färber 2018-08-09 0:50 ` Andreas Färber 2018-08-09 0:50 ` Andreas Färber 2018-07-04 18:26 ` Stefan Schmidt 2018-07-04 18:26 ` Stefan Schmidt 2018-07-04 18:26 ` Stefan Schmidt 2018-07-05 10:43 ` Helmut Tschemernjak 2018-07-05 10:43 ` Helmut Tschemernjak 2018-07-05 10:43 ` Helmut Tschemernjak 2018-07-11 2:07 ` Andreas Färber 2018-07-11 2:07 ` Andreas Färber 2018-07-11 2:07 ` Andreas Färber 2018-07-11 11:45 ` Helmut Tschemernjak 2018-07-11 11:45 ` Helmut Tschemernjak 2018-07-11 11:45 ` Helmut Tschemernjak 2018-07-11 15:21 ` Ben Whitten 2018-07-11 15:21 ` Ben Whitten 2018-07-11 15:21 ` Ben Whitten 2018-07-15 18:13 ` Andreas Färber 2018-07-15 18:13 ` Andreas Färber 2018-07-15 18:13 ` Andreas Färber 2018-07-18 11:28 ` Ben Whitten 2018-07-18 11:28 ` Ben Whitten 2018-07-18 11:28 ` Ben Whitten 2018-07-18 11:28 ` Ben Whitten 2018-08-02 7:52 ` Jian-Hong Pan 2018-08-02 7:52 ` Jian-Hong Pan 2018-08-02 7:52 ` Jian-Hong Pan 2018-08-02 7:52 ` Jian-Hong Pan 2018-08-03 8:44 ` linux-lora.git and LoRaWAN (was: [RFC net-next 00/15] net: A socket API for LoRa) Andreas Färber 2018-08-03 8:44 ` Andreas Färber 2018-08-05 12:49 ` Jian-Hong Pan 2018-08-05 12:49 ` Jian-Hong Pan 2018-08-05 12:49 ` Jian-Hong Pan 2018-08-05 12:49 ` Jian-Hong Pan [not found] ` <20180803150258.791b9942@alans-desktop> 2018-08-05 14:08 ` Jian-Hong Pan 2018-08-05 14:08 ` Jian-Hong Pan 2018-08-05 13:49 ` [RFC net-next 00/15] net: A socket API for LoRa Andreas Färber 2018-08-05 13:49 ` Andreas Färber 2018-08-05 13:49 ` Andreas Färber
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20180701110804.32415-11-afaerber@suse.de \ --to=afaerber@suse.de \ --cc=davem@davemloft.net \ --cc=dollar.chen@wtmec.com \ --cc=jiri@resnulli.us \ --cc=jpiwek@arroweurope.com \ --cc=ken.yu@rakwireless.com \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-kernel@vger.kernel.org \ --cc=marcel@holtmann.org \ --cc=mbrugger@suse.com \ --cc=michael.roeder@avnet.eu \ --cc=netdev@vger.kernel.org \ --cc=starnight@g.ncu.edu.tw \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.