linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Andreas Färber" <afaerber@suse.de>
To: linux-lpwan@lists.infradead.org, linux-serial@vger.kernel.org
Cc: linux-usb@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, "Johan Hovold" <johan@kernel.org>,
	"Rob Herring" <robh@kernel.org>,
	"Andreas Färber" <afaerber@suse.de>,
	"David S. Miller" <davem@davemloft.net>,
	"Oliver Neukum" <oneukum@suse.com>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	netdev@vger.kernel.org
Subject: [RFC lora-next 5/5] HACK: net: lora: sx130x: Add PicoCell gateway shim for cdc-acm
Date: Fri,  4 Jan 2019 12:21:31 +0100	[thread overview]
Message-ID: <20190104112131.14451-6-afaerber@suse.de> (raw)
In-Reply-To: <20190104112131.14451-1-afaerber@suse.de>

Ignore our device in cdc-acm probing and add a new driver for it,
dispatching to cdc-acm for the actual implementation.

WARNING: It is likely that this VID/PID is in use for unrelated devices.
Only the Product string hints what this really is in current v0.2.1.
Previous code v0.2.0 was using a Semtech VID/PID, but no card shipping
with such firmware is known to me.

While this may seem unorthodox, no internals of the driver are accessed,
just the set of driver callbacks is duplicated as shim.

Use this shim construct to fake DT nodes for serdev on probe.
For testing purposes these nodes do not have a parent yet.
This results in two "Error -2 creating of_node link" warnings on probe.

Cc: Johan Hovold <johan@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Signed-off-by: Andreas Färber <afaerber@suse.de>
---
 drivers/net/lora/Makefile   |   2 +
 drivers/net/lora/picogw.c   | 337 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/class/cdc-acm.c |   4 +
 3 files changed, 343 insertions(+)
 create mode 100644 drivers/net/lora/picogw.c

diff --git a/drivers/net/lora/Makefile b/drivers/net/lora/Makefile
index 5fef38abf5ed..bdcf7560dd65 100644
--- a/drivers/net/lora/Makefile
+++ b/drivers/net/lora/Makefile
@@ -27,6 +27,8 @@ lora-sx130x-y := sx130x.o
 lora-sx130x-y += sx130x_radio.o
 obj-$(CONFIG_LORA_SX130X) += lora-sx130x-picogw.o
 lora-sx130x-picogw-y := sx130x_picogw.o
+obj-$(CONFIG_LORA_SX130X) += lora-picogw.o
+lora-picogw-y := picogw.o
 
 obj-$(CONFIG_LORA_USI) += lora-usi.o
 lora-usi-y := usi.o
