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:  http://www.pi4j.com/
012 * **********************************************************************
013 * %%
014 * Copyright (C) 2012 - 2015 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 = Spi.wiringPiSPISetupMode(channel.getChannel(), speed, mode.getMode());
066            if (fd <= -1) {
067                throw new IOException("SPI port setup failed, wiringPiSPISetupMode returned " + fd);
068            }
069        } catch (UnsatisfiedLinkError e) {
070            throw new IOException("SPI port setup failed, no SPI available.", e);
071        }
072    }
073
074    /**
075     * Creates the SPI Device at the given spi and input channel
076     *
077     * @param channel
078     *            spi channel to use
079     * @param speed
080     *            spi speed/rate (in Hertz) for channel to communicate at
081     *            (range is 500kHz - 32MHz)
082     */
083    public SpiDeviceImpl(SpiChannel channel, int speed) throws IOException {
084        this(channel, speed, DEFAULT_SPI_MODE);
085    }
086
087    /**
088     * Creates the SPI Device at the given SPI and input channel
089     * (A default speed of 1 MHz will be used)
090     *
091     * @param channel
092     *            spi channel to use
093     * @param mode
094     *            spi mode (see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers)
095     */
096    public SpiDeviceImpl(SpiChannel channel, SpiMode mode) throws IOException {
097        this(channel, DEFAULT_SPI_SPEED, mode);
098    }
099
100    /**
101     * Creates the SPI Device at the given SPI and input channel
102     * (A default speed of 1 MHz will be used)
103     *
104     * @param channel
105     *            spi channel to use
106     */
107    public SpiDeviceImpl(SpiChannel channel) throws IOException {
108        this(channel, DEFAULT_SPI_SPEED);
109    }
110
111    @Override
112    public String write(String data, String charset) throws IOException {
113        byte[] buffer = data.getBytes(charset);
114        return new String(write(buffer), charset);
115    }
116
117    @Override
118    public String write(String data, Charset charset) throws IOException {
119        byte[] buffer = data.getBytes(charset);
120        return new String(write(buffer), charset);
121    }
122
123    @Override
124    public ByteBuffer write(ByteBuffer data) throws IOException {
125        return ByteBuffer.wrap(write(data.array()));
126    }
127
128    @Override
129    public byte[] write(InputStream input) throws IOException {
130
131        // ensure bytes are available
132        if(input.available() <= 0){
133            throw new IOException("No available bytes in input stream to write to SPI channel: " + channel.getChannel());
134        }
135        else if(input.available() > MAX_SUPPORTED_BYTES){
136            throw new IOException("Number of bytes in stream exceed the maximum bytes allowed to write SPI channel in a single call");
137        }
138
139        // create a temporary buffer to store read bytes from stream
140        byte[] buffer = new byte[MAX_SUPPORTED_BYTES];
141
142        // read maximum number of supported bytes
143        int length = input.read(buffer, 0 , MAX_SUPPORTED_BYTES);
144
145        // write bytes to SPI channel
146        return write(buffer, 0, length);
147    }
148
149    @Override
150    public int write(InputStream input, OutputStream output) throws IOException {
151        // write stream data to SPI device
152        byte[] buffer = write(input);
153
154        //write resulting byte array to output stream
155        output.write(buffer);
156
157        // return data length
158        return buffer.length;
159    }
160
161    @Override
162    public byte[] write(byte... data) throws IOException {
163        return write(data, 0, data.length);
164    }
165
166    @Override
167    public short[] write(short... data) throws IOException {
168        return write(data, 0, data.length);
169    }
170
171    @Override
172    public byte[] write(byte[] data, int start, int length) throws IOException {
173
174        // ensure the length does not exceed the data array
175        length = Math.min(data.length - start, length);
176
177        // validate max length allowed
178        if (length > MAX_SUPPORTED_BYTES) {
179            throw new IOException("Number of bytes in data to write exceed the maximum bytes allowed to write SPI channel in a single call");
180        }
181
182        // we make a copy of the data argument because we don't want to modify the original source data
183        byte[] buffer = new byte[length];
184        System.arraycopy(data, start, buffer, 0, length);
185
186        synchronized (channel) {
187                // write the bytes from the temporary buffer to the SPI channel
188                if (Spi.wiringPiSPIDataRW(channel.getChannel(), buffer) <= 0) {
189                    throw new IOException("Failed to write data to SPI channel: " + channel.getChannel());
190                }
191            }
192        // return the updated byte buffer as the SPI read results
193        return buffer;
194    }
195
196    @Override
197    public short[] write(short[] data, int start, int length) throws IOException {
198
199        // ensure the length does not exceed the data array
200        length = Math.min(data.length - start, length);
201
202        // validate max length allowed
203        if (length > MAX_SUPPORTED_BYTES) {
204            throw new IOException("Number of bytes in data to write exceed the maximum bytes allowed to write SPI channel in a single call");
205        }
206
207        // we make a copy of the data argument because we don't want to modify the original source data
208        short[] buffer = new short[length];
209        System.arraycopy(data, start, buffer, 0, length);
210
211        synchronized (channel) {
212            // write the bytes from the temporary buffer to the SPI channel
213            if (Spi.wiringPiSPIDataRW(channel.getChannel(), buffer) <= 0) {
214                throw new IOException("Failed to write data to SPI channel: " + channel.getChannel());
215            }
216
217            // return the updated byte buffer as the SPI read results
218            return buffer;
219        }
220    }
221
222}