xref: /freebsd/usr.sbin/spi/spi.c (revision ab40f58ccfe6c07ebefddc72f4661a52fe746353)
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$");
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 
56 struct 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 
73 static void	usage(void);
74 static int	interpret_command_bytes(const char *parg, struct spi_options *popt);
75 static void *	prep_write_buffer(struct spi_options *popt);
76 static int	_read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb);
77 static int	_do_data_output(void *pr, struct spi_options *popt);
78 static int	get_info(int hdev, const char *dev_name);
79 static int	set_mode(int hdev, struct spi_options *popt);
80 static int	set_speed(int hdev, struct spi_options *popt);
81 static int	hexval(char c);
82 static int	perform_read(int hdev, struct spi_options *popt);
83 static int	perform_write(int hdev, struct spi_options *popt);
84 static int	perform_readwrite(int hdev, struct spi_options *popt);
85 static 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  */
92 static 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 
128 static void
129 usage(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 
160 int
161 main(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 
374 the_end:
375 
376 	if (hdev != -1)
377 		close(hdev);
378 
379 	free(opt.pcmd);
380 
381 	return (err);
382 }
383 
384 static int
385 interpret_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 
453 the_end:
454 
455 	/* at this point popt->pcmd is NULL or a valid pointer */
456 
457 	return err;
458 }
459 
460 static int
461 get_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 
489 static int
490 set_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 
500 static int
501 set_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 
511 static int
512 hexval(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 
524 static void *
525 prep_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) {
583 invalid_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 
676 static 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 
722 static 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 
793 static int
794 perform_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 
830 the_end:
831 
832 	free(pr);
833 	free(pw);
834 
835 	return err;
836 }
837 
838 static int
839 perform_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 
855 the_end:
856 
857 	free(pw);
858 
859 	return err;
860 }
861 
862 static int
863 perform_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 
892 the_end:
893 
894 	free(pr);
895 	free(pw);
896 
897 	return err;
898 }
899 
900 
901 static void
902 verbose_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