diff --git a/drivers/net/lora/picogw.c b/drivers/net/lora/picogw.c
new file mode 100644
index 000000000000..aa5b83d21bb3
--- /dev/null
+++ b/drivers/net/lora/picogw.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Semtech PicoCell gateway USB interface
+ *
+ * Copyright (c) 2018-2019 Andreas Färber
+ */
+
+#define pr_fmt(fmt) "picocell: " fmt
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/serial.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+
+#define PICO_VID 0x0483
+#define PICO_PID 0x5740
+
+static struct usb_driver *picogw_get_acm_driver(struct usb_interface *iface)
+{
+	struct device_driver *drv;
+
+	drv = driver_find("cdc_acm", iface->dev.bus);
+	if (!drv)
+		return NULL;
+
+	return to_usb_driver(drv);
+}
+
+static void picogw_kobj_release(struct kobject *kobj)
+{
+	struct device_node *node = container_of(kobj, struct device_node, kobj);
+	struct property *prop;
+
+	prop = node->properties;
+	while (prop) {
+		struct property *next = prop->next;
+		kfree(prop);
+		prop = next;
+	}
+
+	kfree(node);
+}
+
+static struct kobj_type picogw_kobj_type = {
+	.release = picogw_kobj_release,
+};
+
+static u32 picogw_radio_a_reg = cpu_to_be32(0);
+static u32 picogw_radio_b_reg = cpu_to_be32(1);
+
+static int picogw_fake_of_nodes(struct device *dev)
+{
+	struct device_node *node = NULL;
+	struct device_node *child, *spi, *radio_a, *radio_b;
+	struct property *prop;
+
+	node = kzalloc(sizeof(*node), GFP_KERNEL);
+	if (!node)
+		return -ENOMEM;
+	node->name = "<NULL>";
+	node->full_name = "usb0483,5740";
+	node->type = "<NULL>";
+	kobject_init(&node->kobj, &picogw_kobj_type);
+	node->fwnode.ops = &of_fwnode_ops;
+
+	child = kzalloc(sizeof(*child), GFP_KERNEL);
+	if (!child) {
+		of_node_put(node);
+		return -ENOMEM;
+	}
+	child->name = "lora";
+	child->full_name = "lora";
+	child->type = "<NULL>";
+	child->parent = node;
+	kobject_init(&child->kobj, &picogw_kobj_type);
+	child->fwnode.ops = &of_fwnode_ops;
+	node->child = child;
+
+	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+	if (!prop) {
+		of_node_put(child);
+		of_node_put(node);
+		return -ENOMEM;
+	}
+	prop->name = "compatible";
+	prop->value = "semtech,lora-picocell";
+	prop->length = 22;
+	child->properties = prop;
+
+	spi = kzalloc(sizeof(*spi), GFP_KERNEL);
+	if (!spi) {
+		of_node_put(child);
+		of_node_put(node);
+		return -ENOMEM;
+	}
+	spi->name = "radio-spi";
+	spi->full_name = "radio-spi";
+	spi->type = "<NULL>";
+	spi->parent = child;
+	kobject_init(&spi->kobj, &picogw_kobj_type);
+	spi->fwnode.ops = &of_fwnode_ops;
+	child->child = spi;
+
+	radio_a = kzalloc(sizeof(*radio_a), GFP_KERNEL);
+	if (!radio_a) {
+		of_node_put(spi);
+		of_node_put(child);
+		of_node_put(node);
+		return -ENOMEM;
+	}
+	radio_a->name = "lora@0";
+	radio_a->full_name = "lora@0";
+	radio_a->type = "<NULL>";
+	radio_a->parent = spi;
+	kobject_init(&radio_a->kobj, &picogw_kobj_type);
+	radio_a->fwnode.ops = &of_fwnode_ops;
+	spi->child = radio_a;
+
+	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+	if (!prop) {
+		of_node_put(radio_a);
+		of_node_put(spi);
+		of_node_put(child);
+		of_node_put(node);
+		return -ENOMEM;
+	}
+	prop->name = "compatible";
+	prop->value = "semtech,sx1257";
+	prop->length = 15;
+	radio_a->properties = prop;
+
+	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+	if (!prop) {
+		of_node_put(radio_a);
+		of_node_put(spi);
+		of_node_put(child);
+		of_node_put(node);
+		return -ENOMEM;
+	}
+	prop->name = "reg";
+	prop->value = &picogw_radio_a_reg;
+	prop->length = 4;
+	radio_a->properties->next = prop;
+
+	radio_b = kzalloc(sizeof(*radio_b), GFP_KERNEL);
+	if (!radio_b) {
+		of_node_put(radio_a);
+		of_node_put(spi);
+		of_node_put(child);
+		of_node_put(node);
+		return -ENOMEM;
+	}
+	radio_b->name = "lora@1";
+	radio_b->full_name = "Lora@1";
+	radio_b->type = "<NULL>";
+	radio_b->parent = spi;
+	kobject_init(&radio_b->kobj, &picogw_kobj_type);
+	radio_b->fwnode.ops = &of_fwnode_ops;
+	radio_a->sibling = radio_b;
+
+	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+	if (!prop) {
+		of_node_put(radio_b);
+		of_node_put(radio_a);
+		of_node_put(spi);
+		of_node_put(child);
+		of_node_put(node);
+		return -ENOMEM;
+	}
+	prop->name = "compatible";
+	prop->value = "semtech,sx1257";
+	prop->length = 15;
+	radio_b->properties = prop;
+
+	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+	if (!prop) {
+		of_node_put(radio_a);
+		of_node_put(spi);
+		of_node_put(child);
+		of_node_put(node);
+		return -ENOMEM;
+	}
+	prop->name = "reg";
+	prop->value = &picogw_radio_b_reg;
+	prop->length = 4;
+	radio_b->properties->next = prop;
+
+	dev->of_node = node;
+
+	return 0;
+}
+
+static void picogw_cleanup_of_nodes(struct device *dev)
+{
+	if (dev->of_node->parent)
+		return;
+
+	of_node_put(dev->of_node->child->child->child->sibling); /* lora@1 */
+	of_node_put(dev->of_node->child->child->child); /* lora@0 */
+	of_node_put(dev->of_node->child->child); /* radio-spi*/
+	of_node_put(dev->of_node->child); /* serdev */
+	of_node_put(dev->of_node); /* usb */
+	dev->of_node = NULL;
+}
+
+static int picogw_probe(struct usb_interface *interface,
+	const struct usb_device_id *id)
+{
+	struct usb_driver *drv;
+	int ret;
+
+	drv = picogw_get_acm_driver(interface);
+	if (!drv) {
+		dev_err(&interface->dev, "driver_find failed\n");
+		return -EPROBE_DEFER;
+	}
+
+	if (!interface->dev.of_node) {
+		dev_dbg(&interface->dev, "no of_node\n");
+		ret = picogw_fake_of_nodes(&interface->dev);
+		if (ret)
+			return ret;
+	}
+
+	ret = drv->probe(interface, id);
+	if (ret) {
+		picogw_cleanup_of_nodes(&interface->dev);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void picogw_disconnect(struct usb_interface *intf)
+{
+	struct usb_driver *drv = picogw_get_acm_driver(intf);
+
+	if (drv)
+		drv->disconnect(intf);
+	else
+		dev_warn(&intf->dev, "%s: failed to get cdc_acm driver\n", __func__);
+
+	picogw_cleanup_of_nodes(&intf->dev);
+}
+
+static int picogw_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_driver *drv = picogw_get_acm_driver(intf);
+
+	if (!drv) {
+		dev_err(&intf->dev, "%s: failed to get cdc_acm driver\n", __func__);
+		return -ENODEV;
+	}
+
+	return drv->suspend(intf, message);
+}
+
+static int picogw_resume(struct usb_interface *intf)
+{
+	struct usb_driver *drv = picogw_get_acm_driver(intf);
+
+	if (!drv) {
+		dev_err(&intf->dev, "%s: failed to get cdc_acm driver\n", __func__);
+		return -ENODEV;
+	}
+
+	return drv->resume(intf);
+}
+
+static int picogw_reset_resume(struct usb_interface *intf)
+{
+	struct usb_driver *drv = picogw_get_acm_driver(intf);
+
+	if (!drv) {
+		dev_err(&intf->dev, "%s: failed to get cdc_acm driver\n", __func__);
+		return -ENODEV;
+	}
+
+	return drv->reset_resume(intf);
+}
+
+static int picogw_pre_reset(struct usb_interface *intf)
+{
+	struct usb_driver *drv = picogw_get_acm_driver(intf);
+
+	if (!drv) {
+		dev_err(&intf->dev, "%s: failed to get cdc_acm driver\n", __func__);
+		return -ENODEV;
+	}
+
+	return drv->pre_reset(intf);
+}
+
+static const struct usb_device_id picogw_usb_id_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(PICO_VID, PICO_PID,
+	  USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_V25TER) },
+	{}
+};
+MODULE_DEVICE_TABLE(usb, picogw_usb_id_table);
+
+static struct usb_driver picogw_usb_driver = {
+	.name = "lora-picogw-usb",
+	.probe = picogw_probe,
+	.disconnect = picogw_disconnect,
+	.suspend = picogw_suspend,
+	.resume = picogw_resume,
+	.reset_resume = picogw_reset_resume,
+	.pre_reset = picogw_pre_reset,
+	.id_table = picogw_usb_id_table,
+	.supports_autosuspend = 1,
+	.disable_hub_initiated_lpm = 1,
+};
+
+static int __init picogw_init(void)
+{
+	int ret;
+
+	ret = usb_register(&picogw_usb_driver);
+	if (ret < 0){
+		pr_err("usb_register failed (%d)\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+module_init(picogw_init);
+
+static void __exit picogw_exit(void)
+{
+	usb_deregister(&picogw_usb_driver);
+}
+module_exit(picogw_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index c225a586c524..541c23b4fbfe 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1865,6 +1865,10 @@ static const struct usb_device_id acm_ids[] = {
 	.driver_info = IGNORE_DEVICE,
 	},
 
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x0483, 0x5740,
+		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_V25TER),
+	.driver_info = IGNORE_DEVICE },
+
 	/* control interfaces without any protocol set */
 	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
 		USB_CDC_PROTO_NONE) },
