xref: /freebsd/usr.sbin/spi/spi.c (revision 6dc12ecfb268d2d043f9415156d475278f3f714b)
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
usage(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] [-S] [-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 	      "        -S constantly stream from stdin to bus\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
main(int argc,char * argv[],char * envp[]__unused)159 main(int argc, char *argv[], char *envp[] __unused)
160 {
161 	struct spi_options opt;
162 	int err, ch, hdev, finfo, stream, fdir;
163 	char *pstr;
164 	char dev_name[PATH_MAX * 2 + 5];
165 
166 	finfo = 0;
167 	stream = 0;
168 	fdir = DIR_NONE;
169 
170 	hdev = -1;
171 	err = 0;
172 
173 	dev_name[0] = 0;
174 
175 	opt.mode = -1;
176 	opt.speed = -1;
177 	opt.count = 0;
178 	opt.ASCII = 0;
179 	opt.binary = 0;
180 	opt.lsb = 0;
181 	opt.verbose = 0;
182 	opt.ncmd = 0;
183 	opt.pcmd = NULL;
184 
185 	while (!err && (ch = getopt(argc, argv, "f:d:m:s:c:C:AbLviSh")) != -1) {
186 		switch (ch) {
187 		case 'd':
188 			if (optarg[0] == 'r') {
189 				if (optarg[1] == 'w' && optarg[2] == 0) {
190 					fdir = DIR_READWRITE;
191 				}
192 				else if (optarg[1] == 0) {
193 					fdir = DIR_READ;
194 				}
195 			}
196 			else if (optarg[0] == 'w' && optarg[1] == 0) {
197 				fdir = DIR_WRITE;
198 			}
199 			else {
200 				err = 1;
201 			}
202 			break;
203 
204 		case 'f':
205 			if (!optarg[0]) {	/* unlikely */
206 				fputs("error - missing device name\n", stderr);
207 				err = 1;
208 			}
209 			else {
210 				if (optarg[0] == '/')
211 					strlcpy(dev_name, optarg,
212 					    sizeof(dev_name));
213 				else
214 					snprintf(dev_name, sizeof(dev_name),
215 					    "/dev/%s", optarg);
216 			}
217 			break;
218 
219 		case 'm':
220 			opt.mode = (int)strtol(optarg, &pstr, 10);
221 
222 			if (!pstr || *pstr || opt.mode < 0 || opt.mode > 3) {
223 				fprintf(stderr, "Invalid mode specified: %s\n",
224 				    optarg);
225 				err = 1;
226 			}
227 			break;
228 
229 		case 's':
230 			opt.speed = (int)strtol(optarg, &pstr, 10);
231 
232 			if (!pstr || *pstr || opt.speed < 0) {
233 				fprintf(stderr, "Invalid speed specified: %s\n",
234 				    optarg);
235 				err = 1;
236 			}
237 			break;
238 
239 		case 'c':
240 			opt.count = (int)strtol(optarg, &pstr, 10);
241 
242 			if (!pstr || *pstr) {
243 				fprintf(stderr, "Invalid count specified: %s\n",
244 				    optarg);
245 				err = 1;
246 			}
247 			break;
248 
249 		case 'C':
250 			if(opt.pcmd) /* specified more than once */
251 				err = 1;
252 			else {
253 				/* get malloc'd buffer or error */
254 				if (interpret_command_bytes(optarg, &opt))
255 					err = 1;
256 			}
257 
258 			break;
259 
260 		case 'A':
261 			opt.ASCII = 1;
262 			break;
263 
264 		case 'b':
265 			opt.binary = 1;
266 			break;
267 
268 		case 'L':
269 			opt.lsb = 1;
270 			break;
271 
272 		case 'v':
273 			opt.verbose++;
274 			break;
275 
276 		case 'i':
277 			finfo = 1;
278 			break;
279 
280 		case 'S':
281 			stream = 1;
282 			break;
283 
284 		default:
285 			err = 1;
286 			/* FALLTHROUGH */
287 		case 'h':
288 			usage();
289 			goto the_end;
290 		}
291 	}
292 
293 	argc -= optind;
294 	argv += optind;
295 
296 	if (err ||
297 	    (fdir == DIR_NONE && !finfo && opt.mode == -1 && opt.speed == -1 && opt.count == 0)) {
298 		/*
299 		 * if any of the direction, mode, speed, or count not specified,
300 		 * print usage
301 		 */
302 
303 		usage();
304 		goto the_end;
305 	}
306 
307 	if ((opt.count != 0 || opt.ncmd != 0) && fdir == DIR_NONE) {
308 		/*
309 		 * count was specified, but direction was not.  default is
310 		 * read/write
311 		 */
312 		/*
313 		 * this includes a negative count, which implies write from
314 		 * stdin
315 		 */
316 		if (opt.count == 0)
317 			fdir = DIR_WRITE;
318 		else
319 			fdir = DIR_READWRITE;
320 	}
321 
322 	if (opt.count < 0 && fdir != DIR_READWRITE && fdir != DIR_WRITE) {
323 		fprintf(stderr, "Invalid length %d when not writing data\n",
324 		    opt.count);
325 
326 		err = 1;
327 		usage();
328 		goto the_end;
329 	}
330 
331 
332 	if (!dev_name[0])	/* no device name specified */
333 		strlcpy(dev_name, DEFAULT_DEVICE_NAME, sizeof(dev_name));
334 
335 	hdev = open(dev_name, O_RDWR);
336 
337 	if (hdev == -1) {
338 		fprintf(stderr, "Error - unable to open '%s', errno=%d\n",
339 		    dev_name, errno);
340 		err = 1;
341 		goto the_end;
342 	}
343 
344 	if (finfo) {
345 		err = get_info(hdev, dev_name);
346 		goto the_end;
347 	}
348 
349 	/* check and assign mode, speed */
350 
351 	if (opt.mode != -1) {
352 		err = set_mode(hdev, &opt);
353 
354 		if (err)
355 			goto the_end;
356 	}
357 
358 	if (opt.speed != -1) {
359 		err = set_speed(hdev, &opt);
360 
361 		if (err)
362 			goto the_end;
363 	}
364 
365 	/* do data transfer */
366 
367 	if (stream) {
368 		while (!err && !feof(stdin)) {
369 			if (fdir == DIR_READ) {
370 				err = perform_read(hdev, &opt);
371 			}
372 			else if (fdir == DIR_WRITE) {
373 				err = perform_write(hdev, &opt);
374 			}
375 			else if (fdir == DIR_READWRITE) {
376 				err = perform_readwrite(hdev, &opt);
377 			}
378 		}
379 	}
380 	else {
381 		if (fdir == DIR_READ) {
382 			err = perform_read(hdev, &opt);
383 		}
384 		else if (fdir == DIR_WRITE) {
385 			err = perform_write(hdev, &opt);
386 		}
387 		else if (fdir == DIR_READWRITE) {
388 			err = perform_readwrite(hdev, &opt);
389 		}
390 	}
391 
392 the_end:
393 
394 	if (hdev != -1)
395 		close(hdev);
396 
397 	free(opt.pcmd);
398 
399 	return (err);
400 }
401 
402 static int
interpret_command_bytes(const char * parg,struct spi_options * popt)403 interpret_command_bytes(const char *parg, struct spi_options *popt)
404 {
405 	int ch, ch2, ctr, cbcmd, err;
406 	const char *ppos;
407 	void *ptemp;
408 	uint8_t *pcur;
409 
410 	err = 0;
411 	cbcmd = DEFAULT_BUFFER_SIZE; /* initial cmd buffer size */
412 	popt->pcmd = (uint8_t *)malloc(cbcmd);
413 
414 	if (!popt->pcmd)
415 		return 1;
416 
417 	pcur = popt->pcmd;
418 
419 	ctr = 0;
420 	ppos = parg;
421 
422 	while (*ppos) {
423 		while (*ppos && *ppos <= ' ') {
424 			ppos++; /* skip (optional) leading white space */
425 		}
426 
427 		if (!*ppos)
428 			break; /* I am done */
429 
430 		ch = hexval(*(ppos++));
431 		if (ch < 0 || !*ppos) { /* must be valid pair of hex characters */
432 			err = 1;
433 			goto the_end;
434 		}
435 
436 		ch2 = hexval(*(ppos++));
437 		if (ch2 < 0) {
438 			err = 1;
439 			goto the_end;
440 		}
441 
442 		ch = (ch * 16 + ch2) & 0xff; /* convert to byte */
443 
444 		if (ctr >= cbcmd) { /* need re-alloc buffer? (unlikely) */
445 			cbcmd += 8192; /* increase by additional 8k */
446 			ptemp = realloc(popt->pcmd, cbcmd);
447 
448 			if (!ptemp) {
449 				err = 1;
450 				fprintf(stderr,
451 					"Not enough memory to interpret command bytes, errno=%d\n",
452 					errno);
453 				goto the_end;
454 			}
455 
456 			popt->pcmd = (uint8_t *)ptemp;
457 			pcur = popt->pcmd + ctr;
458 		}
459 
460 		if (popt->lsb)
461 			*pcur = reversebits[ch];
462 		else
463 			*pcur = (uint8_t)ch;
464 
465 		pcur++;
466 		ctr++;
467 	}
468 
469 	popt->ncmd = ctr; /* record num bytes in '-C' argument */
470 
471 the_end:
472 
473 	/* at this point popt->pcmd is NULL or a valid pointer */
474 
475 	return err;
476 }
477 
478 static int
get_info(int hdev,const char * dev_name)479 get_info(int hdev, const char *dev_name)
480 {
481 	uint32_t fmode, fspeed;
482 	int err;
483 	char temp_buf[PATH_MAX], cpath[PATH_MAX];
484 
485 	if (!realpath(dev_name, cpath)) /* get canonical name for info purposes */
486 		strlcpy(cpath, temp_buf, sizeof(cpath));  /* this shouldn't happen */
487 
488 	err = ioctl(hdev, SPIGENIOC_GET_SPI_MODE, &fmode);
489 
490 	if (err == 0)
491 		err = ioctl(hdev, SPIGENIOC_GET_CLOCK_SPEED, &fspeed);
492 
493 	if (err == 0) {
494 		fprintf(stderr,
495 		        "Device name:   %s\n"
496 		        "Device mode:   %d\n"
497 		        "Device speed:  %d\n",
498 		        cpath, fmode, fspeed);//, max_cmd, max_data, temp_buf);
499 	}
500 	else
501 		fprintf(stderr, "Unable to query info (err=%d), errno=%d\n",
502 		    err, errno);
503 
504 	return err;
505 }
506 
507 static int
set_mode(int hdev,struct spi_options * popt)508 set_mode(int hdev, struct spi_options *popt)
509 {
510 	uint32_t fmode = popt->mode;
511 
512 	if (popt->mode < 0)	/* use default? */
513 		return 0;
514 
515 	return ioctl(hdev, SPIGENIOC_SET_SPI_MODE, &fmode);
516 }
517 
518 static int
set_speed(int hdev,struct spi_options * popt)519 set_speed(int hdev, struct spi_options *popt)
520 {
521 	uint32_t clock_speed = popt->speed;
522 
523 	if (popt->speed < 0)
524 		return 0;
525 
526 	return ioctl(hdev, SPIGENIOC_SET_CLOCK_SPEED, &clock_speed);
527 }
528 
529 static int
hexval(char c)530 hexval(char c)
531 {
532 	if (c >= '0' && c <= '9') {
533 		return c - '0';
534 	} else if (c >= 'A' && c <= 'F') {
535 		return c - 'A' + 10;
536 	} else if (c >= 'a' && c <= 'f') {
537 		return c - 'a' + 10;
538 	}
539 	return -1;
540 }
541 
542 static void *
prep_write_buffer(struct spi_options * popt)543 prep_write_buffer(struct spi_options *popt)
544 {
545 	int ch, ch2, ch3, ncmd, lsb, err;
546 	uint8_t *pdata, *pdat2;
547 	size_t cbdata, cbread;
548 	const char *szbytes;
549 
550 	ncmd = popt->ncmd; /* num command bytes (can be zero) */
551 
552 	if (ncmd == 0 && popt->count == 0)
553 		return NULL;	/* always since it's an error if it happens
554 				 * now */
555 
556 	if (popt->count < 0) {
557 		cbdata = DEFAULT_BUFFER_SIZE;
558 	}
559 	else {
560 		cbdata = popt->count;
561 	}
562 
563 	lsb = popt->lsb; /* non-zero if LSB order; else MSB */
564 
565 	pdata = malloc(cbdata + ncmd + 1);
566 	cbread = 0;
567 
568 	err = 0;
569 
570 	if (!pdata)
571 		return NULL;
572 
573 	if (popt->pcmd && ncmd > 0) {
574 		memcpy(pdata, popt->pcmd, ncmd); /* copy command bytes */
575 		pdat2 = pdata + ncmd;
576 	}
577 	else
578 		pdat2 = pdata; /* no prepended command data */
579 
580 	/*
581 	 * read up to 'cbdata' bytes.  If I get an EOF, do one of two things:
582 	 * a) change the data count to match how many bytes I read in b) fill
583 	 * the rest of the input buffer with zeros
584 	 *
585 	 * If the specified length is negative, I do 'a', else 'b'
586 	 */
587 
588 	while (!err && cbread < cbdata && (ch = fgetc(stdin)) != EOF) {
589 		if (popt->ASCII) {
590 			/* skip consecutive white space */
591 
592 			while (ch <= ' ') {
593 				if ((ch = fgetc(stdin)) == EOF)
594 					break;
595 			}
596 
597 			if (ch != EOF) {
598 				ch2 = hexval(ch);
599 
600 				if (ch2 < 0) {
601 invalid_character:
602 					fprintf(stderr,
603 					    "Invalid input character '%c'\n", ch);
604 					err = 1;
605 					break;
606 				}
607 
608 				ch = fgetc(stdin);
609 
610 				if (ch != EOF) {
611 					ch3 = hexval(ch);
612 
613 					if (ch3 < 0)
614 						goto invalid_character;
615 
616 					ch = ch2 * 16 + ch3;
617 				}
618 			}
619 
620 			if (err || ch == EOF)
621 				break;
622 		}
623 
624 		/* for LSB, flip the bits - otherwise, just copy the value */
625 		if (lsb)
626 			pdat2[cbread] = reversebits[ch];
627 		else
628 			pdat2[cbread] = (uint8_t) ch;
629 
630 		cbread++; /* increment num bytes read so far */
631 	}
632 
633 	/* if it was an error, not an EOF, that ended the I/O, return NULL */
634 
635 	if (err || ferror(stdin)) {
636 		free(pdata);
637 		return NULL;
638 	}
639 
640 	if (popt->verbose > 0) {
641 		const char *sz_bytes;
642 
643 		if (cbread != 1)
644 			sz_bytes = "bytes";	/* correct plurality of 'byte|bytes' */
645 		else
646 			sz_bytes = "byte";
647 
648 		if (popt->ASCII)
649 			fprintf(stderr, "ASCII input of %zd %s\n", cbread,
650 			    sz_bytes);
651 		else
652 			fprintf(stderr, "Binary input of %zd %s\n", cbread,
653 			    sz_bytes);
654 	}
655 
656 	/*
657 	 * if opt.count is negative, copy actual byte count to opt.count which does
658 	 * not include any of the 'command' bytes that are being sent.  Can be zero.
659 	 */
660 	if (popt->count < 0) {
661 		popt->count = cbread;
662 	}
663 	/*
664 	 * for everything else, fill the rest of the read buffer with '0'
665 	 * bytes, as per the standard practice for SPI
666 	 */
667 	else {
668 		while (cbread < cbdata)
669 			pdat2[cbread++] = 0;
670 	}
671 
672 	/*
673 	 * popt->count bytes will be sent and read from the SPI, preceded by the
674 	 * 'popt->ncmd' command bytes (if any).
675 	 * So we must use 'popt->count' and 'popt->ncmd' from this point on in
676 	 * the code.
677 	 */
678 
679 	if (popt->verbose > 0 && popt->count + popt->ncmd) {
680 		if ((popt->count + popt->ncmd) == 1)
681 			szbytes = "byte";
682 		else
683 			szbytes = "bytes";
684 
685 		fprintf(stderr, "Writing %d %s to SPI device\n",
686 		        popt->count + popt->ncmd, szbytes);
687 
688 		verbose_dump_buffer(pdata, popt->count + popt->ncmd, lsb);
689 	}
690 
691 	return pdata;
692 }
693 
694 static int
_read_write(int hdev,void * bufw,void * bufr,int cbrw,int lsb)695 _read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb)
696 {
697 	int	err, ctr;
698 	struct spigen_transfer spi;
699 
700 	if (!cbrw)
701 		return 0;
702 
703 	if (!bufr)
704 		bufr = bufw;
705 	else
706 		memcpy(bufr, bufw, cbrw);	/* transaction uses bufr for
707 						 * both R and W */
708 
709 	bzero(&spi, sizeof(spi));	/* zero structure first */
710 
711 	/* spigen code seems to suggest there must be at least 1 command byte */
712 
713 	spi.st_command.iov_base = bufr;
714 	spi.st_command.iov_len = cbrw;
715 
716 	/*
717 	 * The remaining members for spi.st_data are zero - all bytes are
718 	 * 'command' for this. The driver doesn't really do anything different
719 	 * for 'command' vs 'data' and at least one command byte must be sent in
720 	 * the transaction.
721 	 */
722 
723 	err = ioctl(hdev, SPIGENIOC_TRANSFER, &spi) < 0 ? -1 : 0;
724 
725 	if (!err && lsb) {
726 		/* flip the bits for 'lsb' mode */
727 		for (ctr = 0; ctr < cbrw; ctr++) {
728 			((uint8_t *) bufr)[ctr] =
729 			    reversebits[((uint8_t *)bufr)[ctr]];
730 		}
731 	}
732 
733 	if (err)
734 		fprintf(stderr, "Error performing SPI transaction, errno=%d\n",
735 		    errno);
736 
737 	return err;
738 }
739 
740 static int
_do_data_output(void * pr,struct spi_options * popt)741 _do_data_output(void *pr, struct spi_options *popt)
742 {
743 	int	err, idx, icount;
744 	const char *sz_bytes, *sz_byte2;
745 	const uint8_t *pbuf;
746 
747 	pbuf = (uint8_t *)pr + popt->ncmd; /* only the data we want */
748 	icount = popt->count;
749 	err = 0;
750 
751 	if (icount <= 0) {
752 		return -1; /* should not but could happen */
753 	}
754 
755 	if (icount != 1)
756 		sz_bytes = "bytes";	/* correct plurality of 'byte|bytes' */
757 	else
758 		sz_bytes = "byte";
759 
760 	if (popt->ncmd != 1)
761 		sz_byte2 = "bytes";
762 	else
763 		sz_byte2 = "byte";
764 
765 	/* binary on stdout */
766 	if (popt->binary || !popt->ASCII) {
767 		if (popt->verbose > 0)
768 			fprintf(stderr, "Binary output of %d %s\n", icount,
769 			    sz_bytes);
770 
771 		err = (int)fwrite(pbuf, 1, icount, stdout) != icount;
772 	}
773 	else if (icount > 0) {
774 		if (popt->verbose > 0)
775 			fprintf(stderr, "ASCII output of %d %s\n", icount,
776 			    sz_bytes);
777 
778 		/* ASCII output */
779 		for (idx = 0; !err && idx < icount; idx++) {
780 			if (idx) {
781 				/*
782 				 * not the first time, insert separating space
783 				 */
784 				err = fputc(' ', stdout) == EOF;
785 			}
786 
787 			if (!err)
788 				err = fprintf(stdout, "%02hhx", pbuf[idx]) < 0;
789 		}
790 
791 		if (!err)
792 			err = fputc('\n', stdout) == EOF;
793 	}
794 
795 	/* verbose text out on stderr */
796 
797 	if (err)
798 		fprintf(stderr, "Error writing to stdout, errno=%d\n", errno);
799 	else if (popt->verbose > 0 && icount) {
800 		fprintf(stderr,
801 		    "%d command %s and %d data %s read from SPI device\n",
802 		    popt->ncmd, sz_byte2, icount, sz_bytes);
803 
804 		/* verbose output will show the command bytes as well */
805 		verbose_dump_buffer(pr, icount + popt->ncmd, popt->lsb);
806 	}
807 
808 	return err;
809 }
810 
811 static int
perform_read(int hdev,struct spi_options * popt)812 perform_read(int hdev, struct spi_options *popt)
813 {
814 	int icount, err;
815 	void   *pr, *pw;
816 
817 	pr = NULL;
818 	icount = popt->count + popt->ncmd;
819 
820 	/* prep write buffer filled with 0 bytes */
821 	pw = malloc(icount);
822 
823 	if (!pw) {
824 		err = -1;
825 		goto the_end;
826 	}
827 
828 	bzero(pw, icount);
829 
830 	/* if I included a command sequence, copy bytes to the write buf */
831 	if (popt->pcmd && popt->ncmd > 0)
832 		memcpy(pw, popt->pcmd, popt->ncmd);
833 
834 	pr = malloc(icount + 1);
835 
836 	if (!pr) {
837 		err = -2;
838 		goto the_end;
839 	}
840 
841 	bzero(pr, icount);
842 
843 	err = _read_write(hdev, pw, pr, icount, popt->lsb);
844 
845 	if (!err && popt->count > 0)
846 		err = _do_data_output(pr, popt);
847 
848 the_end:
849 
850 	free(pr);
851 	free(pw);
852 
853 	return err;
854 }
855 
856 static int
perform_write(int hdev,struct spi_options * popt)857 perform_write(int hdev, struct spi_options *popt)
858 {
859 	int err;
860 	void   *pw;
861 
862 	/* read data from cmd buf and stdin and write to 'write' buffer */
863 
864 	pw = prep_write_buffer(popt);
865 
866 	if (!pw) {
867 		err = -1;
868 		goto the_end;
869 	}
870 
871 	err = _read_write(hdev, pw, NULL, popt->count + popt->ncmd, popt->lsb);
872 
873 the_end:
874 
875 	free(pw);
876 
877 	return err;
878 }
879 
880 static int
perform_readwrite(int hdev,struct spi_options * popt)881 perform_readwrite(int hdev, struct spi_options *popt)
882 {
883 	int icount, err;
884 	void   *pr, *pw;
885 
886 	pr = NULL;
887 
888 	pw = prep_write_buffer(popt);
889 	icount = popt->count + popt->ncmd; /* assign after fn call */
890 
891 	if (!pw) {
892 		err = -1;
893 		goto the_end;
894 	}
895 
896 	pr = malloc(icount + 1);
897 
898 	if (!pr) {
899 		err = -2;
900 		goto the_end;
901 	}
902 
903 	bzero(pr, icount);
904 
905 	err = _read_write(hdev, pw, pr, icount, popt->lsb);
906 
907 	if (!err)
908 		err = _do_data_output(pr, popt);
909 
910 the_end:
911 
912 	free(pr);
913 	free(pw);
914 
915 	return err;
916 }
917 
918 
919 static void
verbose_dump_buffer(void * pbuf,int icount,int lsb)920 verbose_dump_buffer(void *pbuf, int icount, int lsb)
921 {
922 	uint8_t	ch;
923 	int	ictr, ictr2, idx;
924 
925 	fputs("        |  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F "
926 	      "|                  |\n", stderr);
927 
928 	for (ictr = 0; ictr < icount; ictr += 16) {
929 		fprintf(stderr, " %6x | ", ictr & 0xfffff0);
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 				fprintf(stderr, "%02hhx ", ch);
941 			}
942 			else {
943 				fputs("   ", stderr);
944 			}
945 		}
946 
947 		fputs("| ", stderr);
948 
949 		for (ictr2 = 0; ictr2 < 16; ictr2++) {
950 			idx = ictr + ictr2;
951 
952 			if (idx < icount) {
953 				ch = ((uint8_t *) pbuf)[idx];
954 
955 				if (lsb)
956 					ch = reversebits[ch];
957 
958 				if (ch < ' ' || ch > 127)
959 					goto out_of_range;
960 
961 				fprintf(stderr, "%c", ch);
962 			}
963 			else if (idx < icount) {
964 		out_of_range:
965 				fputc('.', stderr);
966 			}
967 			else {
968 				fputc(' ', stderr);
969 			}
970 		}
971 
972 		fputs(" |\n", stderr);
973 	}
974 
975 	fflush(stderr);
976 }
977