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

org.bidib.jbidibc.usbi2c.adapter.BaseUsbI2cAdapter Maven / Gradle / Ivy

/*
 * Copyright (c) 2019 Victor Antonovich 
 *
 * This work 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 2.1
 * of the License, or (at your option) any later version.
 *
 * This work 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 Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 */

package org.bidib.jbidibc.usbi2c.adapter;

import java.io.IOException;
import java.util.concurrent.locks.ReentrantLock;

import org.bidib.jbidibc.usbhid.UsbDevice;
import org.bidib.jbidibc.usbhid.UsbDeviceConnection;
import org.bidib.jbidibc.usbhid.UsbEndpoint;
import org.bidib.jbidibc.usbhid.UsbInterface;
import org.bidib.jbidibc.usbi2c.UsbI2cAdapter;
import org.bidib.jbidibc.usbi2c.UsbI2cDevice;
import org.bidib.jbidibc.usbi2c.UsbI2cManager;

abstract class BaseUsbI2cAdapter implements UsbI2cAdapter {
    // Linux kernel flags
    static final int I2C_M_RD = 0x01; // read data, from slave to master

    protected static final int USB_TIMEOUT_MILLIS = 1000;

    protected final UsbI2cManager i2cManager;

    protected final UsbDevice usbDevice;

    private UsbDeviceConnection usbDeviceConnection;

    private UsbEndpoint usbReadEndpoint;

    private UsbEndpoint usbWriteEndpoint;

    protected static final int MAX_MESSAGE_SIZE = 8192;

    private final byte[] buffer = new byte[MAX_MESSAGE_SIZE + 1];

    protected final ReentrantLock accessLock = new ReentrantLock();

    protected int clockSpeed = CLOCK_SPEED_STANDARD;

    protected abstract class BaseUsbI2cDevice implements UsbI2cDevice {
        final int address;

        BaseUsbI2cDevice(int address) {
            this.address = (address & 0x7f);
        }

        @Override
        public int getAddress() {
            return address;
        }

        @Override
        public byte readRegByte(int reg) throws IOException {
            try {
                accessLock.lock();
                readRegBuffer(reg, buffer, 1);
                return buffer[0];
            }
            finally {
                accessLock.unlock();
            }
        }

        @Override
        public short readRegWord(int reg) throws IOException {
            try {
                accessLock.lock();
                readRegBuffer(reg, buffer, 2);
                return (short) ((buffer[0] & 0xFF) | (buffer[1] << 8));
            }
            finally {
                accessLock.unlock();
            }
        }

        @Override
        public void writeRegByte(int reg, byte data) throws IOException {
            try {
                accessLock.lock();
                buffer[0] = (byte) reg;
                buffer[1] = data;
                write(buffer, 2);
            }
            finally {
                accessLock.unlock();
            }
        }

        @Override
        public void writeRegWord(int reg, short data) throws IOException {
            try {
                accessLock.lock();
                buffer[0] = (byte) reg;
                buffer[1] = (byte) data;
                buffer[2] = (byte) (data >>> 8);
                write(buffer, 3);
            }
            finally {
                accessLock.unlock();
            }
        }

        @Override
        public void writeRegBuffer(int reg, byte[] buffer, int length) throws IOException {
            try {
                accessLock.lock();
                if (length > MAX_MESSAGE_SIZE) {
                    throw new IllegalArgumentException("Message is too long: " + length + " byte(s)");
                }
                BaseUsbI2cAdapter.this.buffer[0] = (byte) reg;
                System.arraycopy(buffer, 0, BaseUsbI2cAdapter.this.buffer, 1, length);
                write(BaseUsbI2cAdapter.this.buffer, length + 1);
            }
            finally {
                accessLock.unlock();
            }
        }

        @Override
        public void readRegBuffer(int reg, byte[] buffer, int length) throws IOException {
            try {
                accessLock.lock();
                deviceReadReg(reg, buffer, length);
            }
            finally {
                accessLock.unlock();
            }
        }

        @Override
        public void read(byte[] buffer, int length) throws IOException {
            try {
                accessLock.lock();
                deviceRead(buffer, length);
            }
            finally {
                accessLock.unlock();
            }
        }

        @Override
        public void write(byte[] buffer, int length) throws IOException {
            try {
                accessLock.lock();
                deviceWrite(buffer, length);
            }
            finally {
                accessLock.unlock();
            }
        }

        protected abstract void deviceReadReg(int reg, byte[] buffer, int length) throws IOException;

        protected abstract void deviceWrite(byte[] buffer, int length) throws IOException;

        protected abstract void deviceRead(byte[] buffer, int length) throws IOException;
    }

    BaseUsbI2cAdapter(UsbI2cManager i2cManager, UsbDevice usbDevice) {
        this.i2cManager = i2cManager;
        this.usbDevice = usbDevice;
    }

    @Override
    public String getId() {
        return usbDevice.getDeviceName();
    }

    protected boolean isOpened() {
        return (usbDeviceConnection != null);
    }

    protected void checkOpened() throws IllegalStateException {
        if (!isOpened()) {
            throw new IllegalStateException("Adapter is not opened or closed");
        }
    }

