All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pi4j.io.i2c.impl.I2CBusImpl Maven / Gradle / Ivy

package com.pi4j.io.i2c.impl;

/*
 * #%L
 * **********************************************************************
ORGANIZATION  :  Pi4J
PROJECT       :  Pi4J :: Java Library (Core)
FILENAME      :  I2CBusImpl.java

This file is part of the Pi4J project. More information about
this project can be found here:  http://www.pi4j.com/
**********************************************************************
 * %%
 * Copyright (C) 2012 - 2018 Pi4J
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.pi4j.io.file.LinuxFile;
import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CConstants;
import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory;

/**
 * This is implementation of i2c bus. This class keeps underlying linux file descriptor of particular bus. As all reads and writes from/to i2c bus are blocked I/Os current implementation uses only one file per bus for all devices. Device
 * implementations use this class file handle.
 *
 * Hint: For concurrency-locking the methods lock() and unlock() are provided. This requires that there is exactly one I2CBus-instance per bus-number what is guaranteed by the I2CFactory class. The locking is done by I2CDeviceImpl by using
 * those methods. The reason for this is to enable other locking-strategies than the simple "lock before and release after access"-strategy.
 *
 * @author Daniel Sendula, refactored by RasPelikan
 *
 */
public class I2CBusImpl implements I2CBus {

    private static final Logger logger = Logger.getLogger(I2CBusImpl.class.getCanonicalName());

    /** File handle for this i2c bus */
    protected LinuxFile file = null;

    protected int lastAddress = -1;

    /** File name of this i2c bus */
    protected String filename;

    /** Used to identifiy the i2c bus within Pi4J **/
    protected int busNumber;

    protected long lockAquireTimeout;

    protected TimeUnit lockAquireTimeoutUnit;

    private final ReentrantLock accessLock = new ReentrantLock(true);

    /**
     * Constructor of i2c bus implementation.
     *
     * @param busNumber used to identifiy the i2c bus within Pi4J

     * @throws IOException thrown in case that file cannot be opened
     */
    protected I2CBusImpl(final int busNumber, final String fileName, final long lockAquireTimeout, final TimeUnit lockAquireTimeoutUnit) {
        this.filename = fileName;
        this.busNumber = busNumber;

        if (lockAquireTimeout < 0) {
            this.lockAquireTimeout = I2CFactory.DEFAULT_LOCKAQUIRE_TIMEOUT;
        } else {
            this.lockAquireTimeout = lockAquireTimeout;
        }

        if (lockAquireTimeoutUnit == null) {
            this.lockAquireTimeoutUnit = I2CFactory.DEFAULT_LOCKAQUIRE_TIMEOUT_UNITS;
        } else {
            this.lockAquireTimeoutUnit = lockAquireTimeoutUnit;
        }
    }

    /**
     * Returns i2c device implementation ({@link I2CDeviceImpl}).
     *
     * @param address address of i2c device
     *
     * @return implementation of i2c device with given address
     *
     * @throws IOException never in this implementation
     */
    @Override
    public I2CDevice getDevice(int address) throws IOException {
        return new I2CDeviceImpl(this, address);
    }

    /**
     * Opens the bus.
     *
     * @throws IOException thrown in case there are problems opening the i2c bus.
     */
    protected void open() throws IOException {
        if (file != null) {
            return;
        }

        file = new LinuxFile(filename, "rw");

        lastAddress = -1;
    }

    /**
     * Closes this i2c bus. Can be used in a thread safe way during bus operations.
     *
     * @throws IOException never in this implementation
     */
    @Override
    public synchronized void close() throws IOException {
        if (file != null) {
            file.close();
            file = null;
        }
    }

    public int readByteDirect(final I2CDevice device) throws IOException {
        return runBusLockedDeviceAction(device, () -> file.readUnsignedByte());
    }

    public int readBytesDirect(final I2CDevice device, final int size, final int offset, final byte[] buffer) throws IOException {
        return runBusLockedDeviceAction(device, () -> file.read(buffer, offset, size));
    }

    public int readByte(final I2CDevice device, final int localAddress) throws IOException {
        return runBusLockedDeviceAction(device, () -> {
            file.writeByte(localAddress);

            return file.readUnsignedByte();
        });
    }

    public int readBytes(final I2CDevice device, final int localAddress, final int size, final int offset, final byte[] buffer) throws IOException {
        return runBusLockedDeviceAction(device, () -> {
            file.writeByte(localAddress);

            return file.read(buffer, offset, size);
        });
    }

    public void writeByteDirect(final I2CDevice device, final byte data) throws IOException {
        runBusLockedDeviceAction(device, () -> {
            file.writeByte(data & 0xFF);

            return null;
        });
    }

    public void writeBytesDirect(final I2CDevice device, final int size, final int offset, final byte[] buffer) throws IOException {
        runBusLockedDeviceAction(device, () -> {
            file.write(buffer, offset, size);

            return null;
        });
    }

    public void writeByte(final I2CDevice device, final int localAddress, final byte data) throws IOException {
        runBusLockedDeviceAction(device, () -> {
            file.write(new byte[] { (byte)localAddress, data });

            return null;
        });
    }

    public void writeBytes(final I2CDevice device, final int localAddress, final int size, final int offset, final byte[] buffer) throws IOException {
        runBusLockedDeviceAction(device, () -> {
            byte[] buf = new byte[size + 1];

            buf[0] = (byte)localAddress;

            System.arraycopy(buffer, offset, buf, 1, size);

            file.write(buf);

            return null;
        });
    }

    public int writeAndReadBytesDirect(final I2CDevice device, final int writeSize, final int writeOffset, final byte[] writeBuffer,
                                       final int readSize, final int readOffset, final byte[] readBuffer) throws IOException {
        return runBusLockedDeviceAction(device, () -> {
            file.write(writeBuffer, writeOffset, writeSize);

            return file.read(readBuffer, readOffset, readSize);
        });
    }

    public void ioctl(final I2CDevice device, final long command, final int value) throws IOException {
        runBusLockedDeviceAction(device, () -> {
            file.ioctl(command, value);

            return null;
        });
    }

    public void ioctl(final I2CDevice device, final long command, final ByteBuffer values, final IntBuffer offsets) throws IOException {
        runBusLockedDeviceAction(device, () -> {
            file.ioctl(command, values, offsets);

            return null;
        });
    }

    /**
     * Selects a device on the bus for an action, and locks parallel access around file descriptor operations.
     * Multiple bus instances may be used in parallel, but a single bus instance must limit parallel access.
     * 

* The timeout used for the acquisition of the lock may be defined on getting the I2CBus from I2CFactory. *

* The 'run'-method of 'action' may throw an 'IOExceptionWrapperException' to wrap IOExceptions. The wrapped IOException is unwrapped by this method and rethrown as IOException. * * @param The result-type of the method * @param device Device to be selected on the bus * @param action The action to be run * @throws RuntimeException thrown by the custom code * @throws IOException see method description above * @see I2CFactory#getInstance(int, long, java.util.concurrent.TimeUnit) */ public T runBusLockedDeviceAction(final I2CDevice device, final Callable action) throws IOException { if (action == null) { throw new NullPointerException("Parameter 'action' is mandatory!"); } testForProperOperationConditions(device); try { if (accessLock.tryLock(lockAquireTimeout, lockAquireTimeoutUnit)) { try { testForProperOperationConditions(device); selectBusSlave(device); return action.call(); } finally { accessLock.unlock(); } } } catch (InterruptedException e) { logger.log(Level.FINER, "Failed locking I2CBusImpl-" + busNumber, e); throw new RuntimeException("Could not obtain an access-lock!", e); } catch (IOException e) { // unwrap IOExceptionWrapperException throw e; } catch (RuntimeException e) { throw e; } catch (Exception e) { // unexpected exceptions throw new RuntimeException(e); } throw new RuntimeException("Could not obtain an access-lock!"); } /** * Selects the slave device if not already selected on this bus. * Uses SharedSecrets to get the POSIX file descriptor, and runs * the required ioctl's via JNI. * * @param device Device to select */ protected void selectBusSlave(final I2CDevice device) throws IOException { final int addr = device.getAddress(); if (lastAddress != addr) { lastAddress = addr; file.ioctl(I2CConstants.I2C_SLAVE, addr & 0xFF); } } protected void testForProperOperationConditions(final I2CDevice device) throws IOException { if (file == null) { throw new IOException(toString() + " has already been closed! A new bus has to be acquired."); } if (device == null) { throw new NullPointerException("Parameter 'device' is mandatory!"); } } @Override public int getBusNumber() { return busNumber; } @Override public String toString() { return "I2CBus '" + busNumber + "' ('" + filename + "')"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy