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