diff --git a/hal/src/main/native/athena/I2C.cpp b/hal/src/main/native/athena/I2C.cpp index e09251df14..9151105f39 100644 --- a/hal/src/main/native/athena/I2C.cpp +++ b/hal/src/main/native/athena/I2C.cpp @@ -95,7 +95,7 @@ void HAL_InitializeI2C(HAL_I2CPort port, int32_t* status) { * @return >= 0 on success or -1 on transfer abort. */ int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress, - uint8_t* dataToSend, int32_t sendSize, + const uint8_t* dataToSend, int32_t sendSize, uint8_t* dataReceived, int32_t receiveSize) { if (port > 1) { // Set port out of range error here @@ -106,7 +106,7 @@ int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress, msgs[0].addr = deviceAddress; msgs[0].flags = 0; msgs[0].len = sendSize; - msgs[0].buf = dataToSend; + msgs[0].buf = const_cast(dataToSend); msgs[1].addr = deviceAddress; msgs[1].flags = I2C_M_RD; msgs[1].len = receiveSize; @@ -137,7 +137,7 @@ int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress, * @return >= 0 on success or -1 on transfer abort. */ int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress, - uint8_t* dataToSend, int32_t sendSize) { + const uint8_t* dataToSend, int32_t sendSize) { if (port > 1) { // Set port out of range error here return -1; @@ -147,7 +147,7 @@ int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress, msg.addr = deviceAddress; msg.flags = 0; msg.len = sendSize; - msg.buf = dataToSend; + msg.buf = const_cast(dataToSend); struct i2c_rdwr_ioctl_data rdwr; rdwr.msgs = &msg; diff --git a/hal/src/main/native/include/HAL/I2C.h b/hal/src/main/native/include/HAL/I2C.h index 9ade477d9c..1a3fe5b7a5 100644 --- a/hal/src/main/native/include/HAL/I2C.h +++ b/hal/src/main/native/include/HAL/I2C.h @@ -17,10 +17,10 @@ extern "C" { void HAL_InitializeI2C(HAL_I2CPort port, int32_t* status); int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress, - uint8_t* dataToSend, int32_t sendSize, + const uint8_t* dataToSend, int32_t sendSize, uint8_t* dataReceived, int32_t receiveSize); int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress, - uint8_t* dataToSend, int32_t sendSize); + const uint8_t* dataToSend, int32_t sendSize); int32_t HAL_ReadI2C(HAL_I2CPort port, int32_t deviceAddress, uint8_t* buffer, int32_t count); void HAL_CloseI2C(HAL_I2CPort port); diff --git a/hal/src/main/native/sim/I2C.cpp b/hal/src/main/native/sim/I2C.cpp index 68c827c76c..e532be06d1 100644 --- a/hal/src/main/native/sim/I2C.cpp +++ b/hal/src/main/native/sim/I2C.cpp @@ -16,14 +16,14 @@ void HAL_InitializeI2C(HAL_I2CPort port, int32_t* status) { SimI2CData[port].SetInitialized(true); } int32_t HAL_TransactionI2C(HAL_I2CPort port, int32_t deviceAddress, - uint8_t* dataToSend, int32_t sendSize, + const uint8_t* dataToSend, int32_t sendSize, uint8_t* dataReceived, int32_t receiveSize) { SimI2CData[port].Write(deviceAddress, dataToSend, sendSize); SimI2CData[port].Read(deviceAddress, dataReceived, receiveSize); return 0; } int32_t HAL_WriteI2C(HAL_I2CPort port, int32_t deviceAddress, - uint8_t* dataToSend, int32_t sendSize) { + const uint8_t* dataToSend, int32_t sendSize) { SimI2CData[port].Write(deviceAddress, dataToSend, sendSize); return 0; } diff --git a/hal/src/main/native/sim/MockData/I2CData.cpp b/hal/src/main/native/sim/MockData/I2CData.cpp index f5d2c95828..dee130c174 100644 --- a/hal/src/main/native/sim/MockData/I2CData.cpp +++ b/hal/src/main/native/sim/MockData/I2CData.cpp @@ -97,10 +97,11 @@ void I2CData::CancelWriteCallback(int32_t uid) { m_writeCallbacks = CancelCallback(m_writeCallbacks, uid); } -void I2CData::Write(int32_t deviceAddress, uint8_t* dataToSend, +void I2CData::Write(int32_t deviceAddress, const uint8_t* dataToSend, int32_t sendSize) { std::lock_guard lock(m_dataMutex); - InvokeCallback(m_writeCallbacks, "Write", dataToSend, sendSize); + InvokeCallback(m_writeCallbacks, "Write", const_cast(dataToSend), + sendSize); } void I2CData::Read(int32_t deviceAddress, uint8_t* buffer, int32_t count) { std::lock_guard lock(m_dataMutex); diff --git a/hal/src/main/native/sim/MockData/I2CDataInternal.h b/hal/src/main/native/sim/MockData/I2CDataInternal.h index 2232e7271e..59cb28983a 100644 --- a/hal/src/main/native/sim/MockData/I2CDataInternal.h +++ b/hal/src/main/native/sim/MockData/I2CDataInternal.h @@ -35,7 +35,8 @@ class I2CData { int32_t RegisterWriteCallback(HAL_BufferCallback callback, void* param); void CancelWriteCallback(int32_t uid); - void Write(int32_t deviceAddress, uint8_t* dataToSend, int32_t sendSize); + void Write(int32_t deviceAddress, const uint8_t* dataToSend, + int32_t sendSize); void Read(int32_t deviceAddress, uint8_t* buffer, int32_t count); void ResetData(); diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/I2C.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/I2C.java index 016db2dfd2..02c27f63f9 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/I2C.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/I2C.java @@ -72,20 +72,15 @@ public class I2C extends SensorBase { */ public synchronized boolean transaction(byte[] dataToSend, int sendSize, byte[] dataReceived, int receiveSize) { - final int status; - - ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(sendSize); - if (sendSize > 0 && dataToSend != null) { - dataToSendBuffer.put(dataToSend); + if (dataToSend.length < sendSize) { + throw new IllegalArgumentException("dataToSend is too small, must be at least " + sendSize); } - ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(receiveSize); - - status = I2CJNI.i2CTransaction(m_port, (byte) m_deviceAddress, dataToSendBuffer, - (byte) sendSize, dataReceivedBuffer, (byte) receiveSize); - if (receiveSize > 0 && dataReceived != null) { - dataReceivedBuffer.get(dataReceived, 0, receiveSize); + if (dataReceived.length < receiveSize) { + throw new IllegalArgumentException( + "dataReceived is too small, must be at least " + receiveSize); } - return status < 0; + return I2CJNI.i2CTransactionB(m_port, (byte) m_deviceAddress, dataToSend, + (byte) sendSize, dataReceived, (byte) receiveSize) < 0; } /** @@ -94,16 +89,17 @@ public class I2C extends SensorBase { *

This is a lower-level interface to the I2C hardware giving you more control over each * transaction. * - * @param dataToSend Buffer of data to send as part of the transaction. Must be allocated using - * ByteBuffer.allocateDirect(). + * @param dataToSend Buffer of data to send as part of the transaction. * @param sendSize Number of bytes to send as part of the transaction. - * @param dataReceived Buffer to read data into. Must be allocated using {@link - * ByteBuffer#allocateDirect(int)}. + * @param dataReceived Buffer to read data into. * @param receiveSize Number of bytes to read from the device. * @return Transfer Aborted... false for success, true for aborted. */ public synchronized boolean transaction(ByteBuffer dataToSend, int sendSize, ByteBuffer dataReceived, int receiveSize) { + if (dataToSend.hasArray() && dataReceived.hasArray()) { + return transaction(dataToSend.array(), sendSize, dataReceived.array(), receiveSize); + } if (!dataToSend.isDirect()) { throw new IllegalArgumentException("dataToSend must be a direct buffer"); } @@ -131,7 +127,7 @@ public class I2C extends SensorBase { * @return Transfer Aborted... false for success, true for aborted. */ public boolean addressOnly() { - return transaction((byte[]) null, (byte) 0, null, (byte) 0); + return transaction(new byte[0], (byte) 0, new byte[0], (byte) 0); } /** @@ -147,12 +143,8 @@ public class I2C extends SensorBase { byte[] buffer = new byte[2]; buffer[0] = (byte) registerAddress; buffer[1] = (byte) data; - - ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(2); - dataToSendBuffer.put(buffer); - - return I2CJNI.i2CWrite(m_port, (byte) m_deviceAddress, dataToSendBuffer, - (byte) buffer.length) < 0; + return I2CJNI.i2CWriteB(m_port, (byte) m_deviceAddress, buffer, + (byte) buffer.length) < 0; } /** @@ -164,11 +156,7 @@ public class I2C extends SensorBase { * @return Transfer Aborted... false for success, true for aborted. */ public synchronized boolean writeBulk(byte[] data) { - ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(data.length); - dataToSendBuffer.put(data); - - return I2CJNI.i2CWrite(m_port, (byte) m_deviceAddress, dataToSendBuffer, - (byte) data.length) < 0; + return writeBulk(data, data.length); } /** @@ -176,10 +164,31 @@ public class I2C extends SensorBase { * *

Write multiple bytes to a register on a device and wait until the transaction is complete. * - * @param data The data to write to the device. Must be created using ByteBuffer.allocateDirect(). + * @param data The data to write to the device. + * @param size The number of data bytes to write. + * @return Transfer Aborted... false for success, true for aborted. + */ + public synchronized boolean writeBulk(byte[] data, int size) { + if (data.length < size) { + throw new IllegalArgumentException( + "buffer is too small, must be at least " + size); + } + return I2CJNI.i2CWriteB(m_port, (byte) m_deviceAddress, data, (byte) size) < 0; + } + + /** + * Execute a write transaction with the device. + * + *

Write multiple bytes to a register on a device and wait until the transaction is complete. + * + * @param data The data to write to the device. + * @param size The number of data bytes to write. * @return Transfer Aborted... false for success, true for aborted. */ public synchronized boolean writeBulk(ByteBuffer data, int size) { + if (data.hasArray()) { + return writeBulk(data.array(), size); + } if (!data.isDirect()) { throw new IllegalArgumentException("must be a direct buffer"); } @@ -208,6 +217,9 @@ public class I2C extends SensorBase { if (count < 1) { throw new BoundaryException("Value must be at least 1, " + count + " given"); } + if (buffer.length < count) { + throw new IllegalArgumentException("buffer is too small, must be at least " + count); + } byte[] registerAddressArray = new byte[1]; registerAddressArray[0] = (byte) registerAddress; @@ -215,6 +227,8 @@ public class I2C extends SensorBase { return transaction(registerAddressArray, registerAddressArray.length, buffer, count); } + private ByteBuffer m_readDataToSendBuffer = null; + /** * Execute a read transaction with the device. * @@ -223,8 +237,7 @@ public class I2C extends SensorBase { * * @param registerAddress The register to read first in the transaction. * @param count The number of bytes to read in the transaction. - * @param buffer A buffer to store the data read from the device. Must be created using - * ByteBuffer.allocateDirect(). + * @param buffer A buffer to store the data read from the device. * @return Transfer Aborted... false for success, true for aborted. */ public boolean read(int registerAddress, int count, ByteBuffer buffer) { @@ -232,6 +245,10 @@ public class I2C extends SensorBase { throw new BoundaryException("Value must be at least 1, " + count + " given"); } + if (buffer.hasArray()) { + return read(registerAddress, count, buffer.array()); + } + if (!buffer.isDirect()) { throw new IllegalArgumentException("must be a direct buffer"); } @@ -239,10 +256,14 @@ public class I2C extends SensorBase { throw new IllegalArgumentException("buffer is too small, must be at least " + count); } - ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(1); - dataToSendBuffer.put(0, (byte) registerAddress); + synchronized (this) { + if (m_readDataToSendBuffer == null) { + m_readDataToSendBuffer = ByteBuffer.allocateDirect(1); + } + m_readDataToSendBuffer.put(0, (byte) registerAddress); - return transaction(dataToSendBuffer, 1, buffer, count); + return transaction(m_readDataToSendBuffer, 1, buffer, count); + } } /** @@ -259,13 +280,12 @@ public class I2C extends SensorBase { if (count < 1) { throw new BoundaryException("Value must be at least 1, " + count + " given"); } + if (buffer.length < count) { + throw new IllegalArgumentException("buffer is too small, must be at least " + count); + } - ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(count); - - int retVal = I2CJNI.i2CRead(m_port, (byte) m_deviceAddress, dataReceivedBuffer, - (byte) count); - dataReceivedBuffer.get(buffer); - return retVal < 0; + return I2CJNI.i2CReadB(m_port, (byte) m_deviceAddress, buffer, + (byte) count) < 0; } /** @@ -273,8 +293,7 @@ public class I2C extends SensorBase { * *

Read bytes from a device. This method does not write any data to prompt the device. * - * @param buffer A pointer to the array of bytes to store the data read from the device. Must be - * created using ByteBuffer.allocateDirect(). + * @param buffer A pointer to the array of bytes to store the data read from the device. * @param count The number of bytes to read in the transaction. * @return Transfer Aborted... false for success, true for aborted. */ @@ -284,6 +303,10 @@ public class I2C extends SensorBase { + " given"); } + if (buffer.hasArray()) { + return readOnly(buffer.array(), count); + } + if (!buffer.isDirect()) { throw new IllegalArgumentException("must be a direct buffer"); } @@ -322,21 +345,21 @@ public class I2C extends SensorBase { public boolean verifySensor(int registerAddress, int count, byte[] expected) { // TODO: Make use of all 7 read bytes - ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(1); + byte[] dataToSend = new byte[1]; - ByteBuffer deviceData = ByteBuffer.allocateDirect(4); + byte[] deviceData = new byte[4]; for (int i = 0, curRegisterAddress = registerAddress; i < count; i += 4, curRegisterAddress += 4) { int toRead = count - i < 4 ? count - i : 4; // Read the chunk of data. Return false if the sensor does not // respond. - dataToSendBuffer.put(0, (byte) curRegisterAddress); - if (transaction(dataToSendBuffer, 1, deviceData, toRead)) { + dataToSend[0] = (byte) curRegisterAddress; + if (transaction(dataToSend, 1, deviceData, toRead)) { return false; } for (byte j = 0; j < toRead; j++) { - if (deviceData.get(j) != expected[i + j]) { + if (deviceData[j] != expected[i + j]) { return false; } } diff --git a/wpilibj/src/main/java/edu/wpi/first/wpilibj/hal/I2CJNI.java b/wpilibj/src/main/java/edu/wpi/first/wpilibj/hal/I2CJNI.java index db20ed1ea2..035c0b2eb7 100644 --- a/wpilibj/src/main/java/edu/wpi/first/wpilibj/hal/I2CJNI.java +++ b/wpilibj/src/main/java/edu/wpi/first/wpilibj/hal/I2CJNI.java @@ -16,10 +16,18 @@ public class I2CJNI extends JNIWrapper { public static native int i2CTransaction(int port, byte address, ByteBuffer dataToSend, byte sendSize, ByteBuffer dataReceived, byte receiveSize); + public static native int i2CTransactionB(int port, byte address, byte[] dataToSend, + byte sendSize, byte[] dataReceived, byte receiveSize); + public static native int i2CWrite(int port, byte address, ByteBuffer dataToSend, byte sendSize); + public static native int i2CWriteB(int port, byte address, byte[] dataToSend, byte sendSize); + public static native int i2CRead(int port, byte address, ByteBuffer dataReceived, byte receiveSize); + public static native int i2CReadB(int port, byte address, byte[] dataReceived, + byte receiveSize); + public static native void i2CClose(int port); } diff --git a/wpilibj/src/main/native/cpp/I2CJNI.cpp b/wpilibj/src/main/native/cpp/I2CJNI.cpp index d12cfe3e9a..771f9e4001 100644 --- a/wpilibj/src/main/native/cpp/I2CJNI.cpp +++ b/wpilibj/src/main/native/cpp/I2CJNI.cpp @@ -13,8 +13,10 @@ #include "HAL/I2C.h" #include "HALUtil.h" +#include "support/jni_util.h" using namespace frc; +using namespace wpi::java; // set the logging level TLogLevel i2cJNILogLevel = logWARNING; @@ -69,6 +71,32 @@ JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CTransaction( return returnValue; } +/* + * Class: edu_wpi_first_wpilibj_hal_I2CJNI + * Method: i2CTransactionB + * Signature: (IB[BB[BB)I + */ +JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CTransactionB( + JNIEnv* env, jclass, jint port, jbyte address, jbyteArray dataToSend, + jbyte sendSize, jbyteArray dataReceived, jbyte receiveSize) { + I2CJNI_LOG(logDEBUG) << "Calling I2CJNI i2CTransactionB"; + I2CJNI_LOG(logDEBUG) << "Port = " << port; + I2CJNI_LOG(logDEBUG) << "Address = " << (jint)address; + I2CJNI_LOG(logDEBUG) << "SendSize = " << (jint)sendSize; + llvm::SmallVector recvBuf; + recvBuf.resize(receiveSize); + I2CJNI_LOG(logDEBUG) << "ReceiveSize = " << (jint)receiveSize; + jint returnValue = + HAL_TransactionI2C(static_cast(port), address, + reinterpret_cast( + JByteArrayRef(env, dataToSend).array().data()), + sendSize, recvBuf.data(), receiveSize); + env->SetByteArrayRegion(dataReceived, 0, receiveSize, + reinterpret_cast(recvBuf.data())); + I2CJNI_LOG(logDEBUG) << "ReturnValue = " << returnValue; + return returnValue; +} + /* * Class: edu_wpi_first_wpilibj_hal_I2CJNI * Method: i2CWrite @@ -92,6 +120,27 @@ JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CWrite( return returnValue; } +/* + * Class: edu_wpi_first_wpilibj_hal_I2CJNI + * Method: i2CWriteB + * Signature: (IB[BB)I + */ +JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CWriteB( + JNIEnv* env, jclass, jint port, jbyte address, jbyteArray dataToSend, + jbyte sendSize) { + I2CJNI_LOG(logDEBUG) << "Calling I2CJNI i2CWrite"; + I2CJNI_LOG(logDEBUG) << "Port = " << port; + I2CJNI_LOG(logDEBUG) << "Address = " << (jint)address; + I2CJNI_LOG(logDEBUG) << "SendSize = " << (jint)sendSize; + jint returnValue = + HAL_WriteI2C(static_cast(port), address, + reinterpret_cast( + JByteArrayRef(env, dataToSend).array().data()), + sendSize); + I2CJNI_LOG(logDEBUG) << "ReturnValue = " << (jint)returnValue; + return returnValue; +} + /* * Class: edu_wpi_first_wpilibj_hal_I2CJNI * Method: i2CRead @@ -112,6 +161,27 @@ JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CRead( return returnValue; } +/* + * Class: edu_wpi_first_wpilibj_hal_I2CJNI + * Method: i2CReadB + * Signature: (IB[BB)I + */ +JNIEXPORT jint JNICALL Java_edu_wpi_first_wpilibj_hal_I2CJNI_i2CReadB( + JNIEnv* env, jclass, jint port, jbyte address, jbyteArray dataReceived, + jbyte receiveSize) { + I2CJNI_LOG(logDEBUG) << "Calling I2CJNI i2CRead"; + I2CJNI_LOG(logDEBUG) << "Port = " << port; + I2CJNI_LOG(logDEBUG) << "Address = " << address; + I2CJNI_LOG(logDEBUG) << "ReceiveSize = " << receiveSize; + llvm::SmallVector recvBuf; + recvBuf.resize(receiveSize); + jint returnValue = HAL_ReadI2C(static_cast(port), address, recvBuf.data(), receiveSize); + env->SetByteArrayRegion(dataReceived, 0, receiveSize, + reinterpret_cast(recvBuf.data())); + I2CJNI_LOG(logDEBUG) << "ReturnValue = " << returnValue; + return returnValue; +} + /* * Class: edu_wpi_first_wpilibj_hal_I2CJNI * Method: i2CClose