    @Override
    public void open() throws IOException {
        if (isOpened()) {
            throw new IllegalStateException("Adapter already opened");
        }

        usbDeviceConnection = i2cManager.getUsbManager().openDevice(usbDevice);
        if (usbDeviceConnection == null) {
            throw new IOException("Can't open adapter");
        }

        for (int i = 0; i < usbDevice.getInterfaceCount(); i++) {
            UsbInterface usbDeviceInterface = usbDevice.getInterface(i);
            if (!usbDeviceConnection.claimInterface(usbDeviceInterface, true)) {
                throw new IOException("Can't claim adapter interfaces");
            }
        }

        init(usbDevice);
    }

    protected void init(UsbDevice usbDevice) throws IOException {
        // Do nothing by default
    }

    @Override
    public void close() throws Exception {
        close(usbDevice);

        if (usbDeviceConnection != null) {
            for (int i = 0; i < usbDevice.getInterfaceCount(); i++) {
                UsbInterface usbDeviceInterface = usbDevice.getInterface(i);
                usbDeviceConnection.releaseInterface(usbDeviceInterface);
            }
            usbDeviceConnection.close();
            usbDeviceConnection = null;
        }
    }

    protected void close(UsbDevice usbDevice) throws IOException {
        // Do nothing by default
    }

    @Override
    public UsbI2cDevice getDevice(int address) {
        checkOpened();
        return getDeviceImpl(address);
    }

    protected abstract BaseUsbI2cDevice getDeviceImpl(int address);

    @Override
    public boolean isClockSpeedSupported(int speed) {
        return (speed == CLOCK_SPEED_STANDARD);
    }

    protected int getClockSpeed() {
        return clockSpeed;
    }

    @Override
    public void setClockSpeed(int speed) throws IOException {
        if (!isClockSpeedSupported(speed)) {
            throw new IllegalArgumentException("Clock speed is not supported: " + speed);
        }

        this.clockSpeed = speed;

        if (isOpened()) {
            try {
                accessLock.lock();
                configure();
            }
            finally {
                accessLock.unlock();
            }
        }
    }

    protected void configure() throws IOException {
        // Do nothing by default
    }

    @Override
    public UsbDevice getUsbDevice() {
        return usbDevice;
    }

    final void controlTransfer(int requestType, int request, int value, int index, byte[] data, int length)
        throws IOException {
        checkOpened();
        int result =
            usbDeviceConnection.controlTransfer(requestType, request, value, index, data, length, USB_TIMEOUT_MILLIS);
        if (result != length) {
            throw new IOException(String
                .format(
                    "controlTransfer(requestType: 0x%x, "
                        + "request: 0x%x, value: 0x%x, index: 0x%x, length: %d) failed: %d",
                    requestType, request, value, index, length, result));
        }
    }

    protected void setBulkEndpoints(UsbEndpoint readEndpoint, UsbEndpoint writeEndpoint) {
        this.usbReadEndpoint = readEndpoint;
        this.usbWriteEndpoint = writeEndpoint;
    }

    /**
     * Read bulk data from USB device to data buffer.
     *
     * @param data
     *            data buffer to read data
     * @param offset
     *            data buffer offset to read data
     * @param length
     *            data length to read
     * @param timeout
     *            data read timeout (in milliseconds)
     * @return actual length of read data
     * @throws IOException
     *             in case of data read error or timeout
     */
    final int bulkRead(byte[] data, int offset, int length, int timeout) throws IOException {
        checkOpened();
        if (usbReadEndpoint == null) {
            throw new IllegalStateException("Bulk read endpoint is not set");
        }
        int res = usbDeviceConnection.bulkTransfer(usbReadEndpoint, data, offset, length, timeout);
        if (res < 0) {
            throw new IOException("Bulk read error: " + res);
        }
        return res;
    }

    /**
     * Write bulk data from data buffer to USB device.
     *
     * @param data
     *            data buffer to write data
     * @param length
     *            data length to write
     * @param timeout
     *            data read timeout (in milliseconds)
     * @throws IOException
     *             in case of data write error or timeout
     */
    final void bulkWrite(byte[] data, int length, int timeout) throws IOException {
        checkOpened();
        if (usbWriteEndpoint == null) {
            throw new IllegalStateException("Bulk write endpoint is not set");
        }
        int res = usbDeviceConnection.bulkTransfer(usbWriteEndpoint, data, length, timeout);
        if (res < 0) {
            throw new IOException("Bulk write error: " + res);
        }
        if (res < length) {
            throw new IOException(
                "Bulk write length error, expected: " + length + " byte(s), written: " + res + " byte(s)");
        }
    }

    final void urbInterrupt(byte[] data, int length, int timeout) throws IOException {
        checkOpened();
        int res = usbDeviceConnection.interruptTransfer(usbWriteEndpoint, data, length, timeout);
        if (res < 0) {
            throw new IOException("Send HID data report error, result: " + res);
        }
    }

    /**
     * Get I2C operation address byte value.
     *
     * @param address
     *            I2C address
     * @param isRead
     *            true for read I2C operation, false for write I2C operation
     * @return I2C operation address byte value
     */
    static byte getAddressByte(int address, boolean isRead) {
        return (byte) ((address << 1) | (isRead ? 0x01 : 0x00));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy