From patchwork Sun Jul 1 11:07:53 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andreas_F=C3=A4rber?= X-Patchwork-Id: 140710 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp2850451ljj; Sun, 1 Jul 2018 04:13:02 -0700 (PDT) X-Google-Smtp-Source: AAOMgpfqf8ekoY7OYKR02uNCWsQnX1d2X1N5Lrj/4WKKAN/skSlH8f2ERhRKUicYiAhGU5H3BHpa X-Received: by 2002:a65:5d88:: with SMTP id f8-v6mr12757512pgt.25.1530443582801; Sun, 01 Jul 2018 04:13:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1530443582; cv=none; d=google.com; s=arc-20160816; b=zBULo1cXlZGfv+uBV2902c5A3IM8NgGvHVDhyYJ0izP9Gm2rif+bZVOa5f8oOLyePQ H5Vh4M7MW6tPf/7j4Uo+ExL72fPlMStikv2MtYUSQ1soVnNoCMdnWWLSh6t00KtD87Gh RL0oDuQLVEAK/+pgHD5QuMMta2y6SprjVQ5P1jJjn0vZmsd2aHJbABnDnP6rrDb8A7hl qtDTuRHeYmFqo+3quALXQd1LEyRsE7fiV026wzqeV3ePWN/nlwaCp9kwRBh4UN1rlwLs y8+K0crewPGPKmGVYMXO+Z6sG78eBKx6xk7UQYF+UXuLrxMsBEk/2tqrXkttkAZrMPc2 5OZA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=9rW+61BRjW+4zcfXI74ssX2Ng+XF3cf5+Zy2oIuv/z0=; b=sCueu7DyV/h53wt9xZ4x6q2HpFGZPE15jb9/aqN1w0Nj0H6xeKlCnaheeF8ucSIdsu FryobEDHkkbMg34N+t/UgElxr7QOOz7OBAK13fAvh6M9NeBDkEuIdCRM3EoBng1soLpz jurUkAp4hd9MG81IMl4SJOA6irgTB6RpGJuHlHp/LeR7Y0XKHJlZr3sCroO1qBHcWQ0F puhUtOSq+0GsFSoj+uzCISJVhCqoJ5qicexcNRGuTBA9ADGCbWyjd9TdgFWeSJBvGK+C 6BLis/DipBpCvit1Tutmwvv9ttngdxQJiMb9rNTNjBO1MOCeVbTY8EyQ+fzw12kyvDoH Xc9g== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 140-v6si12387086pgd.19.2018.07.01.04.13.02; Sun, 01 Jul 2018 04:13:02 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752757AbeGALM6 (ORCPT + 9 others); Sun, 1 Jul 2018 07:12:58 -0400 Received: from mx2.suse.de ([195.135.220.15]:47682 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751898AbeGALI7 (ORCPT ); Sun, 1 Jul 2018 07:08:59 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 7E356AE54; Sun, 1 Jul 2018 11:08:57 +0000 (UTC) From: =?utf-8?q?Andreas_F=C3=A4rber?= To: netdev@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Jian-Hong Pan , Jiri Pirko , Marcel Holtmann , "David S . Miller" , Matthias Brugger , Janus Piwek , =?utf-8?q?Michael_R=C3=B6der?= , Dollar Chen , Ken Yu , =?utf-8?q?Andreas_F=C3=A4rber?= Subject: [RFC net-next 04/15] net: Add lora subsystem Date: Sun, 1 Jul 2018 13:07:53 +0200 Message-Id: <20180701110804.32415-5-afaerber@suse.de> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20180701110804.32415-1-afaerber@suse.de> References: <20180701110804.32415-1-afaerber@suse.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Implement or stub out PF_LORA net_proto_family and datagram proto_ops. Signed-off-by: Andreas Färber --- include/linux/lora/skb.h | 29 +++++ net/Kconfig | 1 + net/Makefile | 1 + net/lora/Kconfig | 9 ++ net/lora/Makefile | 8 ++ net/lora/af_lora.c | 152 ++++++++++++++++++++++++ net/lora/af_lora.h | 13 +++ net/lora/dgram.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 506 insertions(+) create mode 100644 include/linux/lora/skb.h create mode 100644 net/lora/Kconfig create mode 100644 net/lora/Makefile create mode 100644 net/lora/af_lora.c create mode 100644 net/lora/af_lora.h create mode 100644 net/lora/dgram.c -- 2.16.4 diff --git a/include/linux/lora/skb.h b/include/linux/lora/skb.h new file mode 100644 index 000000000000..8806741464d0 --- /dev/null +++ b/include/linux/lora/skb.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * linux/lora/skb.h + * + * Copyright (c) 2017-2018 Andreas Färber + */ +#ifndef _LORA_SKB_H +#define _LORA_SKB_H + +#include +#include + +struct lora_skb_priv { + int ifindex; +}; + +static inline struct lora_skb_priv *lora_skb_prv(struct sk_buff *skb) +{ + return (struct lora_skb_priv *)(skb->head); +} + +static inline void lora_skb_reserve(struct sk_buff *skb) +{ + skb_reserve(skb, sizeof(struct lora_skb_priv)); +} + +struct sk_buff *alloc_lora_skb(struct net_device *dev, u8 **data); + +#endif /* _LORA_SKB_H */ diff --git a/net/Kconfig b/net/Kconfig index f738a6f27665..d294ecc50a3b 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -223,6 +223,7 @@ source "net/phonet/Kconfig" source "net/6lowpan/Kconfig" source "net/ieee802154/Kconfig" source "net/mac802154/Kconfig" +source "net/lora/Kconfig" source "net/sched/Kconfig" source "net/dcb/Kconfig" source "net/dns_resolver/Kconfig" diff --git a/net/Makefile b/net/Makefile index bdaf53925acd..e80b84313851 100644 --- a/net/Makefile +++ b/net/Makefile @@ -62,6 +62,7 @@ endif obj-$(CONFIG_6LOWPAN) += 6lowpan/ obj-$(CONFIG_IEEE802154) += ieee802154/ obj-$(CONFIG_MAC802154) += mac802154/ +obj-$(CONFIG_LORA) += lora/ ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o diff --git a/net/lora/Kconfig b/net/lora/Kconfig new file mode 100644 index 000000000000..44972ea8769f --- /dev/null +++ b/net/lora/Kconfig @@ -0,0 +1,9 @@ +# +# LoRa +# + +menuconfig LORA + depends on NET + tristate "LoRa subsystem support" + help + LoRa ... diff --git a/net/lora/Makefile b/net/lora/Makefile new file mode 100644 index 000000000000..b58675ef7846 --- /dev/null +++ b/net/lora/Makefile @@ -0,0 +1,8 @@ +# +# LoRa +# + +obj-$(CONFIG_LORA) += lora.o + +lora-y := af_lora.o +lora-y += dgram.o diff --git a/net/lora/af_lora.c b/net/lora/af_lora.c new file mode 100644 index 000000000000..662482fe4c9b --- /dev/null +++ b/net/lora/af_lora.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2017-2018 Andreas Färber + */ + +#include +#include +#include +#include +#include +#include + +#include "af_lora.h" + +int lora_send(struct sk_buff *skb) +{ + int ret; + + pr_debug("lora: %s\n", __func__); + + skb->protocol = htons(ETH_P_LORA); + + if (unlikely(skb->len > skb->dev->mtu)) { + ret = -EMSGSIZE; + goto err_skb; + } + + if (unlikely(skb->dev->type != ARPHRD_LORA)) { + ret = -EPERM; + goto err_skb; + } + + if (unlikely(!(skb->dev->flags & IFF_UP))) { + ret = -ENETDOWN; + goto err_skb; + } + + skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + + if (false) { + skb->pkt_type = PACKET_LOOPBACK; + } else + skb->pkt_type = PACKET_HOST; + + ret = dev_queue_xmit(skb); + if (ret > 0) + ret = net_xmit_errno(ret); + if (ret) + return ret; + + return 0; + +err_skb: + kfree_skb(skb); + return ret; +} +EXPORT_SYMBOL(lora_send); + +static void lora_sock_destruct(struct sock *sk) +{ + pr_debug("lora: %s\n", __func__); + + skb_queue_purge(&sk->sk_receive_queue); +} + +static int lora_create(struct net *net, struct socket *sock, int protocol, + int kern) +{ + struct sock *sk; + + pr_debug("lora: %s\n", __func__); + + sock->state = SS_UNCONNECTED; + + if (protocol < 0 || protocol > LORA_NPROTO) + return -EINVAL; + + /*if (!net_eq(net, &init_net)) + return -EAFNOSUPPORT;*/ + + if (sock->type != SOCK_DGRAM) + return -ESOCKTNOSUPPORT; + + sock->ops = &dgram_proto_ops; + + sk = sk_alloc(net, PF_LORA, GFP_KERNEL, &dgram_proto, kern); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sk->sk_family = PF_LORA; + sk->sk_destruct = lora_sock_destruct; + + if (sk->sk_prot->init) { + int ret = sk->sk_prot->init(sk); + if (ret) { + sock_orphan(sk); + sock_put(sk); + return ret; + } + } + + return 0; +} + +static const struct net_proto_family lora_net_proto_family = { + .family = PF_LORA, + .owner = THIS_MODULE, + .create = lora_create, +}; + +static __init int lora_init(void) +{ + int ret; + + pr_debug("lora: init\n"); + + ret = proto_register(&dgram_proto, 1); + if (ret) + goto err_dgram; + + ret = sock_register(&lora_net_proto_family); + if (ret) + goto err_sock; + + return 0; + +err_sock: + proto_unregister(&dgram_proto); +err_dgram: + return ret; +} + +static __exit void lora_exit(void) +{ + pr_debug("lora: exit\n"); + + sock_unregister(PF_LORA); + proto_unregister(&dgram_proto); +} + +module_init(lora_init); +module_exit(lora_exit); + +MODULE_DESCRIPTION("LoRa PF_LORA core"); +MODULE_AUTHOR("Andreas Färber "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(PF_LORA); diff --git a/net/lora/af_lora.h b/net/lora/af_lora.h new file mode 100644 index 000000000000..47dd863531b2 --- /dev/null +++ b/net/lora/af_lora.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2017-2018 Andreas Färber + */ +#ifndef AF_LORA_H +#define AF_LORA_H + +extern struct proto dgram_proto; +extern const struct proto_ops dgram_proto_ops; + +int lora_send(struct sk_buff *skb); + +#endif /* AF_LORA_H */ diff --git a/net/lora/dgram.c b/net/lora/dgram.c new file mode 100644 index 000000000000..4d931fd3778a --- /dev/null +++ b/net/lora/dgram.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2017-2018 Andreas Färber + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "af_lora.h" + +struct dgram_sock { + struct sock sk; + int ifindex; + bool bound; + struct notifier_block notifier; +}; + +static inline struct dgram_sock *dgram_sk(const struct sock *sk) +{ + return (struct dgram_sock *)sk; +} + +static int dgram_bind(struct socket *sock, struct sockaddr *uaddr, int len) +{ + struct sockaddr_lora *addr = (struct sockaddr_lora *)uaddr; + struct sock *sk = sock->sk; + struct dgram_sock *dgram = dgram_sk(sk); + int ifindex; + int ret = 0; + bool notify_enetdown = false; + + pr_debug("lora: %s\n", __func__); + + if (len < sizeof(*addr)) + return -EINVAL; + + lock_sock(sk); + + if (dgram->bound && addr->lora_ifindex == dgram->ifindex) + goto out; + + if (addr->lora_ifindex) { + struct net_device *netdev; + + netdev = dev_get_by_index(sock_net(sk), addr->lora_ifindex); + if (!netdev) { + ret = -ENODEV; + goto out; + } + if (netdev->type != ARPHRD_LORA) { + dev_put(netdev); + ret = -ENODEV; + goto out; + } + if (!(netdev->flags & IFF_UP)) + notify_enetdown = true; + + ifindex = netdev->ifindex; + + dev_put(netdev); + } else + ifindex = 0; + + dgram->ifindex = ifindex; + dgram->bound = true; + +out: + release_sock(sk); + + if (notify_enetdown) { + sk->sk_err = ENETDOWN; + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_error_report(sk); + } + + return ret; +} + +static int dgram_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) +{ + struct sock *sk = sock->sk; + struct dgram_sock *dgram = dgram_sk(sk); + struct sk_buff *skb; + struct net_device *netdev; + int ifindex; + int ret; + + pr_debug("lora: %s\n", __func__); + + if (msg->msg_name) { + DECLARE_SOCKADDR(struct sockaddr_lora *, addr, msg->msg_name); + + if (msg->msg_namelen < sizeof(*addr)) + return -EINVAL; + + if (addr->lora_family != AF_LORA) + return -EINVAL; + + ifindex = addr->lora_ifindex; + } else + ifindex = dgram->ifindex; + + netdev = dev_get_by_index(sock_net(sk), ifindex); + if (!netdev) + return -ENXIO; + + skb = sock_alloc_send_skb(sk, size + sizeof(struct lora_skb_priv), + msg->msg_flags & MSG_DONTWAIT, &ret); + if (!skb) + goto err_sock_alloc_send_skb; + + lora_skb_reserve(skb); + lora_skb_prv(skb)->ifindex = netdev->ifindex; + + ret = memcpy_from_msg(skb_put(skb, size), msg, size); + if (ret < 0) + goto err_memcpy_from_msg; + + sock_tx_timestamp(sk, + sk->sk_tsflags, + &skb_shinfo(skb)->tx_flags); + + skb->dev = netdev; + skb->sk = sk; + skb->priority = sk->sk_priority; + + ret = lora_send(skb); + + dev_put(netdev); + + if (ret) + return ret; + + return size; + +err_memcpy_from_msg: + kfree_skb(skb); +err_sock_alloc_send_skb: + dev_put(netdev); + return ret; +} + +static int dgram_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + pr_debug("lora: %s\n", __func__); + + return -ENOIOCTLCMD; +} + +static int dgram_getname(struct socket *sock, struct sockaddr *uaddr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) + int *len, +#endif + int peer) +{ + struct sockaddr_lora *addr = (struct sockaddr_lora *)uaddr; + struct sock *sk = sock->sk; + struct dgram_sock *dgram = dgram_sk(sk); + + pr_debug("lora: %s\n", __func__); + + if (peer) + return -EOPNOTSUPP; + + memset(addr, 0, sizeof(*addr)); + addr->lora_family = AF_LORA; + addr->lora_ifindex = dgram->ifindex; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + return sizeof(*addr); +#else + *len = sizeof(*addr); + return 0; +#endif +} + +static int dgram_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct dgram_sock *dgram; + + pr_debug("lora: %s\n", __func__); + + if (!sk) + return 0; + + dgram = dgram_sk(sk); + + unregister_netdevice_notifier(&dgram->notifier); + + lock_sock(sk); + + dgram->ifindex = 0; + dgram->bound = false; + + sock_orphan(sk); + sock->sk = NULL; + + release_sock(sk); + sock_put(sk); + + return 0; +} + +const struct proto_ops dgram_proto_ops = { + .family = PF_LORA, + .release = dgram_release, + .bind = dgram_bind, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = dgram_getname, + .poll = datagram_poll, + .ioctl = dgram_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = dgram_sendmsg, + .recvmsg = sock_no_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static int dgram_notifier(struct notifier_block *nb, unsigned long msg, void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct dgram_sock *dgram = container_of(nb, struct dgram_sock, notifier); + struct sock *sk = &dgram->sk; + + pr_debug("lora: %s\n", __func__); + + if (!net_eq(dev_net(netdev), sock_net(sk))) + return NOTIFY_DONE; + + if (netdev->type != ARPHRD_LORA) + return NOTIFY_DONE; + + if (dgram->ifindex != netdev->ifindex) + return NOTIFY_DONE; + + switch (msg) { + case NETDEV_UNREGISTER: + lock_sock(sk); + + dgram->ifindex = 0; + dgram->bound = false; + + release_sock(sk); + + sk->sk_err = ENODEV; + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_error_report(sk); + break; + + case NETDEV_DOWN: + sk->sk_err = ENETDOWN; + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_error_report(sk); + break; + } + + return NOTIFY_DONE; +} + +static int dgram_init(struct sock *sk) +{ + struct dgram_sock *dgram = dgram_sk(sk); + + pr_debug("lora: %s\n", __func__); + + dgram->bound = false; + dgram->ifindex = 0; + + dgram->notifier.notifier_call = dgram_notifier; + register_netdevice_notifier(&dgram->notifier); + + return 0; +} + +struct proto dgram_proto __read_mostly = { + .name = "LoRa", + .owner = THIS_MODULE, + .obj_size = sizeof(struct dgram_sock), + .init = dgram_init, +}; From patchwork Sun Jul 1 11:07:54 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andreas_F=C3=A4rber?= X-Patchwork-Id: 140709 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp2850396ljj; Sun, 1 Jul 2018 04:12:57 -0700 (PDT) X-Google-Smtp-Source: AAOMgpcGvdbULIdB+v6C/KGKxwkxVwY6mvGIfeYPLKG+gWF+8Yl1MPNAvOfL0pJwtJWlahLBTqti X-Received: by 2002:a62:8917:: with SMTP id v23-v6mr21535004pfd.127.1530443577336; Sun, 01 Jul 2018 04:12:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1530443577; cv=none; d=google.com; s=arc-20160816; b=X1xnb8NBrWq06vJwONXJhDkdpHkAC46Yl/ahoi9WzSTHitivHpEKapwxd7fWpDmJMe +de6lfACKuI4StZiA5WlcxpOQiIG62XBppq7XOGzBlEP/EVTEN+g9AThdhSRH2sU7wHS kgegFSGf5GZPh0f6+CKJxpQWFzxFrc6JhxI7XKpH1iFdhNwGpqCLkLNb8ktGVzFtHUgz rfK6VEjhGXp/8rc8RYrPiucpwomieZHeknThZkGVQviJs7PBzvdYe8dBzFi/47rPIQsH e3y6l57CE9wPlPOYJksb/88WQX7OCYgTX4XNSKj+tTOzaldb+IpGsO81/bSyVICxs0S8 Lv5A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=YojGNVfZuq9XNEJqZt+Fqigp3Eu2DKfOqh7hFRj96nY=; b=GPJi6b6Gx0CSUOTMiBUa/9KksHb+P/YSIX6v5AsyG5FDlR2jD6h5igkfqJF5o32rIa QOQXiqBZarpGwt3LyeSsCuSj+1T/PskJpLDv6M9qRuquejzvXiP94NGxwCh9ba1alv8G jgFZjctP5QDP1Hp51LiIKB0jVtmb7WBds0D+obJ8U9Iw/zcTr+jgg4A5em5Enk6+UVG5 sWLY0YcovqGIkMZFYC+uXLKJakJ6PgofqG+d8wYeEKdkE3ytNspKR/tALE5yJ7uTmhjE RymKK06T5IjrnT+/gri7zk75MpyZH4OYSL2Me44bPKVozpotmX/C9wi0Zq5Ok3urZ3Zg ujpg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id w1-v6si10199028pgw.546.2018.07.01.04.12.57; Sun, 01 Jul 2018 04:12:57 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752731AbeGALMz (ORCPT + 9 others); Sun, 1 Jul 2018 07:12:55 -0400 Received: from mx2.suse.de ([195.135.220.15]:47714 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752031AbeGALI7 (ORCPT ); Sun, 1 Jul 2018 07:08:59 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 8B3BCAE55; Sun, 1 Jul 2018 11:08:57 +0000 (UTC) From: =?utf-8?q?Andreas_F=C3=A4rber?= To: netdev@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Jian-Hong Pan , Jiri Pirko , Marcel Holtmann , "David S . Miller" , Matthias Brugger , Janus Piwek , =?utf-8?q?Michael_R=C3=B6der?= , Dollar Chen , Ken Yu , =?utf-8?q?Andreas_F=C3=A4rber?= Subject: [RFC net-next 05/15] HACK: net: lora: Deal with .poll_mask in 4.18-rc2 Date: Sun, 1 Jul 2018 13:07:54 +0200 Message-Id: <20180701110804.32415-6-afaerber@suse.de> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20180701110804.32415-1-afaerber@suse.de> References: <20180701110804.32415-1-afaerber@suse.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org linux-next and 4.18-rc2 both identify as LINUX_VERSION(4,18,0), but commit a11e1d432b51f63ba698d044441284a661f01144 (Revert changes to convert to ->poll_mask() and aio IOCB_CMD_POLL) reverted .poll_mask to .poll again. Signed-off-by: Andreas Färber --- net/lora/dgram.c | 4 ++++ 1 file changed, 4 insertions(+) -- 2.16.4 diff --git a/net/lora/dgram.c b/net/lora/dgram.c index 4d931fd3778a..ef56fd90e762 100644 --- a/net/lora/dgram.c +++ b/net/lora/dgram.c @@ -217,7 +217,11 @@ const struct proto_ops dgram_proto_ops = { .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = dgram_getname, +#if 0 /* LINUX_VERSION_CODE >= LINUX_VERSION(4, 18, 0) */ + .poll_mask = datagram_poll_mask, +#else .poll = datagram_poll, +#endif .ioctl = dgram_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, From patchwork Sun Jul 1 11:07:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andreas_F=C3=A4rber?= X-Patchwork-Id: 140706 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp2849848ljj; Sun, 1 Jul 2018 04:12:10 -0700 (PDT) X-Google-Smtp-Source: ADUXVKKgDeup+Lp08HOAf3DacwYKC78gRHxe5AhXRuxR1/gQctAKqolgYxCaT4mDscgxWLhzuS0G X-Received: by 2002:a63:6a45:: with SMTP id f66-v6mr17811192pgc.81.1530443530636; Sun, 01 Jul 2018 04:12:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1530443530; cv=none; d=google.com; s=arc-20160816; b=bxUr5OiwtczJ2wsXAw+uAIpHNTe2SOyeutg9BRc8FkrbFCqAKLKuWkct4Y1U1mvjs6 mojmCZrQ+tQ5Q5vfKDP9VW/qhiZNo3eDEbOwW/qZObWaXRcfvDB1dGJ2K+RWsnDVSzJF EBlLkuZc1NKPmcFA833RxFWGoWjIsc3cGkoen/tDnJsc9txR7wCoUulvEEeooSAA0pLw FmilHeofskKHOAsTNV7uU2tCklljPrghi0DQPpmDuANzb9p+a4J7+5P1lcqds7hf744W HIxlgr2kzR5M07VYBIFlPQc0MpLGzPZFW943DadlbwvZt9Z+NZoIMUYKyP+lM86c6Iem 3Liw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=eiBkK8Mi4og66I/dgG8VHlb/zw0geW8cu7EBGMcoQiY=; b=SDjeIvLGwQ2pPyH+AhJ+7AiCpoT697yik4cay2C9sE+tVlGZU7xUTT7IeAD0ctp5GD fRVkavU5i/VFNQazAQoY/lDRgwa65RC8w7FkA/xkpBsK1WAY9S84jYlhpqbu/Tg0ptsL m/hMWzfmw0/oVS8LsGCzfkwwSWVb8vOjQ7ZWKYsUAA8albhZVc9JclRka9vEmCFv2fnA AsJye6zI+7SKJ9ZcZqxsSST5vbZnmqv7kjO/pY87ayVc68GhY3ovvGeS5qlHxk6fdgs/ 6UO5pWo1fn44Gqrz5aQvrL3UPJHuVSHYnPzEyQoaEafsbb1R7IQ0WXH3RQB9Xe/ucsKm WrSw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id i12-v6si12282000pgp.325.2018.07.01.04.12.10; Sun, 01 Jul 2018 04:12:10 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752437AbeGALMI (ORCPT + 9 others); Sun, 1 Jul 2018 07:12:08 -0400 Received: from mx2.suse.de ([195.135.220.15]:47812 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752153AbeGALJB (ORCPT ); Sun, 1 Jul 2018 07:09:01 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 0856BAEC9; Sun, 1 Jul 2018 11:08:59 +0000 (UTC) From: =?utf-8?q?Andreas_F=C3=A4rber?= To: netdev@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Jian-Hong Pan , Jiri Pirko , Marcel Holtmann , "David S . Miller" , Matthias Brugger , Janus Piwek , =?utf-8?q?Michael_R=C3=B6der?= , Dollar Chen , Ken Yu , =?utf-8?q?Andreas_F=C3=A4rber?= Subject: [RFC net-next 07/15] net: lora: Add Semtech SX1276 Date: Sun, 1 Jul 2018 13:07:56 +0200 Message-Id: <20180701110804.32415-8-afaerber@suse.de> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20180701110804.32415-1-afaerber@suse.de> References: <20180701110804.32415-1-afaerber@suse.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Semtech SX1276/77/78/79 and SX1272/73 are LoRa transceivers with a SPI interface. They also offer a non-LoRa mode (not exposed here). Signed-off-by: Andreas Färber --- drivers/net/lora/Kconfig | 11 + drivers/net/lora/Makefile | 3 + drivers/net/lora/sx1276.c | 608 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 622 insertions(+) create mode 100644 drivers/net/lora/sx1276.c -- 2.16.4 diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig index 40969b148a50..0436f6b09a1c 100644 --- a/drivers/net/lora/Kconfig +++ b/drivers/net/lora/Kconfig @@ -15,4 +15,15 @@ config LORA_DEV # Alphabetically sorted. # +if LORA_DEV + +config LORA_SX1276 + tristate "Semtech SX127x SPI driver" + default y + depends on SPI + help + Semtech SX1272/1276/1278 + +endif + endmenu diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile index 8f9d25ea4e70..8845542dba50 100644 --- a/drivers/net/lora/Makefile +++ b/drivers/net/lora/Makefile @@ -8,3 +8,6 @@ lora-dev-y := dev.o # # Alphabetically sorted. # + +obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o +lora-sx1276-y := sx1276.o diff --git a/drivers/net/lora/sx1276.c b/drivers/net/lora/sx1276.c new file mode 100644 index 000000000000..d6732111247a --- /dev/null +++ b/drivers/net/lora/sx1276.c @@ -0,0 +1,608 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Semtech SX1272/SX1276 LoRa transceiver + * + * Copyright (c) 2016-2018 Andreas Färber + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_FIFO 0x00 +#define REG_OPMODE 0x01 +#define REG_FRF_MSB 0x06 +#define REG_FRF_MID 0x07 +#define REG_FRF_LSB 0x08 +#define REG_PA_CONFIG 0x09 +#define LORA_REG_FIFO_ADDR_PTR 0x0d +#define LORA_REG_FIFO_TX_BASE_ADDR 0x0e +#define LORA_REG_IRQ_FLAGS_MASK 0x11 +#define LORA_REG_IRQ_FLAGS 0x12 +#define LORA_REG_PAYLOAD_LENGTH 0x22 +#define LORA_REG_SYNC_WORD 0x39 +#define REG_DIO_MAPPING1 0x40 +#define REG_DIO_MAPPING2 0x41 +#define REG_VERSION 0x42 +#define REG_PA_DAC 0x4d + +#define REG_OPMODE_LONG_RANGE_MODE BIT(7) +#define REG_OPMODE_LOW_FREQUENCY_MODE_ON BIT(3) +#define REG_OPMODE_MODE_MASK GENMASK(2, 0) +#define REG_OPMODE_MODE_SLEEP (0x0 << 0) +#define REG_OPMODE_MODE_STDBY (0x1 << 0) +#define REG_OPMODE_MODE_TX (0x3 << 0) +#define REG_OPMODE_MODE_RXCONTINUOUS (0x5 << 0) +#define REG_OPMODE_MODE_RXSINGLE (0x6 << 0) + +#define REG_PA_CONFIG_PA_SELECT BIT(7) + +#define LORA_REG_IRQ_FLAGS_TX_DONE BIT(3) + +#define REG_DIO_MAPPING1_DIO0_MASK GENMASK(7, 6) + +struct sx1276_priv { + struct lora_priv lora; + struct spi_device *spi; + + size_t fifosize; + int dio_gpio[6]; + + struct mutex spi_lock; + + struct sk_buff *tx_skb; + int tx_len; + + struct workqueue_struct *wq; + struct work_struct tx_work; +}; + +static int sx1276_read_single(struct spi_device *spi, u8 reg, u8 *val) +{ + u8 addr = reg & 0x7f; + return spi_write_then_read(spi, &addr, 1, val, 1); +} + +static int sx1276_write_single(struct spi_device *spi, u8 reg, u8 val) +{ + u8 buf[2]; + + buf[0] = reg | BIT(7); + buf[1] = val; + return spi_write(spi, buf, 2); +} + +static int sx1276_write_burst(struct spi_device *spi, u8 reg, size_t len, void *val) +{ + u8 buf = reg | BIT(7); + struct spi_transfer xfers[2] = { + [0] = { + .tx_buf = &buf, + .len = 1, + }, + [1] = { + .tx_buf = val, + .len = len, + }, + }; + + return spi_sync_transfer(spi, xfers, 2); +} + +static int sx1276_write_fifo(struct spi_device *spi, size_t len, void *val) +{ + return sx1276_write_burst(spi, REG_FIFO, len, val); +} + +static netdev_tx_t sx1276_loradev_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct sx1276_priv *priv = netdev_priv(netdev); + + netdev_dbg(netdev, "%s\n", __func__); + + if (priv->tx_skb || priv->tx_len) { + netdev_warn(netdev, "TX busy\n"); + return NETDEV_TX_BUSY; + } + + if (skb->protocol != htons(ETH_P_LORA)) { + 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 sx1276_tx(struct spi_device *spi, void *data, int data_len) +{ + u8 addr, val; + int ret; + + dev_dbg(&spi->dev, "%s\n", __func__); + + ret = sx1276_read_single(spi, REG_OPMODE, &val); + if (ret) { + dev_err(&spi->dev, "Failed to read RegOpMode (%d)\n", ret); + return ret; + } + dev_dbg(&spi->dev, "RegOpMode = 0x%02x\n", val); + if (!(val & REG_OPMODE_LONG_RANGE_MODE)) + dev_err(&spi->dev, "LongRange Mode not active!\n"); + if ((val & REG_OPMODE_MODE_MASK) == REG_OPMODE_MODE_SLEEP) + dev_err(&spi->dev, "Cannot access FIFO in Sleep Mode!\n"); + + ret = sx1276_read_single(spi, LORA_REG_FIFO_TX_BASE_ADDR, &addr); + if (ret) { + dev_err(&spi->dev, "Failed to read RegFifoTxBaseAddr (%d)\n", ret); + return ret; + } + dev_dbg(&spi->dev, "RegFifoTxBaseAddr = 0x%02x\n", addr); + + ret = sx1276_write_single(spi, LORA_REG_FIFO_ADDR_PTR, addr); + if (ret) { + dev_err(&spi->dev, "Failed to write RegFifoAddrPtr (%d)\n", ret); + return ret; + } + + ret = sx1276_write_single(spi, LORA_REG_PAYLOAD_LENGTH, data_len); + if (ret) { + dev_err(&spi->dev, "Failed to write RegPayloadLength (%d)\n", ret); + return ret; + } + + ret = sx1276_write_fifo(spi, data_len, data); + if (ret) { + dev_err(&spi->dev, "Failed to write into FIFO (%d)\n", ret); + return ret; + } + + ret = sx1276_read_single(spi, LORA_REG_IRQ_FLAGS, &val); + if (ret) { + dev_err(&spi->dev, "Failed to read RegIrqFlags (%d)\n", ret); + return ret; + } + dev_dbg(&spi->dev, "RegIrqFlags = 0x%02x\n", val); + + ret = sx1276_write_single(spi, LORA_REG_IRQ_FLAGS, LORA_REG_IRQ_FLAGS_TX_DONE); + if (ret) { + dev_err(&spi->dev, "Failed to write RegIrqFlags (%d)\n", ret); + return ret; + } + + ret = sx1276_read_single(spi, LORA_REG_IRQ_FLAGS_MASK, &val); + if (ret) { + dev_err(&spi->dev, "Failed to read RegIrqFlagsMask (%d)\n", ret); + return ret; + } + dev_dbg(&spi->dev, "RegIrqFlagsMask = 0x%02x\n", val); + + val &= ~LORA_REG_IRQ_FLAGS_TX_DONE; + ret = sx1276_write_single(spi, LORA_REG_IRQ_FLAGS_MASK, val); + if (ret) { + dev_err(&spi->dev, "Failed to write RegIrqFlagsMask (%d)\n", ret); + return ret; + } + + ret = sx1276_read_single(spi, REG_DIO_MAPPING1, &val); + if (ret) { + dev_err(&spi->dev, "Failed to read RegDioMapping1 (%d)\n", ret); + return ret; + } + + val &= ~REG_DIO_MAPPING1_DIO0_MASK; + val |= 0x1 << 6; + ret = sx1276_write_single(spi, REG_DIO_MAPPING1, val); + if (ret) { + dev_err(&spi->dev, "Failed to write RegDioMapping1 (%d)\n", ret); + return ret; + } + + ret = sx1276_read_single(spi, REG_OPMODE, &val); + if (ret) { + dev_err(&spi->dev, "Failed to read RegOpMode (%d)\n", ret); + return ret; + } + + val &= ~REG_OPMODE_MODE_MASK; + val |= REG_OPMODE_MODE_TX; + ret = sx1276_write_single(spi, REG_OPMODE, val); + if (ret) { + dev_err(&spi->dev, "Failed to write RegOpMode (%d)\n", ret); + return ret; + } + + dev_dbg(&spi->dev, "%s: done\n", __func__); + + return 0; +} + +static void sx1276_tx_work_handler(struct work_struct *ws) +{ + struct sx1276_priv *priv = container_of(ws, struct sx1276_priv, tx_work); + struct spi_device *spi = priv->spi; + struct net_device *netdev = spi_get_drvdata(spi); + + netdev_dbg(netdev, "%s\n", __func__); + + mutex_lock(&priv->spi_lock); + + if (priv->tx_skb) { + sx1276_tx(spi, priv->tx_skb->data, priv->tx_skb->data_len); + priv->tx_len = 1 + priv->tx_skb->data_len; + if (!(netdev->flags & IFF_ECHO) || + priv->tx_skb->pkt_type != PACKET_LOOPBACK || + priv->tx_skb->protocol != htons(ETH_P_LORA)) + kfree_skb(priv->tx_skb); + priv->tx_skb = NULL; + } + + mutex_unlock(&priv->spi_lock); +} + +static irqreturn_t sx1276_dio_interrupt(int irq, void *dev_id) +{ + struct net_device *netdev = dev_id; + struct sx1276_priv *priv = netdev_priv(netdev); + struct spi_device *spi = priv->spi; + u8 val; + int ret; + + netdev_dbg(netdev, "%s\n", __func__); + + mutex_lock(&priv->spi_lock); + + ret = sx1276_read_single(spi, LORA_REG_IRQ_FLAGS, &val); + if (ret) { + netdev_warn(netdev, "Failed to read RegIrqFlags (%d)\n", ret); + val = 0; + } + + if (val & LORA_REG_IRQ_FLAGS_TX_DONE) { + 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); + + ret = sx1276_write_single(spi, LORA_REG_IRQ_FLAGS, LORA_REG_IRQ_FLAGS_TX_DONE); + if (ret) + netdev_warn(netdev, "Failed to write RegIrqFlags (%d)\n", ret); + } + + mutex_unlock(&priv->spi_lock); + + return IRQ_HANDLED; +} + +static int sx1276_loradev_open(struct net_device *netdev) +{ + struct sx1276_priv *priv = netdev_priv(netdev); + struct spi_device *spi = to_spi_device(netdev->dev.parent); + u8 val; + int ret, irq; + + netdev_dbg(netdev, "%s\n", __func__); + + ret = open_loradev(netdev); + if (ret) + return ret; + + mutex_lock(&priv->spi_lock); + + ret = sx1276_read_single(spi, REG_OPMODE, &val); + if (ret) { + netdev_err(netdev, "Failed to read RegOpMode (%d)\n", ret); + goto err_opmode; + } + + val &= ~REG_OPMODE_MODE_MASK; + val |= REG_OPMODE_MODE_STDBY; + ret = sx1276_write_single(spi, REG_OPMODE, val); + if (ret) { + netdev_err(netdev, "Failed to write RegOpMode (%d)\n", ret); + goto err_opmode; + } + + priv->tx_skb = NULL; + priv->tx_len = 0; + + priv->wq = alloc_workqueue("sx1276_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, 0); + INIT_WORK(&priv->tx_work, sx1276_tx_work_handler); + + if (gpio_is_valid(priv->dio_gpio[0])) { + irq = gpio_to_irq(priv->dio_gpio[0]); + if (irq <= 0) + netdev_warn(netdev, "Failed to obtain interrupt for DIO0 (%d)\n", irq); + else { + netdev_info(netdev, "Succeeded in obtaining interrupt for DIO0: %d\n", irq); + ret = request_threaded_irq(irq, NULL, sx1276_dio_interrupt, IRQF_ONESHOT | IRQF_TRIGGER_RISING, netdev->name, netdev); + if (ret) { + netdev_err(netdev, "Failed to request interrupt for DIO0 (%d)\n", ret); + goto err_irq; + } + } + } + + netif_wake_queue(netdev); + + mutex_unlock(&priv->spi_lock); + + return 0; + +err_irq: + destroy_workqueue(priv->wq); + priv->wq = NULL; +err_opmode: + close_loradev(netdev); + mutex_unlock(&priv->spi_lock); + return ret; +} + +static int sx1276_loradev_stop(struct net_device *netdev) +{ + struct sx1276_priv *priv = netdev_priv(netdev); + struct spi_device *spi = to_spi_device(netdev->dev.parent); + u8 val; + int ret, irq; + + netdev_dbg(netdev, "%s\n", __func__); + + close_loradev(netdev); + + mutex_lock(&priv->spi_lock); + + ret = sx1276_write_single(spi, LORA_REG_IRQ_FLAGS_MASK, 0xff); + if (ret) { + netdev_err(netdev, "Failed to write RegIrqFlagsMask (%d)\n", ret); + goto err_irqmask; + } + + ret = sx1276_read_single(spi, REG_OPMODE, &val); + if (ret) { + netdev_err(netdev, "Failed to read RegOpMode (%d)\n", ret); + goto err_opmode; + } + + val &= ~REG_OPMODE_MODE_MASK; + val |= REG_OPMODE_MODE_SLEEP; + ret = sx1276_write_single(spi, REG_OPMODE, val); + if (ret) { + netdev_err(netdev, "Failed to write RegOpMode (%d)\n", ret); + goto err_opmode; + } + + if (gpio_is_valid(priv->dio_gpio[0])) { + irq = gpio_to_irq(priv->dio_gpio[0]); + if (irq > 0) { + netdev_dbg(netdev, "Freeing IRQ %d\n", irq); + free_irq(irq, 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; + + mutex_unlock(&priv->spi_lock); + + return 0; + +err_opmode: +err_irqmask: + mutex_unlock(&priv->spi_lock); + return ret; +} + +static const struct net_device_ops sx1276_netdev_ops = { + .ndo_open = sx1276_loradev_open, + .ndo_stop = sx1276_loradev_stop, + .ndo_start_xmit = sx1276_loradev_start_xmit, +}; + +static int sx1276_probe(struct spi_device *spi) +{ + struct net_device *netdev; + struct sx1276_priv *priv; + int rst, dio[6], ret, model, i; + u32 freq_xosc, freq_band; + unsigned long long freq_rf; + u8 val; + + rst = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0); + if (rst == -ENOENT) + dev_warn(&spi->dev, "no reset GPIO available, ignoring"); + + for (i = 0; i < 6; i++) { + dio[i] = of_get_named_gpio(spi->dev.of_node, "dio-gpios", i); + if (dio[i] == -ENOENT) + dev_dbg(&spi->dev, "DIO%d not available, ignoring", i); + else { + ret = gpio_direction_input(dio[i]); + if (ret) + dev_err(&spi->dev, "couldn't set DIO%d to input", i); + } + } + + if (gpio_is_valid(rst)) { + gpio_set_value(rst, 1); + udelay(100); + gpio_set_value(rst, 0); + msleep(5); + } + + spi->bits_per_word = 8; + spi_setup(spi); + + ret = sx1276_read_single(spi, REG_VERSION, &val); + if (ret) { + dev_err(&spi->dev, "version read failed"); + return ret; + } + + if (val == 0x22) + model = 1272; + else { + if (gpio_is_valid(rst)) { + gpio_set_value(rst, 0); + udelay(100); + gpio_set_value(rst, 1); + msleep(5); + } + + ret = sx1276_read_single(spi, REG_VERSION, &val); + if (ret) { + dev_err(&spi->dev, "version read failed"); + return ret; + } + + if (val == 0x12) + model = 1276; + else { + dev_err(&spi->dev, "transceiver not recognized (RegVersion = 0x%02x)", (unsigned)val); + return -EINVAL; + } + } + + ret = of_property_read_u32(spi->dev.of_node, "clock-frequency", &freq_xosc); + if (ret) { + dev_err(&spi->dev, "failed reading clock-frequency"); + return ret; + } + + ret = of_property_read_u32(spi->dev.of_node, "radio-frequency", &freq_band); + if (ret) { + dev_err(&spi->dev, "failed reading radio-frequency"); + return ret; + } + + val = REG_OPMODE_LONG_RANGE_MODE | REG_OPMODE_MODE_SLEEP; + if (freq_band < 525000000) + val |= REG_OPMODE_LOW_FREQUENCY_MODE_ON; + ret = sx1276_write_single(spi, REG_OPMODE, val); + if (ret) { + dev_err(&spi->dev, "failed writing opmode"); + return ret; + } + + freq_rf = freq_band; + freq_rf *= (1 << 19); + freq_rf /= freq_xosc; + dev_dbg(&spi->dev, "Frf = %llu", freq_rf); + + ret = sx1276_write_single(spi, REG_FRF_MSB, freq_rf >> 16); + if (!ret) + ret = sx1276_write_single(spi, REG_FRF_MID, freq_rf >> 8); + if (!ret) + ret = sx1276_write_single(spi, REG_FRF_LSB, freq_rf); + if (ret) { + dev_err(&spi->dev, "failed writing frequency (%d)", ret); + return ret; + } + + ret = sx1276_read_single(spi, REG_PA_CONFIG, &val); + if (ret) { + dev_err(&spi->dev, "failed reading RegPaConfig\n"); + return ret; + } + if (true) + val |= REG_PA_CONFIG_PA_SELECT; + val &= ~GENMASK(3, 0); + val |= (23 - 3) - 5; + ret = sx1276_write_single(spi, REG_PA_CONFIG, val); + if (ret) { + dev_err(&spi->dev, "failed writing RegPaConfig\n"); + return ret; + } + + ret = sx1276_read_single(spi, REG_PA_DAC, &val); + if (ret) { + dev_err(&spi->dev, "failed reading RegPaDac\n"); + return ret; + } + val &= ~GENMASK(2, 0); + val |= 0x7; + ret = sx1276_write_single(spi, REG_PA_DAC, val); + if (ret) { + dev_err(&spi->dev, "failed writing RegPaDac\n"); + return ret; + } + + netdev = alloc_loradev(sizeof(struct sx1276_priv)); + if (!netdev) + return -ENOMEM; + + netdev->netdev_ops = &sx1276_netdev_ops; + netdev->flags |= IFF_ECHO; + + priv = netdev_priv(netdev); + priv->spi = spi; + mutex_init(&priv->spi_lock); + for (i = 0; i < 6; i++) + priv->dio_gpio[i] = dio[i]; + + spi_set_drvdata(spi, netdev); + SET_NETDEV_DEV(netdev, &spi->dev); + + ret = register_loradev(netdev); + if (ret) { + free_loradev(netdev); + return ret; + } + + dev_info(&spi->dev, "SX1276 module probed (SX%d)", model); + + return 0; +} + +static int sx1276_remove(struct spi_device *spi) +{ + struct net_device *netdev = spi_get_drvdata(spi); + + unregister_loradev(netdev); + free_loradev(netdev); + + dev_info(&spi->dev, "SX1276 module removed"); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sx1276_dt_ids[] = { + { .compatible = "semtech,sx1272" }, + { .compatible = "semtech,sx1276" }, + {} +}; +MODULE_DEVICE_TABLE(of, sx1276_dt_ids); +#endif + +static struct spi_driver sx1276_spi_driver = { + .driver = { + .name = "sx1276", + .of_match_table = of_match_ptr(sx1276_dt_ids), + }, + .probe = sx1276_probe, + .remove = sx1276_remove, +}; + +module_spi_driver(sx1276_spi_driver); + +MODULE_DESCRIPTION("SX1276 SPI driver"); +MODULE_AUTHOR("Andreas Färber "); +MODULE_LICENSE("GPL"); From patchwork Sun Jul 1 11:07:58 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andreas_F=C3=A4rber?= X-Patchwork-Id: 140697 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp2847438ljj; Sun, 1 Jul 2018 04:09:16 -0700 (PDT) X-Google-Smtp-Source: AAOMgpew1SoOovh5cpSxw7x8Ridc4pkMXn1F3Vwbx/lTW+lJJss9rdcGz6foxfrqgu1WxPBrLh8o X-Received: by 2002:a62:6882:: with SMTP id d124-v6mr21293100pfc.122.1530443356444; Sun, 01 Jul 2018 04:09:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1530443356; cv=none; d=google.com; s=arc-20160816; b=oPXZntqhzqLmxx7U1drZaZ6z14I3scg8RHoRUY+SWOtK8F+EvQlY+r9UZS4X4Au+5i mJvH2JjHuuGuMlryBzEVF48HnWyRbZXDp0RYBvlx41NW6d2ZayuJUgBwIZZEG9oH9sFi WuuJl80B6S2f4W3fZVFiUCgX3oNOuxqdDpF7TLmORYQI/7em3K3c9mbuqHY3o6x3B6E7 Ne08ZvFqcaLZlzAKHWyIvkSks1yss40rNpbcgZpzm2PCw8qZ952Vn4W4hoEw5yyQhWFH xmwG0ZH2RZOIpSGSPNi0idN1WWRUv+rz642JajeVHYd+SC88gJIkzWo1uTysgMX5lNCp nRTg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=fc+cCNYLxauDxS94MVbgW9rSb0y8H9XsCDbU6iiJdyM=; b=jOAbRUpqUKGNHImLzBnCV/kGGC/Xaj6nsC3QwLJOJau0DcuxpuDVxW5lMhtsW4IPq2 ci4fP7ORdKqAT6G7QnTle03/U8FC5Kt8NmYLKv847b6zbx0XEhPxHlHwV73HNLOvoRPM Sq2vVG5hv75/Pq31W0bWZiivRtnkoxNA+aqnSorgU64k0t6bhl6O530InwJvTWqibshA 3GTgcfk3H39n7w+nYtSXKrwuhJ67n6xNZJy5TCFQsHKOac7pDHEd39Dah7Fa28njT5O9 BQDAjRmTfJ7O+awf+T6muSelZmzYOCO8pCen9idIfczApAKimE2145ZSs0dAK5l4bxId c9TA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l26-v6si11251445pgu.191.2018.07.01.04.09.16; Sun, 01 Jul 2018 04:09:16 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752366AbeGALJF (ORCPT + 9 others); Sun, 1 Jul 2018 07:09:05 -0400 Received: from mx2.suse.de ([195.135.220.15]:47842 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752292AbeGALJC (ORCPT ); Sun, 1 Jul 2018 07:09:02 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id EA72CAF54; Sun, 1 Jul 2018 11:08:59 +0000 (UTC) From: =?utf-8?q?Andreas_F=C3=A4rber?= To: netdev@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Jian-Hong Pan , Jiri Pirko , Marcel Holtmann , "David S . Miller" , Matthias Brugger , Janus Piwek , =?utf-8?q?Michael_R=C3=B6der?= , Dollar Chen , Ken Yu , =?utf-8?q?Andreas_F=C3=A4rber?= Subject: [RFC net-next 09/15] net: lora: Prepare EUI helpers Date: Sun, 1 Jul 2018 13:07:58 +0200 Message-Id: <20180701110804.32415-10-afaerber@suse.de> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20180701110804.32415-1-afaerber@suse.de> References: <20180701110804.32415-1-afaerber@suse.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org These will be used by the RN2483 and other LoRaWAN capable modules. Signed-off-by: Andreas Färber --- include/linux/lora/dev.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) -- 2.16.4 diff --git a/include/linux/lora/dev.h b/include/linux/lora/dev.h index 531e68f0c9a6..153f9b2992ca 100644 --- a/include/linux/lora/dev.h +++ b/include/linux/lora/dev.h @@ -9,6 +9,27 @@ #include +typedef u8 lora_eui[8]; + +#define PRIxLORAEUI "%02x%02x%02x%02x%02x%02x%02x%02x" +#define PRIXLORAEUI "%02X%02X%02X%02X%02X%02X%02X%02X" +#define LORA_EUI(x) x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7] + +static inline int lora_strtoeui(const char *str, lora_eui *val) +{ + char buf[3]; + int i, ret; + + for (i = 0; i < 8; i++) { + strncpy(buf, str + i * 2, 2); + buf[2] = 0; + ret = kstrtou8(buf, 16, &(*val)[i]); + if (ret) + return ret; + } + return 0; +} + struct net_device *alloc_loradev(int sizeof_priv); void free_loradev(struct net_device *dev); int register_loradev(struct net_device *dev); From patchwork Sun Jul 1 11:07:59 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andreas_F=C3=A4rber?= X-Patchwork-Id: 140703 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp2848672ljj; Sun, 1 Jul 2018 04:10:34 -0700 (PDT) X-Google-Smtp-Source: AAOMgpdqzn3ATxxmZ9AE64yUBRClFncZ7TTE7/GfBJ2D993+KoRRXXWeSilB0BXDmutCLfeUz/Vq X-Received: by 2002:a17:902:5381:: with SMTP id c1-v6mr17130682pli.137.1530443434631; Sun, 01 Jul 2018 04:10:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1530443434; cv=none; d=google.com; s=arc-20160816; b=uX1e9qk7xMpdbWX3ikbPnSERzQw6chtrpFPud/r5pFj0OBBoCHLiDbIN3+AnINzvAj mpnkfsBFQe0GZKnTab4/+Ok2MY9CUbpJeHzyhHc8K5jYLyl1SSwhP/sONbW8QId1sBdU 4NXrGArnI4zlkcWbHW137c6rE9R6JA5lFlhns6J/U8RXWU3NwZYvXStNh82Qt6ypjJIu dhmn+dOst1tlPSVJyVBL1qVdJNJ1PYrOx3YM+dd90KJr+aVRdB1fstG2K7WixfrCeaXK VIWBDDT7Z2cQ5VHBwIwQ6SEht8yHkzxukSSTFmd0pYprJOtMC0/9564hu8mFcL8cfwLM bQEw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=X/P1PSbYoYsioTvQvPR3jQCWLF3Lg+1jXZl9XTjgg/4=; b=rbUIyKYErTi9B3uHlWTlLi7vjnSnE8sIqhHEU8/0i3C0vpEPzfW4tLYcMIfcuk315C w0CSkvt9GDB45m/9gqXP7Hmg4FRNjlFhKjoSNFm51kSSYEWogeUyNp5BSuF6tFMbGP2o lIZL1lfF2x3+K4Onr0hgaO4nMVUh75cFiSqpB20cnIWAb3BThETltbWxTwbXaj0iXlhh 5m0iVjxCudqUIALRDS7BpZ/guW+JdAH76ObY+XwcqwGqfNwJflEVt7Uucj1nPLkB3chb 2j1K+seog094MWvOtdCVzWiBcq8xOvUyQ0Vqxtbsx+x2OSYWLu1iNHRo8jlLCS/MY9jD WCKw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l68-v6si12115640pgl.84.2018.07.01.04.10.34; Sun, 01 Jul 2018 04:10:34 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752682AbeGALKb (ORCPT + 9 others); Sun, 1 Jul 2018 07:10:31 -0400 Received: from mx2.suse.de ([195.135.220.15]:47858 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752311AbeGALJC (ORCPT ); Sun, 1 Jul 2018 07:09:02 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id C79AAAF62; Sun, 1 Jul 2018 11:09:00 +0000 (UTC) From: =?utf-8?q?Andreas_F=C3=A4rber?= To: netdev@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Jian-Hong Pan , Jiri Pirko , Marcel Holtmann , "David S . Miller" , Matthias Brugger , Janus Piwek , =?utf-8?q?Michael_R=C3=B6der?= , Dollar Chen , Ken Yu , =?utf-8?q?Andreas_F=C3=A4rber?= Subject: [RFC net-next 10/15] net: lora: Add Microchip RN2483 Date: Sun, 1 Jul 2018 13:07:59 +0200 Message-Id: <20180701110804.32415-11-afaerber@suse.de> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20180701110804.32415-1-afaerber@suse.de> References: <20180701110804.32415-1-afaerber@suse.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 -- 2.16.4 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 +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 +#include +#include +#include +#include + +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; +} From patchwork Sun Jul 1 11:08:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andreas_F=C3=A4rber?= X-Patchwork-Id: 140701 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp2848575ljj; Sun, 1 Jul 2018 04:10:27 -0700 (PDT) X-Google-Smtp-Source: AAOMgpd6KkEjX2hZfy71bSVNseE9vBPfp1G5jxpX8zPK1Bzzz3IgaUs0+5zVmCim+c+0OT0Aj5M+ X-Received: by 2002:a17:902:be04:: with SMTP id r4-v6mr3885360pls.324.1530443427319; Sun, 01 Jul 2018 04:10:27 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1530443427; cv=none; d=google.com; s=arc-20160816; b=H+LI5lk7u75Oxbk8R+7mSA1LRciWSS15TPzxcU29nq8TAwTDKsbCvKnEK7Tv7s+y3H LKogqrvY44un/j+eZ2cqrO0eplQ9W9rwVyGKrn0tlQXxgpvQQ4bduGJMdjMKSHJ1azxp 5BMVOnFbFX3vlh4mQVhDmh0Rhu9wenP+z0hYanh/35sgscUadLApXke7meO6CFgA19Sq fpcH/jPzMGaQfpXF4kEh8W6Le3IKagAYk4Jlf4/UnfajkgeAUKLl6Rfde7mOvpkmxu+O 8WpGISP32nz1M/2AyhwALbAxU2dG+rcEtUAWqGGwhrE5KeYgThTlMBzP+QCeI9C5ePxt H8Ng== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=iJMht8OjPwVFqfuZh+SWi4ATZmND3EjGZWoEe5O7sY4=; b=0wurkxP66fSMG35FISryUt27I+xOYnyd0U6NCMDUqYKDaLmLKi96Is+TV6r92KUy6J pC0l/KzjIviOiZbg93owCfvEPwpmuTJdIadNayg9tllxrk3kEVo7VkmFBu0QHqw/Xq2+ MuStqvBKidbAW10uzCegM03SZgy3Ja7syYj8hryT23cF/XoS3qQ+r/J0O3LX2DlnuH7z BZYpf9P8H4xeMFiHzJCRi05UAg+nCWrtef2aMumsRjTsGKi5l5c1zqoka+MwyaZGQ/W3 WwIW3SloqEN8atyJm4QoorkBBKgVZjGXuOl8CBBSFxYwYjyV7BJXfrR4Qivf2AV2ho4w XotA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l68-v6si12115640pgl.84.2018.07.01.04.10.27; Sun, 01 Jul 2018 04:10:27 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752610AbeGALKW (ORCPT + 9 others); Sun, 1 Jul 2018 07:10:22 -0400 Received: from mx2.suse.de ([195.135.220.15]:47946 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752332AbeGALJD (ORCPT ); Sun, 1 Jul 2018 07:09:03 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 44ED3AFA4; Sun, 1 Jul 2018 11:09:02 +0000 (UTC) From: =?utf-8?q?Andreas_F=C3=A4rber?= To: netdev@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Jian-Hong Pan , Jiri Pirko , Marcel Holtmann , "David S . Miller" , Matthias Brugger , Janus Piwek , =?utf-8?q?Michael_R=C3=B6der?= , Dollar Chen , Ken Yu , =?utf-8?q?Andreas_F=C3=A4rber?= Subject: [RFC net-next 12/15] net: lora: Add USI WM-SG-SM-42 Date: Sun, 1 Jul 2018 13:08:01 +0200 Message-Id: <20180701110804.32415-13-afaerber@suse.de> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20180701110804.32415-1-afaerber@suse.de> References: <20180701110804.32415-1-afaerber@suse.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org The USI WM-SG-SM-42 offers a UART based AT command interface. Cc: Dollar Chen (陳義元) Signed-off-by: Andreas Färber --- drivers/net/lora/Kconfig | 7 + drivers/net/lora/Makefile | 3 + drivers/net/lora/usi.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 421 insertions(+) create mode 100644 drivers/net/lora/usi.c -- 2.16.4 diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig index 2e05caef8645..72a9d2a0f2be 100644 --- a/drivers/net/lora/Kconfig +++ b/drivers/net/lora/Kconfig @@ -31,6 +31,13 @@ config LORA_SX1276 help Semtech SX1272/1276/1278 +config LORA_USI + tristate "USI WM-SG-SM-42 driver" + default y + depends on SERIAL_DEV_BUS + help + USI WM-SG-SM-42 + config LORA_WIMOD tristate "IMST WiMOD driver" default y diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile index ecb326c859a5..dfa9a43dcfb3 100644 --- a/drivers/net/lora/Makefile +++ b/drivers/net/lora/Makefile @@ -16,5 +16,8 @@ lora-rn2483-y += rn2483_cmd.o obj-$(CONFIG_LORA_SX1276) += lora-sx1276.o lora-sx1276-y := sx1276.o +obj-$(CONFIG_LORA_USI) += lora-usi.o +lora-usi-y := usi.o + obj-$(CONFIG_LORA_WIMOD) += lora-wimod.o lora-wimod-y := wimod.o diff --git a/drivers/net/lora/usi.c b/drivers/net/lora/usi.c new file mode 100644 index 000000000000..f0c697e2cde2 --- /dev/null +++ b/drivers/net/lora/usi.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * USI WM-SG-SM-42 + * + * Copyright (c) 2017-2018 Andreas Färber + */ + +#include +#include +#include +#include +#include +#include + +struct usi_device { + struct serdev_device *serdev; + + int mode; + + char rx_buf[4096]; + int rx_len; + + struct completion prompt_recv_comp; + struct completion tx_event_recv_comp; +}; + +static bool usi_cmd_ok(const char *resp) +{ + int len = strlen(resp); + + return (len == 4 && !strcmp(resp, "OK\r\n")) || + (len >= 6 && !strcmp(resp + len - 6, "\r\nOK\r\n")); +} + +static int usi_send_command(struct usi_device *usidev, const char *cmd, char **data, unsigned long timeout) +{ + struct serdev_device *sdev = usidev->serdev; + const char cr = '\r'; + int cmd_len, resp_len; + char *resp; + + cmd_len = strlen(cmd); + serdev_device_write_buf(sdev, cmd, cmd_len); + serdev_device_write_buf(sdev, &cr, 1); + + timeout = wait_for_completion_timeout(&usidev->prompt_recv_comp, timeout); + if (!timeout) + return -ETIMEDOUT; + + resp = usidev->rx_buf; + resp_len = usidev->rx_len; + if (!strncmp(resp, cmd, cmd_len) && resp[cmd_len] == '\r') { + dev_dbg(&sdev->dev, "Skipping echo\n"); + resp += cmd_len + 1; + resp_len -= cmd_len + 1; + } + dev_dbg(&sdev->dev, "Received: '%s'\n", resp); + if (data) + *data = kstrdup(resp, GFP_KERNEL); + + usidev->rx_len = 0; + reinit_completion(&usidev->prompt_recv_comp); + + return 0; +} + +static int usi_simple_cmd(struct usi_device *usidev, const char *cmd, unsigned long timeout) +{ + char *resp; + int ret; + + ret = usi_send_command(usidev, cmd, &resp, timeout); + if (ret) + return ret; + + if (strcmp(resp, "OK\r\n") == 0) { + kfree(resp); + return 0; + } + + kfree(resp); + + return -EINVAL; +} + +static int usi_cmd_reset(struct usi_device *usidev) +{ + int ret; + + ret = usi_send_command(usidev, "ATZ", NULL, HZ); + if (ret) + return ret; + + mdelay(1000); + + return 0; +} + +static int usi_cmd_read_reg(struct usi_device *usidev, u8 addr, u8 *val) +{ + char *cmd; + char *resp; + char *sz; + int ret; + + cmd = kasprintf(GFP_KERNEL, "AT+RREG=0x%02x", (int)addr); + if (!cmd) + return -ENOMEM; + + ret = usi_send_command(usidev, cmd, &resp, HZ); + if (ret) { + kfree(cmd); + return ret; + } + if (!usi_cmd_ok(resp)) { + kfree(resp); + kfree(cmd); + return -EINVAL; + } + resp[strlen(resp) - 6] = '\0'; + sz = resp; + if (strstarts(sz, "+Reg=")) + sz += 5; + if (strncasecmp(sz, cmd + 8, 4) == 0 && strstarts(sz + 4, ", ")) + sz += 6; + + kfree(cmd); + + dev_dbg(&usidev->serdev->dev, "Parsing '%s'\n", sz); + ret = kstrtou8(sz, 0, val); + if (ret) { + kfree(resp); + return ret; + } + + kfree(resp); + + return 0; +} + +static int usi_receive_buf(struct serdev_device *sdev, const u8 *data, size_t count) +{ + struct usi_device *usidev = serdev_device_get_drvdata(sdev); + size_t i = 0; + int len = 0; + + dev_dbg(&sdev->dev, "Receive (%d)\n", (int)count); + + for (i = 0; i < count; i++) { + dev_dbg(&sdev->dev, "Receive: 0x%02x\n", (int)data[i]); + } + i = 0; + + if (completion_done(&usidev->prompt_recv_comp) || + completion_done(&usidev->tx_event_recv_comp)) { + dev_info(&sdev->dev, "RX waiting on completion\n"); + return 0; + } + if (usidev->rx_len == sizeof(usidev->rx_buf) - 1) { + dev_warn(&sdev->dev, "RX buffer full\n"); + return 0; + } + + i = min(count, sizeof(usidev->rx_buf) - 1 - usidev->rx_len); + if (i > 0) { + memcpy(&usidev->rx_buf[usidev->rx_len], data, i); + usidev->rx_len += i; + len += i; + } + if (usidev->rx_len >= 3 && strncmp(&usidev->rx_buf[usidev->rx_len - 3], "\r# ", 3) == 0) { + usidev->rx_len -= 3; + usidev->rx_buf[usidev->rx_len] = '\0'; + complete(&usidev->prompt_recv_comp); + } else if (usidev->rx_len > 7 && strstarts(usidev->rx_buf, "+RCV") && + strncmp(&usidev->rx_buf[usidev->rx_len - 2], "\r\n", 2) == 0) { + usidev->rx_buf[usidev->rx_len - 2] = '\0'; + dev_info(&sdev->dev, "RCV event: '%s'\n", usidev->rx_buf + 4); + usidev->rx_len = 0; + } else if (usidev->rx_len > 6 && strstarts(usidev->rx_buf, "+TX: ") && + strncmp(&usidev->rx_buf[usidev->rx_len - 2], "\r\n", 2) == 0) { + usidev->rx_buf[usidev->rx_len - 2] = '\0'; + dev_info(&sdev->dev, "TX event: '%s'\n", usidev->rx_buf + 5); + complete(&usidev->tx_event_recv_comp); + } + + return len; +} + +static const struct serdev_device_ops usi_serdev_client_ops = { + .receive_buf = usi_receive_buf, +}; + +static int usi_probe(struct serdev_device *sdev) +{ + struct usi_device *usidev; + //unsigned long timeout; + char *resp; + u8 val; + int ret; + + dev_info(&sdev->dev, "Probing"); + + usidev = devm_kzalloc(&sdev->dev, sizeof(struct usi_device), GFP_KERNEL); + if (!usidev) + return -ENOMEM; + + usidev->serdev = sdev; + usidev->mode = -1; + init_completion(&usidev->prompt_recv_comp); + init_completion(&usidev->tx_event_recv_comp); + serdev_device_set_drvdata(sdev, usidev); + + ret = serdev_device_open(sdev); + if (ret) { + dev_err(&sdev->dev, "Failed to open (%d)", ret); + return ret; + } + + serdev_device_set_baudrate(sdev, 115200); + serdev_device_set_flow_control(sdev, false); + serdev_device_set_client_ops(sdev, &usi_serdev_client_ops); + + ret = usi_cmd_reset(usidev); + if (ret) + dev_warn(&sdev->dev, "Reset failed\n"); + + ret = usi_send_command(usidev, "ATE=0", NULL, HZ); + if (ret) + dev_warn(&sdev->dev, "ATE failed\n"); + + /* Dropped in firmware 2.8 */ + ret = usi_send_command(usidev, "ATI", &resp, HZ); + if (!ret) { + if (usi_cmd_ok(resp)) { + resp[strlen(resp) - 6] = '\0'; + dev_info(&sdev->dev, "Firmware '%s'\n", resp); + } + kfree(resp); + } + + ret = usi_send_command(usidev, "AT+DEFMODE", &resp, HZ); + if (ret) { + dev_err(&sdev->dev, "Checking DEFMODE failed (%d)\n", ret); + serdev_device_close(sdev); + return ret; + } + if (usi_cmd_ok(resp)) { + resp[strlen(resp) - 6] = '\0'; + dev_info(&sdev->dev, "Default mode '%s'\n", resp); + if (!strcmp(resp, "MFG_WAN_MODE")) + usidev->mode = 6; + else if (!strcmp(resp, "MFG_TEST_IDLE")) + usidev->mode = 0; + else if (!strcmp(resp, "MFG_TX_TONE")) + usidev->mode = 1; + else if (!strcmp(resp, "MFG_TX_PACKET")) + usidev->mode = 2; + else if (!strcmp(resp, "MFG_ERROR_LESS_ARGUMENETS")) + usidev->mode = 3; + else if (!strcmp(resp, "MFG_TX_TEXT")) + usidev->mode = 4; + else if (!strcmp(resp, "MFG_TEST_STOP")) + usidev->mode = 5; + } + kfree(resp); + + if (usidev->mode != 3) { + ret = usi_simple_cmd(usidev, "AT+DEFMODE=3", HZ); + if (ret) { + dev_err(&sdev->dev, "Setting DEFMODE failed (%d)\n", ret); + serdev_device_close(sdev); + return ret; + } + +#if 1 + ret = usi_simple_cmd(usidev, "AT+WDCT", 5 * HZ); + if (ret) { + dev_err(&sdev->dev, "Writing DCT failed (%d)\n", ret); + serdev_device_close(sdev); + return ret; + } + + ret = usi_cmd_reset(usidev); + if (ret) { + dev_err(&sdev->dev, "Reset failed\n"); + serdev_device_close(sdev); + return ret; + } + + ret = usi_send_command(usidev, "ATE=0", NULL, HZ); + if (ret) + dev_warn(&sdev->dev, "ATE failed\n"); +#endif + + usidev->mode = -1; + ret = usi_send_command(usidev, "AT+DEFMODE", &resp, HZ); + if (ret) { + dev_err(&sdev->dev, "Checking DEFMODE failed (%d)\n", ret); + serdev_device_close(sdev); + return ret; + } + if (usi_cmd_ok(resp)) { + resp[strlen(resp) - 6] = '\0'; + dev_info(&sdev->dev, "Default mode '%s'\n", resp); + if (!strcmp(resp, "MFG_WAN_MODE")) { + usidev->mode = 6; + } + } + kfree(resp); + } + + ret = usi_send_command(usidev, "AT+VER", &resp, HZ); + if (!ret) { + if (usi_cmd_ok(resp)) { + resp[strlen(resp) - 6] = '\0'; + dev_info(&sdev->dev, "LoRaWAN version '%s'\n", + (strstarts(resp, "+VER=")) ? (resp + 5) : resp); + } + kfree(resp); + } + + ret = usi_simple_cmd(usidev, "AT+RF=20,868000000,7,0,1,0,8,0,0,0", HZ); + if (ret) { + dev_err(&sdev->dev, "AT+RF failed (%d)\n", ret); + serdev_device_close(sdev); + return ret; + } + + /*ret = usi_simple_cmd(usidev, "AT+TXT=1,deadbeef", 2 * HZ); + if (ret) { + dev_err(&sdev->dev, "TX failed (%d)\n", ret); + serdev_device_close(sdev); + return ret; + } + + timeout = wait_for_completion_timeout(&usidev->tx_event_recv_comp, 5 * HZ); + if (!timeout) { + serdev_device_close(sdev); + return -ETIMEDOUT; + } + usidev->rx_len = 0; + reinit_completion(&usidev->tx_event_recv_comp);*/ + + ret = usi_cmd_read_reg(usidev, 0x42, &val); + if (!ret) { + dev_info(&sdev->dev, "SX1272 VERSION 0x%02x\n", (int)val); + } + + ret = usi_cmd_read_reg(usidev, 0x39, &val); + if (!ret) { + dev_info(&sdev->dev, "SX1272 SyncWord 0x%02x\n", (int)val); + } + + ret = usi_cmd_read_reg(usidev, 0x01, &val); + if (!ret) { + dev_info(&sdev->dev, "SX1272 OpMode 0x%02x\n", (int)val); + } + + dev_info(&sdev->dev, "Done."); + + return 0; +} + +static void usi_remove(struct serdev_device *sdev) +{ + struct usi_device *usidev = serdev_device_get_drvdata(sdev); + + usi_send_command(usidev, "ATE=1\r", NULL, HZ); + + serdev_device_close(sdev); + + dev_info(&sdev->dev, "Removed\n"); +} + +static const struct of_device_id usi_of_match[] = { + { .compatible = "usi,wm-sg-sm-42" }, + {} +}; +MODULE_DEVICE_TABLE(of, usi_of_match); + +static struct serdev_device_driver usi_serdev_driver = { + .probe = usi_probe, + .remove = usi_remove, + .driver = { + .name = "usi", + .of_match_table = usi_of_match, + }, +}; + +static int __init usi_init(void) +{ + int ret; + + ret = serdev_device_driver_register(&usi_serdev_driver); + if (ret) + return ret; + + return 0; +} + +static void __exit usi_exit(void) +{ + serdev_device_driver_unregister(&usi_serdev_driver); +} + +module_init(usi_init); +module_exit(usi_exit); + +MODULE_DESCRIPTION("USI WM-SG-SM-42 serdev driver"); +MODULE_AUTHOR("Andreas Färber "); +MODULE_LICENSE("GPL"); From patchwork Sun Jul 1 11:08:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andreas_F=C3=A4rber?= X-Patchwork-Id: 140700 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp2848539ljj; Sun, 1 Jul 2018 04:10:22 -0700 (PDT) X-Google-Smtp-Source: AAOMgpeW0NsTtIud0cxzNP+jMkIV3srjtmnBXl+HgyLw8J4lxUysnNiBQ2badM+3IR0x83Sl5/6a X-Received: by 2002:a65:4541:: with SMTP id x1-v6mr13248133pgr.26.1530443422873; Sun, 01 Jul 2018 04:10:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1530443422; cv=none; d=google.com; s=arc-20160816; b=jj7+chXLaWnHzPcWwTgz8Rn5r+P/JQQOXsscYyGMJAHHbjbK0Is+9FFtJ5y+RjHVhB Tt+XPA8HZHf17COGPv885gr22Pemma1qa0YDZz2HRZGnOF8YHi8wGolYziuicpQ8arvY z9XWWzVbsyJyx7FpJsN8gxchkBK4/xg/DiTSnYdMA1Wj7vNlQOaIrWE7faOkd9ecJqr2 fT1KXl2KuxTb3VGPevQW6/mMJrRnE3FGvB8TvNg3GrUprE5PN5fSChuIaFoAjL7oIZs3 lubu3v0sn/hFmaDja6xDy0EUusEXeXlhanmYzCXtHZqiGkgUSEFf4Dznl0uczEHQUBqv d1BA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=9Xt1jV+HFlmqnrPDQH0JwfRRmK8DX5nicTFPRsKaDvk=; b=Z5iDKZ6D+3qftRBzHqIwolXYDtKCgMSo6Buo6kzM2Jz2r6z9LaxBT5QnsbdbFRjqLJ sPzfHbbm2ZuaKJ/iTjUFflwQ7yOeVMxCSL5Ec6KNMqy8H28HwcT3bND1meJazP++srHW odp0q+YuJ9OzrviZsWq+9KXCwbLli2D/Ba0Yu4u3e23nUcfSwD6ut2gsuLjHcbInIEuX sey5ZT7A8xbzp9wVqA0uK+KF01cC17QTfY8iJ90c+OmL64JWMkkgqoq0BHW+wlOjFIsu xKFpRcZbFZcC1YQbUJG6Q61b+FFzmeJOKWHsCRYiAUXBnJI/eeSkTiO8qlPy1+6u1QUn hY8g== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id q130-v6si11406707pgq.526.2018.07.01.04.10.22; Sun, 01 Jul 2018 04:10:22 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of netdev-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752339AbeGALKU (ORCPT + 9 others); Sun, 1 Jul 2018 07:10:20 -0400 Received: from mx2.suse.de ([195.135.220.15]:47958 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752334AbeGALJE (ORCPT ); Sun, 1 Jul 2018 07:09:04 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id F2471AFA5; Sun, 1 Jul 2018 11:09:02 +0000 (UTC) From: =?utf-8?q?Andreas_F=C3=A4rber?= To: netdev@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Jian-Hong Pan , Jiri Pirko , Marcel Holtmann , "David S . Miller" , Matthias Brugger , Janus Piwek , =?utf-8?q?Michael_R=C3=B6der?= , Dollar Chen , Ken Yu , =?utf-8?q?Andreas_F=C3=A4rber?= Subject: [RFC net-next 13/15] net: lora: Prepare RAK RAK811 Date: Sun, 1 Jul 2018 13:08:02 +0200 Message-Id: <20180701110804.32415-14-afaerber@suse.de> X-Mailer: git-send-email 2.16.4 In-Reply-To: <20180701110804.32415-1-afaerber@suse.de> References: <20180701110804.32415-1-afaerber@suse.de> MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org The RAK811 and RAK812 offer a UART based AT command interface. It allows both LoRaWAN and LoRa modes. Cc: Ken Yu (禹凯) Signed-off-by: Andreas Färber --- drivers/net/lora/Kconfig | 7 ++ drivers/net/lora/Makefile | 3 + drivers/net/lora/rak811.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 drivers/net/lora/rak811.c -- 2.16.4 diff --git a/drivers/net/lora/Kconfig b/drivers/net/lora/Kconfig index 72a9d2a0f2be..3e384493cbdd 100644 --- a/drivers/net/lora/Kconfig +++ b/drivers/net/lora/Kconfig @@ -17,6 +17,13 @@ config LORA_DEV if LORA_DEV +config LORA_RAK811 + tristate "RAK RAK811 driver" + default y + depends on SERIAL_DEV_BUS + help + RAK RAK811/RAK812 + config LORA_RN2483 tristate "Microchip RN2483/RN2903 driver" default y diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile index dfa9a43dcfb3..6b6870ffbfd8 100644 --- a/drivers/net/lora/Makefile +++ b/drivers/net/lora/Makefile @@ -9,6 +9,9 @@ lora-dev-y := dev.o # Alphabetically sorted. # +obj-$(CONFIG_LORA_RAK811) += lora-rak811.o +lora-rak811-y := rak811.o + obj-$(CONFIG_LORA_RN2483) += lora-rn2483.o lora-rn2483-y := rn2483.o lora-rn2483-y += rn2483_cmd.o diff --git a/drivers/net/lora/rak811.c b/drivers/net/lora/rak811.c new file mode 100644 index 000000000000..ad3d0980c489 --- /dev/null +++ b/drivers/net/lora/rak811.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RAK RAK811 + * + * Copyright (c) 2017-2018 Andreas Färber + */ + +#include +#include +#include +#include +#include +#include +#include + +struct rak811_device { + struct serdev_device *serdev; + + char rx_buf[4096]; + int rx_len; + + struct completion line_recv_comp; +}; + +static int rak811_send_command(struct rak811_device *rakdev, const char *cmd, char **data, unsigned long timeout) +{ + struct serdev_device *sdev = rakdev->serdev; + const char *crlf = "\r\n"; + char *resp; + + serdev_device_write_buf(sdev, cmd, strlen(cmd)); + serdev_device_write_buf(sdev, crlf, 2); + + timeout = wait_for_completion_timeout(&rakdev->line_recv_comp, timeout); + if (!timeout) + return -ETIMEDOUT; + + resp = rakdev->rx_buf; + dev_dbg(&sdev->dev, "Received: '%s'\n", resp); + if (data) + *data = kstrdup(resp, GFP_KERNEL); + + rakdev->rx_len = 0; + reinit_completion(&rakdev->line_recv_comp); + + return 0; +} + +static int rak811_simple_cmd(struct rak811_device *rakdev, const char *cmd, unsigned long timeout) +{ + char *resp; + int ret; + + ret = rak811_send_command(rakdev, cmd, &resp, timeout); + if (ret) + return ret; + + if (strncmp(resp, "OK", 2) == 0) { + kfree(resp); + return 0; + } + + kfree(resp); + + return -EINVAL; +} + +static int rak811_get_version(struct rak811_device *rakdev, char **version, unsigned long timeout) +{ + char *resp; + int ret; + + ret = rak811_send_command(rakdev, "at+version", &resp, timeout); + if (ret) + return ret; + + if (strncmp(resp, "OK", 2) == 0) { + *version = kstrdup(resp + 2, GFP_KERNEL); + kfree(resp); + return 0; + } + + kfree(resp); + + return -EINVAL; +} + +static int rak811_receive_buf(struct serdev_device *sdev, const u8 *data, size_t count) +{ + struct rak811_device *rakdev = serdev_device_get_drvdata(sdev); + size_t i = 0; + int len = 0; + + dev_dbg(&sdev->dev, "Receive (%d)\n", (int)count); + + for (i = 0; i < count; i++) { + dev_dbg(&sdev->dev, "Receive: 0x%02x\n", (int)data[i]); + } + + if (completion_done(&rakdev->line_recv_comp)) { + dev_info(&sdev->dev, "RX waiting on completion\n"); + return 0; + } + if (rakdev->rx_len == sizeof(rakdev->rx_buf) - 1) { + dev_warn(&sdev->dev, "RX buffer full\n"); + return 0; + } + + i = min(count, sizeof(rakdev->rx_buf) - 1 - rakdev->rx_len); + if (i > 0) { + memcpy(&rakdev->rx_buf[rakdev->rx_len], data, i); + rakdev->rx_len += i; + len += i; + } + if (rakdev->rx_len >= 2 && strncmp(&rakdev->rx_buf[rakdev->rx_len - 2], "\r\n", 2) == 0) { + rakdev->rx_len -= 2; + rakdev->rx_buf[rakdev->rx_len] = '\0'; + complete(&rakdev->line_recv_comp); + } + + return len; +} + +static const struct serdev_device_ops rak811_serdev_client_ops = { + .receive_buf = rak811_receive_buf, +}; + +static int rak811_probe(struct serdev_device *sdev) +{ + struct rak811_device *rakdev; + char *sz; + int ret; + + dev_info(&sdev->dev, "Probing\n"); + + rakdev = devm_kzalloc(&sdev->dev, sizeof(struct rak811_device), GFP_KERNEL); + if (!rakdev) + return -ENOMEM; + + rakdev->serdev = sdev; + init_completion(&rakdev->line_recv_comp); + serdev_device_set_drvdata(sdev, rakdev); + + ret = serdev_device_open(sdev); + if (ret) { + dev_err(&sdev->dev, "Failed to open (%d)\n", ret); + return ret; + } + + serdev_device_set_baudrate(sdev, 115200); + serdev_device_set_flow_control(sdev, false); + serdev_device_set_client_ops(sdev, &rak811_serdev_client_ops); + + ret = rak811_get_version(rakdev, &sz, HZ); + if (ret) { + dev_err(&sdev->dev, "Failed to get version (%d)\n", ret); + serdev_device_close(sdev); + return ret; + } + + dev_info(&sdev->dev, "firmware version: %s\n", sz); + kfree(sz); + + ret = rak811_simple_cmd(rakdev, "at+mode=1", 2 * HZ); + if (ret) { + dev_err(&sdev->dev, "Failed to set mode to P2P (%d)\n", ret); + serdev_device_close(sdev); + return ret; + } + + dev_info(&sdev->dev, "Done.\n"); + + return 0; +} + +static void rak811_remove(struct serdev_device *sdev) +{ + serdev_device_close(sdev); + + dev_info(&sdev->dev, "Removed\n"); +} + +static const struct of_device_id rak811_of_match[] = { + { .compatible = "rakwireless,rak811" }, + {} +}; +MODULE_DEVICE_TABLE(of, rak811_of_match); + +static struct serdev_device_driver rak811_serdev_driver = { + .probe = rak811_probe, + .remove = rak811_remove, + .driver = { + .name = "rak811", + .of_match_table = rak811_of_match, + }, +}; + +static int __init rak811_init(void) +{ + int ret; + + ret = serdev_device_driver_register(&rak811_serdev_driver); + if (ret) + return ret; + + return 0; +} + +static void __exit rak811_exit(void) +{ + serdev_device_driver_unregister(&rak811_serdev_driver); +} + +module_init(rak811_init); +module_exit(rak811_exit); + +MODULE_DESCRIPTION("RAK811 serdev driver"); +MODULE_AUTHOR("Andreas Färber "); +MODULE_LICENSE("GPL");