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

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

Go to download

jBiDiB jbidibc USB POM Install the libusb drivers for using the libusb access. Under Windows OS you must install the Zadig driver as described here: https://github.com/libusb/libusb/wiki/Windows. Zadic can be downloaded from: http://zadig.akeo.ie/ In the Zadig UI you must check Options > List all devices and the select the 'USB-EPP /I2C... CH341A' device. Then select the target driver with the spinners (I used libusb-win32 (v1.2.6.0)) and click the 'Install driver' button.

There is a newer version: 2.0.26
Show newest version
/*
 * 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 org.bidib.jbidibc.usbhid.UsbConstants;
import org.bidib.jbidibc.usbhid.UsbDevice;
import org.bidib.jbidibc.usbhid.UsbEndpoint;
import org.bidib.jbidibc.usbhid.UsbInterface;
import org.bidib.jbidibc.usbi2c.UsbI2cManager;
import org.bidib.jbidibc.usbi2c.UsbI2cManager.UsbDeviceIdentifier;

/**
 * The Silicon Labs CP2112 chip is a USB HID device which provides an SMBus controller for talking to slave devices.
 * 

* Data Sheet: http://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf Programming Interface Specification: * https://www.silabs.com/documents/public/application-notes/an495-cp2112-interface-specification.pdf */ public class Cp2112UsbI2cAdapter extends BaseUsbI2cAdapter { // HID feature request GET_REPORT code private static final int HID_FEATURE_REQUEST_REPORT_GET = 0x01; // HID feature request SET_REPORT code private static final int HID_FEATURE_REQUEST_REPORT_SET = 0x09; // HID feature request INPUT report type private static final int HID_FEATURE_REQUEST_REPORT_TYPE_INPUT = 0x100; // HID feature request OUTPUT report type private static final int HID_FEATURE_REQUEST_REPORT_TYPE_OUTPUT = 0x300; // USB Interrupt IN/OUT packet max size private static final int MAX_INTERRUPT_TRANSFER_SIZE = 64; private final byte[] buffer = new byte[MAX_INTERRUPT_TRANSFER_SIZE]; // CP2112 report IDs private static final byte REPORT_ID_SMBUS_CONFIG = 0x06; private static final byte REPORT_ID_DATA_READ_REQUEST = 0x10; private static final byte REPORT_ID_DATA_WRITE_READ_REQUEST = 0x11; private static final byte REPORT_ID_DATA_READ_FORCE_SEND = 0x12; private static final byte REPORT_ID_DATA_READ_RESPONSE = 0x13; private static final byte REPORT_ID_DATA_WRITE_REQUEST = 0x14; private static final byte REPORT_ID_TRANSFER_STATUS_REQUEST = 0x15; private static final byte REPORT_ID_TRANSFER_STATUS_RESPONSE = 0x16; private static final byte REPORT_ID_CANCEL_TRANSFER = 0x17; // CP2112 SMBus configuration size private static final int SMBUS_CONFIG_SIZE = 13; // CP2112 SMBus configuration: Clock Speed private static final int SMBUS_CONFIG_CLOCK_SPEED_OFFSET = 1; // CP2112 SMBus configuration: Retry Time private static final int SMBUS_CONFIG_RETRY_TIME_OFFSET = 12; // CP2112 max I2C data write length (single write transfer) private static final int MAX_DATA_WRITE_LENGTH = 61; // CP2112 max I2C data read length (multiple read transfers) private static final int MAX_DATA_READ_LENGTH = 512; private static final int NUM_DRAIN_DATA_REPORTS_RETRIES = 10; // CP2112 max data transfer status reads while waiting for transfer completion private static final int NUM_TRANSFER_STATUS_RETRIES = 10; // CP2112 transfer status codes private static final int TRANSFER_STATUS_IDLE = 0x00; private static final int TRANSFER_STATUS_BUSY = 0x01; private static final int TRANSFER_STATUS_COMPLETE = 0x02; private static final int TRANSFER_STATUS_ERROR = 0x03; // CP2112 min clock speed private static final int MIN_CLOCK_SPEED = 10000; // CP2112 max clock speed private static final int MAX_CLOCK_SPEED = CLOCK_SPEED_HIGH; class Cp2112UsbI2cDevice extends BaseUsbI2cDevice { Cp2112UsbI2cDevice(int address) { super(address); } @Override protected void deviceReadReg(int reg, byte[] buffer, int length) throws IOException { readRegData(address, reg, buffer, length); } @Override protected void deviceRead(byte[] buffer, int length) throws IOException { readData(address, buffer, length); } @Override protected void deviceWrite(byte[] buffer, int length) throws IOException { writeData(address, buffer, length); } } public Cp2112UsbI2cAdapter(UsbI2cManager manager, UsbDevice usbDevice) { super(manager, usbDevice); } @Override public Cp2112UsbI2cDevice getDeviceImpl(int address) { return new Cp2112UsbI2cDevice(address); } @Override protected void init(UsbDevice usbDevice) throws IOException { if (usbDevice.getInterfaceCount() == 0) { throw new IOException("No interfaces found for device: " + usbDevice); } UsbInterface usbInterface = usbDevice.getInterface(0); if (usbInterface.getEndpointCount() < 2) { throw new IOException("No endpoints found for device: " + usbDevice); } UsbEndpoint usbReadEndpoint = null, usbWriteEndpoint = null; for (int i = 0; i < usbInterface.getEndpointCount(); i++) { UsbEndpoint usbEndpoint = usbInterface.getEndpoint(i); if (usbEndpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_INT) { if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_IN) { usbReadEndpoint = usbEndpoint; } else { usbWriteEndpoint = usbEndpoint; } } } if (usbReadEndpoint == null || usbWriteEndpoint == null) { throw new IOException("No read or write HID endpoint found for device: " + usbDevice); } setBulkEndpoints(usbReadEndpoint, usbWriteEndpoint); configure(); // Drain all stale data reports drainPendingDataReports(); } @Override protected void configure() throws IOException { // Get current config getHidFeatureReport(REPORT_ID_SMBUS_CONFIG, buffer, SMBUS_CONFIG_SIZE + 1); // reserve one byte for Report ID // Clock Speed (in Hertz, default 0x000186A0 - 100 kHz) int clockSpeed = getClockSpeed(); buffer[SMBUS_CONFIG_CLOCK_SPEED_OFFSET] = (byte) (clockSpeed >> 24); buffer[SMBUS_CONFIG_CLOCK_SPEED_OFFSET + 1] = (byte) (clockSpeed >> 16); buffer[SMBUS_CONFIG_CLOCK_SPEED_OFFSET + 2] = (byte) (clockSpeed >> 8); buffer[SMBUS_CONFIG_CLOCK_SPEED_OFFSET + 3] = (byte) clockSpeed; // Retry Time (number of retries, default 0 - no limit) buffer[SMBUS_CONFIG_RETRY_TIME_OFFSET] = 0x00; buffer[SMBUS_CONFIG_RETRY_TIME_OFFSET + 1] = 0x01; sendHidFeatureReport(REPORT_ID_SMBUS_CONFIG, buffer, SMBUS_CONFIG_SIZE + 1); } @Override public boolean isClockSpeedSupported(int speed) { return (speed >= MIN_CLOCK_SPEED && speed <= MAX_CLOCK_SPEED); } private void checkWriteDataLength(int length) { checkDataLength(length, MAX_DATA_WRITE_LENGTH); } private void checkReadDataLength(int length) { checkDataLength(length, MAX_DATA_READ_LENGTH); } private void checkDataLength(int length, int maxLength) { if (length < 1 || length > maxLength) { throw new IllegalArgumentException( String.format("Invalid data length: %d (min 1, max %d)", length, maxLength)); } } private void writeData(int address, byte[] data, int length) throws IOException { checkWriteDataLength(length); buffer[0] = REPORT_ID_DATA_WRITE_REQUEST; buffer[1] = getAddressByte(address, false); buffer[2] = (byte) length; System.arraycopy(data, 0, buffer, 3, length); sendHidDataReport(buffer, length + 3, USB_TIMEOUT_MILLIS); waitTransferComplete(); } private void readData(int address, byte[] data, int length) throws IOException { checkReadDataLength(length); buffer[0] = REPORT_ID_DATA_READ_REQUEST; buffer[1] = getAddressByte(address, false); // read bit is not required buffer[2] = (byte) ((length >> 8) & 0xff); buffer[3] = (byte) length; sendHidDataReport(buffer, 4, USB_TIMEOUT_MILLIS); waitTransferComplete(); readDataFully(data, length); } private void readRegData(int address, int reg, byte[] data, int length) throws IOException { checkReadDataLength(length); buffer[0] = REPORT_ID_DATA_WRITE_READ_REQUEST; buffer[1] = getAddressByte(address, false); // read bit is not required buffer[2] = (byte) ((length >> 8) & 0xff); buffer[3] = (byte) length; buffer[4] = 0x01; // number of bytes in target address (register ID) buffer[5] = (byte) reg; sendHidDataReport(buffer, 6, USB_TIMEOUT_MILLIS); waitTransferComplete(); readDataFully(data, length); } private void readDataFully(byte[] data, int length) throws IOException { int totalReadLen = 0; while (totalReadLen < length) { sendForceReadDataRequest(length - totalReadLen); getHidDataReport(buffer, USB_TIMEOUT_MILLIS); if (buffer[0] != REPORT_ID_DATA_READ_RESPONSE) { throw new IOException(String.format("Unexpected data read report ID: 0x%02x", buffer[0])); } if (buffer[1] == TRANSFER_STATUS_ERROR) { throw new IOException(String.format("Data read status error, condition: 0x%02x", buffer[2])); } int lastReadLen = buffer[2] & 0xff; if (lastReadLen > length - totalReadLen) { throw new IOException(String .format("Too many data read: " + "%d byte(s), expected: %d byte(s)", lastReadLen, length - totalReadLen)); } System.arraycopy(buffer, 3, data, totalReadLen, lastReadLen); totalReadLen += lastReadLen; } } private void waitTransferComplete() throws IOException { int tryNum = 1; while (tryNum++ <= NUM_TRANSFER_STATUS_RETRIES) { sendTransferStatusRequest(); getHidDataReport(buffer, USB_TIMEOUT_MILLIS); if (buffer[0] != REPORT_ID_TRANSFER_STATUS_RESPONSE) { throw new IOException(String.format("Unexpected transfer status report ID: 0x%02x", buffer[0])); } switch (buffer[1]) { case TRANSFER_STATUS_BUSY: continue; case TRANSFER_STATUS_COMPLETE: return; default: throw new IOException(String.format("Invalid transfer status: 0x%02x", buffer[1])); } } // Retries limit was reached and TRANSFER_STATUS_COMPLETE status is not reached cancelTransfer(); throw new IOException("Transfer retries limit reached"); } private void sendForceReadDataRequest(int length) throws IOException { buffer[0] = REPORT_ID_DATA_READ_FORCE_SEND; buffer[1] = (byte) ((length >> 8) & 0xff); buffer[2] = (byte) length; sendHidDataReport(buffer, 3, USB_TIMEOUT_MILLIS); } private void sendTransferStatusRequest() throws IOException { buffer[0] = REPORT_ID_TRANSFER_STATUS_REQUEST; buffer[1] = 0x01; sendHidDataReport(buffer, 2, USB_TIMEOUT_MILLIS); } private void cancelTransfer() throws IOException { buffer[0] = REPORT_ID_CANCEL_TRANSFER; buffer[1] = 0x01; // sendHidDataReport(buffer, 2, USB_TIMEOUT_MILLIS); urbInterrupt(buffer, MAX_INTERRUPT_TRANSFER_SIZE, USB_TIMEOUT_MILLIS); } private void drainPendingDataReports() throws IOException { int tryNum = 1; while (tryNum++ <= NUM_DRAIN_DATA_REPORTS_RETRIES) { try { getHidDataReport(buffer, 5); } catch (IOException e) { break; } } if (tryNum >= NUM_DRAIN_DATA_REPORTS_RETRIES) { throw new IOException("Can't drain pending data reports"); } cancelTransfer(); } private void checkReportId(int reportId) { if ((reportId & 0xff) != reportId) { throw new IllegalArgumentException("Invalid report ID: " + reportId); } } /** * Read HID feature report from USB device to data buffer. * * @param reportId * feature report ID * @param data * data buffer to read report into * @param length * feature report data length to read * @throws IOException * in case of I/O error */ private void getHidFeatureReport(int reportId, byte[] data, int length) throws IOException { checkReportId(reportId); controlTransfer( UsbConstants.USB_TYPE_CLASS | UsbConstants.USB_DIR_IN | UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, HID_FEATURE_REQUEST_REPORT_GET, reportId | HID_FEATURE_REQUEST_REPORT_TYPE_OUTPUT, 0, data, length); } /** * Send HID feature report from data buffer to USB device. * * @param reportId * feature report ID * @param data * feature report data buffer * @param length * feature report data length to send * @throws IOException * in case of I/O error */ private void sendHidFeatureReport(int reportId, byte[] data, int length) throws IOException { checkReportId(reportId); controlTransfer( UsbConstants.USB_TYPE_CLASS | UsbConstants.USB_DIR_OUT | UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, HID_FEATURE_REQUEST_REPORT_SET, reportId | HID_FEATURE_REQUEST_REPORT_TYPE_OUTPUT, 0, data, length); } /** * Read HID data report from USB device to data buffer. * * @param data * data buffer to read report into * @param timeout * read timeout in milliseconds * @throws IOException * in case of data report read error or timeout */ private void getHidDataReport(byte[] data, int timeout) throws IOException { bulkRead(data, 0, data.length, timeout); } /** * Send HID data report from data buffer to USB device. * * @param data * data buffer to send report from * @param length * data report length * @param timeout * send timeout in milliseconds * @throws IOException * in case of data report send error */ private void sendHidDataReport(byte[] data, int length, int timeout) throws IOException { bulkWrite(data, length, USB_TIMEOUT_MILLIS); } public static UsbDeviceIdentifier[] getSupportedUsbDeviceIdentifiers() { return new UsbDeviceIdentifier[] { new UsbDeviceIdentifier(0x10c4, 0xea90) }; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy