git: 54e231b373ef - main - Add support for i2c-tiny-usb: usb to iic bridge

From: Warner Losh <imp_at_FreeBSD.org>
Date: Fri, 19 Apr 2024 22:48:55 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=54e231b373ef617c348706c6c64a2e049ea738ec

commit 54e231b373ef617c348706c6c64a2e049ea738ec
Author:     Denis Bodor <lefinnois@lefinnois.net>
AuthorDate: 2024-04-19 22:38:37 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2024-04-19 22:40:23 +0000

    Add support for i2c-tiny-usb: usb to iic bridge
    
    Reviewed by: imp
    Pull Request: https://github.com/freebsd/freebsd-src/pull/1123
---
 share/man/man4/Makefile             |   1 +
 share/man/man4/i2ctinyusb.4         |  85 ++++++++++
 sys/conf/files                      |   1 +
 sys/dev/usb/misc/i2ctinyusb.c       | 302 ++++++++++++++++++++++++++++++++++++
 sys/modules/usb/Makefile            |   1 +
 sys/modules/usb/i2ctinyusb/Makefile |  35 +++++
 6 files changed, 425 insertions(+)

diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index b668cccf50ae..020b009893d5 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -207,6 +207,7 @@ MAN=	aac.4 \
 	${_hv_vss.4} \
 	hwpmc.4 \
 	${_hwpstate_intel.4} \
+	i2ctinyusb.4 \
 	iavf.4 \
 	ichsmb.4 \
 	${_ichwd.4} \
diff --git a/share/man/man4/i2ctinyusb.4 b/share/man/man4/i2ctinyusb.4
new file mode 100644
index 000000000000..78169a0506ff
--- /dev/null
+++ b/share/man/man4/i2ctinyusb.4
@@ -0,0 +1,85 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2024 Denis Bodor <dbodor@rollmops.ninja>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd February 18, 2024
+.Dt I2CTINYUSB 4
+.Os
+.Sh NAME
+.Nm i2ctinyusb
+.Nd driver for a USB / I2C bridge device
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device i2ctinyusb"
+.Cd "device usb"
+.Cd "device iicbus"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+i2ctinyusb_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the device designed by Till Harbaum known
+as i2c-tiny-usb. This is initially a very simple circuit built with
+an Atmel AVR ATtiny45, but a Raspberry Pi Pico (RP2040) implementation
+also exists.
+.Pp
+The
+.Nm
+driver creates a
+.Xr iicbus 4
+child bus to expose the iic functions, enabling I2C sensors, converters
+and displays to be connected to any computer with a USB port.
+.Pp
+More information about this device can be found at:
+.Bd -literal -offset indent
+https://github.com/harbaum/I2C-Tiny-USB
+.Ed
+.Pp
+and (for the Raspberry Pi Pico version):
+.Bd -literal -offset indent
+https://github.com/Nicolai-Electronics/rp2040-i2c-interface
+.Ed
+.Pp
+The I2C controller supports read and write transactions with up to 1024
+bytes of data, and a write followed by the repeated start followed by a
+read transactions up to 1024 bytes.
+Zero length transfers are not supported.
+.Sh SEE ALSO
+.Xr iicbus 4 ,
+.Xr usb 4
+.Sh HISTORY
+The
+.Nm
+driver and this manual page was written by
+.An Denis Bodor Aq Mt dbodor@rollmops.ninja .
diff --git a/sys/conf/files b/sys/conf/files
index 941fa6a5a48b..27ef78e51001 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3355,6 +3355,7 @@ dev/usb/misc/cp2112.c		optional cp2112
 dev/usb/misc/udbp.c		optional udbp
 dev/usb/misc/ugold.c		optional ugold
 dev/usb/misc/uled.c		optional uled
+dev/usb/misc/i2ctinyusb.c	optional i2ctinyusb
 #
 # USB input drivers
 #
diff --git a/sys/dev/usb/misc/i2ctinyusb.c b/sys/dev/usb/misc/i2ctinyusb.c
new file mode 100644
index 000000000000..cae20880e441
--- /dev/null
+++ b/sys/dev/usb/misc/i2ctinyusb.c
@@ -0,0 +1,302 @@
+/*-
+ * Copyright (c) 2024 Denis Bodor <dbodor@rollmops.ninja>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * i2c-tiny-usb, DIY USB to IIC bridge (using AVR or RP2040) from
+ * Till Harbaum & Nicolai Electronics
+ * See :
+ *   https://github.com/harbaum/I2C-Tiny-USB
+ * and
+ *   https://github.com/Nicolai-Electronics/rp2040-i2c-interface
+ */
+
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/unistd.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usb_device.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include "iicbus_if.h"
+
+// commands via USB, must match command ids in the firmware
+#define CMD_ECHO		0
+#define CMD_GET_FUNC		1
+#define CMD_SET_DELAY		2
+#define CMD_GET_STATUS		3
+#define CMD_I2C_IO		4
+#define CMD_SET_LED		8
+#define CMD_I2C_IO_BEGIN	(1 << 0)
+#define CMD_I2C_IO_END		(1 << 1)
+#define STATUS_IDLE		0
+#define STATUS_ADDRESS_ACK	1
+#define STATUS_ADDRESS_NAK	2
+
+struct i2ctinyusb_softc {
+	struct usb_device	*sc_udev;
+	device_t		sc_iic_dev;
+	device_t		iicbus_dev;
+	struct mtx		sc_mtx;
+};
+
+#define USB_VENDOR_EZPROTOTYPES	0x1c40
+#define USB_VENDOR_FTDI		0x0403
+
+static const STRUCT_USB_HOST_ID i2ctinyusb_devs[] = {
+	{ USB_VPI(USB_VENDOR_EZPROTOTYPES, 0x0534, 0) },
+	{ USB_VPI(USB_VENDOR_FTDI, 0xc631, 0) },
+};
+
+/* Prototypes. */
+static int i2ctinyusb_probe(device_t dev);
+static int i2ctinyusb_attach(device_t dev);
+static int i2ctinyusb_detach(device_t dev);
+static int i2ctinyusb_transfer(device_t dev, struct iic_msg *msgs,
+		uint32_t nmsgs);
+static int i2ctinyusb_reset(device_t dev, u_char speed, u_char addr,
+		u_char *oldaddr);
+
+static int
+usb_read(struct i2ctinyusb_softc *sc, int cmd, int value, int index,
+		void *data, int len)
+{
+	int error;
+	struct usb_device_request req;
+	uint16_t actlen;
+
+	req.bmRequestType = UT_READ_VENDOR_INTERFACE;
+	req.bRequest = cmd;
+	USETW(req.wValue, value);
+	USETW(req.wIndex, (index >> 1));
+	USETW(req.wLength, len);
+
+	error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, data, 0,
+			&actlen, 2000);
+
+	if (error)
+		actlen = -1;
+
+	return (actlen);
+}
+
+static int
+usb_write(struct i2ctinyusb_softc *sc, int cmd, int value, int index,
+		void *data, int len)
+{
+	int error;
+	struct usb_device_request req;
+	uint16_t actlen;
+
+	req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
+	req.bRequest = cmd;
+	USETW(req.wValue, value);
+	USETW(req.wIndex, (index >> 1));
+	USETW(req.wLength, len);
+
+	error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, data, 0,
+			&actlen, 2000);
+
+	if (error) {
+		actlen = -1;
+	}
+
+	return (actlen);
+}
+
+static int
+i2ctinyusb_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa;
+
+	uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST)
+		return (ENXIO);
+
+	if (usbd_lookup_id_by_uaa(i2ctinyusb_devs, sizeof(i2ctinyusb_devs),
+				uaa) == 0) {
+		device_set_desc(dev, "I2C-Tiny-USB I2C interface");
+		return (BUS_PROBE_DEFAULT);
+	}
+
+	return (ENXIO);
+}
+
+static int
+i2ctinyusb_attach(device_t dev)
+{
+	struct i2ctinyusb_softc *sc;
+	struct usb_attach_arg *uaa;
+	int err;
+
+	sc = device_get_softc(dev);
+
+	uaa = device_get_ivars(dev);
+	device_set_usb_desc(dev);
+
+	sc->sc_udev = uaa->device;
+	mtx_init(&sc->sc_mtx, "i2ctinyusb lock", NULL, MTX_DEF | MTX_RECURSE);
+
+	sc->iicbus_dev = device_add_child(dev, "iicbus", -1);
+	if (sc->iicbus_dev == NULL) {
+		device_printf(dev, "iicbus creation failed\n");
+		err = ENXIO;
+		goto detach;
+	}
+	err = bus_generic_attach(dev);
+
+	return (0);
+
+detach:
+	i2ctinyusb_detach(dev);
+	return (err);
+}
+
+static int
+i2ctinyusb_detach(device_t dev)
+{
+	struct i2ctinyusb_softc *sc;
+	int err;
+
+	sc = device_get_softc(dev);
+
+	err = bus_generic_detach(dev);
+	if (err != 0)
+		return (err);
+	device_delete_children(dev);
+
+	mtx_destroy(&sc->sc_mtx);
+
+	return (0);
+}
+
+static int
+i2ctinyusb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
+{
+	struct i2ctinyusb_softc *sc;
+	uint32_t i;
+	int ret = 0;
+	int cmd = CMD_I2C_IO;
+	struct iic_msg *pmsg;
+	unsigned char pstatus;
+
+	sc = device_get_softc(dev);
+
+	mtx_lock(&sc->sc_mtx);
+
+	for (i = 0; i < nmsgs; i++) {
+		pmsg = &msgs[i];
+		if (i == 0)
+			cmd |= CMD_I2C_IO_BEGIN;
+		if (i == nmsgs - 1)
+			cmd |= CMD_I2C_IO_END;
+
+		if ((msgs[i].flags & IIC_M_RD) != 0) {
+			if ((ret = usb_read(sc, cmd, pmsg->flags, pmsg->slave, pmsg->buf,
+							pmsg->len)) != pmsg->len) {
+				printf("Read error: got %u\n", ret);
+				ret = EIO;
+				goto out;
+			}
+		} else {
+			if ((ret = usb_write(sc, cmd, pmsg->flags, pmsg->slave, pmsg->buf,
+							pmsg->len)) != pmsg->len) {
+				printf("Write error: got %u\n", ret);
+				ret = EIO;
+				goto out;
+			}
+
+		}
+		// check status
+		if ((ret = usb_read(sc, CMD_GET_STATUS, 0, 0, &pstatus, 1)) != 1) {
+			ret = EIO;
+			goto out;
+		}
+
+		if (pstatus == STATUS_ADDRESS_NAK) {
+			ret = EIO;
+			goto out;
+		}
+	}
+
+	ret = 0;
+
+out:
+	mtx_unlock(&sc->sc_mtx);
+	return (ret);
+}
+
+static int
+i2ctinyusb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
+{
+	struct i2ctinyusb_softc *sc;
+	int ret;
+
+	sc = device_get_softc(dev);
+
+	mtx_lock(&sc->sc_mtx);
+	ret = usb_write(sc, CMD_SET_DELAY, 10, 0, NULL, 0);
+	mtx_unlock(&sc->sc_mtx);
+
+	if (ret < 0)
+		printf("i2ctinyusb_reset error!\n");
+
+	return (0);
+}
+
+static device_method_t i2ctinyusb_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe, i2ctinyusb_probe),
+	DEVMETHOD(device_attach, i2ctinyusb_attach),
+	DEVMETHOD(device_detach, i2ctinyusb_detach),
+
+	/* I2C methods */
+	DEVMETHOD(iicbus_transfer, i2ctinyusb_transfer),
+	DEVMETHOD(iicbus_reset, i2ctinyusb_reset),
+	DEVMETHOD(iicbus_callback, iicbus_null_callback),
+
+	DEVMETHOD_END
+};
+
+static driver_t i2ctinyusb_driver = {
+	.name = "iichb",
+	.methods = i2ctinyusb_methods,
+	.size = sizeof(struct i2ctinyusb_softc),
+};
+
+DRIVER_MODULE(i2ctinyusb, uhub, i2ctinyusb_driver, NULL, NULL);
+MODULE_DEPEND(i2ctinyusb, usb, 1, 1, 1);
+MODULE_DEPEND(i2ctinyusb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
+MODULE_VERSION(i2ctinyusb, 1);
+
+/* vi: set ts=8 sw=8: */
diff --git a/sys/modules/usb/Makefile b/sys/modules/usb/Makefile
index 3a81c7fd44f3..2d166a95dc03 100644
--- a/sys/modules/usb/Makefile
+++ b/sys/modules/usb/Makefile
@@ -50,6 +50,7 @@ SUBDIR += atp cfumass uhid uhid_snes ukbd ums udbp uep wmt wsp ugold uled \
 	  usbhid
 SUBDIR += ucom u3g uark ubsa ubser uchcom ucycom ufoma uftdi ugensa uipaq ulpt \
 	  umct umcs umodem umoscom uplcom uslcom uvisor uvscom
+SUBDIR += i2ctinyusb
 SUBDIR += cp2112
 SUBDIR += udl
 SUBDIR += uether aue axe axge cdce cdceem cue ${_kue} mos rue smsc udav uhso \
diff --git a/sys/modules/usb/i2ctinyusb/Makefile b/sys/modules/usb/i2ctinyusb/Makefile
new file mode 100644
index 000000000000..9cb37843935c
--- /dev/null
+++ b/sys/modules/usb/i2ctinyusb/Makefile
@@ -0,0 +1,35 @@
+#
+#
+# Copyright (c) Denis Bodor <dbodor@rollmops.ninja>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+S=     ${SRCTOP}/sys
+
+.PATH: $S/dev/usb/misc
+
+KMOD=	i2ctinyusb
+SRCS=	i2ctinyusb.c
+SRCS+=	i2ctinyusb.c device_if.h bus_if.h opt_usb.h usbdevs.h iicbus_if.h
+
+.include <bsd.kmod.mk>