]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/spi/spi.c
zfs: merge openzfs/zfs@233d34e47
[FreeBSD/FreeBSD.git] / usr.sbin / spi / spi.c
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 }