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
53struct 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
70static void	usage(void);
71static int	interpret_command_bytes(const char *parg, struct spi_options *popt);
72static void *	prep_write_buffer(struct spi_options *popt);
73static int	_read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb);
74static int	_do_data_output(void *pr, struct spi_options *popt);
75static int	get_info(int hdev, const char *dev_name);
76static int	set_mode(int hdev, struct spi_options *popt);
77static int	set_speed(int hdev, struct spi_options *popt);
78static int	hexval(char c);
79static int	perform_read(int hdev, struct spi_options *popt);
80static int	perform_write(int hdev, struct spi_options *popt);
81static int	perform_readwrite(int hdev, struct spi_options *popt);
82static 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 */
89static 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
125static void
126usage(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
157int
158main(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
371the_end:
372
373	if (hdev != -1)
374		close(hdev);
375
376	free(opt.pcmd);
377
378	return (err);
379}
380
381static int
382interpret_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
450the_end:
451
452	/* at this point popt->pcmd is NULL or a valid pointer */
453
454	return err;
455}
456
457static int
458get_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
486static int
487set_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
497static int
498set_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
508static int
509hexval(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
521static void *
522prep_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) {
580invalid_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
673static 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
719static 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
790static int
791perform_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
827the_end:
828
829	free(pr);
830	free(pw);
831
832	return err;
833}
834
835static int
836perform_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
852the_end:
853
854	free(pw);
855
856	return err;
857}
858
859static int
860perform_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
889the_end:
890
891	free(pr);
892	free(pw);
893
894	return err;
895}
896
897
898static void
899verbose_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