All of lore.kernel.org
 help / color / mirror / Atom feed
From: Johan Hovold <johan@kernel.org>
To: Johan Hovold <johan@kernel.org>
Cc: Marc Ferland <ferlandm@amotus.ca>,
	linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 1/2] gnss: add USB support
Date: Mon, 20 Dec 2021 12:19:00 +0100	[thread overview]
Message-ID: <20211220111901.23206-2-johan@kernel.org> (raw)
In-Reply-To: <20211220111901.23206-1-johan@kernel.org>

Add a generic driver for GNSS receivers with a USB interface with two
bulk endpoints.

The driver currently assumes that the device protocol is NMEA (only) but
this can be generalised later as needed.

Signed-off-by: Johan Hovold <johan@kernel.org>
---
 drivers/gnss/Kconfig  |  11 +++
 drivers/gnss/Makefile |   3 +
 drivers/gnss/usb.c    | 209 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 223 insertions(+)
 create mode 100644 drivers/gnss/usb.c

diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig
index bd12e3d57baa..d7fe265c2869 100644
--- a/drivers/gnss/Kconfig
+++ b/drivers/gnss/Kconfig
@@ -54,4 +54,15 @@ config GNSS_UBX_SERIAL
 
 	  If unsure, say N.
 
+config GNSS_USB
+	tristate "USB GNSS receiver support"
+	depends on USB
+	help
+	  Say Y here if you have a GNSS receiver which uses a USB interface.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called gnss-usb.
+
+	  If unsure, say N.
+
 endif # GNSS
diff --git a/drivers/gnss/Makefile b/drivers/gnss/Makefile
index 451f11401ecc..bb2cbada3435 100644
--- a/drivers/gnss/Makefile
+++ b/drivers/gnss/Makefile
@@ -17,3 +17,6 @@ gnss-sirf-y := sirf.o
 
 obj-$(CONFIG_GNSS_UBX_SERIAL)		+= gnss-ubx.o
 gnss-ubx-y := ubx.o
+
+obj-$(CONFIG_GNSS_USB)			+= gnss-usb.o
+gnss-usb-y := usb.o
diff --git a/drivers/gnss/usb.c b/drivers/gnss/usb.c
new file mode 100644
index 000000000000..5c0251034def
--- /dev/null
+++ b/drivers/gnss/usb.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic USB GNSS receiver driver
+ *
+ * Copyright (C) 2021 Johan Hovold <johan@kernel.org>
+ */
+
+#include <linux/errno.h>
+#include <linux/gnss.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#define GNSS_USB_READ_BUF_LEN	512
+#define GNSS_USB_WRITE_TIMEOUT	1000
+
+static const struct usb_device_id gnss_usb_id_table[] = {
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, gnss_usb_id_table);
+
+struct gnss_usb {
+	struct usb_device *udev;
+	struct usb_interface *intf;
+	struct gnss_device *gdev;
+	struct urb *read_urb;
+	unsigned int write_pipe;
+};
+
+static void gnss_usb_rx_complete(struct urb *urb)
+{
+	struct gnss_usb *gusb = urb->context;
+	struct gnss_device *gdev = gusb->gdev;
+	int status = urb->status;
+	int len;
+	int ret;
+
+	switch (status) {
+	case 0:
+		break;
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		dev_dbg(&gdev->dev, "urb stopped: %d\n", status);
+		return;
+	case -EPIPE:
+		dev_err(&gdev->dev, "urb stopped: %d\n", status);
+		return;
+	default:
+		dev_dbg(&gdev->dev, "nonzero urb status: %d\n", status);
+		goto resubmit;
+	}
+
+	len = urb->actual_length;
+	if (len == 0)
+		goto resubmit;
+
+	ret = gnss_insert_raw(gdev, urb->transfer_buffer, len);
+	if (ret < len)
+		dev_dbg(&gdev->dev, "dropped %d bytes\n", len - ret);
+resubmit:
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret && ret != -EPERM && ret != -ENODEV)
+		dev_err(&gdev->dev, "failed to resubmit urb: %d\n", ret);
+}
+
+static int gnss_usb_open(struct gnss_device *gdev)
+{
+	struct gnss_usb *gusb = gnss_get_drvdata(gdev);
+	int ret;
+
+	ret = usb_submit_urb(gusb->read_urb, GFP_KERNEL);
+	if (ret) {
+		if (ret != -EPERM && ret != -ENODEV)
+			dev_err(&gdev->dev, "failed to submit urb: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void gnss_usb_close(struct gnss_device *gdev)
+{
+	struct gnss_usb *gusb = gnss_get_drvdata(gdev);
+
+	usb_kill_urb(gusb->read_urb);
+}
+
+static int gnss_usb_write_raw(struct gnss_device *gdev,
+		const unsigned char *buf, size_t count)
+{
+	struct gnss_usb *gusb = gnss_get_drvdata(gdev);
+	void *tbuf;
+	int ret;
+
+	tbuf = kmemdup(buf, count, GFP_KERNEL);
+	if (!tbuf)
+		return -ENOMEM;
+
+	ret = usb_bulk_msg(gusb->udev, gusb->write_pipe, tbuf, count, NULL,
+			GNSS_USB_WRITE_TIMEOUT);
+	kfree(tbuf);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static const struct gnss_operations gnss_usb_gnss_ops = {
+	.open		= gnss_usb_open,
+	.close		= gnss_usb_close,
+	.write_raw	= gnss_usb_write_raw,
+};
+
+static int gnss_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_endpoint_descriptor *in, *out;
+	struct gnss_device *gdev;
+	struct gnss_usb *gusb;
+	struct urb *urb;
+	size_t buf_len;
+	void *buf;
+	int ret;
+
+	ret = usb_find_common_endpoints(intf->cur_altsetting, &in, &out, NULL,
+			NULL);
+	if (ret)
+		return ret;
+
+	gusb = kzalloc(sizeof(*gusb), GFP_KERNEL);
+	if (!gusb)
+		return -ENOMEM;
+
+	gdev = gnss_allocate_device(&intf->dev);
+	if (!gdev) {
+		ret = -ENOMEM;
+		goto err_free_gusb;
+	}
+
+	gdev->ops = &gnss_usb_gnss_ops;
+	gdev->type = GNSS_TYPE_NMEA;
+	gnss_set_drvdata(gdev, gusb);
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		goto err_put_gdev;
+
+	buf_len = max(usb_endpoint_maxp(in), GNSS_USB_READ_BUF_LEN);
+
+	buf = kzalloc(buf_len, GFP_KERNEL);
+	if (!buf)
+		goto err_free_urb;
+
+	usb_fill_bulk_urb(urb, udev,
+			usb_rcvbulkpipe(udev, usb_endpoint_num(in)),
+			buf, buf_len, gnss_usb_rx_complete, gusb);
+
+	gusb->intf = intf;
+	gusb->udev = udev;
+	gusb->gdev = gdev;
+	gusb->read_urb = urb;
+	gusb->write_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(out));
+
+	ret = gnss_register_device(gdev);
+	if (ret)
+		goto err_free_buf;
+
+	usb_set_intfdata(intf, gusb);
+
+	return 0;
+
+err_free_buf:
+	kfree(buf);
+err_free_urb:
+	usb_free_urb(urb);
+err_put_gdev:
+	gnss_put_device(gdev);
+err_free_gusb:
+	kfree(gusb);
+
+	return ret;
+}
+
+static void gnss_usb_disconnect(struct usb_interface *intf)
+{
+	struct gnss_usb *gusb = usb_get_intfdata(intf);
+
+	gnss_deregister_device(gusb->gdev);
+
+	kfree(gusb->read_urb->transfer_buffer);
+	usb_free_urb(gusb->read_urb);
+	gnss_put_device(gusb->gdev);
+	kfree(gusb);
+}
+
+static struct usb_driver gnss_usb_driver = {
+	.name		= "gnss-usb",
+	.probe		= gnss_usb_probe,
+	.disconnect	= gnss_usb_disconnect,
+	.id_table	= gnss_usb_id_table,
+};
+module_usb_driver(gnss_usb_driver);
+
+MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
+MODULE_DESCRIPTION("Generic USB GNSS receiver driver");
+MODULE_LICENSE("GPL v2");
-- 
2.32.0


  reply	other threads:[~2021-12-20 11:19 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-20 11:18 [PATCH 0/2] gnss: add USB support Johan Hovold
2021-12-20 11:19 ` Johan Hovold [this message]
2021-12-20 12:49   ` [PATCH 1/2] " Oliver Neukum
2021-12-20 13:25     ` Johan Hovold
2021-12-20 11:19 ` [PATCH 2/2] gnss: usb: add support for Sierra Wireless XM1210 Johan Hovold
2021-12-20 19:04   ` Marc Ferland
2021-12-22 14:46     ` Johan Hovold
2021-12-20 12:15 ` [PATCH 0/2] gnss: add USB support Greg KH
2021-12-20 13:26   ` Johan Hovold
  -- strict thread matches above, loose matches on Subject: below --
2022-01-05 17:20 [PATCH 1/2] " kernel test robot
2022-01-07  8:15 ` Dan Carpenter
2022-01-07  9:15 ` 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=20211220111901.23206-2-johan@kernel.org \
    --to=johan@kernel.org \
    --cc=ferlandm@amotus.ca \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.