1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2018 S.F.T. Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: stable/11/usr.sbin/spi/spi.c 346518 2019-04-22 03:55:02Z ian $");
30
31#include <sys/types.h>
32#include <sys/ioccom.h>
33#include <sys/spigenio.h>
34#include <sys/sysctl.h>
35
36#include <errno.h>
37#include <fcntl.h>
38#include <inttypes.h>
39#include <limits.h>
40#include <memory.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <string.h>
45#include <unistd.h>
46
47#define	DEFAULT_DEVICE_NAME	"/dev/spigen0.0"
48
49#define	DEFAULT_BUFFER_SIZE	8192
50
51#define	DIR_READ		0
52#define	DIR_WRITE		1
53#define	DIR_READWRITE		2
54#define	DIR_NONE		-1
55
56struct spi_options {
57	int	mode;		/* mode (0,1,2,3, -1 == use default) */
58	int	speed;		/* speed (in Hz, -1 == use default) */
59	int	count;		/* count (0 through 'n' bytes, negative for
60				 * stdin length) */
61	int	binary;		/* non-zero for binary output or zero for
62				 * ASCII output when ASCII != 0 */
63	int	ASCII;		/* zero for binary input and output.
64				 * non-zero for ASCII input, 'binary'
65				 * determines output */
66	int	lsb;		/* non-zero for LSB order (default order is
67				 * MSB) */
68	int	verbose;	/* non-zero for verbosity */
69	int	ncmd;		/* bytes to skip for incoming data */
70	uint8_t	*pcmd;		/* command data (NULL if none) */
71};
72
73static void	usage(void);
74static int	interpret_command_bytes(const char *parg, struct spi_options *popt);
75static void *	prep_write_buffer(struct spi_options *popt);
76static int	_read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb);
77static int	_do_data_output(void *pr, struct spi_options *popt);
78static int	get_info(int hdev, const char *dev_name);
79static int	set_mode(int hdev, struct spi_options *popt);
80static int	set_speed(int hdev, struct spi_options *popt);
81static int	hexval(char c);
82static int	perform_read(int hdev, struct spi_options *popt);
83static int	perform_write(int hdev, struct spi_options *popt);
84static int	perform_readwrite(int hdev, struct spi_options *popt);
85static void	verbose_dump_buffer(void *pbuf, int icount, int lsb);
86
87/*
88 * LSB array - reversebits[n] is the LSB value of n as an MSB.  Use this array
89 * to obtain a reversed bit pattern of the index value when bits must
90 * be sent/received in an LSB order vs the default MSB
91 */
92static uint8_t reversebits[256] = {
93	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
94	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
95	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
96	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
97	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
98	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
99	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
100	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
101	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
102	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
103	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
104	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
105	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
106	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
107	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
108	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
109	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
110	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
111	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
112	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
113	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
114	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
115	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
116	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
117	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
118	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
119	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
120	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
121	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
122	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
123	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
124	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
125};
126
127
128static void
129usage(void)
130{
131	fputs(getprogname(), stderr);
132	fputs(" - communicate on SPI bus with slave devices\n"
133	      "Usage:\n"
134	      "        spi [-f device] [-d r|w|rw] [-m mode] [-s max-speed] [-c count]\n"
135	      "            [-C \"command bytes\"] [-A] [-b] [-L] [-v]\n"
136	      "        spi -i [-f device] [-v]\n"
137	      "        spi -h\n"
138	      " where\n"
139	      "        -f specifies the device (default is spigen0.0)\n"
140	      "        -d specifies the operation (r, w, or rw; default is rw)\n"
141	      "        -m specifies the mode (0, 1, 2, or 3)\n"
142	      "        -s specifies the maximum speed (default is 0, device default)\n"
143	      "        -c specifies the number of data bytes to transfer (default 0, i.e. none)\n"
144	      "           A negative value uses the length of the input data\n"
145	      "        -C specifies 'command bytes' to be sent, as 2 byte hexadecimal values\n"
146	      "           (these should be quoted, separated by optional white space)\n"
147	      "        -L specifies 'LSB' order on the SPI bus (default is MSB)\n"
148	      "        -i query information about the device\n"
149	      "        -A uses ASCII for input/output as 2-digit hex values\n"
150	      "        -b Override output format as binary (only valid with '-A')\n"
151	      "        -v verbose output\n"
152	      "        -h prints this message\n"
153	      "\n"
154	      "NOTE:  setting the mode and/or speed is 'sticky'.  Subsequent transactions\n"
155	      "       on that device will, by default, use the previously set values.\n"
156	      "\n",
157	      stderr);
158}
159
160int
161main(int argc, char *argv[], char *envp[] __unused)
162{
163	struct spi_options opt;
164	int err, ch, hdev, finfo, fdir;
165	char *pstr;
166	char dev_name[PATH_MAX * 2 + 5];
167
168	finfo = 0;
169	fdir = DIR_NONE;
170
171	hdev = -1;
172	err = 0;
173
174	dev_name[0] = 0;
175
176	opt.mode = -1;
177	opt.speed = -1;
178	opt.count = 0;
179	opt.ASCII = 0;
180	opt.binary = 0;
181	opt.lsb = 0;
182	opt.verbose = 0;
183	opt.ncmd = 0;
184	opt.pcmd = NULL;
185
186	while (!err && (ch = getopt(argc, argv, "f:d:m:s:c:C:AbLvih")) != -1) {
187		switch (ch) {
188		case 'd':
189			if (optarg[0] == 'r') {
190				if (optarg[1] == 'w' && optarg[2] == 0) {
191					fdir = DIR_READWRITE;
192				}
193				else if (optarg[1] == 0) {
194					fdir = DIR_READ;
195				}
196			}
197			else if (optarg[0] == 'w' && optarg[1] == 0) {
198				fdir = DIR_WRITE;
199			}
200			else {
201				err = 1;
202			}
203			break;
204
205		case 'f':
206			if (!optarg[0]) {	/* unlikely */
207				fputs("error - missing device name\n", stderr);
208				err = 1;
209			}
210			else {
211				if (optarg[0] == '/')
212					strlcpy(dev_name, optarg,
213					    sizeof(dev_name));
214				else
215					snprintf(dev_name, sizeof(dev_name),
216					    "/dev/%s", optarg);
217			}
218			break;
219
220		case 'm':
221			opt.mode = (int)strtol(optarg, &pstr, 10);
222
223			if (!pstr || *pstr || opt.mode < 0 || opt.mode > 3) {
224				fprintf(stderr, "Invalid mode specified: %s\n",
225				    optarg);
226				err = 1;
227			}
228			break;
229
230		case 's':
231			opt.speed = (int)strtol(optarg, &pstr, 10);
232
233			if (!pstr || *pstr || opt.speed < 0) {
234				fprintf(stderr, "Invalid speed specified: %s\n",
235				    optarg);
236				err = 1;
237			}
238			break;
239
240		case 'c':
241			opt.count = (int)strtol(optarg, &pstr, 10);
242
243			if (!pstr || *pstr) {
244				fprintf(stderr, "Invalid count specified: %s\n",
245				    optarg);
246				err = 1;
247			}
248			break;
249
250		case 'C':
251			if(opt.pcmd) /* specified more than once */
252				err = 1;
253			else {
254				/* get malloc'd buffer or error */
255				if (interpret_command_bytes(optarg, &opt))
256					err = 1;
257			}
258
259			break;
260
261		case 'A':
262			opt.ASCII = 1;
263			break;
264
265		case 'b':
266			opt.binary = 1;
267			break;
268
269		case 'L':
270			opt.lsb = 1;
271			break;
272
273		case 'v':
274			opt.verbose++;
275			break;
276
277		case 'i':
278			finfo = 1;
279			break;
280
281		default:
282			err = 1;
283			/* FALLTHROUGH */
284		case 'h':
285			usage();
286			goto the_end;
287		}
288	}
289
290	argc -= optind;
291	argv += optind;
292
293	if (err ||
294	    (fdir == DIR_NONE && !finfo && opt.mode == -1 && opt.speed == -1 && opt.count == 0)) {
295		/*
296		 * if any of the direction, mode, speed, or count not specified,
297		 * print usage
298		 */
299
300		usage();
301		goto the_end;
302	}
303
304	if ((opt.count != 0 || opt.ncmd != 0) && fdir == DIR_NONE) {
305		/*
306		 * count was specified, but direction was not.  default is
307		 * read/write
308		 */
309		/*
310		 * this includes a negative count, which implies write from
311		 * stdin
312		 */
313		if (opt.count == 0)
314			fdir = DIR_WRITE;
315		else
316			fdir = DIR_READWRITE;
317	}
318
319	if (opt.count < 0 && fdir != DIR_READWRITE && fdir != DIR_WRITE) {
320		fprintf(stderr, "Invalid length %d when not writing data\n",
321		    opt.count);
322
323		err = 1;
324		usage();
325		goto the_end;
326	}
327
328
329	if (!dev_name[0])	/* no device name specified */
330		strlcpy(dev_name, DEFAULT_DEVICE_NAME, sizeof(dev_name));
331
332	hdev = open(dev_name, O_RDWR);
333
334	if (hdev == -1) {
335		fprintf(stderr, "Error - unable to open '%s', errno=%d\n",
336		    dev_name, errno);
337		err = 1;
338		goto the_end;
339	}
340
341	if (finfo) {
342		err = get_info(hdev, dev_name);
343		goto the_end;
344	}
345
346	/* check and assign mode, speed */
347
348	if (opt.mode != -1) {
349		err = set_mode(hdev, &opt);
350
351		if (err)
352			goto the_end;
353	}
354
355	if (opt.speed != -1) {
356		err = set_speed(hdev, &opt);
357
358		if (err)
359			goto the_end;
360	}
361
362	/* do data transfer */
363
364	if (fdir == DIR_READ) {
365		err = perform_read(hdev, &opt);
366	}
367	else if (fdir == DIR_WRITE) {
368		err = perform_write(hdev, &opt);
369	}
370	else if (fdir == DIR_READWRITE) {
371		err = perform_readwrite(hdev, &opt);
372	}
373
374the_end:
375
376	if (hdev != -1)
377		close(hdev);
378
379	free(opt.pcmd);
380
381	return (err);
382}
383
384static int
385interpret_command_bytes(const char *parg, struct spi_options *popt)
386{
387	int ch, ch2, ctr, cbcmd, err;
388	const char *ppos;
389	void *ptemp;
390	uint8_t *pcur;
391
392	err = 0;
393	cbcmd = DEFAULT_BUFFER_SIZE; /* initial cmd buffer size */
394	popt->pcmd = (uint8_t *)malloc(cbcmd);
395
396	if (!popt->pcmd)
397		return 1;
398
399	pcur = popt->pcmd;
400
401	ctr = 0;
402	ppos = parg;
403
404	while (*ppos) {
405		while (*ppos && *ppos <= ' ') {
406			ppos++; /* skip (optional) leading white space */
407		}
408
409		if (!*ppos)
410			break; /* I am done */
411
412		ch = hexval(*(ppos++));
413		if (ch < 0 || !*ppos) { /* must be valid pair of hex characters */
414			err = 1;
415			goto the_end;
416		}
417
418		ch2 = hexval(*(ppos++));
419		if (ch2 < 0) {
420			err = 1;
421			goto the_end;
422		}
423
424		ch = (ch * 16 + ch2) & 0xff; /* convert to byte */
425
426		if (ctr >= cbcmd) { /* need re-alloc buffer? (unlikely) */
427			cbcmd += 8192; /* increase by additional 8k */
428			ptemp = realloc(popt->pcmd, cbcmd);
429
430			if (!ptemp) {
431				err = 1;
432				fprintf(stderr,
433					"Not enough memory to interpret command bytes, errno=%d\n",
434					errno);
435				goto the_end;
436			}
437
438			popt->pcmd = (uint8_t *)ptemp;
439			pcur = popt->pcmd + ctr;
440		}
441
442		if (popt->lsb)
443			*pcur = reversebits[ch];
444		else
445			*pcur = (uint8_t)ch;
446
447		pcur++;
448		ctr++;
449	}
450
451	popt->ncmd = ctr; /* record num bytes in '-C' argument */
452
453the_end:
454
455	/* at this point popt->pcmd is NULL or a valid pointer */
456
457	return err;
458}
459
460static int
461get_info(int hdev, const char *dev_name)
462{
463	uint32_t fmode, fspeed;
464	int err;
465	char temp_buf[PATH_MAX], cpath[PATH_MAX];
466
467	if (!realpath(dev_name, cpath)) /* get canonical name for info purposes */
468		strlcpy(cpath, temp_buf, sizeof(cpath));  /* this shouldn't happen */
469
470	err = ioctl(hdev, SPIGENIOC_GET_SPI_MODE, &fmode);
471
472	if (err == 0)
473		err = ioctl(hdev, SPIGENIOC_GET_CLOCK_SPEED, &fspeed);
474
475	if (err == 0) {
476		fprintf(stderr,
477		        "Device name:   %s\n"
478		        "Device mode:   %d\n"
479		        "Device speed:  %d\n",
480		        cpath, fmode, fspeed);//, max_cmd, max_data, temp_buf);
481	}
482	else
483		fprintf(stderr, "Unable to query info (err=%d), errno=%d\n",
484		    err, errno);
485
486	return err;
487}
488
489static int
490set_mode(int hdev, struct spi_options *popt)
491{
492	uint32_t fmode = popt->mode;
493
494	if (popt->mode < 0)	/* use default? */
495		return 0;
496
497	return ioctl(hdev, SPIGENIOC_SET_SPI_MODE, &fmode);
498}
499
500static int
501set_speed(int hdev, struct spi_options *popt)
502{
503	uint32_t clock_speed = popt->speed;
504
505	if (popt->speed < 0)
506		return 0;
507
508	return ioctl(hdev, SPIGENIOC_SET_CLOCK_SPEED, &clock_speed);
509}
510
511static int
512hexval(char c)
513{
514	if (c >= '0' && c <= '9') {
515		return c - '0';
516	} else if (c >= 'A' && c <= 'F') {
517		return c - 'A' + 10;
518	} else if (c >= 'a' && c <= 'f') {
519		return c - 'a' + 10;
520	}
521	return -1;
522}
523
524static void *
525prep_write_buffer(struct spi_options *popt)
526{
527	int ch, ch2, ch3, ncmd, lsb, err;
528	uint8_t *pdata, *pdat2;
529	size_t cbdata, cbread;
530	const char *szbytes;
531
532	ncmd = popt->ncmd; /* num command bytes (can be zero) */
533
534	if (ncmd == 0 && popt->count == 0)
535		return NULL;	/* always since it's an error if it happens
536				 * now */
537
538	if (popt->count < 0) {
539		cbdata = DEFAULT_BUFFER_SIZE;
540	}
541	else {
542		cbdata = popt->count;
543	}
544
545	lsb = popt->lsb; /* non-zero if LSB order; else MSB */
546
547	pdata = malloc(cbdata + ncmd + 1);
548	cbread = 0;
549
550	err = 0;
551
552	if (!pdata)
553		return NULL;
554
555	if (popt->pcmd && ncmd > 0) {
556		memcpy(pdata, popt->pcmd, ncmd); /* copy command bytes */
557		pdat2 = pdata + ncmd;
558	}
559	else
560		pdat2 = pdata; /* no prepended command data */
561
562	/*
563	 * read up to 'cbdata' bytes.  If I get an EOF, do one of two things:
564	 * a) change the data count to match how many bytes I read in b) fill
565	 * the rest of the input buffer with zeros
566	 *
567	 * If the specified length is negative, I do 'a', else 'b'
568	 */
569
570	while (!err && cbread < cbdata && (ch = fgetc(stdin)) != EOF) {
571		if (popt->ASCII) {
572			/* skip consecutive white space */
573
574			while (ch <= ' ') {
575				if ((ch = fgetc(stdin)) == EOF)
576					break;
577			}
578
579			if (ch != EOF) {
580				ch2 = hexval(ch);
581
582				if (ch2 < 0) {
583invalid_character:
584					fprintf(stderr,
585					    "Invalid input character '%c'\n", ch);
586					err = 1;
587					break;
588				}
589
590				ch = fgetc(stdin);
591
592				if (ch != EOF) {
593					ch3 = hexval(ch);
594
595					if (ch3 < 0)
596						goto invalid_character;
597
598					ch = ch2 * 16 + ch3;
599				}
600			}
601
602			if (err || ch == EOF)
603				break;
604		}
605
606		/* for LSB, flip the bits - otherwise, just copy the value */
607		if (lsb)
608			pdat2[cbread] = reversebits[ch];
609		else
610			pdat2[cbread] = (uint8_t) ch;
611
612		cbread++; /* increment num bytes read so far */
613	}
614
615	/* if it was an error, not an EOF, that ended the I/O, return NULL */
616
617	if (err || ferror(stdin)) {
618		free(pdata);
619		return NULL;
620	}
621
622	if (popt->verbose > 0) {
623		const char *sz_bytes;
624
625		if (cbread != 1)
626			sz_bytes = "bytes";	/* correct plurality of 'byte|bytes' */
627		else
628			sz_bytes = "byte";
629
630		if (popt->ASCII)
631			fprintf(stderr, "ASCII input of %zd %s\n", cbread,
632			    sz_bytes);
633		else
634			fprintf(stderr, "Binary input of %zd %s\n", cbread,
635			    sz_bytes);
636	}
637
638	/*
639	 * if opt.count is negative, copy actual byte count to opt.count which does
640	 * not include any of the 'command' bytes that are being sent.  Can be zero.
641	 */
642	if (popt->count < 0) {
643		popt->count = cbread;
644	}
645	/*
646	 * for everything else, fill the rest of the read buffer with '0'
647	 * bytes, as per the standard practice for SPI
648	 */
649	else {
650		while (cbread < cbdata)
651			pdat2[cbread++] = 0;
652	}
653
654	/*
655	 * popt->count bytes will be sent and read from the SPI, preceded by the
656	 * 'popt->ncmd' command bytes (if any).
657	 * So we must use 'popt->count' and 'popt->ncmd' from this point on in
658	 * the code.
659	 */
660
661	if (popt->verbose > 0 && popt->count + popt->ncmd) {
662		if ((popt->count + popt->ncmd) == 1)
663			szbytes = "byte";
664		else
665			szbytes = "bytes";
666
667		fprintf(stderr, "Writing %d %s to SPI device\n",
668		        popt->count + popt->ncmd, szbytes);
669
670		verbose_dump_buffer(pdata, popt->count + popt->ncmd, lsb);
671	}
672
673	return pdata;
674}
675
676static int
677_read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb)
678{
679	int	err, ctr;
680	struct spigen_transfer spi;
681
682	if (!cbrw)
683		return 0;
684
685	if (!bufr)
686		bufr = bufw;
687	else
688		memcpy(bufr, bufw, cbrw);	/* transaction uses bufr for
689						 * both R and W */
690
691	bzero(&spi, sizeof(spi));	/* zero structure first */
692
693	/* spigen code seems to suggest there must be at least 1 command byte */
694
695	spi.st_command.iov_base = bufr;
696	spi.st_command.iov_len = cbrw;
697
698	/*
699	 * The remaining members for spi.st_data are zero - all bytes are
700	 * 'command' for this. The driver doesn't really do anything different
701	 * for 'command' vs 'data' and at least one command byte must be sent in
702	 * the transaction.
703	 */
704
705	err = ioctl(hdev, SPIGENIOC_TRANSFER, &spi) < 0 ? -1 : 0;
706
707	if (!err && lsb) {
708		/* flip the bits for 'lsb' mode */
709		for (ctr = 0; ctr < cbrw; ctr++) {
710			((uint8_t *) bufr)[ctr] =
711			    reversebits[((uint8_t *)bufr)[ctr]];
712		}
713	}
714
715	if (err)
716		fprintf(stderr, "Error performing SPI transaction, errno=%d\n",
717		    errno);
718
719	return err;
720}
721
722static int
723_do_data_output(void *pr, struct spi_options *popt)
724{
725	int	err, idx, icount;
726	const char *sz_bytes, *sz_byte2;
727	const uint8_t *pbuf;
728
729	pbuf = (uint8_t *)pr + popt->ncmd; /* only the data we want */
730	icount = popt->count;
731	err = 0;
732
733	if (icount <= 0) {
734		return -1; /* should not but could happen */
735	}
736
737	if (icount != 1)
738		sz_bytes = "bytes";	/* correct plurality of 'byte|bytes' */
739	else
740		sz_bytes = "byte";
741
742	if (popt->ncmd != 1)
743		sz_byte2 = "bytes";
744	else
745		sz_byte2 = "byte";
746
747	/* binary on stdout */
748	if (popt->binary || !popt->ASCII) {
749		if (popt->verbose > 0)
750			fprintf(stderr, "Binary output of %d %s\n", icount,
751			    sz_bytes);
752
753		err = (int)fwrite(pbuf, 1, icount, stdout) != icount;
754	}
755	else if (icount > 0) {
756		if (popt->verbose > 0)
757			fprintf(stderr, "ASCII output of %d %s\n", icount,
758			    sz_bytes);
759
760		/* ASCII output */
761		for (idx = 0; !err && idx < icount; idx++) {
762			if (idx) {
763				/*
764				 * not the first time, insert separating space
765				 */
766				err = fputc(' ', stdout) == EOF;
767			}
768
769			if (!err)
770				err = fprintf(stdout, "%02hhx", pbuf[idx]) < 0;
771		}
772
773		if (!err)
774			err = fputc('\n', stdout) == EOF;
775	}
776
777	/* verbose text out on stderr */
778
779	if (err)
780		fprintf(stderr, "Error writing to stdout, errno=%d\n", errno);
781	else if (popt->verbose > 0 && icount) {
782		fprintf(stderr,
783		    "%d command %s and %d data %s read from SPI device\n",
784		    popt->ncmd, sz_byte2, icount, sz_bytes);
785
786		/* verbose output will show the command bytes as well */
787		verbose_dump_buffer(pr, icount + popt->ncmd, popt->lsb);
788	}
789
790	return err;
791}
792
793static int
794perform_read(int hdev, struct spi_options *popt)
795{
796	int icount, err;
797	void   *pr, *pw;
798
799	pr = NULL;
800	icount = popt->count + popt->ncmd;
801
802	/* prep write buffer filled with 0 bytes */
803	pw = malloc(icount);
804
805	if (!pw) {
806		err = -1;
807		goto the_end;
808	}
809
810	bzero(pw, icount);
811
812	/* if I included a command sequence, copy bytes to the write buf */
813	if (popt->pcmd && popt->ncmd > 0)
814		memcpy(pw, popt->pcmd, popt->ncmd);
815
816	pr = malloc(icount + 1);
817
818	if (!pr) {
819		err = -2;
820		goto the_end;
821	}
822
823	bzero(pr, icount);
824
825	err = _read_write(hdev, pw, pr, icount, popt->lsb);
826
827	if (!err && popt->count > 0)
828		err = _do_data_output(pr, popt);
829
830the_end:
831
832	free(pr);
833	free(pw);
834
835	return err;
836}
837
838static int
839perform_write(int hdev, struct spi_options *popt)
840{
841	int err;
842	void   *pw;
843
844	/* read data from cmd buf and stdin and write to 'write' buffer */
845
846	pw = prep_write_buffer(popt);
847
848	if (!pw) {
849		err = -1;
850		goto the_end;
851	}
852
853	err = _read_write(hdev, pw, NULL, popt->count + popt->ncmd, popt->lsb);
854
855the_end:
856
857	free(pw);
858
859	return err;
860}
861
862static int
863perform_readwrite(int hdev, struct spi_options *popt)
864{
865	int icount, err;
866	void   *pr, *pw;
867
868	pr = NULL;
869
870	pw = prep_write_buffer(popt);
871	icount = popt->count + popt->ncmd; /* assign after fn call */
872
873	if (!pw) {
874		err = -1;
875		goto the_end;
876	}
877
878	pr = malloc(icount + 1);
879
880	if (!pr) {
881		err = -2;
882		goto the_end;
883	}
884
885	bzero(pr, icount);
886
887	err = _read_write(hdev, pw, pr, icount, popt->lsb);
888
889	if (!err)
890		err = _do_data_output(pr, popt);
891
892the_end:
893
894	free(pr);
895	free(pw);
896
897	return err;
898}
899
900
901static void
902verbose_dump_buffer(void *pbuf, int icount, int lsb)
903{
904	uint8_t	ch;
905	int	ictr, ictr2, idx;
906
907	fputs("        |  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F "
908	      "|                  |\n", stderr);
909
910	for (ictr = 0; ictr < icount; ictr += 16) {
911		fprintf(stderr, " %6x | ", ictr & 0xfffff0);
912
913		for (ictr2 = 0; ictr2 < 16; ictr2++) {
914			idx = ictr + ictr2;
915
916			if (idx < icount) {
917				ch = ((uint8_t *) pbuf)[idx];
918
919				if (lsb)
920					ch = reversebits[ch];
921
922				fprintf(stderr, "%02hhx ", ch);
923			}
924			else {
925				fputs("   ", stderr);
926			}
927		}
928
929		fputs("| ", stderr);
930
931		for (ictr2 = 0; ictr2 < 16; ictr2++) {
932			idx = ictr + ictr2;
933
934			if (idx < icount) {
935				ch = ((uint8_t *) pbuf)[idx];
936
937				if (lsb)
938					ch = reversebits[ch];
939
940				if (ch < ' ' || ch > 127)
941					goto out_of_range;
942
943				fprintf(stderr, "%c", ch);
944			}
945			else if (idx < icount) {
946		out_of_range:
947				fputc('.', stderr);
948			}
949			else {
950				fputc(' ', stderr);
951			}
952		}
953
954		fputs(" |\n", stderr);
955	}
956
957	fflush(stderr);
958}
959