001package com.pi4j.io.spi.impl;
002
003/*
004 * #%L
005 * **********************************************************************
006 * ORGANIZATION  :  Pi4J
007 * PROJECT       :  Pi4J :: Java Library (Core)
008 * FILENAME      :  SpiDeviceImpl.java
009 *
010 * This file is part of the Pi4J project. More information about
011 * this project can be found here:  https://www.pi4j.com/
012 * **********************************************************************
013 * %%
014 * Copyright (C) 2012 - 2019 Pi4J
015 * %%
016 * This program is free software: you can redistribute it and/or modify
017 * it under the terms of the GNU Lesser General Public License as
018 * published by the Free Software Foundation, either version 3 of the
019 * License, or (at your option) any later version.
020 *
021 * This program is distributed in the hope that it will be useful,
022 * but WITHOUT ANY WARRANTY; without even the implied warranty of
023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
024 * GNU General Lesser Public License for more details.
025 *
026 * You should have received a copy of the GNU General Lesser Public
027 * License along with this program.  If not, see
028 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
029 * #L%
030 */
031
032
033import com.pi4j.io.spi.SpiChannel;
034import com.pi4j.io.spi.SpiDevice;
035import com.pi4j.io.spi.SpiMode;
036import com.pi4j.wiringpi.Spi;
037
038import java.io.IOException;
039import java.io.InputStream;
040import java.io.OutputStream;
041import java.nio.ByteBuffer;
042import java.nio.charset.Charset;
043
044public class SpiDeviceImpl implements SpiDevice {
045
046    protected final SpiChannel channel;
047    protected final SpiMode mode;
048
049    /**
050     * Creates the SPI Device at the given spi and input channel
051     *
052     * @param channel
053     *            spi channel to use
054     * @param speed
055     *            spi speed/rate (in Hertz) for channel to communicate at
056     *            (range is 500kHz - 32MHz)
057     * @param mode
058     *            spi mode (see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers)
059     *
060     */
061    public SpiDeviceImpl(SpiChannel channel, int speed, SpiMode mode) throws IOException {
062        this.channel = channel;
063        this.mode = mode;
064        try {
065            int fd;
066            if(mode == SpiMode.MODE_0){
067                // using this hack because the Odroid port of WiringPi does not currently support 'wiringPiSPISetupMode'
068                fd = Spi.wiringPiSPISetup(channel.getChannel(), speed);
069            }
070            else{
071                fd = Spi.wiringPiSPISetupMode(channel.getChannel(), speed, mode.getMode());
072            }
073            if (fd <= -1) {
074                throw new IOException("SPI port setup failed, wiringPiSPISetupMode returned " + fd);
075            }
076        } catch (UnsatisfiedLinkError e) {
077            throw new IOException("SPI port setup failed, no SPI available.", e);
078        }
079    }
080
081    /**
082     * Creates the SPI Device at the given spi and input channel
083     *
084     * @param channel
085     *            spi channel to use
086     * @param speed
087     *            spi speed/rate (in Hertz) for channel to communicate at
088     *            (range is 500kHz - 32MHz)
089     */
090    public SpiDeviceImpl(SpiChannel channel, int speed) throws IOException {
091        this(channel, speed, DEFAULT_SPI_MODE);
092    }
093
094    /**
095     * Creates the SPI Device at the given SPI and input channel
096     * (A default speed of 1 MHz will be used)
097     *
098     * @param channel
099     *            spi channel to use
100     * @param mode
101     *            spi mode (see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers)
102     */
103    public SpiDeviceImpl(SpiChannel channel, SpiMode mode) throws IOException {
104        this(channel, DEFAULT_SPI_SPEED, mode);
105    }
106
107    /**
108     * Creates the SPI Device at the given SPI and input channel
109     * (A default speed of 1 MHz will be used)
110     *
111     * @param channel
112     *            spi channel to use
113     */
114    public SpiDeviceImpl(SpiChannel channel) throws IOException {
115        this(channel, DEFAULT_SPI_SPEED);
116    }
117
118    @Override
119    public String write(String data, String charset) throws IOException {
120        byte[] buffer = data.getBytes(charset);
121        return new String(write(buffer), charset);
122    }
123
124    @Override
125    public String write(String data, Charset charset) throws IOException {
126        byte[] buffer = data.getBytes(charset);
127        return new String(write(buffer), charset);
128    }
129
130    @Override
131    public ByteBuffer write(ByteBuffer data) throws IOException {
132        return ByteBuffer.wrap(write(data.array()));
133    }
134
135    @Override
136    public byte[] write(InputStream input) throws IOException {
137
138        // ensure bytes are available
139        if(input.available() <= 0){
140            throw new IOException("No available bytes in input stream to write to SPI channel: " + channel.getChannel());
141        }
142        else if(input.available() > MAX_SUPPORTED_BYTES){
143            throw new IOException("Number of bytes in stream exceed the maximum bytes allowed to write SPI channel in a single call");
144        }
145
146        // create a temporary buffer to store read bytes from stream
147        byte[] buffer = new byte[MAX_SUPPORTED_BYTES];
148
149        // read maximum number of supported bytes
150        int length = input.read(buffer, 0 , MAX_SUPPORTED_BYTES);
151
152        // write bytes to SPI channel
153        return write(buffer, 0, length);
154    }
155
156    @Override
157    public int write(InputStream input, OutputStream output) throws IOException {
158        // write stream data to SPI device
159        byte[] buffer = write(input);
160
161        //write resulting byte array to output stream
162        output.write(buffer);
163
164        // return data length
165        return buffer.length;
166    }
167
168    @Override
169    public byte[] write(byte... data) throws IOException {
170        return write(data, 0, data.length);
171    }
172
173    @Override
174    public short[] write(short... data) throws IOException {
175        return write(data, 0, data.length);
176    }
177
178    @Override
179    public byte[] write(byte[] data, int start, int length) throws IOException {
180
181        // ensure the length does not exceed the data array
182        length = Math.min(data.length - start, length);
183
184        // validate max length allowed
185        if (length > MAX_SUPPORTED_BYTES) {
186            throw new IOException("Number of bytes in data to write exceed the maximum bytes allowed to write SPI channel in a single call");
187        }
188
189        // we make a copy of the data argument because we don't want to modify the original source data
190        byte[] buffer = new byte[length];
191        System.arraycopy(data, start, buffer, 0, length);
192
193        synchronized (channel) {
194                // write the bytes from the temporary buffer to the SPI channel
195                if (Spi.wiringPiSPIDataRW(channel.getChannel(), buffer) <= 0) {
196                    throw new IOException("Failed to write data to SPI channel: " + channel.getChannel());
197                }
198            }
199        // return the updated byte buffer as the SPI read results
200        return buffer;
201    }
202
203    @Override
204    public short[] write(short[] data, int start, int length) throws IOException {
205
206        // ensure the length does not exceed the data array
207        length = Math.min(data.length - start, length);
208
209        // validate max length allowed
210        if (length > MAX_SUPPORTED_BYTES) {
211            throw new IOException("Number of bytes in data to write exceed the maximum bytes allowed to write SPI channel in a single call");
212        }
213
214        // we make a copy of the data argument because we don't want to modify the original source data
215        short[] buffer = new short[length];
216        System.arraycopy(data, start, buffer, 0, length);
217
218        synchronized (channel) {
219            // write the bytes from the temporary buffer to the SPI channel
220            if (Spi.wiringPiSPIDataRW(channel.getChannel(), buffer) <= 0) {
221                throw new IOException("Failed to write data to SPI channel: " + channel.getChannel());
222            }
223
224            // return the updated byte buffer as the SPI read results
225            return buffer;
226        }
227    }
228
229}