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

com.oracle.dio.spibus.impl.SPICompositeMessageImpl Maven / Gradle / Ivy

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