com.oracle.dio.spibus.impl.SPICompositeMessageImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.openjdk.dio Show documentation
Show all versions of org.openjdk.dio Show documentation
Maven/OSGi repackaging of OpenJDK's Device I/O library
The newest version!
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.dio.spibus.impl;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import com.oracle.dio.utils.ExceptionMessage;
import java.io.InterruptedIOException;
import java.nio.BufferOverflowException;
import java.util.ConcurrentModificationException;
import jdk.dio.ClosedDeviceException;
import jdk.dio.UnavailableDeviceException;
import jdk.dio.spibus.SPICompositeMessage;
import jdk.dio.spibus.SPIDevice;
final class SPICompositeMessageImpl implements SPICompositeMessage {
private final ArrayList messageList = new ArrayList<>();
private boolean isAlreadyTransferedOnce;
/* Owner of the message */
private final SPISlaveImpl device;
// delay between operations
private int delay;
// number of messages to read
private int rxMsgs;
private class Message {
ByteBuffer tx, rx, newTx, newRx;
int skip, delay;
public Message(ByteBuffer tx, int skip, ByteBuffer rx, int delay) {
this.rx = rx;
this.skip = skip;
this.tx = tx;
this.delay = delay;
}
}
private void checkStatus() throws ClosedDeviceException {
if (isAlreadyTransferedOnce) {
throw new IllegalStateException(
ExceptionMessage.format(ExceptionMessage.I2CBUS_ALREADY_TRANSFERRED_MESSAGE)
);
}
if (!device.isOpen()) {
throw new ClosedDeviceException();
}
}
private void check(Message message) throws ClosedDeviceException {
checkStatus();
if (0 > message.skip) {
throw new IllegalArgumentException(
ExceptionMessage.format(ExceptionMessage.I2CBUS_NEGATIVE_SKIP_ARG)
);
}
for (int i = 0; i < messageList.size(); i++) {
ByteBuffer tx = messageList.get(i).tx;
ByteBuffer rx = messageList.get(i).rx;
if ((null != tx && (tx == message.tx ||
tx == message.rx))
|| (null != rx && (rx == message.tx ||
rx == message.rx))) {
throw new IllegalArgumentException(
ExceptionMessage.format(ExceptionMessage.I2CBUS_BUFFER_GIVEN_TWICE)
);
}
}
if (null != message.rx) {
rxMsgs++;
}
}
/**
* Creates a new {@code SPICompositeMessageImpl} instance.
*/
SPICompositeMessageImpl(SPISlaveImpl device) {
this.device = device;
}
@Override
public SPICompositeMessage appendRead(ByteBuffer rxBuf) throws IOException, ClosedDeviceException {
return appendRead(0, rxBuf);
}
@Override
public SPICompositeMessage appendRead(int rxSkip, ByteBuffer rxBuf) throws IOException, ClosedDeviceException {
//null check
rxBuf.limit();
return append(null, rxSkip, rxBuf);
}
@Override
public SPICompositeMessage appendWrite(ByteBuffer txBuf) throws IOException,
ClosedDeviceException {
//null check
txBuf.limit();
return append(txBuf, 0, null);
}
@Override
public SPICompositeMessage appendWriteAndRead(ByteBuffer src, ByteBuffer dst) throws IOException, ClosedDeviceException {
return appendWriteAndRead(src, 0, dst);
}
@Override
public SPICompositeMessage appendWriteAndRead(ByteBuffer src, int skip, ByteBuffer dst) throws IOException, ClosedDeviceException {
//null check
src.limit();
dst.limit();
return append(src, skip, dst);
}
private synchronized SPICompositeMessage append(ByteBuffer src, int skip, ByteBuffer dst) throws IOException, ClosedDeviceException {
Message message = new Message(src, skip, dst, delay);
check(message);
messageList.add(message);
return this;
}
@Override
public synchronized SPICompositeMessage appendDelay(int delay) throws ClosedDeviceException {
checkStatus();
this.delay = delay;
return this;
}
@Override
public SPIDevice getTargetedDevice() {
return device;
}
/**
* Returns buffers are suitable for low level SPI operations
* New src buffer is located at index 0, dst buffer is at index 1
* The both buffers are direct to avoid native resource allocations in low levels
* The both buffers are the same length
*
* @param originalSrc original array to be sent
* @param originalDst The buffer into which bytes are to be transferred
* @return New buffers in the array. newSrcBuf = array[0], newDstBuf = array[1]
*/
private ByteBuffer[] getBuffersForTransfer(ByteBuffer originalSrc, int skip, ByteBuffer originalDst) {
int bytesInSrc = originalSrc == null ? 0 : originalSrc.remaining();
int bytesInDst = originalDst == null ? 0 : originalDst.remaining();
int newRequiredSizeOfBuffers = bytesInSrc < (skip + bytesInDst) ?
skip + bytesInDst :
bytesInSrc;
ByteBuffer[] array = new ByteBuffer[2];
if (originalSrc == null || !originalSrc.isDirect() || originalSrc.remaining() < newRequiredSizeOfBuffers) {
array[0] = (ByteBuffer) ByteBuffer.allocateDirect(newRequiredSizeOfBuffers);
if (originalSrc != null) {
array[0].put(originalSrc);
}
array[0].rewind();
} else {
//Can not use originalSrc as is, because caller code can change position and limit
//after calling read/write/append operations
array[0] = originalSrc.slice();
}
if (originalDst == null || !originalDst.isDirect() || originalDst.remaining() < newRequiredSizeOfBuffers) {
array[1] = ByteBuffer.allocateDirect(newRequiredSizeOfBuffers);
} else {
//Can not use originalDst as is, because caller code can change position and limit
//after calling read/write/append operations
array[1] = originalDst.slice();
}
return array;
}
@Override
public int[] transfer() throws IOException, UnavailableDeviceException, ClosedDeviceException {
// global handle lock to prevent access from other threads.
synchronized (device.getHandle()) {
/* Forbid adding more messages to this combined message */
isAlreadyTransferedOnce = true;
if (0 == messageList.size()) {
return new int[0];
}
device.beginTransaction();
try {
final int size = messageList.size();
for (int i = 0; i < size; i++) {
Message message = messageList.get(i);
ByteBuffer[] newBuffers = getBuffersForTransfer(message.tx, message.skip, message.rx);
message.newTx = newBuffers[0];
message.newRx = newBuffers[1];
//New buffers have the same length and to avoid a dummy native call
//just make tranfers of non-empty buffers
if (message.newRx.remaining() > 0) {
device.transferWithLock(message.newTx, message.newRx);
}
Thread.currentThread().sleep((message.delay / 1000), (message.delay % 1000) * 1000);
}
} catch (InterruptedException ex) {
throw new InterruptedIOException(ExceptionMessage.format(
ExceptionMessage.SPIBUS_TRANSFER_INTERRUPTED));
} finally {
device.endTransaction();
}
int j = 0;
int[] bytesRead = new int[rxMsgs];
for (Message message : messageList) {
if (message.tx != null) {
message.tx.position(message.tx.limit());
}
if (message.rx != null) {
message.newRx.position(message.skip);
try {
message.newRx.limit(message.skip + message.rx.remaining());
bytesRead[j++] = message.newRx.remaining();
message.rx.put(message.newRx);
} catch (BufferOverflowException | IllegalArgumentException ex) {
//IAE for newRx.limit and BOE is for rx.put
throw new ConcurrentModificationException(
ExceptionMessage.format(
ExceptionMessage.SPIBUS_BYTE_BUFFER_MODIFICATION));
}
}
message.newRx = null;
message.newTx = null;
}
return bytesRead;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy