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