-- 
2.16.4


  parent reply	other threads:[~2019-01-04 11:21 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-04 11:21 [PATCH RFC lora-next 0/5] net: lora: sx130x: USB CDC Picocell Gateway serdev driver via fake DT nodes Andreas Färber
2019-01-04 11:21 ` [PATCH lora-next 1/5] net: lora: sx130x: Factor out SPI specific parts Andreas Färber
2019-01-04 11:21 ` [PATCH lora-next 2/5] net: lora: sx130x: Prepare storing driver-specific data Andreas Färber
2019-01-04 11:21 ` [PATCH lora-next 3/5] net: lora: sx130x: Add PicoCell serdev driver Andreas Färber
2019-01-05  1:32   ` Andreas Färber
2019-01-05  1:49   ` Andreas Färber
2019-01-05  9:18   ` Ben Whitten
2019-01-07 12:11   ` Oliver Neukum
2019-01-04 11:21 ` [PATCH RFC 4/5] usb: cdc-acm: Enable serdev support Andreas Färber
2019-01-07 13:48   ` Oliver Neukum
2019-01-07 15:02     ` Johan Hovold
2019-01-04 11:21 ` Andreas Färber [this message]
2019-01-04 17:07   ` [RFC lora-next 5/5] HACK: net: lora: sx130x: Add PicoCell gateway shim for cdc-acm Rob Herring
2019-01-04 23:43     ` Andreas Färber
2019-01-07 15:28       ` Johan Hovold

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=20190104112131.14451-6-afaerber@suse.de \
    --to=afaerber@suse.de \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=johan@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-lpwan@lists.infradead.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=oneukum@suse.com \
    --cc=robh@kernel.org \
    /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: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).