Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F132801368
D15029.id41631.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
29 KB
Referenced Files
None
Subscribers
None
D15029.id41631.diff
View Options
Index: usr.sbin/Makefile
===================================================================
--- usr.sbin/Makefile
+++ usr.sbin/Makefile
@@ -82,6 +82,7 @@
setpmac \
smbmsg \
snapinfo \
+ spi \
spray \
syslogd \
sysrc \
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,172 @@
+.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
+An
+.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 .
+.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.
+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
+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 spigen1, but do
+not transmit nor receive any data
+.Pp
+spi -f spigen1 -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
Index: usr.sbin/spi/spi.c
===================================================================
--- /dev/null
+++ usr.sbin/spi/spi.c
@@ -0,0 +1,1052 @@
+/*-
+ *
+ * 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 "/dev/spigen0"
+
+#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 */
+ int max_cmd_len; /* max command length from driver sysctl */
+ int max_data_len; /* max data length from driver sysctl */
+ uint8_t *pcmd; /* command data (NULL if none) */
+};
+
+void usage(void);
+int interpret_command_bytes(const char *parg, struct spi_options *popt);
+void * prep_write_buffer(struct spi_options *popt);
+int _read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb);
+int _do_data_output(void *pr, struct spi_options *popt);
+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 hexval(char c);
+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 - 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
+};
+
+
+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)\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)
+{
+ int err, ch, hdev, finfo, fdir;
+ struct spi_options opt;
+ 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 neither the direction, mode, speed, nor 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;
+ }
+
+
+ /* 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 >= 0 &&
+ ((opt.count + opt.ncmd) > opt.max_cmd_len ||
+ (opt.count + opt.ncmd) > opt.max_data_len)) {
+ fprintf(stderr,
+ "Error, data count %d exceeds maximum command/data length %d\n",
+ opt.count + opt.ncmd,
+ (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 */
+
+ 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);
+
+ if (opt.pcmd)
+ free(opt.pcmd);
+
+ return err;
+}
+
+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 = 8192; /* 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;
+ 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;
+}
+
+int
+get_sysctl_byname(const char *dev_name, const char *var_name, void *pbuf, int bufsize)
+{
+ size_t cbbuf;
+ int dev_name_offset, dev_unit, err;
+ char the_var[PATH_MAX];
+ char *pstr;
+
+
+ if (!pbuf || bufsize <= 0)
+ return -1; /* simple param check */
+
+ strlcpy(the_var, "dev.spigen0.", sizeof(the_var)); /* default */
+
+ cbbuf = bufsize;
+
+ if (!strncmp(&(dev_name[0]), "/dev/", 5)) {
+ dev_name_offset = 5;
+ }
+ else {
+ fprintf(stderr, "Unrecognized device path - '%s'\n", &(dev_name[0]));
+ return 1;
+ }
+
+ if (!strncmp(&(dev_name[dev_name_offset]), "spigen", 6) &&
+ dev_name[dev_name_offset + 6] >= '0') {
+
+ dev_unit = (int)strtoul(&(dev_name[dev_name_offset + 6]), &pstr, 10);
+ if (!pstr || *pstr != '\0')
+ goto dev_name_err;
+
+ snprintf(the_var, sizeof(the_var) - 1, "dev.spigen.%d.", dev_unit);
+
+ strlcat(the_var, var_name, sizeof(the_var));
+
+ 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 < ((size_t)bufsize - 1)) { /* assume it is a
+ * string? */
+ ((char *)pbuf)[cbbuf] = 0;
+ }
+ }
+ else {
+dev_name_err:
+ 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 fmode, fspeed, max_cmd, max_data;
+ int err;
+ char temp_buf[PATH_MAX];
+
+ err = ioctl(hdev, SPIGENIOC_GET_SPI_MODE, &fmode);
+
+ if (err == 0)
+ err = ioctl(hdev, SPIGENIOC_GET_CLOCK_SPEED, &fspeed);
+
+ 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, fmode, fspeed, 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 fmode = popt->mode;
+
+ if (popt->mode < 0) /* use default? */
+ return 0;
+
+ return ioctl(hdev, SPIGENIOC_SET_SPI_MODE, &fmode);
+}
+
+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);
+}
+
+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, 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) {
+ /* 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;
+ }
+
+ 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 %d %s\n", (int)cbread, sz_bytes);
+ else
+ fprintf(stderr, "Binary input of %d %s\n", (int)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;
+}
+
+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;
+}
+
+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' for
+ * verbose messages */
+ 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;
+ }
+
+ /* verbose text out on stderr */
+
+ if (err)
+ fprintf(stderr, "Error writing to stdout, errno=%d\n", errno);
+ else if (popt->verbose > 0 && icount) {
+ fprintf(stderr, "%d command %s and %d data %s read from SPI device\n",
+ popt->ncmd, sz_byte2, icount, sz_bytes);
+
+ /* verbose output will show the command bytes as well */
+ verbose_dump_buffer(pr, icount + popt->ncmd, popt->lsb);
+ }
+
+ return err;
+}
+
+int
+perform_read(int hdev, struct spi_options *popt)
+{
+ int icount, err;
+ void *pr, *pw;
+
+ pr = NULL;
+ icount = popt->count + popt->ncmd;
+
+ /* prep write buffer filled with 0 bytes */
+ pw = malloc(icount);
+
+ if (!pw) {
+ err = -1;
+ goto the_end;
+ }
+
+ bzero(pw, icount);
+
+ /* if I included a command sequence, copy bytes to the write buf */
+ if (popt->pcmd && popt->ncmd > 0)
+ memcpy(pw, popt->pcmd, popt->ncmd);
+
+ pr = malloc(icount + 1);
+
+ if (!pr) {
+ err = -2;
+ goto the_end;
+ }
+
+ bzero(pr, icount);
+
+ err = _read_write(hdev, pw, pr, icount, popt->lsb);
+
+ if (!err && popt->count > 0)
+ 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 cmd buf and 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->ncmd, popt->lsb);
+
+the_end:
+
+ if (pw)
+ free(pw);
+
+ return err;
+}
+
+int
+perform_readwrite(int hdev, struct spi_options *popt)
+{
+ int icount, err;
+ void *pr, *pw;
+
+ pr = NULL;
+
+ pw = prep_write_buffer(popt);
+ icount = popt->count + popt->ncmd; /* assign after fn call */
+
+ if (!pw) {
+ err = -1;
+ goto the_end;
+ }
+
+ pr = malloc(icount + 1);
+
+ if (!pr) {
+ err = -2;
+ goto the_end;
+ }
+
+ bzero(pr, icount);
+
+ err = _read_write(hdev, pw, pr, icount, 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 = reversebits[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 = reversebits[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(" |\n", stderr);
+ }
+
+ fflush(stderr);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Oct 21, 2:16 AM (12 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
23995496
Default Alt Text
D15029.id41631.diff (29 KB)
Attached To
Mode
D15029: new utility usr.sbin/spi
Attached
Detach File
Event Timeline
Log In to Comment