├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── src └── main.rs └── travis.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | travis.yaml -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "utf8parse", 17 | ] 18 | 19 | [[package]] 20 | name = "anstyle" 21 | version = "1.0.4" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" 24 | 25 | [[package]] 26 | name = "anstyle-parse" 27 | version = "0.2.2" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" 30 | dependencies = [ 31 | "utf8parse", 32 | ] 33 | 34 | [[package]] 35 | name = "anstyle-query" 36 | version = "1.0.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 39 | dependencies = [ 40 | "windows-sys", 41 | ] 42 | 43 | [[package]] 44 | name = "anstyle-wincon" 45 | version = "3.0.1" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" 48 | dependencies = [ 49 | "anstyle", 50 | "windows-sys", 51 | ] 52 | 53 | [[package]] 54 | name = "autocfg" 55 | version = "1.1.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 58 | 59 | [[package]] 60 | name = "bitflags" 61 | version = "1.3.2" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 64 | 65 | [[package]] 66 | name = "bitflags" 67 | version = "2.4.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" 70 | 71 | [[package]] 72 | name = "cfg-if" 73 | version = "1.0.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 76 | 77 | [[package]] 78 | name = "clap" 79 | version = "4.4.6" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" 82 | dependencies = [ 83 | "clap_builder", 84 | "clap_derive", 85 | ] 86 | 87 | [[package]] 88 | name = "clap_builder" 89 | version = "4.4.6" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" 92 | dependencies = [ 93 | "anstream", 94 | "anstyle", 95 | "clap_lex", 96 | "strsim", 97 | ] 98 | 99 | [[package]] 100 | name = "clap_derive" 101 | version = "4.4.2" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" 104 | dependencies = [ 105 | "heck", 106 | "proc-macro2", 107 | "quote", 108 | "syn", 109 | ] 110 | 111 | [[package]] 112 | name = "clap_lex" 113 | version = "0.5.1" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" 116 | 117 | [[package]] 118 | name = "colorchoice" 119 | version = "1.0.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 122 | 123 | [[package]] 124 | name = "heck" 125 | version = "0.4.1" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 128 | 129 | [[package]] 130 | name = "libc" 131 | version = "0.2.149" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" 134 | 135 | [[package]] 136 | name = "memoffset" 137 | version = "0.7.1" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" 140 | dependencies = [ 141 | "autocfg", 142 | ] 143 | 144 | [[package]] 145 | name = "nix" 146 | version = "0.26.4" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" 149 | dependencies = [ 150 | "bitflags 1.3.2", 151 | "cfg-if", 152 | "libc", 153 | "memoffset", 154 | "pin-utils", 155 | ] 156 | 157 | [[package]] 158 | name = "pin-utils" 159 | version = "0.1.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 162 | 163 | [[package]] 164 | name = "proc-macro2" 165 | version = "1.0.67" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" 168 | dependencies = [ 169 | "unicode-ident", 170 | ] 171 | 172 | [[package]] 173 | name = "quote" 174 | version = "1.0.33" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 177 | dependencies = [ 178 | "proc-macro2", 179 | ] 180 | 181 | [[package]] 182 | name = "spicat" 183 | version = "0.1.4" 184 | dependencies = [ 185 | "clap", 186 | "libc", 187 | "spidev", 188 | ] 189 | 190 | [[package]] 191 | name = "spidev" 192 | version = "0.6.0" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "c2800c129338c77998c1dcb6d4200447e018beb7d29959eb104cba2d45f83c1a" 195 | dependencies = [ 196 | "bitflags 2.4.0", 197 | "libc", 198 | "nix", 199 | ] 200 | 201 | [[package]] 202 | name = "strsim" 203 | version = "0.10.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 206 | 207 | [[package]] 208 | name = "syn" 209 | version = "2.0.38" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" 212 | dependencies = [ 213 | "proc-macro2", 214 | "quote", 215 | "unicode-ident", 216 | ] 217 | 218 | [[package]] 219 | name = "unicode-ident" 220 | version = "1.0.12" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 223 | 224 | [[package]] 225 | name = "utf8parse" 226 | version = "0.2.1" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 229 | 230 | [[package]] 231 | name = "windows-sys" 232 | version = "0.48.0" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 235 | dependencies = [ 236 | "windows-targets", 237 | ] 238 | 239 | [[package]] 240 | name = "windows-targets" 241 | version = "0.48.5" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 244 | dependencies = [ 245 | "windows_aarch64_gnullvm", 246 | "windows_aarch64_msvc", 247 | "windows_i686_gnu", 248 | "windows_i686_msvc", 249 | "windows_x86_64_gnu", 250 | "windows_x86_64_gnullvm", 251 | "windows_x86_64_msvc", 252 | ] 253 | 254 | [[package]] 255 | name = "windows_aarch64_gnullvm" 256 | version = "0.48.5" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 259 | 260 | [[package]] 261 | name = "windows_aarch64_msvc" 262 | version = "0.48.5" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 265 | 266 | [[package]] 267 | name = "windows_i686_gnu" 268 | version = "0.48.5" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 271 | 272 | [[package]] 273 | name = "windows_i686_msvc" 274 | version = "0.48.5" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 277 | 278 | [[package]] 279 | name = "windows_x86_64_gnu" 280 | version = "0.48.5" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 283 | 284 | [[package]] 285 | name = "windows_x86_64_gnullvm" 286 | version = "0.48.5" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 289 | 290 | [[package]] 291 | name = "windows_x86_64_msvc" 292 | version = "0.48.5" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 295 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spicat" 3 | description = "Full-duplex SPI transactions from the command line" 4 | version = "0.1.5" 5 | license = "BSD-2-Clause" 6 | repository = "https://github.com/fusion-engineering/spicat" 7 | authors = [ 8 | "Fusion Engineering", 9 | "Maarten de Vries " 10 | ] 11 | edition = "2018" 12 | keywords = ["spi", "spidev"] 13 | categories = ["command-line-utilities", "embedded"] 14 | 15 | [dependencies] 16 | spidev = "0.6" 17 | libc = "0.2.149" 18 | clap = { version = "4.4.6", features = ["derive"] } 19 | 20 | [badges] 21 | travis-ci = { repository = "fusion-engineering/spicat", branch = "master" } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, Fusion Engineering 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/fusion-engineering/spicat.svg?branch=master)](https://travis-ci.org/fusion-engineering/spicat) 2 | 3 | # spicat 4 | 5 | A command line tool to perform full-duplex SPI transactions. 6 | 7 | Example: 8 | ```sh 9 | $ echo -n 'Hello world!' | spicat /dev/spidev1.0 --speed 10000000 10 | 48 65 6c 6c 6f 20 74 68 65 72 65 21 11 | ``` 12 | 13 | This will read 'Hello world!' from standard input, 14 | and send it over SPI to the connected device. 15 | The response will be printed to standard output. 16 | 17 | The output format depends on whether or not output is going to a terminal. 18 | If it is, output is printed in hexadecimal format by default. 19 | Otherwise, the raw bytes are printed by default. 20 | This behaviour can be overridden with the `--format` option. 21 | 22 | The transaction can be repeated a number of times with the `--repeat` option, 23 | to stress-test an SPI bus or device. 24 | 25 | The `--pre-delay` option can be used to add a delay after asserting the chip select, 26 | before transmitting the data. 27 | This can be useful to give an SPI device some time to react to the chip select. 28 | Note that this wait time is implemented by the Linux kernel, 29 | which may mean the exact delay can be a few microseconds longer than the requested value. 30 | 31 | See `spicat --help` for a list of every available option. 32 | 33 | ## Install 34 | Run `cargo install spicat` to install the tool with cargo. 35 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! A command line tool to perform full-duplex SPI transactions. 2 | //! 3 | //! Example: 4 | //! ```sh 5 | //! $ echo -n 'Hello world!' | spicat /dev/spidev1.0 --speed 10000000 6 | //! 48 65 6c 6c 6f 20 74 68 65 72 65 21 7 | //! ``` 8 | //! 9 | //! This will read 'Hello world!' from standard input, 10 | //! and send it over SPI to the connected device. 11 | //! The response will be printed to standard output. 12 | //! 13 | //! The output format depends on whether or not output is going to a terminal. 14 | //! If it is, output is printed in hexadecimal format by default. 15 | //! Otherwise, the raw bytes are printed by default. 16 | //! This behaviour can be overridden with the `--format` option. 17 | //! 18 | //! The transaction can be repeated a number of times with the `--repeat` option, 19 | //! to stress-test an SPI bus or device. 20 | //! 21 | //! The `--pre-delay` option can be used to add a delay after asserting the chip select, 22 | //! before transmitting the data. 23 | //! This can be useful to give an SPI device some time to react to the chip select. 24 | //! Note that this wait time is implemented by the Linux kernel, 25 | //! which may mean the exact delay can be a few microseconds longer than the requested value. 26 | //! 27 | //! See `spicat --help` for a list of every available option. 28 | //! 29 | //! # Install 30 | //! Run `cargo install spicat` to install the tool with cargo. 31 | 32 | use spidev::{Spidev, SpiModeFlags, SpidevTransfer, SpidevOptions}; 33 | use std::io::{Read, Write}; 34 | use std::path::{Path, PathBuf}; 35 | use std::os::unix::io::AsRawFd; 36 | 37 | #[derive(Debug, Copy, Clone)] 38 | #[derive(clap::ValueEnum)] 39 | enum OutputFormat { 40 | #[clap(name = "dec")] 41 | #[clap(alias = "decimal")] 42 | Decimal, 43 | #[clap(name = "hex")] 44 | #[clap(alias = "hexadecimal")] 45 | Hexadecimal, 46 | Raw, 47 | } 48 | 49 | #[repr(u8)] 50 | #[derive(Debug, Copy, Clone)] 51 | #[derive(clap::ValueEnum)] 52 | enum SpiMode { 53 | #[clap(name = "0")] 54 | M0 = 0, 55 | #[clap(name = "1")] 56 | M1 = 1, 57 | #[clap(name = "2")] 58 | M2 = 2, 59 | #[clap(name = "3")] 60 | M3 = 3, 61 | } 62 | 63 | impl SpiMode { 64 | fn flags(&self) -> SpiModeFlags { 65 | match *self { 66 | Self::M0 => SpiModeFlags::SPI_MODE_0, 67 | Self::M1 => SpiModeFlags::SPI_MODE_1, 68 | Self::M2 => SpiModeFlags::SPI_MODE_2, 69 | Self::M3 => SpiModeFlags::SPI_MODE_3, 70 | } 71 | } 72 | } 73 | 74 | #[repr(u8)] 75 | #[derive(Debug, Copy, Clone)] 76 | #[derive(clap::ValueEnum)] 77 | enum ChipSelect { 78 | ActiveLow, 79 | ActiveHigh, 80 | Disabled, 81 | } 82 | 83 | impl ChipSelect { 84 | fn flags(&self) -> SpiModeFlags { 85 | match *self { 86 | ChipSelect::ActiveLow => SpiModeFlags::empty(), 87 | ChipSelect::ActiveHigh => SpiModeFlags::SPI_CS_HIGH, 88 | ChipSelect::Disabled => SpiModeFlags::SPI_NO_CS, 89 | } 90 | } 91 | } 92 | 93 | impl std::fmt::Display for ChipSelect { 94 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 95 | match self { 96 | ChipSelect::ActiveLow => write!(f, "active-low"), 97 | ChipSelect::ActiveHigh => write!(f, "active-high"), 98 | ChipSelect::Disabled => write!(f, "disabled"), 99 | } 100 | } 101 | } 102 | 103 | #[derive(clap::Parser)] 104 | #[clap(author = "Fusion Engineering")] 105 | struct Options { 106 | /// The spidev to open. 107 | #[clap(value_name = "SPIDEV")] 108 | spidev: PathBuf, 109 | 110 | /// Read input from a file, or - for standard input. 111 | #[clap(long = "in", short)] 112 | #[clap(value_name = "PATH")] 113 | #[clap(default_value = "-")] 114 | input: PathBuf, 115 | 116 | /// Write output to a file, or - for standard output. 117 | #[clap(long = "out", short)] 118 | #[clap(value_name = "PATH")] 119 | #[clap(default_value = "-")] 120 | output: PathBuf, 121 | 122 | /// The speed in Hz for the SPI transaction. 123 | #[clap(long, short)] 124 | #[clap(value_name = "HZ")] 125 | #[clap(default_value = "1000000")] 126 | speed: u32, 127 | 128 | /// Repeat the transaction COUNT times, 129 | #[clap(long, short)] 130 | #[clap(value_name = "COUNT")] 131 | #[clap(default_value = "1")] 132 | repeat: usize, 133 | 134 | /// Print the response in the given format: raw, hex[adecimal] or dec[imal]. 135 | /// 136 | /// If not specified, the output format depends on whether output if going to a TTY. 137 | /// If it is, hex is used by default, otherwise raw is used. 138 | #[clap(long, short)] 139 | format: Option, 140 | 141 | /// SPI mode to use: 0, 1, 2 or 3. 142 | #[clap(long)] 143 | #[clap(value_enum)] 144 | #[clap(value_name = "MODE")] 145 | #[clap(default_value = "0")] 146 | mode: SpiMode, 147 | 148 | /// Chip select mode: active-low, active-high or disabled. 149 | #[clap(long)] 150 | #[clap(value_enum)] 151 | #[clap(default_value = "active-low")] 152 | chip_select: ChipSelect, 153 | 154 | /// Bits per word for the SPI transaction. 155 | #[clap(long = "bits")] 156 | #[clap(value_enum)] 157 | #[clap(value_name = "N")] 158 | #[clap(default_value = "8")] 159 | bits_per_word: u8, 160 | 161 | /// Delay in microseconds after enabling the chip select line before sending data. 162 | #[clap(long)] 163 | #[clap(value_name = "MICROSECONDS")] 164 | pre_delay: Option, 165 | } 166 | 167 | fn main() { 168 | do_main(clap::Parser::parse()) 169 | .unwrap_or_else(|error| { 170 | eprintln!("Error: {}", error); 171 | std::process::exit(1); 172 | }); 173 | } 174 | 175 | fn do_main(options: Options) -> Result<(), String> { 176 | let stdin = std::io::stdin(); 177 | let stdout = std::io::stdout(); 178 | 179 | let mut spi = Spidev::open(&options.spidev) 180 | .map_err(|e| format!("Failed to open spidev {}: {}", options.spidev.display(), e))?; 181 | spi.configure(&SpidevOptions::new().bits_per_word(options.bits_per_word).build()) 182 | .map_err(|e| format!("Failed to set {} bits per word: {}", options.bits_per_word, e))?; 183 | spi.configure(&SpidevOptions::new().max_speed_hz(options.speed).build()) 184 | .map_err(|e| format!("Failed to max speed to {} Hz: {}", options.speed, e))?; 185 | spi.configure(&SpidevOptions::new().mode(options.mode.flags()).build()) 186 | .map_err(|e| format!("Failed to set SPI mode to {}: {}", options.mode as u8, e))?; 187 | spi.configure(&SpidevOptions::new().mode(options.chip_select.flags()).build()) 188 | .map_err(|e| format!("Failed to set chip select mode to {}: {}", options.chip_select, e))?; 189 | 190 | 191 | let mut input : Box = if options.input == Path::new("-") { 192 | Box::new(stdin.lock()) 193 | } else { 194 | Box::new(std::fs::File::open(&options.input) 195 | .map_err(|e| format!("Failed to open input file {}: {}", options.input.display(), e))? 196 | ) 197 | }; 198 | 199 | let output_fd: i32; 200 | let mut output : Box = if options.output == Path::new("-") { 201 | let stdout = stdout.lock(); 202 | output_fd = stdout.as_raw_fd(); 203 | Box::new(stdout) 204 | } else { 205 | let file = std::fs::File::create(&options.output) 206 | .map_err(|e| format!("Failed to create output file {}: {}", options.output.display(), e))?; 207 | output_fd = file.as_raw_fd(); 208 | Box::new(file) 209 | }; 210 | 211 | let mut tx_buf = Vec::new(); 212 | input.read_to_end(&mut tx_buf) 213 | .map_err(|e| format!("Failed to read input message: {}", e))?; 214 | 215 | let mut rx_buf = Vec::new(); 216 | rx_buf.resize(tx_buf.len(), 0u8); 217 | 218 | let format = options.format.unwrap_or_else(|| { 219 | if unsafe { libc::isatty(output_fd) } != 0 { 220 | OutputFormat::Hexadecimal 221 | } else { 222 | OutputFormat::Raw 223 | } 224 | }); 225 | 226 | for _ in 0..options.repeat { 227 | // If we have a pre-delay, add a dummy write with delay_usecs and cs_change = 0. 228 | if let Some(pre_delay) = options.pre_delay { 229 | let mut transfers = [ 230 | SpidevTransfer::write(&[]), 231 | SpidevTransfer::read_write(&tx_buf, &mut rx_buf), 232 | ]; 233 | 234 | transfers[0].cs_change = 0; 235 | transfers[0].delay_usecs = pre_delay; 236 | transfers[0].speed_hz = options.speed; 237 | transfers[1].speed_hz = options.speed; 238 | spi.transfer_multiple(&mut transfers) 239 | .map_err(|e| format!("SPI transaction failed: {}", e))?; 240 | 241 | // Else just do the single transfer. 242 | } else { 243 | let mut transfer = SpidevTransfer::read_write(&tx_buf, &mut rx_buf); 244 | transfer.speed_hz = options.speed; 245 | spi.transfer(&mut transfer) 246 | .map_err(|e| format!("SPI transaction failed: {}", e))?; 247 | } 248 | 249 | 250 | // Print the received data in the desired format. 251 | match format { 252 | OutputFormat::Raw => { 253 | output.write_all(&rx_buf).map_err(|e| format!("Failed to write to output stream: {}", e))?; 254 | }, 255 | OutputFormat::Hexadecimal => { 256 | for (i, byte) in rx_buf.iter().enumerate() { 257 | if i != 0 { 258 | write!(output, " ").map_err(|e| format!("Failed to write to output stream: {}", e))?; 259 | } 260 | write!(output, "{:02X}", byte).map_err(|e| format!("Failed to write to output stream: {}", e))?; 261 | } 262 | writeln!(output).map_err(|e| format!("Failed to write to output stream: {}", e))?; 263 | }, 264 | OutputFormat::Decimal => { 265 | for (i, byte) in rx_buf.iter().enumerate() { 266 | if i != 0 { 267 | write!(output, " ").map_err(|e| format!("Failed to write to output stream: {}", e))?; 268 | } 269 | write!(output, "{}", byte).map_err(|e| format!("Failed to write to output stream: {}", e))?; 270 | } 271 | writeln!(output).map_err(|e| format!("Failed to write to output stream: {}", e))?; 272 | }, 273 | } 274 | } 275 | 276 | Ok(()) 277 | } 278 | -------------------------------------------------------------------------------- /travis.yaml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | --------------------------------------------------------------------------------