static int iicioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td) { device_t iicdev = IIC_DEVICE(minor(dev)); struct iic_softc *sc = IIC_SOFTC(minor(dev)); device_t parent = device_get_parent(iicdev); struct iiccmd *s = (struct iiccmd *)data; int error, count; if (!sc) return (EINVAL); if ((error = iicbus_request_bus(device_get_parent(iicdev), iicdev, (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR)))) return (error); switch (cmd) { case I2CSTART: error = iicbus_start(parent, s->slave, 0); /* * Implicitly set the chip addr to the slave addr passed as * parameter. Consequently, start/stop shall be called before * the read or the write of a block. */ if (!error) sc->sc_addr = s->slave; break; case I2CSTOP: error = iicbus_stop(parent); break; case I2CRSTCARD: error = iicbus_reset(parent, 0, 0, NULL); break; case I2CWRITE: error = iicbus_write(parent, s->buf, s->count, &count, 10); break; case I2CREAD: error = iicbus_read(parent, s->buf, s->count, &count, s->last, 10); break; default: error = ENODEV; } iicbus_release_bus(device_get_parent(iicdev), iicdev); return (error); }
/* * Generic version of iicbus_transfer that calls the appropriate * routines to accomplish this. See note above about acceptable * buffer addresses. */ int iicbus_transfer_gen(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { int i, error, lenread, lenwrote, nkid, rpstart, addr; device_t *children, bus; bool nostop; if ((error = device_get_children(dev, &children, &nkid)) != 0) return (IIC_ERESOURCE); if (nkid != 1) { free(children, M_TEMP); return (IIC_ENOTSUPP); } bus = children[0]; rpstart = 0; free(children, M_TEMP); nostop = iicbus_get_nostop(dev); for (i = 0, error = 0; i < nmsgs && error == 0; i++) { addr = msgs[i].slave; if (msgs[i].flags & IIC_M_RD) addr |= LSB; else addr &= ~LSB; if (!(msgs[i].flags & IIC_M_NOSTART)) { if (rpstart) error = iicbus_repeated_start(bus, addr, 0); else error = iicbus_start(bus, addr, 0); } if (error != 0) break; if (msgs[i].flags & IIC_M_RD) error = iicbus_read(bus, msgs[i].buf, msgs[i].len, &lenread, IIC_LAST_READ, 0); else error = iicbus_write(bus, msgs[i].buf, msgs[i].len, &lenwrote, 0); if (error != 0) break; if ((msgs[i].flags & IIC_M_NOSTOP) != 0 || (nostop && i + 1 < nmsgs)) { rpstart = 1; /* Next message gets repeated start */ } else { rpstart = 0; iicbus_stop(bus); } } if (error != 0 && !nostop) iicbus_stop(bus); return (error); }
static int iicuio(struct cdev *dev, struct uio *uio, int ioflag) { device_t parent; struct iic_cdevpriv *priv; int error; uint8_t addr; priv = NULL; error = devfs_get_cdevpriv((void**)&priv); if (error != 0) return (error); KASSERT(priv != NULL, ("iic cdevpriv should not be NULL!")); IIC_LOCK(priv); if (priv->started || (priv->addr == 0)) { IIC_UNLOCK(priv); return (ENXIO); } parent = device_get_parent(priv->sc->sc_dev); error = iicbus_request_bus(parent, priv->sc->sc_dev, (ioflag & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR)); if (error != 0) { IIC_UNLOCK(priv); return (error); } if (uio->uio_rw == UIO_READ) addr = priv->addr | LSB; else addr = priv->addr & ~LSB; error = iicbus_start(parent, addr, 0); if (error != 0) { iicbus_release_bus(parent, priv->sc->sc_dev); IIC_UNLOCK(priv); return (error); } error = iicuio_move(priv, uio, IIC_LAST_READ); iicbus_stop(parent); iicbus_release_bus(parent, priv->sc->sc_dev); IIC_UNLOCK(priv); return (error); }
/* * iicbus_block_read() * * Read a block of data from slave ; start/stop protocol managed */ int iicbus_block_read(device_t bus, u_char slave, char *buf, int len, int *read) { u_char addr = slave | LSB; int error; if ((error = iicbus_start(bus, addr, 0))) return (error); error = iicbus_read(bus, buf, len, read, IIC_LAST_READ, 0); iicbus_stop(bus); return (error); }
/* * iicbus_block_write() * * Write a block of data to slave ; start/stop protocol managed */ int iicbus_block_write(device_t bus, u_char slave, char *buf, int len, int *sent) { u_char addr = slave & ~LSB; int error; if ((error = iicbus_start(bus, addr, 0))) return (error); error = iicbus_write(bus, buf, len, sent, 0); iicbus_stop(bus); return (error); }
static int iic_probe_device(device_t dev, u_char addr) { int count; char byte; if ((addr & 1) == 0) { /* is device writable? */ if (!iicbus_start(dev, (u_char)addr, 0)) { iicbus_stop(dev); return (1); } } else { /* is device readable? */ if (!iicbus_block_read(dev, (u_char)addr, &byte, 1, &count)) return (1); } return (0); }
static int iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { device_t parent, iicdev; struct iiccmd *s; struct uio ubuf; struct iovec uvec; struct iic_cdevpriv *priv; int error; s = (struct iiccmd *)data; error = devfs_get_cdevpriv((void**)&priv); if (error != 0) return (error); KASSERT(priv != NULL, ("iic cdevpriv should not be NULL!")); iicdev = priv->sc->sc_dev; parent = device_get_parent(iicdev); IIC_LOCK(priv); switch (cmd) { case I2CSTART: if (priv->started) { error = EINVAL; break; } error = iicbus_request_bus(parent, iicdev, (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR)); if (error == 0) error = iicbus_start(parent, s->slave, 0); if (error == 0) { priv->addr = s->slave; priv->started = true; } else iicbus_release_bus(parent, iicdev); break; case I2CSTOP: if (priv->started) { error = iicbus_stop(parent); iicbus_release_bus(parent, iicdev); priv->started = false; } break; case I2CRSTCARD: /* * Bus should be owned before we reset it. * We allow the bus to be already owned as the result of an in-progress * sequence; however, bus reset will always be followed by release * (a new start is presumably needed for I/O anyway). */ if (!priv->started) error = iicbus_request_bus(parent, iicdev, (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR)); if (error == 0) { error = iicbus_reset(parent, IIC_UNKNOWN, 0, NULL); /* * Ignore IIC_ENOADDR as it only means we have a master-only * controller. */ if (error == IIC_ENOADDR) error = 0; iicbus_release_bus(parent, iicdev); priv->started = false; } break; case I2CWRITE: if (!priv->started) { error = EINVAL; break; } uvec.iov_base = s->buf; uvec.iov_len = s->count; ubuf.uio_iov = &uvec; ubuf.uio_iovcnt = 1; ubuf.uio_segflg = UIO_USERSPACE; ubuf.uio_td = td; ubuf.uio_resid = s->count; ubuf.uio_offset = 0; ubuf.uio_rw = UIO_WRITE; error = iicuio_move(priv, &ubuf, 0); break; case I2CREAD: if (!priv->started) { error = EINVAL; break; } uvec.iov_base = s->buf; uvec.iov_len = s->count; ubuf.uio_iov = &uvec; ubuf.uio_iovcnt = 1; ubuf.uio_segflg = UIO_USERSPACE; ubuf.uio_td = td; ubuf.uio_resid = s->count; ubuf.uio_offset = 0; ubuf.uio_rw = UIO_READ; error = iicuio_move(priv, &ubuf, s->last); break; case I2CRDWR: /* * The rdwr list should be a self-contained set of * transactions. Fail if another transaction is in progress. */ if (priv->started) { error = EINVAL; break; } error = iicrdwr(priv, (struct iic_rdwr_data *)data, flags); break; case I2CRPTSTART: if (!priv->started) { error = EINVAL; break; } error = iicbus_repeated_start(parent, s->slave, 0); break; case I2CSADDR: priv->addr = *((uint8_t*)data); break; default: error = ENOTTY; } IIC_UNLOCK(priv); return (error); }