Example #1
0
/*
 * 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);
}
Example #2
0
File: iic.c Project: MarginC/kame
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);
}
Example #3
0
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);
}
Example #4
0
/*
 * 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);
}
Example #5
0
/*
 * 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);
}
Example #6
0
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);
}
Example #7
0
static void
iicdtor(void *data)
{
	device_t iicdev, parent;
	struct iic_cdevpriv *priv;

	priv = data;
	KASSERT(priv != NULL, ("iic cdevpriv should not be NULL!"));

	iicdev = priv->sc->sc_dev;
	parent = device_get_parent(iicdev);

	if (priv->started) {
		iicbus_stop(parent);
		iicbus_reset(parent, IIC_UNKNOWN, 0, NULL);
		iicbus_release_bus(parent, iicdev);
	}

	sx_destroy(&priv->lock);
	free(priv, M_IIC);
}
Example #8
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);
}