xref: /freebsd/sys/dev/usb/misc/i2ctinyusb.c (revision 54e231b373ef617c348706c6c64a2e049ea738ec)
1*54e231b3SDenis Bodor /*-
2*54e231b3SDenis Bodor  * Copyright (c) 2024 Denis Bodor <dbodor@rollmops.ninja>
3*54e231b3SDenis Bodor  * All rights reserved.
4*54e231b3SDenis Bodor  *
5*54e231b3SDenis Bodor  * Redistribution and use in source and binary forms, with or without
6*54e231b3SDenis Bodor  * modification, are permitted provided that the following conditions
7*54e231b3SDenis Bodor  * are met:
8*54e231b3SDenis Bodor  * 1. Redistributions of source code must retain the above copyright
9*54e231b3SDenis Bodor  *    notice, this list of conditions and the following disclaimer.
10*54e231b3SDenis Bodor  * 2. Redistributions in binary form must reproduce the above copyright
11*54e231b3SDenis Bodor  *    notice, this list of conditions and the following disclaimer in the
12*54e231b3SDenis Bodor  *    documentation and/or other materials provided with the distribution.
13*54e231b3SDenis Bodor  *
14*54e231b3SDenis Bodor  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15*54e231b3SDenis Bodor  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16*54e231b3SDenis Bodor  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17*54e231b3SDenis Bodor  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18*54e231b3SDenis Bodor  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19*54e231b3SDenis Bodor  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20*54e231b3SDenis Bodor  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21*54e231b3SDenis Bodor  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22*54e231b3SDenis Bodor  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*54e231b3SDenis Bodor  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*54e231b3SDenis Bodor  * SUCH DAMAGE.
25*54e231b3SDenis Bodor  */
26*54e231b3SDenis Bodor 
27*54e231b3SDenis Bodor /*
28*54e231b3SDenis Bodor  * i2c-tiny-usb, DIY USB to IIC bridge (using AVR or RP2040) from
29*54e231b3SDenis Bodor  * Till Harbaum & Nicolai Electronics
30*54e231b3SDenis Bodor  * See :
31*54e231b3SDenis Bodor  *   https://github.com/harbaum/I2C-Tiny-USB
32*54e231b3SDenis Bodor  * and
33*54e231b3SDenis Bodor  *   https://github.com/Nicolai-Electronics/rp2040-i2c-interface
34*54e231b3SDenis Bodor  */
35*54e231b3SDenis Bodor 
36*54e231b3SDenis Bodor #include <sys/systm.h>
37*54e231b3SDenis Bodor #include <sys/kernel.h>
38*54e231b3SDenis Bodor #include <sys/bus.h>
39*54e231b3SDenis Bodor #include <sys/module.h>
40*54e231b3SDenis Bodor #include <sys/mutex.h>
41*54e231b3SDenis Bodor #include <sys/condvar.h>
42*54e231b3SDenis Bodor #include <sys/unistd.h>
43*54e231b3SDenis Bodor #include <dev/usb/usb.h>
44*54e231b3SDenis Bodor #include <dev/usb/usbdi.h>
45*54e231b3SDenis Bodor #include <dev/usb/usbhid.h>
46*54e231b3SDenis Bodor #include <dev/usb/usb_device.h>
47*54e231b3SDenis Bodor 
48*54e231b3SDenis Bodor #include <dev/iicbus/iiconf.h>
49*54e231b3SDenis Bodor #include <dev/iicbus/iicbus.h>
50*54e231b3SDenis Bodor #include "iicbus_if.h"
51*54e231b3SDenis Bodor 
52*54e231b3SDenis Bodor // commands via USB, must match command ids in the firmware
53*54e231b3SDenis Bodor #define CMD_ECHO		0
54*54e231b3SDenis Bodor #define CMD_GET_FUNC		1
55*54e231b3SDenis Bodor #define CMD_SET_DELAY		2
56*54e231b3SDenis Bodor #define CMD_GET_STATUS		3
57*54e231b3SDenis Bodor #define CMD_I2C_IO		4
58*54e231b3SDenis Bodor #define CMD_SET_LED		8
59*54e231b3SDenis Bodor #define CMD_I2C_IO_BEGIN	(1 << 0)
60*54e231b3SDenis Bodor #define CMD_I2C_IO_END		(1 << 1)
61*54e231b3SDenis Bodor #define STATUS_IDLE		0
62*54e231b3SDenis Bodor #define STATUS_ADDRESS_ACK	1
63*54e231b3SDenis Bodor #define STATUS_ADDRESS_NAK	2
64*54e231b3SDenis Bodor 
65*54e231b3SDenis Bodor struct i2ctinyusb_softc {
66*54e231b3SDenis Bodor 	struct usb_device	*sc_udev;
67*54e231b3SDenis Bodor 	device_t		sc_iic_dev;
68*54e231b3SDenis Bodor 	device_t		iicbus_dev;
69*54e231b3SDenis Bodor 	struct mtx		sc_mtx;
70*54e231b3SDenis Bodor };
71*54e231b3SDenis Bodor 
72*54e231b3SDenis Bodor #define USB_VENDOR_EZPROTOTYPES	0x1c40
73*54e231b3SDenis Bodor #define USB_VENDOR_FTDI		0x0403
74*54e231b3SDenis Bodor 
75*54e231b3SDenis Bodor static const STRUCT_USB_HOST_ID i2ctinyusb_devs[] = {
76*54e231b3SDenis Bodor 	{ USB_VPI(USB_VENDOR_EZPROTOTYPES, 0x0534, 0) },
77*54e231b3SDenis Bodor 	{ USB_VPI(USB_VENDOR_FTDI, 0xc631, 0) },
78*54e231b3SDenis Bodor };
79*54e231b3SDenis Bodor 
80*54e231b3SDenis Bodor /* Prototypes. */
81*54e231b3SDenis Bodor static int i2ctinyusb_probe(device_t dev);
82*54e231b3SDenis Bodor static int i2ctinyusb_attach(device_t dev);
83*54e231b3SDenis Bodor static int i2ctinyusb_detach(device_t dev);
84*54e231b3SDenis Bodor static int i2ctinyusb_transfer(device_t dev, struct iic_msg *msgs,
85*54e231b3SDenis Bodor 		uint32_t nmsgs);
86*54e231b3SDenis Bodor static int i2ctinyusb_reset(device_t dev, u_char speed, u_char addr,
87*54e231b3SDenis Bodor 		u_char *oldaddr);
88*54e231b3SDenis Bodor 
89*54e231b3SDenis Bodor static int
90*54e231b3SDenis Bodor usb_read(struct i2ctinyusb_softc *sc, int cmd, int value, int index,
91*54e231b3SDenis Bodor 		void *data, int len)
92*54e231b3SDenis Bodor {
93*54e231b3SDenis Bodor 	int error;
94*54e231b3SDenis Bodor 	struct usb_device_request req;
95*54e231b3SDenis Bodor 	uint16_t actlen;
96*54e231b3SDenis Bodor 
97*54e231b3SDenis Bodor 	req.bmRequestType = UT_READ_VENDOR_INTERFACE;
98*54e231b3SDenis Bodor 	req.bRequest = cmd;
99*54e231b3SDenis Bodor 	USETW(req.wValue, value);
100*54e231b3SDenis Bodor 	USETW(req.wIndex, (index >> 1));
101*54e231b3SDenis Bodor 	USETW(req.wLength, len);
102*54e231b3SDenis Bodor 
103*54e231b3SDenis Bodor 	error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, data, 0,
104*54e231b3SDenis Bodor 			&actlen, 2000);
105*54e231b3SDenis Bodor 
106*54e231b3SDenis Bodor 	if (error)
107*54e231b3SDenis Bodor 		actlen = -1;
108*54e231b3SDenis Bodor 
109*54e231b3SDenis Bodor 	return (actlen);
110*54e231b3SDenis Bodor }
111*54e231b3SDenis Bodor 
112*54e231b3SDenis Bodor static int
113*54e231b3SDenis Bodor usb_write(struct i2ctinyusb_softc *sc, int cmd, int value, int index,
114*54e231b3SDenis Bodor 		void *data, int len)
115*54e231b3SDenis Bodor {
116*54e231b3SDenis Bodor 	int error;
117*54e231b3SDenis Bodor 	struct usb_device_request req;
118*54e231b3SDenis Bodor 	uint16_t actlen;
119*54e231b3SDenis Bodor 
120*54e231b3SDenis Bodor 	req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
121*54e231b3SDenis Bodor 	req.bRequest = cmd;
122*54e231b3SDenis Bodor 	USETW(req.wValue, value);
123*54e231b3SDenis Bodor 	USETW(req.wIndex, (index >> 1));
124*54e231b3SDenis Bodor 	USETW(req.wLength, len);
125*54e231b3SDenis Bodor 
126*54e231b3SDenis Bodor 	error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, data, 0,
127*54e231b3SDenis Bodor 			&actlen, 2000);
128*54e231b3SDenis Bodor 
129*54e231b3SDenis Bodor 	if (error) {
130*54e231b3SDenis Bodor 		actlen = -1;
131*54e231b3SDenis Bodor 	}
132*54e231b3SDenis Bodor 
133*54e231b3SDenis Bodor 	return (actlen);
134*54e231b3SDenis Bodor }
135*54e231b3SDenis Bodor 
136*54e231b3SDenis Bodor static int
137*54e231b3SDenis Bodor i2ctinyusb_probe(device_t dev)
138*54e231b3SDenis Bodor {
139*54e231b3SDenis Bodor 	struct usb_attach_arg *uaa;
140*54e231b3SDenis Bodor 
141*54e231b3SDenis Bodor 	uaa = device_get_ivars(dev);
142*54e231b3SDenis Bodor 
143*54e231b3SDenis Bodor 	if (uaa->usb_mode != USB_MODE_HOST)
144*54e231b3SDenis Bodor 		return (ENXIO);
145*54e231b3SDenis Bodor 
146*54e231b3SDenis Bodor 	if (usbd_lookup_id_by_uaa(i2ctinyusb_devs, sizeof(i2ctinyusb_devs),
147*54e231b3SDenis Bodor 				uaa) == 0) {
148*54e231b3SDenis Bodor 		device_set_desc(dev, "I2C-Tiny-USB I2C interface");
149*54e231b3SDenis Bodor 		return (BUS_PROBE_DEFAULT);
150*54e231b3SDenis Bodor 	}
151*54e231b3SDenis Bodor 
152*54e231b3SDenis Bodor 	return (ENXIO);
153*54e231b3SDenis Bodor }
154*54e231b3SDenis Bodor 
155*54e231b3SDenis Bodor static int
156*54e231b3SDenis Bodor i2ctinyusb_attach(device_t dev)
157*54e231b3SDenis Bodor {
158*54e231b3SDenis Bodor 	struct i2ctinyusb_softc *sc;
159*54e231b3SDenis Bodor 	struct usb_attach_arg *uaa;
160*54e231b3SDenis Bodor 	int err;
161*54e231b3SDenis Bodor 
162*54e231b3SDenis Bodor 	sc = device_get_softc(dev);
163*54e231b3SDenis Bodor 
164*54e231b3SDenis Bodor 	uaa = device_get_ivars(dev);
165*54e231b3SDenis Bodor 	device_set_usb_desc(dev);
166*54e231b3SDenis Bodor 
167*54e231b3SDenis Bodor 	sc->sc_udev = uaa->device;
168*54e231b3SDenis Bodor 	mtx_init(&sc->sc_mtx, "i2ctinyusb lock", NULL, MTX_DEF | MTX_RECURSE);
169*54e231b3SDenis Bodor 
170*54e231b3SDenis Bodor 	sc->iicbus_dev = device_add_child(dev, "iicbus", -1);
171*54e231b3SDenis Bodor 	if (sc->iicbus_dev == NULL) {
172*54e231b3SDenis Bodor 		device_printf(dev, "iicbus creation failed\n");
173*54e231b3SDenis Bodor 		err = ENXIO;
174*54e231b3SDenis Bodor 		goto detach;
175*54e231b3SDenis Bodor 	}
176*54e231b3SDenis Bodor 	err = bus_generic_attach(dev);
177*54e231b3SDenis Bodor 
178*54e231b3SDenis Bodor 	return (0);
179*54e231b3SDenis Bodor 
180*54e231b3SDenis Bodor detach:
181*54e231b3SDenis Bodor 	i2ctinyusb_detach(dev);
182*54e231b3SDenis Bodor 	return (err);
183*54e231b3SDenis Bodor }
184*54e231b3SDenis Bodor 
185*54e231b3SDenis Bodor static int
186*54e231b3SDenis Bodor i2ctinyusb_detach(device_t dev)
187*54e231b3SDenis Bodor {
188*54e231b3SDenis Bodor 	struct i2ctinyusb_softc *sc;
189*54e231b3SDenis Bodor 	int err;
190*54e231b3SDenis Bodor 
191*54e231b3SDenis Bodor 	sc = device_get_softc(dev);
192*54e231b3SDenis Bodor 
193*54e231b3SDenis Bodor 	err = bus_generic_detach(dev);
194*54e231b3SDenis Bodor 	if (err != 0)
195*54e231b3SDenis Bodor 		return (err);
196*54e231b3SDenis Bodor 	device_delete_children(dev);
197*54e231b3SDenis Bodor 
198*54e231b3SDenis Bodor 	mtx_destroy(&sc->sc_mtx);
199*54e231b3SDenis Bodor 
200*54e231b3SDenis Bodor 	return (0);
201*54e231b3SDenis Bodor }
202*54e231b3SDenis Bodor 
203*54e231b3SDenis Bodor static int
204*54e231b3SDenis Bodor i2ctinyusb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
205*54e231b3SDenis Bodor {
206*54e231b3SDenis Bodor 	struct i2ctinyusb_softc *sc;
207*54e231b3SDenis Bodor 	uint32_t i;
208*54e231b3SDenis Bodor 	int ret = 0;
209*54e231b3SDenis Bodor 	int cmd = CMD_I2C_IO;
210*54e231b3SDenis Bodor 	struct iic_msg *pmsg;
211*54e231b3SDenis Bodor 	unsigned char pstatus;
212*54e231b3SDenis Bodor 
213*54e231b3SDenis Bodor 	sc = device_get_softc(dev);
214*54e231b3SDenis Bodor 
215*54e231b3SDenis Bodor 	mtx_lock(&sc->sc_mtx);
216*54e231b3SDenis Bodor 
217*54e231b3SDenis Bodor 	for (i = 0; i < nmsgs; i++) {
218*54e231b3SDenis Bodor 		pmsg = &msgs[i];
219*54e231b3SDenis Bodor 		if (i == 0)
220*54e231b3SDenis Bodor 			cmd |= CMD_I2C_IO_BEGIN;
221*54e231b3SDenis Bodor 		if (i == nmsgs - 1)
222*54e231b3SDenis Bodor 			cmd |= CMD_I2C_IO_END;
223*54e231b3SDenis Bodor 
224*54e231b3SDenis Bodor 		if ((msgs[i].flags & IIC_M_RD) != 0) {
225*54e231b3SDenis Bodor 			if ((ret = usb_read(sc, cmd, pmsg->flags, pmsg->slave, pmsg->buf,
226*54e231b3SDenis Bodor 							pmsg->len)) != pmsg->len) {
227*54e231b3SDenis Bodor 				printf("Read error: got %u\n", ret);
228*54e231b3SDenis Bodor 				ret = EIO;
229*54e231b3SDenis Bodor 				goto out;
230*54e231b3SDenis Bodor 			}
231*54e231b3SDenis Bodor 		} else {
232*54e231b3SDenis Bodor 			if ((ret = usb_write(sc, cmd, pmsg->flags, pmsg->slave, pmsg->buf,
233*54e231b3SDenis Bodor 							pmsg->len)) != pmsg->len) {
234*54e231b3SDenis Bodor 				printf("Write error: got %u\n", ret);
235*54e231b3SDenis Bodor 				ret = EIO;
236*54e231b3SDenis Bodor 				goto out;
237*54e231b3SDenis Bodor 			}
238*54e231b3SDenis Bodor 
239*54e231b3SDenis Bodor 		}
240*54e231b3SDenis Bodor 		// check status
241*54e231b3SDenis Bodor 		if ((ret = usb_read(sc, CMD_GET_STATUS, 0, 0, &pstatus, 1)) != 1) {
242*54e231b3SDenis Bodor 			ret = EIO;
243*54e231b3SDenis Bodor 			goto out;
244*54e231b3SDenis Bodor 		}
245*54e231b3SDenis Bodor 
246*54e231b3SDenis Bodor 		if (pstatus == STATUS_ADDRESS_NAK) {
247*54e231b3SDenis Bodor 			ret = EIO;
248*54e231b3SDenis Bodor 			goto out;
249*54e231b3SDenis Bodor 		}
250*54e231b3SDenis Bodor 	}
251*54e231b3SDenis Bodor 
252*54e231b3SDenis Bodor 	ret = 0;
253*54e231b3SDenis Bodor 
254*54e231b3SDenis Bodor out:
255*54e231b3SDenis Bodor 	mtx_unlock(&sc->sc_mtx);
256*54e231b3SDenis Bodor 	return (ret);
257*54e231b3SDenis Bodor }
258*54e231b3SDenis Bodor 
259*54e231b3SDenis Bodor static int
260*54e231b3SDenis Bodor i2ctinyusb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
261*54e231b3SDenis Bodor {
262*54e231b3SDenis Bodor 	struct i2ctinyusb_softc *sc;
263*54e231b3SDenis Bodor 	int ret;
264*54e231b3SDenis Bodor 
265*54e231b3SDenis Bodor 	sc = device_get_softc(dev);
266*54e231b3SDenis Bodor 
267*54e231b3SDenis Bodor 	mtx_lock(&sc->sc_mtx);
268*54e231b3SDenis Bodor 	ret = usb_write(sc, CMD_SET_DELAY, 10, 0, NULL, 0);
269*54e231b3SDenis Bodor 	mtx_unlock(&sc->sc_mtx);
270*54e231b3SDenis Bodor 
271*54e231b3SDenis Bodor 	if (ret < 0)
272*54e231b3SDenis Bodor 		printf("i2ctinyusb_reset error!\n");
273*54e231b3SDenis Bodor 
274*54e231b3SDenis Bodor 	return (0);
275*54e231b3SDenis Bodor }
276*54e231b3SDenis Bodor 
277*54e231b3SDenis Bodor static device_method_t i2ctinyusb_methods[] = {
278*54e231b3SDenis Bodor 	/* Device interface */
279*54e231b3SDenis Bodor 	DEVMETHOD(device_probe, i2ctinyusb_probe),
280*54e231b3SDenis Bodor 	DEVMETHOD(device_attach, i2ctinyusb_attach),
281*54e231b3SDenis Bodor 	DEVMETHOD(device_detach, i2ctinyusb_detach),
282*54e231b3SDenis Bodor 
283*54e231b3SDenis Bodor 	/* I2C methods */
284*54e231b3SDenis Bodor 	DEVMETHOD(iicbus_transfer, i2ctinyusb_transfer),
285*54e231b3SDenis Bodor 	DEVMETHOD(iicbus_reset, i2ctinyusb_reset),
286*54e231b3SDenis Bodor 	DEVMETHOD(iicbus_callback, iicbus_null_callback),
287*54e231b3SDenis Bodor 
288*54e231b3SDenis Bodor 	DEVMETHOD_END
289*54e231b3SDenis Bodor };
290*54e231b3SDenis Bodor 
291*54e231b3SDenis Bodor static driver_t i2ctinyusb_driver = {
292*54e231b3SDenis Bodor 	.name = "iichb",
293*54e231b3SDenis Bodor 	.methods = i2ctinyusb_methods,
294*54e231b3SDenis Bodor 	.size = sizeof(struct i2ctinyusb_softc),
295*54e231b3SDenis Bodor };
296*54e231b3SDenis Bodor 
297*54e231b3SDenis Bodor DRIVER_MODULE(i2ctinyusb, uhub, i2ctinyusb_driver, NULL, NULL);
298*54e231b3SDenis Bodor MODULE_DEPEND(i2ctinyusb, usb, 1, 1, 1);
299*54e231b3SDenis Bodor MODULE_DEPEND(i2ctinyusb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
300*54e231b3SDenis Bodor MODULE_VERSION(i2ctinyusb, 1);
301*54e231b3SDenis Bodor 
302*54e231b3SDenis Bodor /* vi: set ts=8 sw=8: */
303