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