xref: /freebsd/usr.sbin/spi/spi.c (revision 4d65a7c6)
166660095SIan Lepore /*-
266660095SIan Lepore  * SPDX-License-Identifier: BSD-2-Clause
366660095SIan Lepore  *
466660095SIan Lepore  * Copyright (c) 2018 S.F.T. Inc.
566660095SIan Lepore  *
666660095SIan Lepore  * Redistribution and use in source and binary forms, with or without
766660095SIan Lepore  * modification, are permitted provided that the following conditions
866660095SIan Lepore  * are met:
966660095SIan Lepore  * 1. Redistributions of source code must retain the above copyright
1066660095SIan Lepore  *    notice, this list of conditions and the following disclaimer.
1166660095SIan Lepore  * 2. Redistributions in binary form must reproduce the above copyright
1266660095SIan Lepore  *    notice, this list of conditions and the following disclaimer in the
1366660095SIan Lepore  *    documentation and/or other materials provided with the distribution.
1466660095SIan Lepore  *
1566660095SIan Lepore  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1666660095SIan Lepore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1766660095SIan Lepore  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1866660095SIan Lepore  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1966660095SIan Lepore  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2066660095SIan Lepore  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2166660095SIan Lepore  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2266660095SIan Lepore  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2366660095SIan Lepore  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2466660095SIan Lepore  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2566660095SIan Lepore  * SUCH DAMAGE.
2666660095SIan Lepore  */
2766660095SIan Lepore 
2866660095SIan Lepore #include <sys/types.h>
2966660095SIan Lepore #include <sys/ioccom.h>
3066660095SIan Lepore #include <sys/spigenio.h>
3166660095SIan Lepore #include <sys/sysctl.h>
3266660095SIan Lepore 
3366660095SIan Lepore #include <errno.h>
3466660095SIan Lepore #include <fcntl.h>
3566660095SIan Lepore #include <inttypes.h>
3666660095SIan Lepore #include <limits.h>
3766660095SIan Lepore #include <memory.h>
3866660095SIan Lepore #include <stdio.h>
3966660095SIan Lepore #include <stdlib.h>
4066660095SIan Lepore #include <string.h>
4166660095SIan Lepore #include <string.h>
4266660095SIan Lepore #include <unistd.h>
4366660095SIan Lepore 
4466660095SIan Lepore #define	DEFAULT_DEVICE_NAME	"/dev/spigen0.0"
4566660095SIan Lepore 
4666660095SIan Lepore #define	DEFAULT_BUFFER_SIZE	8192
4766660095SIan Lepore 
4866660095SIan Lepore #define	DIR_READ		0
4966660095SIan Lepore #define	DIR_WRITE		1
5066660095SIan Lepore #define	DIR_READWRITE		2
5166660095SIan Lepore #define	DIR_NONE		-1
5266660095SIan Lepore 
5366660095SIan Lepore struct spi_options {
5466660095SIan Lepore 	int	mode;		/* mode (0,1,2,3, -1 == use default) */
5566660095SIan Lepore 	int	speed;		/* speed (in Hz, -1 == use default) */
5666660095SIan Lepore 	int	count;		/* count (0 through 'n' bytes, negative for
5766660095SIan Lepore 				 * stdin length) */
5866660095SIan Lepore 	int	binary;		/* non-zero for binary output or zero for
5966660095SIan Lepore 				 * ASCII output when ASCII != 0 */
6066660095SIan Lepore 	int	ASCII;		/* zero for binary input and output.
6166660095SIan Lepore 				 * non-zero for ASCII input, 'binary'
6266660095SIan Lepore 				 * determines output */
6366660095SIan Lepore 	int	lsb;		/* non-zero for LSB order (default order is
6466660095SIan Lepore 				 * MSB) */
6566660095SIan Lepore 	int	verbose;	/* non-zero for verbosity */
6666660095SIan Lepore 	int	ncmd;		/* bytes to skip for incoming data */
6766660095SIan Lepore 	uint8_t	*pcmd;		/* command data (NULL if none) */
6866660095SIan Lepore };
6966660095SIan Lepore 
7066660095SIan Lepore static void	usage(void);
7166660095SIan Lepore static int	interpret_command_bytes(const char *parg, struct spi_options *popt);
7266660095SIan Lepore static void *	prep_write_buffer(struct spi_options *popt);
7366660095SIan Lepore static int	_read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb);
7466660095SIan Lepore static int	_do_data_output(void *pr, struct spi_options *popt);
7566660095SIan Lepore static int	get_info(int hdev, const char *dev_name);
7666660095SIan Lepore static int	set_mode(int hdev, struct spi_options *popt);
7766660095SIan Lepore static int	set_speed(int hdev, struct spi_options *popt);
7866660095SIan Lepore static int	hexval(char c);
7966660095SIan Lepore static int	perform_read(int hdev, struct spi_options *popt);
8066660095SIan Lepore static int	perform_write(int hdev, struct spi_options *popt);
8166660095SIan Lepore static int	perform_readwrite(int hdev, struct spi_options *popt);
8266660095SIan Lepore static void	verbose_dump_buffer(void *pbuf, int icount, int lsb);
8366660095SIan Lepore 
8466660095SIan Lepore /*
8566660095SIan Lepore  * LSB array - reversebits[n] is the LSB value of n as an MSB.  Use this array
8666660095SIan Lepore  * to obtain a reversed bit pattern of the index value when bits must
8766660095SIan Lepore  * be sent/received in an LSB order vs the default MSB
8866660095SIan Lepore  */
8966660095SIan Lepore static uint8_t reversebits[256] = {
9066660095SIan Lepore 	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
9166660095SIan Lepore 	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
9266660095SIan Lepore 	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
9366660095SIan Lepore 	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
9466660095SIan Lepore 	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
9566660095SIan Lepore 	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
9666660095SIan Lepore 	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
9766660095SIan Lepore 	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
9866660095SIan Lepore 	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
9966660095SIan Lepore 	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
10066660095SIan Lepore 	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
10166660095SIan Lepore 	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
10266660095SIan Lepore 	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
10366660095SIan Lepore 	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
10466660095SIan Lepore 	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
10566660095SIan Lepore 	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
10666660095SIan Lepore 	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
10766660095SIan Lepore 	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
10866660095SIan Lepore 	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
10966660095SIan Lepore 	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
11066660095SIan Lepore 	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
11166660095SIan Lepore 	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
11266660095SIan Lepore 	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
11366660095SIan Lepore 	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
11466660095SIan Lepore 	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
11566660095SIan Lepore 	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
11666660095SIan Lepore 	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
11766660095SIan Lepore 	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
11866660095SIan Lepore 	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
11966660095SIan Lepore 	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
12066660095SIan Lepore 	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
12166660095SIan Lepore 	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
12266660095SIan Lepore };
12366660095SIan Lepore 
12466660095SIan Lepore 
12566660095SIan Lepore static void
usage(void)12666660095SIan Lepore usage(void)
12766660095SIan Lepore {
12866660095SIan Lepore 	fputs(getprogname(), stderr);
12966660095SIan Lepore 	fputs(" - communicate on SPI bus with slave devices\n"
13066660095SIan Lepore 	      "Usage:\n"
13166660095SIan Lepore 	      "        spi [-f device] [-d r|w|rw] [-m mode] [-s max-speed] [-c count]\n"
13266660095SIan Lepore 	      "            [-C \"command bytes\"] [-A] [-b] [-L] [-v]\n"
13366660095SIan Lepore 	      "        spi -i [-f device] [-v]\n"
13466660095SIan Lepore 	      "        spi -h\n"
13566660095SIan Lepore 	      " where\n"
13666660095SIan Lepore 	      "        -f specifies the device (default is spigen0.0)\n"
13766660095SIan Lepore 	      "        -d specifies the operation (r, w, or rw; default is rw)\n"
13866660095SIan Lepore 	      "        -m specifies the mode (0, 1, 2, or 3)\n"
13966660095SIan Lepore 	      "        -s specifies the maximum speed (default is 0, device default)\n"
14066660095SIan Lepore 	      "        -c specifies the number of data bytes to transfer (default 0, i.e. none)\n"
14166660095SIan Lepore 	      "           A negative value uses the length of the input data\n"
14266660095SIan Lepore 	      "        -C specifies 'command bytes' to be sent, as 2 byte hexadecimal values\n"
14366660095SIan Lepore 	      "           (these should be quoted, separated by optional white space)\n"
14466660095SIan Lepore 	      "        -L specifies 'LSB' order on the SPI bus (default is MSB)\n"
14566660095SIan Lepore 	      "        -i query information about the device\n"
14666660095SIan Lepore 	      "        -A uses ASCII for input/output as 2-digit hex values\n"
14766660095SIan Lepore 	      "        -b Override output format as binary (only valid with '-A')\n"
14866660095SIan Lepore 	      "        -v verbose output\n"
14966660095SIan Lepore 	      "        -h prints this message\n"
15066660095SIan Lepore 	      "\n"
15166660095SIan Lepore 	      "NOTE:  setting the mode and/or speed is 'sticky'.  Subsequent transactions\n"
15266660095SIan Lepore 	      "       on that device will, by default, use the previously set values.\n"
15366660095SIan Lepore 	      "\n",
15466660095SIan Lepore 	      stderr);
15566660095SIan Lepore }
15666660095SIan Lepore 
15766660095SIan Lepore int
main(int argc,char * argv[],char * envp[]__unused)15866660095SIan Lepore main(int argc, char *argv[], char *envp[] __unused)
15966660095SIan Lepore {
16066660095SIan Lepore 	struct spi_options opt;
16166660095SIan Lepore 	int err, ch, hdev, finfo, fdir;
16266660095SIan Lepore 	char *pstr;
16366660095SIan Lepore 	char dev_name[PATH_MAX * 2 + 5];
16466660095SIan Lepore 
16566660095SIan Lepore 	finfo = 0;
16666660095SIan Lepore 	fdir = DIR_NONE;
16766660095SIan Lepore 
16866660095SIan Lepore 	hdev = -1;
16966660095SIan Lepore 	err = 0;
17066660095SIan Lepore 
17166660095SIan Lepore 	dev_name[0] = 0;
17266660095SIan Lepore 
17366660095SIan Lepore 	opt.mode = -1;
17466660095SIan Lepore 	opt.speed = -1;
17566660095SIan Lepore 	opt.count = 0;
17666660095SIan Lepore 	opt.ASCII = 0;
17766660095SIan Lepore 	opt.binary = 0;
17866660095SIan Lepore 	opt.lsb = 0;
17966660095SIan Lepore 	opt.verbose = 0;
18066660095SIan Lepore 	opt.ncmd = 0;
18166660095SIan Lepore 	opt.pcmd = NULL;
18266660095SIan Lepore 
18366660095SIan Lepore 	while (!err && (ch = getopt(argc, argv, "f:d:m:s:c:C:AbLvih")) != -1) {
18466660095SIan Lepore 		switch (ch) {
18566660095SIan Lepore 		case 'd':
18666660095SIan Lepore 			if (optarg[0] == 'r') {
18766660095SIan Lepore 				if (optarg[1] == 'w' && optarg[2] == 0) {
18866660095SIan Lepore 					fdir = DIR_READWRITE;
18966660095SIan Lepore 				}
19066660095SIan Lepore 				else if (optarg[1] == 0) {
19166660095SIan Lepore 					fdir = DIR_READ;
19266660095SIan Lepore 				}
19366660095SIan Lepore 			}
19466660095SIan Lepore 			else if (optarg[0] == 'w' && optarg[1] == 0) {
19566660095SIan Lepore 				fdir = DIR_WRITE;
19666660095SIan Lepore 			}
19766660095SIan Lepore 			else {
19866660095SIan Lepore 				err = 1;
19966660095SIan Lepore 			}
20066660095SIan Lepore 			break;
20166660095SIan Lepore 
20266660095SIan Lepore 		case 'f':
20366660095SIan Lepore 			if (!optarg[0]) {	/* unlikely */
20466660095SIan Lepore 				fputs("error - missing device name\n", stderr);
20566660095SIan Lepore 				err = 1;
20666660095SIan Lepore 			}
20766660095SIan Lepore 			else {
20866660095SIan Lepore 				if (optarg[0] == '/')
20966660095SIan Lepore 					strlcpy(dev_name, optarg,
21066660095SIan Lepore 					    sizeof(dev_name));
21166660095SIan Lepore 				else
21266660095SIan Lepore 					snprintf(dev_name, sizeof(dev_name),
21366660095SIan Lepore 					    "/dev/%s", optarg);
21466660095SIan Lepore 			}
21566660095SIan Lepore 			break;
21666660095SIan Lepore 
21766660095SIan Lepore 		case 'm':
21866660095SIan Lepore 			opt.mode = (int)strtol(optarg, &pstr, 10);
21966660095SIan Lepore 
22066660095SIan Lepore 			if (!pstr || *pstr || opt.mode < 0 || opt.mode > 3) {
22166660095SIan Lepore 				fprintf(stderr, "Invalid mode specified: %s\n",
22266660095SIan Lepore 				    optarg);
22366660095SIan Lepore 				err = 1;
22466660095SIan Lepore 			}
22566660095SIan Lepore 			break;
22666660095SIan Lepore 
22766660095SIan Lepore 		case 's':
22866660095SIan Lepore 			opt.speed = (int)strtol(optarg, &pstr, 10);
22966660095SIan Lepore 
23066660095SIan Lepore 			if (!pstr || *pstr || opt.speed < 0) {
23166660095SIan Lepore 				fprintf(stderr, "Invalid speed specified: %s\n",
23266660095SIan Lepore 				    optarg);
23366660095SIan Lepore 				err = 1;
23466660095SIan Lepore 			}
23566660095SIan Lepore 			break;
23666660095SIan Lepore 
23766660095SIan Lepore 		case 'c':
23866660095SIan Lepore 			opt.count = (int)strtol(optarg, &pstr, 10);
23966660095SIan Lepore 
24066660095SIan Lepore 			if (!pstr || *pstr) {
24166660095SIan Lepore 				fprintf(stderr, "Invalid count specified: %s\n",
24266660095SIan Lepore 				    optarg);
24366660095SIan Lepore 				err = 1;
24466660095SIan Lepore 			}
24566660095SIan Lepore 			break;
24666660095SIan Lepore 
24766660095SIan Lepore 		case 'C':
24866660095SIan Lepore 			if(opt.pcmd) /* specified more than once */
24966660095SIan Lepore 				err = 1;
25066660095SIan Lepore 			else {
25166660095SIan Lepore 				/* get malloc'd buffer or error */
25266660095SIan Lepore 				if (interpret_command_bytes(optarg, &opt))
25366660095SIan Lepore 					err = 1;
25466660095SIan Lepore 			}
25566660095SIan Lepore 
25666660095SIan Lepore 			break;
25766660095SIan Lepore 
25866660095SIan Lepore 		case 'A':
25966660095SIan Lepore 			opt.ASCII = 1;
26066660095SIan Lepore 			break;
26166660095SIan Lepore 
26266660095SIan Lepore 		case 'b':
26366660095SIan Lepore 			opt.binary = 1;
26466660095SIan Lepore 			break;
26566660095SIan Lepore 
26666660095SIan Lepore 		case 'L':
26766660095SIan Lepore 			opt.lsb = 1;
26866660095SIan Lepore 			break;
26966660095SIan Lepore 
27066660095SIan Lepore 		case 'v':
27166660095SIan Lepore 			opt.verbose++;
27266660095SIan Lepore 			break;
27366660095SIan Lepore 
27466660095SIan Lepore 		case 'i':
27566660095SIan Lepore 			finfo = 1;
27666660095SIan Lepore 			break;
27766660095SIan Lepore 
27866660095SIan Lepore 		default:
27966660095SIan Lepore 			err = 1;
28066660095SIan Lepore 			/* FALLTHROUGH */
28166660095SIan Lepore 		case 'h':
28266660095SIan Lepore 			usage();
28366660095SIan Lepore 			goto the_end;
28466660095SIan Lepore 		}
28566660095SIan Lepore 	}
28666660095SIan Lepore 
28766660095SIan Lepore 	argc -= optind;
28866660095SIan Lepore 	argv += optind;
28966660095SIan Lepore 
29066660095SIan Lepore 	if (err ||
29166660095SIan Lepore 	    (fdir == DIR_NONE && !finfo && opt.mode == -1 && opt.speed == -1 && opt.count == 0)) {
29266660095SIan Lepore 		/*
29366660095SIan Lepore 		 * if any of the direction, mode, speed, or count not specified,
29466660095SIan Lepore 		 * print usage
29566660095SIan Lepore 		 */
29666660095SIan Lepore 
29766660095SIan Lepore 		usage();
29866660095SIan Lepore 		goto the_end;
29966660095SIan Lepore 	}
30066660095SIan Lepore 
30166660095SIan Lepore 	if ((opt.count != 0 || opt.ncmd != 0) && fdir == DIR_NONE) {
30266660095SIan Lepore 		/*
30366660095SIan Lepore 		 * count was specified, but direction was not.  default is
30466660095SIan Lepore 		 * read/write
30566660095SIan Lepore 		 */
30666660095SIan Lepore 		/*
30766660095SIan Lepore 		 * this includes a negative count, which implies write from
30866660095SIan Lepore 		 * stdin
30966660095SIan Lepore 		 */
31066660095SIan Lepore 		if (opt.count == 0)
31166660095SIan Lepore 			fdir = DIR_WRITE;
31266660095SIan Lepore 		else
31366660095SIan Lepore 			fdir = DIR_READWRITE;
31466660095SIan Lepore 	}
31566660095SIan Lepore 
31666660095SIan Lepore 	if (opt.count < 0 && fdir != DIR_READWRITE && fdir != DIR_WRITE) {
31766660095SIan Lepore 		fprintf(stderr, "Invalid length %d when not writing data\n",
31866660095SIan Lepore 		    opt.count);
31966660095SIan Lepore 
32066660095SIan Lepore 		err = 1;
32166660095SIan Lepore 		usage();
32266660095SIan Lepore 		goto the_end;
32366660095SIan Lepore 	}
32466660095SIan Lepore 
32566660095SIan Lepore 
32666660095SIan Lepore 	if (!dev_name[0])	/* no device name specified */
32766660095SIan Lepore 		strlcpy(dev_name, DEFAULT_DEVICE_NAME, sizeof(dev_name));
32866660095SIan Lepore 
32966660095SIan Lepore 	hdev = open(dev_name, O_RDWR);
33066660095SIan Lepore 
33166660095SIan Lepore 	if (hdev == -1) {
33266660095SIan Lepore 		fprintf(stderr, "Error - unable to open '%s', errno=%d\n",
33366660095SIan Lepore 		    dev_name, errno);
33466660095SIan Lepore 		err = 1;
33566660095SIan Lepore 		goto the_end;
33666660095SIan Lepore 	}
33766660095SIan Lepore 
33866660095SIan Lepore 	if (finfo) {
33966660095SIan Lepore 		err = get_info(hdev, dev_name);
34066660095SIan Lepore 		goto the_end;
34166660095SIan Lepore 	}
34266660095SIan Lepore 
34366660095SIan Lepore 	/* check and assign mode, speed */
34466660095SIan Lepore 
34566660095SIan Lepore 	if (opt.mode != -1) {
34666660095SIan Lepore 		err = set_mode(hdev, &opt);
34766660095SIan Lepore 
34866660095SIan Lepore 		if (err)
34966660095SIan Lepore 			goto the_end;
35066660095SIan Lepore 	}
35166660095SIan Lepore 
35266660095SIan Lepore 	if (opt.speed != -1) {
35366660095SIan Lepore 		err = set_speed(hdev, &opt);
35466660095SIan Lepore 
35566660095SIan Lepore 		if (err)
35666660095SIan Lepore 			goto the_end;
35766660095SIan Lepore 	}
35866660095SIan Lepore 
35966660095SIan Lepore 	/* do data transfer */
36066660095SIan Lepore 
36166660095SIan Lepore 	if (fdir == DIR_READ) {
36266660095SIan Lepore 		err = perform_read(hdev, &opt);
36366660095SIan Lepore 	}
36466660095SIan Lepore 	else if (fdir == DIR_WRITE) {
36566660095SIan Lepore 		err = perform_write(hdev, &opt);
36666660095SIan Lepore 	}
36766660095SIan Lepore 	else if (fdir == DIR_READWRITE) {
36866660095SIan Lepore 		err = perform_readwrite(hdev, &opt);
36966660095SIan Lepore 	}
37066660095SIan Lepore 
37166660095SIan Lepore the_end:
37266660095SIan Lepore 
37366660095SIan Lepore 	if (hdev != -1)
37466660095SIan Lepore 		close(hdev);
37566660095SIan Lepore 
37666660095SIan Lepore 	free(opt.pcmd);
37766660095SIan Lepore 
37866660095SIan Lepore 	return (err);
37966660095SIan Lepore }
38066660095SIan Lepore 
38166660095SIan Lepore static int
interpret_command_bytes(const char * parg,struct spi_options * popt)38266660095SIan Lepore interpret_command_bytes(const char *parg, struct spi_options *popt)
38366660095SIan Lepore {
38466660095SIan Lepore 	int ch, ch2, ctr, cbcmd, err;
38566660095SIan Lepore 	const char *ppos;
38666660095SIan Lepore 	void *ptemp;
38766660095SIan Lepore 	uint8_t *pcur;
38866660095SIan Lepore 
38966660095SIan Lepore 	err = 0;
39066660095SIan Lepore 	cbcmd = DEFAULT_BUFFER_SIZE; /* initial cmd buffer size */
39166660095SIan Lepore 	popt->pcmd = (uint8_t *)malloc(cbcmd);
39266660095SIan Lepore 
39366660095SIan Lepore 	if (!popt->pcmd)
39466660095SIan Lepore 		return 1;
39566660095SIan Lepore 
39666660095SIan Lepore 	pcur = popt->pcmd;
39766660095SIan Lepore 
39866660095SIan Lepore 	ctr = 0;
39966660095SIan Lepore 	ppos = parg;
40066660095SIan Lepore 
40166660095SIan Lepore 	while (*ppos) {
40266660095SIan Lepore 		while (*ppos && *ppos <= ' ') {
40366660095SIan Lepore 			ppos++; /* skip (optional) leading white space */
40466660095SIan Lepore 		}
40566660095SIan Lepore 
40666660095SIan Lepore 		if (!*ppos)
40766660095SIan Lepore 			break; /* I am done */
40866660095SIan Lepore 
40966660095SIan Lepore 		ch = hexval(*(ppos++));
41066660095SIan Lepore 		if (ch < 0 || !*ppos) { /* must be valid pair of hex characters */
41166660095SIan Lepore 			err = 1;
41266660095SIan Lepore 			goto the_end;
41366660095SIan Lepore 		}
41466660095SIan Lepore 
41566660095SIan Lepore 		ch2 = hexval(*(ppos++));
41666660095SIan Lepore 		if (ch2 < 0) {
41766660095SIan Lepore 			err = 1;
41866660095SIan Lepore 			goto the_end;
41966660095SIan Lepore 		}
42066660095SIan Lepore 
42166660095SIan Lepore 		ch = (ch * 16 + ch2) & 0xff; /* convert to byte */
42266660095SIan Lepore 
42366660095SIan Lepore 		if (ctr >= cbcmd) { /* need re-alloc buffer? (unlikely) */
42466660095SIan Lepore 			cbcmd += 8192; /* increase by additional 8k */
42566660095SIan Lepore 			ptemp = realloc(popt->pcmd, cbcmd);
42666660095SIan Lepore 
42766660095SIan Lepore 			if (!ptemp) {
42866660095SIan Lepore 				err = 1;
42966660095SIan Lepore 				fprintf(stderr,
43066660095SIan Lepore 					"Not enough memory to interpret command bytes, errno=%d\n",
43166660095SIan Lepore 					errno);
43266660095SIan Lepore 				goto the_end;
43366660095SIan Lepore 			}
43466660095SIan Lepore 
43566660095SIan Lepore 			popt->pcmd = (uint8_t *)ptemp;
43666660095SIan Lepore 			pcur = popt->pcmd + ctr;
43766660095SIan Lepore 		}
43866660095SIan Lepore 
43966660095SIan Lepore 		if (popt->lsb)
44066660095SIan Lepore 			*pcur = reversebits[ch];
44166660095SIan Lepore 		else
44266660095SIan Lepore 			*pcur = (uint8_t)ch;
44366660095SIan Lepore 
44466660095SIan Lepore 		pcur++;
44566660095SIan Lepore 		ctr++;
44666660095SIan Lepore 	}
44766660095SIan Lepore 
44866660095SIan Lepore 	popt->ncmd = ctr; /* record num bytes in '-C' argument */
44966660095SIan Lepore 
45066660095SIan Lepore the_end:
45166660095SIan Lepore 
45266660095SIan Lepore 	/* at this point popt->pcmd is NULL or a valid pointer */
45366660095SIan Lepore 
45466660095SIan Lepore 	return err;
45566660095SIan Lepore }
45666660095SIan Lepore 
45766660095SIan Lepore static int
get_info(int hdev,const char * dev_name)45866660095SIan Lepore get_info(int hdev, const char *dev_name)
45966660095SIan Lepore {
46066660095SIan Lepore 	uint32_t fmode, fspeed;
46166660095SIan Lepore 	int err;
46266660095SIan Lepore 	char temp_buf[PATH_MAX], cpath[PATH_MAX];
46366660095SIan Lepore 
46466660095SIan Lepore 	if (!realpath(dev_name, cpath)) /* get canonical name for info purposes */
46566660095SIan Lepore 		strlcpy(cpath, temp_buf, sizeof(cpath));  /* this shouldn't happen */
46666660095SIan Lepore 
46766660095SIan Lepore 	err = ioctl(hdev, SPIGENIOC_GET_SPI_MODE, &fmode);
46866660095SIan Lepore 
46966660095SIan Lepore 	if (err == 0)
47066660095SIan Lepore 		err = ioctl(hdev, SPIGENIOC_GET_CLOCK_SPEED, &fspeed);
47166660095SIan Lepore 
47266660095SIan Lepore 	if (err == 0) {
47366660095SIan Lepore 		fprintf(stderr,
47466660095SIan Lepore 		        "Device name:   %s\n"
47566660095SIan Lepore 		        "Device mode:   %d\n"
47666660095SIan Lepore 		        "Device speed:  %d\n",
47766660095SIan Lepore 		        cpath, fmode, fspeed);//, max_cmd, max_data, temp_buf);
47866660095SIan Lepore 	}
47966660095SIan Lepore 	else
48066660095SIan Lepore 		fprintf(stderr, "Unable to query info (err=%d), errno=%d\n",
48166660095SIan Lepore 		    err, errno);
48266660095SIan Lepore 
48366660095SIan Lepore 	return err;
48466660095SIan Lepore }
48566660095SIan Lepore 
48666660095SIan Lepore static int
set_mode(int hdev,struct spi_options * popt)48766660095SIan Lepore set_mode(int hdev, struct spi_options *popt)
48866660095SIan Lepore {
48966660095SIan Lepore 	uint32_t fmode = popt->mode;
49066660095SIan Lepore 
49166660095SIan Lepore 	if (popt->mode < 0)	/* use default? */
49266660095SIan Lepore 		return 0;
49366660095SIan Lepore 
49466660095SIan Lepore 	return ioctl(hdev, SPIGENIOC_SET_SPI_MODE, &fmode);
49566660095SIan Lepore }
49666660095SIan Lepore 
49766660095SIan Lepore static int
set_speed(int hdev,struct spi_options * popt)49866660095SIan Lepore set_speed(int hdev, struct spi_options *popt)
49966660095SIan Lepore {
50066660095SIan Lepore 	uint32_t clock_speed = popt->speed;
50166660095SIan Lepore 
50266660095SIan Lepore 	if (popt->speed < 0)
50366660095SIan Lepore 		return 0;
50466660095SIan Lepore 
50566660095SIan Lepore 	return ioctl(hdev, SPIGENIOC_SET_CLOCK_SPEED, &clock_speed);
50666660095SIan Lepore }
50766660095SIan Lepore 
50866660095SIan Lepore static int
hexval(char c)50966660095SIan Lepore hexval(char c)
51066660095SIan Lepore {
51166660095SIan Lepore 	if (c >= '0' && c <= '9') {
51266660095SIan Lepore 		return c - '0';
51366660095SIan Lepore 	} else if (c >= 'A' && c <= 'F') {
51466660095SIan Lepore 		return c - 'A' + 10;
51566660095SIan Lepore 	} else if (c >= 'a' && c <= 'f') {
51666660095SIan Lepore 		return c - 'a' + 10;
51766660095SIan Lepore 	}
51866660095SIan Lepore 	return -1;
51966660095SIan Lepore }
52066660095SIan Lepore 
52166660095SIan Lepore static void *
prep_write_buffer(struct spi_options * popt)52266660095SIan Lepore prep_write_buffer(struct spi_options *popt)
52366660095SIan Lepore {
52466660095SIan Lepore 	int ch, ch2, ch3, ncmd, lsb, err;
52566660095SIan Lepore 	uint8_t *pdata, *pdat2;
52666660095SIan Lepore 	size_t cbdata, cbread;
52766660095SIan Lepore 	const char *szbytes;
52866660095SIan Lepore 
52966660095SIan Lepore 	ncmd = popt->ncmd; /* num command bytes (can be zero) */
53066660095SIan Lepore 
53166660095SIan Lepore 	if (ncmd == 0 && popt->count == 0)
53266660095SIan Lepore 		return NULL;	/* always since it's an error if it happens
53366660095SIan Lepore 				 * now */
53466660095SIan Lepore 
53566660095SIan Lepore 	if (popt->count < 0) {
53666660095SIan Lepore 		cbdata = DEFAULT_BUFFER_SIZE;
53766660095SIan Lepore 	}
53866660095SIan Lepore 	else {
53966660095SIan Lepore 		cbdata = popt->count;
54066660095SIan Lepore 	}
54166660095SIan Lepore 
54266660095SIan Lepore 	lsb = popt->lsb; /* non-zero if LSB order; else MSB */
54366660095SIan Lepore 
54466660095SIan Lepore 	pdata = malloc(cbdata + ncmd + 1);
54566660095SIan Lepore 	cbread = 0;
54666660095SIan Lepore 
54766660095SIan Lepore 	err = 0;
54866660095SIan Lepore 
54966660095SIan Lepore 	if (!pdata)
55066660095SIan Lepore 		return NULL;
55166660095SIan Lepore 
55266660095SIan Lepore 	if (popt->pcmd && ncmd > 0) {
55366660095SIan Lepore 		memcpy(pdata, popt->pcmd, ncmd); /* copy command bytes */
55466660095SIan Lepore 		pdat2 = pdata + ncmd;
55566660095SIan Lepore 	}
55666660095SIan Lepore 	else
55766660095SIan Lepore 		pdat2 = pdata; /* no prepended command data */
55866660095SIan Lepore 
55966660095SIan Lepore 	/*
56066660095SIan Lepore 	 * read up to 'cbdata' bytes.  If I get an EOF, do one of two things:
56166660095SIan Lepore 	 * a) change the data count to match how many bytes I read in b) fill
56266660095SIan Lepore 	 * the rest of the input buffer with zeros
56366660095SIan Lepore 	 *
56466660095SIan Lepore 	 * If the specified length is negative, I do 'a', else 'b'
56566660095SIan Lepore 	 */
56666660095SIan Lepore 
56766660095SIan Lepore 	while (!err && cbread < cbdata && (ch = fgetc(stdin)) != EOF) {
56866660095SIan Lepore 		if (popt->ASCII) {
56966660095SIan Lepore 			/* skip consecutive white space */
57066660095SIan Lepore 
57166660095SIan Lepore 			while (ch <= ' ') {
57266660095SIan Lepore 				if ((ch = fgetc(stdin)) == EOF)
57366660095SIan Lepore 					break;
57466660095SIan Lepore 			}
57566660095SIan Lepore 
57666660095SIan Lepore 			if (ch != EOF) {
57766660095SIan Lepore 				ch2 = hexval(ch);
57866660095SIan Lepore 
57966660095SIan Lepore 				if (ch2 < 0) {
58066660095SIan Lepore invalid_character:
58166660095SIan Lepore 					fprintf(stderr,
58266660095SIan Lepore 					    "Invalid input character '%c'\n", ch);
58366660095SIan Lepore 					err = 1;
58466660095SIan Lepore 					break;
58566660095SIan Lepore 				}
58666660095SIan Lepore 
58766660095SIan Lepore 				ch = fgetc(stdin);
58866660095SIan Lepore 
58966660095SIan Lepore 				if (ch != EOF) {
59066660095SIan Lepore 					ch3 = hexval(ch);
59166660095SIan Lepore 
59266660095SIan Lepore 					if (ch3 < 0)
59366660095SIan Lepore 						goto invalid_character;
59466660095SIan Lepore 
59566660095SIan Lepore 					ch = ch2 * 16 + ch3;
59666660095SIan Lepore 				}
59766660095SIan Lepore 			}
59866660095SIan Lepore 
59966660095SIan Lepore 			if (err || ch == EOF)
60066660095SIan Lepore 				break;
60166660095SIan Lepore 		}
60266660095SIan Lepore 
60366660095SIan Lepore 		/* for LSB, flip the bits - otherwise, just copy the value */
60466660095SIan Lepore 		if (lsb)
60566660095SIan Lepore 			pdat2[cbread] = reversebits[ch];
60666660095SIan Lepore 		else
60766660095SIan Lepore 			pdat2[cbread] = (uint8_t) ch;
60866660095SIan Lepore 
60966660095SIan Lepore 		cbread++; /* increment num bytes read so far */
61066660095SIan Lepore 	}
61166660095SIan Lepore 
61266660095SIan Lepore 	/* if it was an error, not an EOF, that ended the I/O, return NULL */
61366660095SIan Lepore 
61466660095SIan Lepore 	if (err || ferror(stdin)) {
61566660095SIan Lepore 		free(pdata);
61666660095SIan Lepore 		return NULL;
61766660095SIan Lepore 	}
61866660095SIan Lepore 
61966660095SIan Lepore 	if (popt->verbose > 0) {
62066660095SIan Lepore 		const char *sz_bytes;
62166660095SIan Lepore 
62266660095SIan Lepore 		if (cbread != 1)
62366660095SIan Lepore 			sz_bytes = "bytes";	/* correct plurality of 'byte|bytes' */
62466660095SIan Lepore 		else
62566660095SIan Lepore 			sz_bytes = "byte";
62666660095SIan Lepore 
62766660095SIan Lepore 		if (popt->ASCII)
62866660095SIan Lepore 			fprintf(stderr, "ASCII input of %zd %s\n", cbread,
62966660095SIan Lepore 			    sz_bytes);
63066660095SIan Lepore 		else
63166660095SIan Lepore 			fprintf(stderr, "Binary input of %zd %s\n", cbread,
63266660095SIan Lepore 			    sz_bytes);
63366660095SIan Lepore 	}
63466660095SIan Lepore 
63566660095SIan Lepore 	/*
63666660095SIan Lepore 	 * if opt.count is negative, copy actual byte count to opt.count which does
63766660095SIan Lepore 	 * not include any of the 'command' bytes that are being sent.  Can be zero.
63866660095SIan Lepore 	 */
63966660095SIan Lepore 	if (popt->count < 0) {
64066660095SIan Lepore 		popt->count = cbread;
64166660095SIan Lepore 	}
64266660095SIan Lepore 	/*
64366660095SIan Lepore 	 * for everything else, fill the rest of the read buffer with '0'
64466660095SIan Lepore 	 * bytes, as per the standard practice for SPI
64566660095SIan Lepore 	 */
64666660095SIan Lepore 	else {
64766660095SIan Lepore 		while (cbread < cbdata)
64866660095SIan Lepore 			pdat2[cbread++] = 0;
64966660095SIan Lepore 	}
65066660095SIan Lepore 
65166660095SIan Lepore 	/*
65266660095SIan Lepore 	 * popt->count bytes will be sent and read from the SPI, preceded by the
65366660095SIan Lepore 	 * 'popt->ncmd' command bytes (if any).
65466660095SIan Lepore 	 * So we must use 'popt->count' and 'popt->ncmd' from this point on in
65566660095SIan Lepore 	 * the code.
65666660095SIan Lepore 	 */
65766660095SIan Lepore 
65866660095SIan Lepore 	if (popt->verbose > 0 && popt->count + popt->ncmd) {
65966660095SIan Lepore 		if ((popt->count + popt->ncmd) == 1)
66066660095SIan Lepore 			szbytes = "byte";
66166660095SIan Lepore 		else
66266660095SIan Lepore 			szbytes = "bytes";
66366660095SIan Lepore 
66466660095SIan Lepore 		fprintf(stderr, "Writing %d %s to SPI device\n",
66566660095SIan Lepore 		        popt->count + popt->ncmd, szbytes);
66666660095SIan Lepore 
66766660095SIan Lepore 		verbose_dump_buffer(pdata, popt->count + popt->ncmd, lsb);
66866660095SIan Lepore 	}
66966660095SIan Lepore 
67066660095SIan Lepore 	return pdata;
67166660095SIan Lepore }
67266660095SIan Lepore 
67366660095SIan Lepore static int
_read_write(int hdev,void * bufw,void * bufr,int cbrw,int lsb)67466660095SIan Lepore _read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb)
67566660095SIan Lepore {
67666660095SIan Lepore 	int	err, ctr;
67766660095SIan Lepore 	struct spigen_transfer spi;
67866660095SIan Lepore 
67966660095SIan Lepore 	if (!cbrw)
68066660095SIan Lepore 		return 0;
68166660095SIan Lepore 
68266660095SIan Lepore 	if (!bufr)
68366660095SIan Lepore 		bufr = bufw;
68466660095SIan Lepore 	else
68566660095SIan Lepore 		memcpy(bufr, bufw, cbrw);	/* transaction uses bufr for
68666660095SIan Lepore 						 * both R and W */
68766660095SIan Lepore 
68866660095SIan Lepore 	bzero(&spi, sizeof(spi));	/* zero structure first */
68966660095SIan Lepore 
69066660095SIan Lepore 	/* spigen code seems to suggest there must be at least 1 command byte */
69166660095SIan Lepore 
69266660095SIan Lepore 	spi.st_command.iov_base = bufr;
69366660095SIan Lepore 	spi.st_command.iov_len = cbrw;
69466660095SIan Lepore 
69566660095SIan Lepore 	/*
69666660095SIan Lepore 	 * The remaining members for spi.st_data are zero - all bytes are
69766660095SIan Lepore 	 * 'command' for this. The driver doesn't really do anything different
69866660095SIan Lepore 	 * for 'command' vs 'data' and at least one command byte must be sent in
69966660095SIan Lepore 	 * the transaction.
70066660095SIan Lepore 	 */
70166660095SIan Lepore 
70266660095SIan Lepore 	err = ioctl(hdev, SPIGENIOC_TRANSFER, &spi) < 0 ? -1 : 0;
70366660095SIan Lepore 
70466660095SIan Lepore 	if (!err && lsb) {
70566660095SIan Lepore 		/* flip the bits for 'lsb' mode */
70666660095SIan Lepore 		for (ctr = 0; ctr < cbrw; ctr++) {
70766660095SIan Lepore 			((uint8_t *) bufr)[ctr] =
70866660095SIan Lepore 			    reversebits[((uint8_t *)bufr)[ctr]];
70966660095SIan Lepore 		}
71066660095SIan Lepore 	}
71166660095SIan Lepore 
71266660095SIan Lepore 	if (err)
71366660095SIan Lepore 		fprintf(stderr, "Error performing SPI transaction, errno=%d\n",
71466660095SIan Lepore 		    errno);
71566660095SIan Lepore 
71666660095SIan Lepore 	return err;
71766660095SIan Lepore }
71866660095SIan Lepore 
71966660095SIan Lepore static int
_do_data_output(void * pr,struct spi_options * popt)72066660095SIan Lepore _do_data_output(void *pr, struct spi_options *popt)
72166660095SIan Lepore {
722917d1846SIan Lepore 	int	err, idx, icount;
72366660095SIan Lepore 	const char *sz_bytes, *sz_byte2;
72466660095SIan Lepore 	const uint8_t *pbuf;
72566660095SIan Lepore 
72666660095SIan Lepore 	pbuf = (uint8_t *)pr + popt->ncmd; /* only the data we want */
72766660095SIan Lepore 	icount = popt->count;
72866660095SIan Lepore 	err = 0;
72966660095SIan Lepore 
73066660095SIan Lepore 	if (icount <= 0) {
73166660095SIan Lepore 		return -1; /* should not but could happen */
73266660095SIan Lepore 	}
73366660095SIan Lepore 
73466660095SIan Lepore 	if (icount != 1)
73566660095SIan Lepore 		sz_bytes = "bytes";	/* correct plurality of 'byte|bytes' */
73666660095SIan Lepore 	else
73766660095SIan Lepore 		sz_bytes = "byte";
73866660095SIan Lepore 
73966660095SIan Lepore 	if (popt->ncmd != 1)
74066660095SIan Lepore 		sz_byte2 = "bytes";
74166660095SIan Lepore 	else
74266660095SIan Lepore 		sz_byte2 = "byte";
74366660095SIan Lepore 
74466660095SIan Lepore 	/* binary on stdout */
74566660095SIan Lepore 	if (popt->binary || !popt->ASCII) {
74666660095SIan Lepore 		if (popt->verbose > 0)
74766660095SIan Lepore 			fprintf(stderr, "Binary output of %d %s\n", icount,
74866660095SIan Lepore 			    sz_bytes);
74966660095SIan Lepore 
75066660095SIan Lepore 		err = (int)fwrite(pbuf, 1, icount, stdout) != icount;
75166660095SIan Lepore 	}
75266660095SIan Lepore 	else if (icount > 0) {
75366660095SIan Lepore 		if (popt->verbose > 0)
75466660095SIan Lepore 			fprintf(stderr, "ASCII output of %d %s\n", icount,
75566660095SIan Lepore 			    sz_bytes);
75666660095SIan Lepore 
75766660095SIan Lepore 		/* ASCII output */
758917d1846SIan Lepore 		for (idx = 0; !err && idx < icount; idx++) {
759917d1846SIan Lepore 			if (idx) {
76066660095SIan Lepore 				/*
76166660095SIan Lepore 				 * not the first time, insert separating space
76266660095SIan Lepore 				 */
76366660095SIan Lepore 				err = fputc(' ', stdout) == EOF;
76466660095SIan Lepore 			}
76566660095SIan Lepore 
76666660095SIan Lepore 			if (!err)
767917d1846SIan Lepore 				err = fprintf(stdout, "%02hhx", pbuf[idx]) < 0;
76866660095SIan Lepore 		}
76966660095SIan Lepore 
77066660095SIan Lepore 		if (!err)
77166660095SIan Lepore 			err = fputc('\n', stdout) == EOF;
77266660095SIan Lepore 	}
77366660095SIan Lepore 
77466660095SIan Lepore 	/* verbose text out on stderr */
77566660095SIan Lepore 
77666660095SIan Lepore 	if (err)
77766660095SIan Lepore 		fprintf(stderr, "Error writing to stdout, errno=%d\n", errno);
77866660095SIan Lepore 	else if (popt->verbose > 0 && icount) {
77966660095SIan Lepore 		fprintf(stderr,
78066660095SIan Lepore 		    "%d command %s and %d data %s read from SPI device\n",
78166660095SIan Lepore 		    popt->ncmd, sz_byte2, icount, sz_bytes);
78266660095SIan Lepore 
78366660095SIan Lepore 		/* verbose output will show the command bytes as well */
78466660095SIan Lepore 		verbose_dump_buffer(pr, icount + popt->ncmd, popt->lsb);
78566660095SIan Lepore 	}
78666660095SIan Lepore 
78766660095SIan Lepore 	return err;
78866660095SIan Lepore }
78966660095SIan Lepore 
79066660095SIan Lepore static int
perform_read(int hdev,struct spi_options * popt)79166660095SIan Lepore perform_read(int hdev, struct spi_options *popt)
79266660095SIan Lepore {
79366660095SIan Lepore 	int icount, err;
79466660095SIan Lepore 	void   *pr, *pw;
79566660095SIan Lepore 
79666660095SIan Lepore 	pr = NULL;
79766660095SIan Lepore 	icount = popt->count + popt->ncmd;
79866660095SIan Lepore 
79966660095SIan Lepore 	/* prep write buffer filled with 0 bytes */
80066660095SIan Lepore 	pw = malloc(icount);
80166660095SIan Lepore 
80266660095SIan Lepore 	if (!pw) {
80366660095SIan Lepore 		err = -1;
80466660095SIan Lepore 		goto the_end;
80566660095SIan Lepore 	}
80666660095SIan Lepore 
80766660095SIan Lepore 	bzero(pw, icount);
80866660095SIan Lepore 
80966660095SIan Lepore 	/* if I included a command sequence, copy bytes to the write buf */
81066660095SIan Lepore 	if (popt->pcmd && popt->ncmd > 0)
81166660095SIan Lepore 		memcpy(pw, popt->pcmd, popt->ncmd);
81266660095SIan Lepore 
81366660095SIan Lepore 	pr = malloc(icount + 1);
81466660095SIan Lepore 
81566660095SIan Lepore 	if (!pr) {
81666660095SIan Lepore 		err = -2;
81766660095SIan Lepore 		goto the_end;
81866660095SIan Lepore 	}
81966660095SIan Lepore 
82066660095SIan Lepore 	bzero(pr, icount);
82166660095SIan Lepore 
82266660095SIan Lepore 	err = _read_write(hdev, pw, pr, icount, popt->lsb);
82366660095SIan Lepore 
82466660095SIan Lepore 	if (!err && popt->count > 0)
82566660095SIan Lepore 		err = _do_data_output(pr, popt);
82666660095SIan Lepore 
82766660095SIan Lepore the_end:
82866660095SIan Lepore 
82966660095SIan Lepore 	free(pr);
83066660095SIan Lepore 	free(pw);
83166660095SIan Lepore 
83266660095SIan Lepore 	return err;
83366660095SIan Lepore }
83466660095SIan Lepore 
83566660095SIan Lepore static int
perform_write(int hdev,struct spi_options * popt)83666660095SIan Lepore perform_write(int hdev, struct spi_options *popt)
83766660095SIan Lepore {
83866660095SIan Lepore 	int err;
83966660095SIan Lepore 	void   *pw;
84066660095SIan Lepore 
84166660095SIan Lepore 	/* read data from cmd buf and stdin and write to 'write' buffer */
84266660095SIan Lepore 
84366660095SIan Lepore 	pw = prep_write_buffer(popt);
84466660095SIan Lepore 
84566660095SIan Lepore 	if (!pw) {
84666660095SIan Lepore 		err = -1;
84766660095SIan Lepore 		goto the_end;
84866660095SIan Lepore 	}
84966660095SIan Lepore 
85066660095SIan Lepore 	err = _read_write(hdev, pw, NULL, popt->count + popt->ncmd, popt->lsb);
85166660095SIan Lepore 
85266660095SIan Lepore the_end:
85366660095SIan Lepore 
85466660095SIan Lepore 	free(pw);
85566660095SIan Lepore 
85666660095SIan Lepore 	return err;
85766660095SIan Lepore }
85866660095SIan Lepore 
85966660095SIan Lepore static int
perform_readwrite(int hdev,struct spi_options * popt)86066660095SIan Lepore perform_readwrite(int hdev, struct spi_options *popt)
86166660095SIan Lepore {
86266660095SIan Lepore 	int icount, err;
86366660095SIan Lepore 	void   *pr, *pw;
86466660095SIan Lepore 
86566660095SIan Lepore 	pr = NULL;
86666660095SIan Lepore 
86766660095SIan Lepore 	pw = prep_write_buffer(popt);
86866660095SIan Lepore 	icount = popt->count + popt->ncmd; /* assign after fn call */
86966660095SIan Lepore 
87066660095SIan Lepore 	if (!pw) {
87166660095SIan Lepore 		err = -1;
87266660095SIan Lepore 		goto the_end;
87366660095SIan Lepore 	}
87466660095SIan Lepore 
87566660095SIan Lepore 	pr = malloc(icount + 1);
87666660095SIan Lepore 
87766660095SIan Lepore 	if (!pr) {
87866660095SIan Lepore 		err = -2;
87966660095SIan Lepore 		goto the_end;
88066660095SIan Lepore 	}
88166660095SIan Lepore 
88266660095SIan Lepore 	bzero(pr, icount);
88366660095SIan Lepore 
88466660095SIan Lepore 	err = _read_write(hdev, pw, pr, icount, popt->lsb);
88566660095SIan Lepore 
88666660095SIan Lepore 	if (!err)
88766660095SIan Lepore 		err = _do_data_output(pr, popt);
88866660095SIan Lepore 
88966660095SIan Lepore the_end:
89066660095SIan Lepore 
89166660095SIan Lepore 	free(pr);
89266660095SIan Lepore 	free(pw);
89366660095SIan Lepore 
89466660095SIan Lepore 	return err;
89566660095SIan Lepore }
89666660095SIan Lepore 
89766660095SIan Lepore 
89866660095SIan Lepore static void
verbose_dump_buffer(void * pbuf,int icount,int lsb)89966660095SIan Lepore verbose_dump_buffer(void *pbuf, int icount, int lsb)
90066660095SIan Lepore {
90166660095SIan Lepore 	uint8_t	ch;
902917d1846SIan Lepore 	int	ictr, ictr2, idx;
90366660095SIan Lepore 
90466660095SIan Lepore 	fputs("        |  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F "
90566660095SIan Lepore 	      "|                  |\n", stderr);
90666660095SIan Lepore 
90766660095SIan Lepore 	for (ictr = 0; ictr < icount; ictr += 16) {
90866660095SIan Lepore 		fprintf(stderr, " %6x | ", ictr & 0xfffff0);
90966660095SIan Lepore 
91066660095SIan Lepore 		for (ictr2 = 0; ictr2 < 16; ictr2++) {
911917d1846SIan Lepore 			idx = ictr + ictr2;
91266660095SIan Lepore 
913917d1846SIan Lepore 			if (idx < icount) {
914917d1846SIan Lepore 				ch = ((uint8_t *) pbuf)[idx];
91566660095SIan Lepore 
91666660095SIan Lepore 				if (lsb)
91766660095SIan Lepore 					ch = reversebits[ch];
91866660095SIan Lepore 
91966660095SIan Lepore 				fprintf(stderr, "%02hhx ", ch);
92066660095SIan Lepore 			}
92166660095SIan Lepore 			else {
92266660095SIan Lepore 				fputs("   ", stderr);
92366660095SIan Lepore 			}
92466660095SIan Lepore 		}
92566660095SIan Lepore 
92666660095SIan Lepore 		fputs("| ", stderr);
92766660095SIan Lepore 
92866660095SIan Lepore 		for (ictr2 = 0; ictr2 < 16; ictr2++) {
929917d1846SIan Lepore 			idx = ictr + ictr2;
93066660095SIan Lepore 
931917d1846SIan Lepore 			if (idx < icount) {
932917d1846SIan Lepore 				ch = ((uint8_t *) pbuf)[idx];
93366660095SIan Lepore 
93466660095SIan Lepore 				if (lsb)
93566660095SIan Lepore 					ch = reversebits[ch];
93666660095SIan Lepore 
93766660095SIan Lepore 				if (ch < ' ' || ch > 127)
93866660095SIan Lepore 					goto out_of_range;
93966660095SIan Lepore 
94066660095SIan Lepore 				fprintf(stderr, "%c", ch);
94166660095SIan Lepore 			}
942917d1846SIan Lepore 			else if (idx < icount) {
94366660095SIan Lepore 		out_of_range:
94466660095SIan Lepore 				fputc('.', stderr);
94566660095SIan Lepore 			}
94666660095SIan Lepore 			else {
94766660095SIan Lepore 				fputc(' ', stderr);
94866660095SIan Lepore 			}
94966660095SIan Lepore 		}
95066660095SIan Lepore 
95166660095SIan Lepore 		fputs(" |\n", stderr);
95266660095SIan Lepore 	}
95366660095SIan Lepore 
95466660095SIan Lepore 	fflush(stderr);
95566660095SIan Lepore }
956