Page MenuHomeFreeBSD

D15029.1794764753.diff
No OneTemporary

Authored By
Unknown
Size
24 KB
Referenced Files
None
Subscribers
None

D15029.1794764753.diff

Index: usr.sbin/spi/Makefile
===================================================================
--- /dev/null
+++ usr.sbin/spi/Makefile
@@ -0,0 +1,10 @@
+# spi utility - author: Bob Frazier, S.F.T. Inc. (mrp3.com)
+# $FreeBSD:
+
+#.include <src.opts.mk>
+
+PROG= spi
+
+MAN8= spi.8
+
+.include <bsd.prog.mk>
Index: usr.sbin/spi/spi.8
===================================================================
--- /dev/null
+++ usr.sbin/spi/spi.8
@@ -0,0 +1,111 @@
+.Dd "14 March 2018"
+.Dt spi 8
+.Os
+.SH NAME
+.Nm spi
+.Nd communicate on SPI bus with slave devices
+
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar device
+.Op Fl d Ar r|w|rw
+.Op Fl m Ar mode
+.Op Fl s Ar max-speed
+.Op Fl c Ar count
+.Op Fl A
+.Op Fl b
+.Op Fl L
+.Op Fl v
+.Nm
+.Op Fl i
+.Op Fl f Ar device
+.Op Fl v
+.Nm
+.Op Fl h
+
+.Sh DESCRIPTION
+The \f[B]spi\fP utility can be used to perform raw data transfers (read, write,
+or simultaneous read/write) with devices on the SPI bus, via the spigen device.
+
+An spigen device is associated with a specific \'chip select\' (cs) pin on the
+spibus, and therefore needs to be specified. If no spigen device name is
+specified on the command line, \f[B]spi\fP assumes spigen0.
+
+For more information on the spigen device, see spigen(4).
+
+The options are as follows:
+.Bl -tag -width ".Fl d Ar direction"
+.It Fl A
+ASCII mode - read and write data is input and output as 2-digit hexadecimal
+values, separaed by white space, such as 00 01 02 etc.. When combined with
+the '-b' flag, the data on stdin remains a sequence of ASCII hexadecimal
+byte values, but the output reverts to binary mode.
+.It Fl b
+Binary (output) mode. Only has an effect when '-A' has been specified. Reverts
+the output back to binary (rather than ASCII), while leaving the input format
+as-is. Use in combination with '-A' to allow using something like 'echo' to
+pass hexadecimal values to the SPI device, but output the received data on
+stdout as binary.
+.It Fl c Ar count
+The total number of bytes to transfer as a decimal integer. If a write or a
+read/write transaction is being performed, and fewer than this number of bytes
+are read in from stdin, the remaining bytes will be sent with a value of '0'.
+If the length can be determined from the input file size, you can use a 'count'
+value of '-1' to base the transfer on the input file's size.
+.It Fl d Ar r|w|rw
+Transfer direction: Use 'r' for read, 'w' for write, and 'rw' for simultaneous
+read and write.
+.It Fl f Ar device
+SPI device to use (default is /dev/spigen0).
+.It Fl h
+Print help text to stderr, explaining the command line options.
+.It Fl i
+Displays information about the SPI device to stderr. Whenever this flag is
+specified, no data is read or written, and the mode and speed are not changed.
+.It Fl L
+LSB bit order. The default is MSB, i.e. the highest order bit is transmitted
+first. Specifying -L caused the LSB to be transmitted and read first.
+.It Fl m Ar 0 - 3
+SPI mode, 0 through 3. This defines the clock phase and timing with respect to
+reading and writing data, as per the SPI specification.
+.It Fl s Ar speed
+Specify the maximum speed, in Hz, for the SPI clock. If the device has been
+configured for a lower speed, or cannot transmit at the specified speed, the lower
+value will be used.
+.It Fl v
+Verbose mode. Diagnostics and information are written to stderr. You can
+specify '-v' more than once to increase verbosity.
+.El
+.Sh EXAMPLES
+Here are a few examples of using the spi utility:
+.Bl -bullet
+.It
+Get information about the default SPI device
+.Pp
+spi -i
+.It
+Set the maximum clock to 200Khz and the mode to 3 on spigen1, but do not
+transmit nor receive any data
+.Pp
+spi -f spigen1 -s 200000 -m 3
+.It
+Transmit a byte value of 5, and receive 2 bytes, displaying their values as
+2-byte ASCII hexadecimal, with mode 2, and a maximum of a 500khz clock
+.Pp
+echo "5" | spi -A -d rw -m 2 -s 500000 -c 2
+.It
+Send a binary file, and output the SPI result through 'od' as hexadecimal bytes,
+using the current maximum clock and mode.
+.Pp
+spi -d rw -c -1 <input_file.bin | od -t x1
+.It
+Send 2 bytes of data, receive a total of 4 bytes, and output the SPI result as
+binary data, piped through 'od', displaying it as two hexadecimal short integers.
+.Pp
+echo "00 01" | spi -A -b -d rw -c 4 | od -t x2
+.El
+
+.Sh SEE ALSO
+spigen(4)
+
+
Index: usr.sbin/spi/spi.c
===================================================================
--- /dev/null
+++ usr.sbin/spi/spi.c
@@ -0,0 +1,895 @@
+/*-
+ *
+ * COPYRIGHT: Copyright (c) 2018 by S.F.T. Inc. - All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* use indent infile outfile -nce -di8 -i8 -lp */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <memory.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/ioccom.h>
+#include <sys/spigenio.h>
+#include <sys/sysctl.h>
+
+#define DEFAULT_DEVICE_NAME "spigen0"
+
+#define DIR_READ 0
+#define DIR_WRITE 1
+#define DIR_READWRITE 2
+#define DIR_NONE -1
+#define DEV_NAME_OFFSET 5
+
+struct spi_options {
+ int mode; /* mode (0,1,2,3, -1 == use default) */
+ int speed; /* speed (in Hz, -1 == use default) */
+ int count; /* count (0 through 'n' bytes, negative for
+ * stdin length) */
+ int binary; /* non-zero for binary output or zero for
+ * ASCII output when ASCII != 0 */
+ int ASCII; /* zero for binary input and output.
+ * non-zero for ASCII input, 'binary'
+ * determines output */
+ int lsb; /* non-zero for LSB order (default order is
+ * MSB) */
+ int verbose; /* non-zero for verbosity */
+ int max_cmd_len; /* max command length from driver sysctl */
+ int max_data_len; /* max data length from driver sysctl */
+};
+
+int get_sysctl_byname(const char *dev_name, const char *var_name, void *pbuf, int bufsize);
+int get_info(int hdev, const char *dev_name);
+int set_mode(int hdev, struct spi_options *popt);
+int set_speed(int hdev, struct spi_options *popt);
+int perform_read(int hdev, struct spi_options *popt);
+int perform_write(int hdev, struct spi_options *popt);
+int perform_readwrite(int hdev, struct spi_options *popt);
+void verbose_dump_buffer(void *pBuf, int icount, int lsb);
+
+/* LSB array - aLSB[n] is the LSB value of n as an MSB */
+static uint8_t aLSB[256] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+
+void
+usage(void)
+{
+ fputs(getprogname(), stderr);
+ fputs(" - communicate on SPI bus with slave devices\n"
+ "Usage:\n"
+ " spi [-f device] [-d r|w|rw] [-m mode] [-s max-speed] [-c count]\n"
+ " [-A] [-b] [-L] [-v]\n"
+ " spi -i [-f device] [-v]\n"
+ " spi -h\n"
+ " where\n"
+ " -f specifies the device (default is spigen0)\n"
+ " -d specifies the operation (r, w, or rw; default is rw)\n"
+ " -m specifies the mode (0, 1, 2, or 3)\n"
+ " -s specifies the maximum speed (default is 0, device default)\n"
+ " -c specifies the number of bytes to transfer (default 0, i.e. none)\n"
+ " A negative value uses the length of the input data\n"
+ " -L specifies 'LSB' order on the SPI bus (default is MSB)\n"
+ " -i query information about the device\n"
+ " -A uses ASCII for input/output as 2-digit hex values\n"
+ " -b Override output format as binary (only valid with '-A')\n"
+ " -v verbose output\n"
+ " -h prints this message\n"
+ "\n"
+ "NOTE: setting the mode and/or speed is 'sticky'. Subsequent transactions\n"
+ " on that device will, by default, use the previously set values.\n"
+ "\n",
+ stderr);
+}
+
+int
+main(int argc, char *argv[], char *envp[])
+{
+ int err, ch, hdev, fInfo, fDir;
+ struct spi_options opt;
+ char dev_name[PATH_MAX * 2 + 5];
+ char temp_buf[PATH_MAX];
+
+
+ fInfo = 0;
+ fDir = DIR_NONE;
+
+ hdev = -1;
+ err = 0;
+
+ /* copy the well-known /dev/ path into 'dev_name' */
+
+ strcpy(dev_name, "/dev/"); /* dev_name[DEV_NAME_OFFSET] is where
+ * I write the dev name later */
+
+ opt.mode = -1;
+ opt.speed = -1;
+ opt.count = 0;
+ opt.ASCII = 0;
+ opt.binary = 0;
+ opt.lsb = 0;
+ opt.verbose = 0;
+
+ while (!err && (ch = getopt(argc, argv, "f:d:m:s:c:AbLvih")) != -1) {
+ switch (ch) {
+ case 'd':
+ if (optarg[0] == 'r') {
+ if (optarg[1] == 'w' && optarg[2] == 0) {
+ fDir = DIR_READWRITE;
+ }
+ else if (optarg[1] == 0) {
+ fDir = DIR_READ;
+ }
+ }
+ else if (optarg[0] == 'w' && optarg[1] == '0') {
+ fDir = DIR_WRITE;
+ }
+ else {
+ err = 1;
+ }
+ break;
+
+ case 'f':
+ if (!optarg[0]) { /* unlikely */
+ fputs("error - missing device name\n", stderr);
+ err = 1;
+ }
+ else {
+ strncat(&(dev_name[DEV_NAME_OFFSET]), optarg, sizeof(dev_name) - DEV_NAME_OFFSET - 1);
+ }
+ break;
+
+ case 'm':
+ opt.mode = atoi(optarg);
+
+ if (opt.mode < 0 || opt.mode > 3) {
+ fprintf(stderr, "Invalid mode specified: %d\n", opt.mode);
+ err = 1;
+ }
+ break;
+
+ case 's':
+ opt.speed = atoi(optarg);
+
+ if (opt.speed < 0) {
+ fprintf(stderr, "Invalid speed specified: %d\n", opt.speed);
+ err = 1;
+ }
+ break;
+
+ case 'c':
+ opt.count = atoi(optarg);
+ break;
+
+ case 'A':
+ opt.ASCII = 1;
+ break;
+
+ case 'b':
+ opt.binary = 1;
+ break;
+
+ case 'L':
+ opt.lsb = 1;
+ break;
+
+ case 'v':
+ opt.verbose++;
+ break;
+
+ case 'i':
+ fInfo = 1;
+ break;
+
+ default:
+ err = 1;
+ /* flows thruogh to next one */
+ case 'h':
+ usage();
+ goto the_end;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (err ||
+ (fDir == DIR_NONE && !fInfo && opt.mode == -1 && opt.speed == -1 && opt.count == 0)) {
+ /*
+ * if neither the direction, mode, speed, nor count not
+ * specified, print usage
+ */
+
+ usage();
+ goto the_end;
+ }
+
+ if (opt.count != 0 && fDir == DIR_NONE) {
+ /*
+ * count was specified, but direction was not. default is
+ * read/write
+ */
+ /*
+ * this includes a negative count, which implies write from
+ * stdin
+ */
+
+ fDir = DIR_READWRITE;
+ }
+
+ if (opt.count < 0 && fDir != DIR_READWRITE && fDir != DIR_WRITE) {
+ fprintf(stderr, "Invalid length %d when not writing data\n", opt.count);
+
+ err = 1;
+ usage();
+ goto the_end;
+ }
+
+
+ if (!dev_name[5]) /* no device name specified */
+ strncat(dev_name, DEFAULT_DEVICE_NAME, sizeof(dev_name) - 1);
+
+
+ hdev = open(dev_name, O_RDWR);
+
+ if (hdev == -1) {
+ fprintf(stderr, "Error - unable to open '%s', errno=%d\n", dev_name, errno);
+ err = 1;
+
+ goto the_end;
+ }
+
+ if (fInfo) {
+ err = get_info(hdev, dev_name);
+ goto the_end;
+ }
+
+
+ /* get max command,data length from sysctl */
+
+ err = get_sysctl_byname(dev_name, "command_length_max", &(opt.max_cmd_len), sizeof(opt.max_cmd_len));
+ if (err)
+ goto the_end;
+
+ err = get_sysctl_byname(dev_name, "data_length_max", &(opt.max_data_len), sizeof(opt.max_data_len));
+
+ if (err)
+ goto the_end;
+
+
+ /*
+ * if what I'm doing exceeds the sysctl max data/cmd length, it's an
+ * error [for now]
+ */
+
+ if (opt.count > opt.max_cmd_len || opt.count > opt.max_data_len) {
+ fprintf(stderr,
+ "Error, data count %d exceeds maximum command/data length %d\n",
+ opt.count,
+ (opt.max_cmd_len < opt.max_data_len ?
+ opt.max_cmd_len : opt.max_data_len));
+
+ err = 1;
+ goto the_end;
+ }
+
+ /* check and assign mode, speed */
+
+ opt.max_data_len = atoi(temp_buf);
+
+ if (opt.mode != -1) {
+ err = set_mode(hdev, &opt);
+
+ if (err)
+ goto the_end;
+ }
+
+ if (opt.speed != -1) {
+ err = set_speed(hdev, &opt);
+
+ if (err)
+ goto the_end;
+ }
+
+ /* do data transfer */
+
+ if (fDir == DIR_READ) {
+ err = perform_read(hdev, &opt);
+ }
+ else if (fDir == DIR_WRITE) {
+ err = perform_write(hdev, &opt);
+ }
+ else if (fDir == DIR_READWRITE) {
+ err = perform_readwrite(hdev, &opt);
+ }
+
+the_end:
+
+ if (hdev != -1) {
+ close(hdev);
+ }
+
+ return err;
+}
+
+int
+get_sysctl_byname(const char *dev_name, const char *var_name, void *pbuf, int bufsize)
+{
+ size_t cbbuf;
+ int err;
+ char the_var[PATH_MAX];
+
+
+ if (!pbuf || bufsize <= 0)
+ return -1; /* simple param check */
+
+ strncpy(the_var, "dev.spigen0.", sizeof(the_var) - 1); /* default */
+
+ cbbuf = bufsize;
+
+ if (!strncmp(&(dev_name[DEV_NAME_OFFSET]), "spigen", 6)) {
+ snprintf(the_var, sizeof(the_var) - 1, "dev.spigen.%d.", atoi(&(dev_name[DEV_NAME_OFFSET + 6])));
+
+ strncat(the_var, var_name, sizeof(the_var) - 1);
+
+ err = sysctlbyname(the_var, pbuf, &cbbuf, NULL, 0);
+
+ if (err)
+ fprintf(stderr, "Unable to get sysctl %s - err=%d, errno=%d\n",
+ the_var, err, errno);
+ else if (cbbuf < (bufsize - 1)) { /* assume it is a
+ * string? */
+ ((char *)pbuf)[cbbuf] = 0;
+ }
+ }
+ else {
+ fprintf(stderr, "Unrecognized device - '%s'\n", &(dev_name[DEV_NAME_OFFSET]));
+ err = 1;
+ }
+
+ return err;
+}
+
+int
+get_info(int hdev, const char *dev_name)
+{
+ uint32_t dwMode, dwSpeed, max_cmd, max_data;
+ int err;
+ char temp_buf[PATH_MAX];
+
+
+ err = ioctl(hdev, SPIGENIOC_GET_SPI_MODE, &dwMode);
+
+ if (err == 0)
+ err = ioctl(hdev, SPIGENIOC_GET_CLOCK_SPEED, &dwSpeed);
+
+ if (err == 0)
+ err = get_sysctl_byname(dev_name, "command_length_max", &max_cmd, sizeof(max_cmd));
+
+ if (err == 0)
+ err = get_sysctl_byname(dev_name, "data_length_max", &max_data, sizeof(&max_data));
+
+ if (err == 0)
+ err = get_sysctl_byname(dev_name, "%location", temp_buf, sizeof(temp_buf));
+
+ if (err == 0) {
+ fprintf(stderr,
+ "Device name: %s\n"
+ "Device mode: %d\n"
+ "Device speed: %d\n"
+ "Max cmd len: %d\n"
+ "Max data len: %d\n"
+ "Chip Select: %s\n",
+ dev_name, dwMode, dwSpeed, max_cmd, max_data, temp_buf);
+ }
+ else
+ fprintf(stderr, "Unable to query info (err=%d), errno=%d\n", err, errno);
+
+
+ return err;
+}
+
+int
+set_mode(int hdev, struct spi_options *popt)
+{
+ uint32_t dwMode = popt->mode;
+
+ if (popt->mode < 0) /* use default? */
+ return 0;
+
+ return ioctl(hdev, SPIGENIOC_SET_SPI_MODE, &dwMode);
+}
+
+int
+set_speed(int hdev, struct spi_options *popt)
+{
+ uint32_t dwClockSpeed = popt->speed;
+
+ if (popt->speed < 0)
+ return 0;
+
+ return ioctl(hdev, SPIGENIOC_SET_CLOCK_SPEED, &dwClockSpeed);
+}
+
+int
+hexval(char c)
+{
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ }
+ else if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ }
+ else if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ }
+ return -1;
+}
+
+void *
+prep_write_buffer(struct spi_options *popt)
+{
+ int ch , ch2, ch3, err;
+ uint8_t *pData;
+ size_t cbData, cbRead;
+
+
+ if (popt->count == 0)
+ return NULL; /* always since it's an error if it happens
+ * now */
+
+ if (popt->count < 0) {
+ /* read data until EOF - malloc maximum possible buffer size */
+
+ cbData = popt->max_cmd_len + popt->max_data_len;
+
+ if (!cbData) { /* we can't handle this */
+ fprintf(stderr, "Unable to alloc input buffer; max size not known\n");
+ return NULL;
+ }
+ }
+ else {
+ cbData = popt->count;
+ }
+
+ pData = malloc(cbData + 1);
+ cbRead = 0;
+
+ err = 0;
+
+ if (!pData)
+ return NULL;
+
+ /*
+ * read up to 'cbData' bytes. If I get an EOF, do one of two things:
+ * a) change the data count to match how many bytes I read in b) fill
+ * the rest of the input buffer with zeros
+ *
+ * If the specified length is negative, I do 'a', else 'b'
+ */
+
+ while (!err && cbRead < cbData && (ch = fgetc(stdin)) != EOF) {
+ if (popt->ASCII) {
+ /* skip consecutive white space */
+
+ while (ch <= ' ') {
+ if ((ch = fgetc(stdin)) == EOF)
+ break;
+ }
+
+ if (ch != EOF) {
+ ch2 = hexval(ch);
+
+ if (ch2 < 0) {
+ invalid_character:
+ fprintf(stderr, "Invalid input character '%c'\n", ch);
+ err = 1;
+ break;
+ }
+
+ ch = fgetc(stdin);
+
+ if (ch != EOF) {
+ ch3 = hexval(ch);
+
+ if (ch3 < 0)
+ goto invalid_character;
+
+ ch = ch2 * 16 + ch3;
+ }
+ }
+
+ if (err || ch == EOF)
+ break;
+ }
+
+ /* for LSB, flip the bits - otherwise, just copy the value */
+ if (popt->lsb)
+ pData[cbRead++] = aLSB[ch];
+ else
+ pData[cbRead++] = (uint8_t) ch;
+ }
+
+ /* if it was an error, not an EOF, that ended the I/O, return NULL */
+
+ if (err || ferror(stdin)) {
+ free(pData);
+ return NULL;
+ }
+
+ if (popt->verbose > 0) {
+ const char *szB;
+
+ if (cbRead != 1)
+ szB = "bytes"; /* correct plurality of 'byte|bytes' */
+ else
+ szB = "byte";
+
+ if (popt->ASCII)
+ fprintf(stderr, "ASCII input of %d %s\n", cbRead, szB);
+ else
+ fprintf(stderr, "Binary input of %d %s\n", cbRead, szB);
+ }
+
+ /* if opt.count is negative, copy actual byte count to opt.count */
+ if (popt->count < 0) {
+ popt->count = cbRead;
+ }
+ /*
+ * for everything else, fill the rest of the read buffer with '0'
+ * bytes
+ */
+ else {
+ while (cbRead < cbData)
+ pData[cbRead++] = 0;
+ }
+
+ /* popt->count bytes will be sent and read from the SPI */
+ /* must use 'popt->count at this point in the code */
+
+ if (popt->verbose > 0 && popt->count) {
+ fprintf(stderr, "Writing %d bytes to SPI device\n", popt->count);
+ verbose_dump_buffer(pData, popt->count, popt->lsb);
+ }
+
+ return pData;
+}
+
+int
+_read_write(int hdev, void *bufW, void *bufR, int cbRW, int lsb)
+{
+ int err, ctr;
+ struct spigen_transfer spi;
+
+ if (!cbRW)
+ return 0;
+
+ if (!bufR)
+ bufR = bufW;
+ else
+ memcpy(bufR, bufW, cbRW); /* transaction uses bufR for
+ * both R and W */
+
+ bzero(&spi, sizeof(spi)); /* zero structure first */
+
+ /* spigen code seems to suggest there must be at least 1 command byte */
+
+ spi.st_command.iov_base = bufR;
+ spi.st_command.iov_len = 1;
+
+ /* the rest of the bytes are data bytes */
+ if (cbRW > 1) {
+ spi.st_data.iov_base = bufR + 1;
+ spi.st_data.iov_len = cbRW - 1;
+ }
+
+ err = ioctl(hdev, SPIGENIOC_TRANSFER, &spi) < 0 ? -1 : 0;
+
+ if (!err && lsb) {
+ /* flip the bits for 'lsb' mode */
+ for (ctr = 0; ctr < cbRW; ctr++) {
+ ((uint8_t *) bufR)[ctr] = aLSB[((uint8_t *) bufR)[ctr]];
+ }
+ }
+
+ if (err)
+ fprintf(stderr, "Error performing SPI transaction, errno=%d\n", errno);
+
+ return err;
+}
+
+int
+_do_data_output(void *pR, struct spi_options *popt)
+{
+ int err, ictr, ictr2, index, icount;
+ const char *szB;
+
+ icount = popt->count;
+ err = 0;
+
+ if (icount <= 0) {
+ return -1; /* should not happen */
+ }
+
+ if (icount != 1)
+ szB = "bytes"; /* correct plurality of 'byte|bytes' for
+ * verbose message */
+ else
+ szB = "byte";
+
+ /* binary on stdout */
+ if (popt->binary || !popt->ASCII) {
+ if (popt->verbose > 0)
+ fprintf(stderr, "Binary output of %d %s\n", icount, szB);
+
+ err = fwrite(pR, 1, icount, stdout) != icount;
+ }
+ else if (icount > 0) {
+ if (popt->verbose > 0)
+ fprintf(stderr, "ASCII output of %d %s\n", icount, szB);
+
+ /* ASCII output */
+ for (index = 0; !err && index < icount; index++) {
+ if (index) {
+ /*
+ * not the first time, insert separating
+ * space
+ */
+ err = fputc(' ', stdout) == EOF;
+ }
+
+ if (!err)
+ err = fprintf(stdout, "%02hhx", ((uint8_t *) pR)[index]) < 0;
+ }
+
+ if (!err)
+ err = fputc('\n', stdout) == EOF;
+ }
+
+ /* verbose text out on stderr */
+
+ if (err)
+ fprintf(stderr, "Error writing to stdout, errno=%d\n", errno);
+ else if (popt->verbose > 0 && icount) {
+ const char *szB;
+
+ if (icount != 1)
+ szB = "bytes"; /* correct plurality of 'byte|bytes' */
+ else
+ szB = "byte";
+
+ fprintf(stderr, "%d %s read from SPI device\n", icount, szB);
+
+ verbose_dump_buffer(pR, icount, popt->lsb);
+ }
+
+ return err;
+}
+
+int
+perform_read(int hdev, struct spi_options *popt)
+{
+ int err;
+ void *pR, *pW;
+
+ pR = NULL;
+
+ /* prep write buffer filled with 0 bytes */
+ pW = malloc(popt->count);
+
+ if (!pW) {
+ err = -1;
+ goto the_end;
+ }
+
+ bzero(pW, popt->count);
+
+ pR = malloc(popt->count);
+
+ if (!pR) {
+ err = -2;
+ goto the_end;
+ }
+
+ bzero(pR, popt->count);
+
+ err = _read_write(hdev, pW, pR, popt->count, popt->lsb);
+
+ if (!err)
+ err = _do_data_output(pR, popt);
+
+the_end:
+
+ if (pR)
+ free(pR);
+ if (pW)
+ free(pW);
+
+ return err;
+}
+
+int
+perform_write(int hdev, struct spi_options *popt)
+{
+ int err;
+ void *pW;
+
+ /* read data from stdin and write to 'write' buffer */
+
+ pW = prep_write_buffer(popt);
+
+ if (!pW) {
+ err = -1;
+ goto the_end;
+ }
+
+ err = _read_write(hdev, pW, NULL, popt->count, popt->lsb);
+
+the_end:
+
+ if (pW)
+ free(pW);
+
+ return err;
+}
+
+int
+perform_readwrite(int hdev, struct spi_options *popt)
+{
+ int err;
+ void *pR, *pW;
+
+ pR = NULL;
+
+ pW = prep_write_buffer(popt);
+
+ if (!pW) {
+ err = -1;
+ goto the_end;
+ }
+
+ pR = malloc(popt->count);
+
+ if (!pR) {
+ err = -2;
+ goto the_end;
+ }
+
+ bzero(pR, popt->count);
+
+ err = _read_write(hdev, pW, pR, popt->count, popt->lsb);
+
+ if (!err)
+ err = _do_data_output(pR, popt);
+
+the_end:
+
+ if (pR)
+ free(pR);
+ if (pW)
+ free(pW);
+
+ return err;
+}
+
+
+void
+verbose_dump_buffer(void *pBuf, int icount, int lsb)
+{
+ uint8_t ch;
+ int ictr, ictr2, index;
+
+ fputs(" | 0 1 2 3 4 5 6 7 8 9 A B C D E F "
+ "| |\n", stderr);
+
+ for (ictr = 0; ictr < icount; ictr += 16) {
+ fprintf(stderr, " %6x | ", ictr & 0xfffff0);
+
+ for (ictr2 = 0; ictr2 < 16; ictr2++) {
+ index = ictr + ictr2;
+
+ if (index < icount) {
+ ch = ((uint8_t *) pBuf)[index];
+
+ if (lsb)
+ ch = aLSB[ch];
+
+ fprintf(stderr, "%02hhx ", ch);
+ }
+ else {
+ fputs(" ", stderr);
+ }
+ }
+
+ fputs("| ", stderr);
+
+ for (ictr2 = 0; ictr2 < 16; ictr2++) {
+ index = ictr + ictr2;
+
+ if (index < icount) {
+ ch = ((uint8_t *) pBuf)[index];
+
+ if (lsb)
+ ch = aLSB[ch];
+
+ if (ch < ' ' || ch > 127)
+ goto out_of_range;
+
+ fprintf(stderr, "%c", ch);
+ }
+ else if (index < icount) {
+ out_of_range:
+ fputc('.', stderr);
+ }
+ else {
+ fputc(' ', stderr);
+ }
+ }
+
+ fputs(" |", stderr);
+ }
+
+ fputc('\n', stderr);
+
+}

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 15, 5:45 PM (13 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41746348
Default Alt Text
D15029.1794764753.diff (24 KB)

Event Timeline