Author: ian
Date: Fri Jun 22 01:59:19 2018
New Revision: 335527
URL: https://svnweb.freebsd.org/changeset/base/335527

Log:
  Add spi(8), a utility for communicating with a device on a SPI bus from
  userland, conceptually similar to what i2c(8) provides for i2c devices.
  
  Submitted by: Bob Frazier
  Differential Revision:        https://reviews.freebsd.org/D15029

Added:
  head/usr.sbin/spi/
  head/usr.sbin/spi/Makefile   (contents, props changed)
  head/usr.sbin/spi/spi.8   (contents, props changed)
  head/usr.sbin/spi/spi.c   (contents, props changed)
Modified:
  head/usr.sbin/Makefile

Modified: head/usr.sbin/Makefile
==============================================================================
--- head/usr.sbin/Makefile      Fri Jun 22 01:10:05 2018        (r335526)
+++ head/usr.sbin/Makefile      Fri Jun 22 01:59:19 2018        (r335527)
@@ -84,6 +84,7 @@ SUBDIR=       adduser \
        setpmac \
        smbmsg \
        snapinfo \
+       spi \
        spray \
        syslogd \
        sysrc \

Added: head/usr.sbin/spi/Makefile
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/usr.sbin/spi/Makefile  Fri Jun 22 01:59:19 2018        (r335527)
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+#.include <src.opts.mk>
+
+PROG= spi
+
+MAN8= spi.8
+
+.include <bsd.prog.mk>

Added: head/usr.sbin/spi/spi.8
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/usr.sbin/spi/spi.8     Fri Jun 22 01:59:19 2018        (r335527)
@@ -0,0 +1,197 @@
+.\" Copyright (c) 2018 by S.F.T. Inc.
+.\" 
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd "15 April 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 C Ar cmd_bytes
+.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
+.Nm
+utility can be used to perform raw data transfers
+.Pq read, write, or simultaneous read/write
+with devices on the SPI bus, via the
+.Xr spigen 4
+device.
+.Pp
+Each
+.Xr spigen 4
+device is associated with a specific
+.Sq chip select
+.Pq cs
+pin on the spibus, and therefore needs to be specified.
+If no device name is specified on the command line,
+.Nm
+assumes
+.Sq spigen0.0 .
+.Pp
+For more information on the spigen device, see
+.Xr spigen 4 .
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl f Ar device"
+.It Fl A
+Specifies ASCII mode.
+Both read and write data is input and output as
+2-character hexadecimal values, optionally separated by white space,
+such as 00 01 02 etc.
+When combined with the
+.Sq -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
+.Pq output
+mode.
+Only has an effect when
+.Sq -A
+has been specified.
+Reverts the output back to binary
+.Pq rather than ASCII ,
+while leaving the input format as-is.
+Use in combination with
+.Sq -A
+to allow using something like
+.Sq echo
+to pass hexadecimal values to the SPI device, but output the received data
+on stdout as binary.
+.It Fl C Ar command bytes
+Sends one or more 
+.Sq command
+bytes, skipping any bytes read-in during the transfer.
+The byte values should be specified as a quoted parameter, similar to the
+format for data on stdin for
+.Sq -A ,
+that is, 2 character hexadecimal values, optionally separated by white space.
+An SPI device will typically require that a command be sent, followed by
+bytes of data.
+You can use this option to send the command without receiving any data bytes
+during the command sequence.
+.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
+.Sq 0 .
+If the length can be determined from the input file size, you can use a
+.Sq count
+value of
+.Sq -1
+to base the transfer on the input file's size.
+.It Fl d Ar r|w|rw
+Transfer direction: Use
+.Sq r
+for read,
+.Sq w for write, and
+.Sq rw
+for simultaneous read and write.
+.It Fl f Ar device
+SPI device to use
+.Pq 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 clock 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.
+The bus will operate at its highest available speed which does not
+exceed this maximum.
+.It Fl v
+Specifies Verbose mode.
+Diagnostics and information are written to stderr.
+You can specify
+.Sq -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 speed to 200Khz and the mode to 3 on spigen0.1, but do
+not transmit nor receive any data
+.Pp
+spi -f spigen0.1 -s 200000 -m 3
+.It
+Send a command sequence consisting of 2 bytes, and read 2 additional bytes
+from the SPI device, using the current mode and speed on the default device
+.Pp
+spi -d r -C "00 01" -c 2
+.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 clock speed of 500khz.
+.Pp
+echo "05" | spi -A -d rw -m 2 -s 500000 -c 2
+.It
+Send a binary file, and output the SPI result through
+.Sq od
+as hexadecimal bytes, using the current maximum clock speed and SPI mode.
+.Pp
+spi -d rw -c -1 <input_file.bin | od -An -t x1
+.It
+Send 2 bytes of data, receive a total of 4 bytes, and output the SPI result
+as binary data, piped through
+.Sq od ,
+displaying it as two hexadecimal unsigned short integer values.
+.Pp
+echo "00 01" | spi -A -b -d rw -c 4 | od -t x2
+.El
+.Pp
+.Sh SEE ALSO
+.Xr spigen 4

Added: head/usr.sbin/spi/spi.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/usr.sbin/spi/spi.c     Fri Jun 22 01:59:19 2018        (r335527)
@@ -0,0 +1,958 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 S.F.T. Inc.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioccom.h>
+#include <sys/spigenio.h>
+#include <sys/sysctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string.h>
+#include <unistd.h>
+
+#define        DEFAULT_DEVICE_NAME     "/dev/spigen0.0"
+
+#define        DEFAULT_BUFFER_SIZE     8192
+
+#define        DIR_READ                0
+#define        DIR_WRITE               1
+#define        DIR_READWRITE           2
+#define        DIR_NONE                -1
+
+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     ncmd;           /* bytes to skip for incoming data */
+       uint8_t *pcmd;          /* command data (NULL if none) */
+};
+
+static void    usage(void);
+static int     interpret_command_bytes(const char *parg, struct spi_options 
*popt);
+static void *  prep_write_buffer(struct spi_options *popt);
+static int     _read_write(int hdev, void *bufw, void *bufr, int cbrw, int 
lsb);
+static int     _do_data_output(void *pr, struct spi_options *popt);
+static int     get_info(int hdev, const char *dev_name);
+static int     set_mode(int hdev, struct spi_options *popt);
+static int     set_speed(int hdev, struct spi_options *popt);
+static int     hexval(char c);
+static int     perform_read(int hdev, struct spi_options *popt);
+static int     perform_write(int hdev, struct spi_options *popt);
+static int     perform_readwrite(int hdev, struct spi_options *popt);
+static void    verbose_dump_buffer(void *pbuf, int icount, int lsb);
+
+/*
+ * LSB array - reversebits[n] is the LSB value of n as an MSB.  Use this array
+ * to obtain a reversed bit pattern of the index value when bits must
+ * be sent/received in an LSB order vs the default MSB
+ */
+static uint8_t reversebits[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
+};
+
+
+static 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"
+             "            [-C \"command bytes\"] [-A] [-b] [-L] [-v]\n"
+             "        spi -i [-f device] [-v]\n"
+             "        spi -h\n"
+             " where\n"
+             "        -f specifies the device (default is spigen0.0)\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 data bytes to transfer 
(default 0, i.e. none)\n"
+             "           A negative value uses the length of the input data\n"
+             "        -C specifies 'command bytes' to be sent, as 2 byte 
hexadecimal values\n"
+             "           (these should be quoted, separated by optional white 
space)\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[] __unused)
+{
+       struct spi_options opt;
+       int err, ch, hdev, finfo, fdir;
+       char *pstr;
+       char dev_name[PATH_MAX * 2 + 5];
+
+       finfo = 0;
+       fdir = DIR_NONE;
+
+       hdev = -1;
+       err = 0;
+
+       dev_name[0] = 0;
+
+       opt.mode = -1;
+       opt.speed = -1;
+       opt.count = 0;
+       opt.ASCII = 0;
+       opt.binary = 0;
+       opt.lsb = 0;
+       opt.verbose = 0;
+       opt.ncmd = 0;
+       opt.pcmd = NULL;
+
+       while (!err && (ch = getopt(argc, argv, "f:d:m:s:c: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 {
+                               if (optarg[0] == '/')
+                                       strlcpy(dev_name, optarg,
+                                           sizeof(dev_name));
+                               else
+                                       snprintf(dev_name, sizeof(dev_name),
+                                           "/dev/%s", optarg);
+                       }
+                       break;
+
+               case 'm':
+                       opt.mode = (int)strtol(optarg, &pstr, 10);
+
+                       if (!pstr || *pstr || opt.mode < 0 || opt.mode > 3) {
+                               fprintf(stderr, "Invalid mode specified: %s\n",
+                                   optarg);
+                               err = 1;
+                       }
+                       break;
+
+               case 's':
+                       opt.speed = (int)strtol(optarg, &pstr, 10);
+
+                       if (!pstr || *pstr || opt.speed < 0) {
+                               fprintf(stderr, "Invalid speed specified: %s\n",
+                                   optarg);
+                               err = 1;
+                       }
+                       break;
+
+               case 'c':
+                       opt.count = (int)strtol(optarg, &pstr, 10);
+
+                       if (!pstr || *pstr) {
+                               fprintf(stderr, "Invalid count specified: %s\n",
+                                   optarg);
+                               err = 1;
+                       }
+                       break;
+
+               case 'C':
+                       if(opt.pcmd) /* specified more than once */
+                               err = 1;
+                       else {
+                               /* get malloc'd buffer or error */
+                               if (interpret_command_bytes(optarg, &opt))
+                                       err = 1;
+                       }
+
+                       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;
+                       /* FALLTHROUGH */
+               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 any of the direction, mode, speed, or count not specified,
+                * print usage
+                */
+
+               usage();
+               goto the_end;
+       }
+
+       if ((opt.count != 0 || opt.ncmd != 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
+                */
+               if (opt.count == 0)
+                       fdir = DIR_WRITE;
+               else
+                       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[0])       /* no device name specified */
+               strlcpy(dev_name, DEFAULT_DEVICE_NAME, sizeof(dev_name));
+
+       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;
+       }
+
+       /* check and assign mode, speed */
+
+       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);
+
+       free(opt.pcmd);
+
+       return (err);
+}
+
+static int
+interpret_command_bytes(const char *parg, struct spi_options *popt)
+{
+       int ch, ch2, ctr, cbcmd, err;
+       const char *ppos;
+       void *ptemp;
+       uint8_t *pcur;
+
+       err = 0;
+       cbcmd = DEFAULT_BUFFER_SIZE; /* initial cmd buffer size */
+       popt->pcmd = (uint8_t *)malloc(cbcmd);
+
+       if (!popt->pcmd)
+               return 1;
+
+       pcur = popt->pcmd;
+
+       ctr = 0;
+       ppos = parg;
+
+       while (*ppos) {
+               while (*ppos && *ppos <= ' ') {
+                       ppos++; /* skip (optional) leading white space */
+               }
+
+               if (!*ppos)
+                       break; /* I am done */
+
+               ch = hexval(*(ppos++));
+               if (ch < 0 || !*ppos) { /* must be valid pair of hex characters 
*/
+                       err = 1;
+                       goto the_end;
+               }
+               
+               ch2 = hexval(*(ppos++));
+               if (ch2 < 0) {
+                       err = 1;
+                       goto the_end;
+               }
+
+               ch = (ch * 16 + ch2) & 0xff; /* convert to byte */
+
+               if (ctr >= cbcmd) { /* need re-alloc buffer? (unlikely) */
+                       cbcmd += 8192; /* increase by additional 8k */
+                       ptemp = realloc(popt->pcmd, cbcmd);
+
+                       if (!ptemp) {
+                               err = 1;
+                               fprintf(stderr,
+                                       "Not enough memory to interpret command 
bytes, errno=%d\n",
+                                       errno);
+                               goto the_end;
+                       }
+
+                       popt->pcmd = (uint8_t *)ptemp;
+                       pcur = popt->pcmd + ctr;
+               }
+
+               if (popt->lsb)
+                       *pcur = reversebits[ch];
+               else
+                       *pcur = (uint8_t)ch;
+
+               pcur++;
+               ctr++;
+       }
+
+       popt->ncmd = ctr; /* record num bytes in '-C' argument */
+
+the_end:
+
+       /* at this point popt->pcmd is NULL or a valid pointer */
+
+       return err;
+}
+
+static int
+get_info(int hdev, const char *dev_name)
+{
+       uint32_t fmode, fspeed;
+       int err;
+       char temp_buf[PATH_MAX], cpath[PATH_MAX];
+
+       if (!realpath(dev_name, cpath)) /* get canonical name for info purposes 
*/
+               strlcpy(cpath, temp_buf, sizeof(cpath));  /* this shouldn't 
happen */
+
+       err = ioctl(hdev, SPIGENIOC_GET_SPI_MODE, &fmode);
+
+       if (err == 0)
+               err = ioctl(hdev, SPIGENIOC_GET_CLOCK_SPEED, &fspeed);
+
+       if (err == 0) {
+               fprintf(stderr,
+                       "Device name:   %s\n"
+                       "Device mode:   %d\n"
+                       "Device speed:  %d\n",
+                       cpath, fmode, fspeed);//, max_cmd, max_data, temp_buf);
+       }
+       else
+               fprintf(stderr, "Unable to query info (err=%d), errno=%d\n",
+                   err, errno);
+
+       return err;
+}
+
+static int
+set_mode(int hdev, struct spi_options *popt)
+{
+       uint32_t fmode = popt->mode;
+
+       if (popt->mode < 0)     /* use default? */
+               return 0;
+
+       return ioctl(hdev, SPIGENIOC_SET_SPI_MODE, &fmode);
+}
+
+static int
+set_speed(int hdev, struct spi_options *popt)
+{
+       uint32_t clock_speed = popt->speed;
+
+       if (popt->speed < 0)
+               return 0;
+
+       return ioctl(hdev, SPIGENIOC_SET_CLOCK_SPEED, &clock_speed);
+}
+
+static 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;
+}
+
+static void *
+prep_write_buffer(struct spi_options *popt)
+{
+       int ch, ch2, ch3, ncmd, lsb, err;
+       uint8_t *pdata, *pdat2;
+       size_t cbdata, cbread;
+       const char *szbytes;
+
+       ncmd = popt->ncmd; /* num command bytes (can be zero) */
+
+       if (ncmd == 0 && popt->count == 0)
+               return NULL;    /* always since it's an error if it happens
+                                * now */
+
+       if (popt->count < 0) {
+               cbdata = DEFAULT_BUFFER_SIZE;
+       }
+       else {
+               cbdata = popt->count;
+       }
+
+       lsb = popt->lsb; /* non-zero if LSB order; else MSB */
+
+       pdata = malloc(cbdata + ncmd + 1);
+       cbread = 0;
+
+       err = 0;
+
+       if (!pdata)
+               return NULL;
+
+       if (popt->pcmd && ncmd > 0) {
+               memcpy(pdata, popt->pcmd, ncmd); /* copy command bytes */
+               pdat2 = pdata + ncmd;
+       }
+       else
+               pdat2 = pdata; /* no prepended command data */
+
+       /*
+        * 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 (lsb)
+                       pdat2[cbread] = reversebits[ch];
+               else
+                       pdat2[cbread] = (uint8_t) ch;
+
+               cbread++; /* increment num bytes read so far */
+       }
+
+       /* 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 *sz_bytes;
+
+               if (cbread != 1)
+                       sz_bytes = "bytes";     /* correct plurality of 
'byte|bytes' */
+               else
+                       sz_bytes = "byte";
+
+               if (popt->ASCII)
+                       fprintf(stderr, "ASCII input of %zd %s\n", cbread,
+                           sz_bytes);
+               else
+                       fprintf(stderr, "Binary input of %zd %s\n", cbread,
+                           sz_bytes);
+       }
+
+       /*
+        * if opt.count is negative, copy actual byte count to opt.count which 
does
+        * not include any of the 'command' bytes that are being sent.  Can be 
zero.
+        */
+       if (popt->count < 0) {
+               popt->count = cbread;
+       }
+       /*
+        * for everything else, fill the rest of the read buffer with '0'
+        * bytes, as per the standard practice for SPI
+        */
+       else {
+               while (cbread < cbdata)
+                       pdat2[cbread++] = 0;
+       }
+
+       /*
+        * popt->count bytes will be sent and read from the SPI, preceded by the
+        * 'popt->ncmd' command bytes (if any).
+        * So we must use 'popt->count' and 'popt->ncmd' from this point on in
+        * the code.
+        */
+
+       if (popt->verbose > 0 && popt->count + popt->ncmd) {
+               if ((popt->count + popt->ncmd) == 1)
+                       szbytes = "byte";
+               else
+                       szbytes = "bytes";
+
+               fprintf(stderr, "Writing %d %s to SPI device\n",
+                       popt->count + popt->ncmd, szbytes);
+
+               verbose_dump_buffer(pdata, popt->count + popt->ncmd, lsb);
+       }
+
+       return pdata;
+}
+
+static 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 = cbrw;
+
+       /*
+        * The remaining members for spi.st_data are zero - all bytes are
+        * 'command' for this. The driver doesn't really do anything different
+        * for 'command' vs 'data' and at least one command byte must be sent in
+        * the transaction.
+        */
+
+       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] =
+                           reversebits[((uint8_t *)bufr)[ctr]];
+               }
+       }
+
+       if (err)
+               fprintf(stderr, "Error performing SPI transaction, errno=%d\n",
+                   errno);
+
+       return err;
+}
+
+static int
+_do_data_output(void *pr, struct spi_options *popt)
+{
+       int     err, index, icount;
+       const char *sz_bytes, *sz_byte2;
+       const uint8_t *pbuf;
+
+       pbuf = (uint8_t *)pr + popt->ncmd; /* only the data we want */
+       icount = popt->count;
+       err = 0;
+
+       if (icount <= 0) {
+               return -1; /* should not but could happen */
+       }
+
+       if (icount != 1)
+               sz_bytes = "bytes";     /* correct plurality of 'byte|bytes' */
+       else
+               sz_bytes = "byte";
+
+       if (popt->ncmd != 1)
+               sz_byte2 = "bytes";
+       else
+               sz_byte2 = "byte";
+
+       /* binary on stdout */
+       if (popt->binary || !popt->ASCII) {
+               if (popt->verbose > 0)
+                       fprintf(stderr, "Binary output of %d %s\n", icount,
+                           sz_bytes);
+
+               err = (int)fwrite(pbuf, 1, icount, stdout) != icount;
+       }
+       else if (icount > 0) {
+               if (popt->verbose > 0)
+                       fprintf(stderr, "ASCII output of %d %s\n", icount,
+                           sz_bytes);
+
+               /* 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", pbuf[index]) < 
0;
+               }
+
+               if (!err)
+                       err = fputc('\n', stdout) == EOF;
+       }

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to