/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2018 S.F.T. Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/ioccom.h>
#include <sys/spigenio.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <unistd.h>
#define DEFAULT_DEVICE_NAME "/dev/spigen0.0"
#define DEFAULT_BUFFER_SIZE 8192
#define DIR_READ 0
#define DIR_WRITE 1
#define DIR_READWRITE 2
#define DIR_NONE -1
struct spi_options {
int mode; /* mode (0,1,2,3, -1 == use default) */
int speed; /* speed (in Hz, -1 == use default) */
int count; /* count (0 through 'n' bytes, negative for
* stdin length) */
int binary; /* non-zero for binary output or zero for
* ASCII output when ASCII != 0 */
int ASCII; /* zero for binary input and output.
* non-zero for ASCII input, 'binary'
* determines output */
int lsb; /* non-zero for LSB order (default order is
* MSB) */
int verbose; /* non-zero for verbosity */
int ncmd; /* bytes to skip for incoming data */
uint8_t *pcmd; /* command data (NULL if none) */
};
static void usage(void);
static int interpret_command_bytes(const char *parg, struct spi_options *popt);
static void * prep_write_buffer(struct spi_options *popt);
static int _read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb);
static int _do_data_output(void *pr, struct spi_options *popt);
static int get_info(int hdev, const char *dev_name);
static int set_mode(int hdev, struct spi_options *popt);
static int set_speed(int hdev, struct spi_options *popt);
static int hexval(char c);
static int perform_read(int hdev, struct spi_options *popt);
static int perform_write(int hdev, struct spi_options *popt);
static int perform_readwrite(int hdev, struct spi_options *popt);
static void verbose_dump_buffer(void *pbuf, int icount, int lsb);
/*
* LSB array - reversebits[n] is the LSB value of n as an MSB. Use this array
* to obtain a reversed bit pattern of the index value when bits must
* be sent/received in an LSB order vs the default MSB
*/
static uint8_t reversebits[256] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};
static void
usage(void)
{
fputs(getprogname(), stderr);
fputs(" - communicate on SPI bus with slave devices\n"
"Usage:\n"
" spi [-f device] [-d r|w|rw] [-m mode] [-s max-speed] [-c count]\n"
" [-C \"command bytes\"] [-A] [-b] [-L] [-v]\n"
" spi -i [-f device] [-v]\n"
" spi -h\n"
" where\n"
" -f specifies the device (default is spigen0.0)\n"
" -d specifies the operation (r, w, or rw; default is rw)\n"
" -m specifies the mode (0, 1, 2, or 3)\n"
" -s specifies the maximum speed (default is 0, device default)\n"
" -c specifies the number of data bytes to transfer (default 0, i.e. none)\n"
" A negative value uses the length of the input data\n"
" -C specifies 'command bytes' to be sent, as 2 byte hexadecimal values\n"
" (these should be quoted, separated by optional white space)\n"
" -L specifies 'LSB' order on the SPI bus (default is MSB)\n"
" -i query information about the device\n"
" -A uses ASCII for input/output as 2-digit hex values\n"
" -b Override output format as binary (only valid with '-A')\n"
" -v verbose output\n"
" -h prints this message\n"
"\n"
"NOTE: setting the mode and/or speed is 'sticky'. Subsequent transactions\n"
" on that device will, by default, use the previously set values.\n"
"\n",
stderr);
}
int
main(int argc, char *argv[], char *envp[] __unused)
{
struct spi_options opt;
int err, ch, hdev, finfo, fdir;
char *pstr;
char dev_name[PATH_MAX * 2 + 5];
finfo = 0;
fdir = DIR_NONE;
hdev = -1;
err = 0;
dev_name[0] = 0;
opt.mode = -1;
opt.speed = -1;
opt.count = 0;
opt.ASCII = 0;
opt.binary = 0;
opt.lsb = 0;
opt.verbose = 0;
opt.ncmd = 0;
opt.pcmd = NULL;
while (!err && (ch = getopt(argc, argv, "f:d:m:s:c:C:AbLvih")) != -1) {
switch (ch) {
case 'd':
if (optarg[0] == 'r') {
if (optarg[1] == 'w' && optarg[2] == 0) {
fdir = DIR_READWRITE;
}
else if (optarg[1] == 0) {
fdir = DIR_READ;
}
}
else if (optarg[0] == 'w' && optarg[1] == 0) {
fdir = DIR_WRITE;
}
else {
err = 1;
}
break;
case 'f':
if (!optarg[0]) { /* unlikely */
fputs("error - missing device name\n", stderr);
err = 1;
}
else {
if (optarg[0] == '/')
strlcpy(dev_name, optarg,
sizeof(dev_name));
else
snprintf(dev_name, sizeof(dev_name),
"/dev/%s", optarg);
}
break;
case 'm':
opt.mode = (int)strtol(optarg, &pstr, 10);
if (!pstr || *pstr || opt.mode < 0 || opt.mode > 3) {
fprintf(stderr, "Invalid mode specified: %s\n",
optarg);
err = 1;
}
break;
case 's':
opt.speed = (int)strtol(optarg, &pstr, 10);
if (!pstr || *pstr || opt.speed < 0) {
fprintf(stderr, "Invalid speed specified: %s\n",
optarg);
err = 1;
}
break;
case 'c':
opt.count = (int)strtol(optarg, &pstr, 10);
if (!pstr || *pstr) {
fprintf(stderr, "Invalid count specified: %s\n",
optarg);
err = 1;
}
break;
case 'C':
if(opt.pcmd) /* specified more than once */
err = 1;
else {
/* get malloc'd buffer or error */
if (interpret_command_bytes(optarg, &opt))
err = 1;
}
break;
case 'A':
opt.ASCII = 1;
break;
case 'b':
opt.binary = 1;
break;
case 'L':
opt.lsb = 1;
break;
case 'v':
opt.verbose++;
break;
case 'i':
finfo = 1;
break;
default:
err = 1;
/* FALLTHROUGH */
case 'h':
usage();
goto the_end;
}
}
argc -= optind;
argv += optind;
if (err ||
(fdir == DIR_NONE && !finfo && opt.mode == -1 && opt.speed == -1 && opt.count == 0)) {
/*
* if any of the direction, mode, speed, or count not specified,
* print usage
*/
usage();
goto the_end;
}
if ((opt.count != 0 || opt.ncmd != 0) && fdir == DIR_NONE) {
/*
* count was specified, but direction was not. default is
* read/write
*/
/*
* this includes a negative count, which implies write from
* stdin
*/
if (opt.count == 0)
fdir = DIR_WRITE;
else
fdir = DIR_READWRITE;
}
if (opt.count < 0 && fdir != DIR_READWRITE && fdir != DIR_WRITE) {
fprintf(stderr, "Invalid length %d when not writing data\n",
opt.count);
err = 1;
usage();
goto the_end;
}
if (!dev_name[0]) /* no device name specified */
strlcpy(dev_name, DEFAULT_DEVICE_NAME, sizeof(dev_name));
hdev = open(dev_name, O_RDWR);
if (hdev == -1) {
fprintf(stderr, "Error - unable to open '%s', errno=%d\n",
dev_name, errno);
err = 1;
goto the_end;
}
if (finfo) {
err = get_info(hdev, dev_name);
goto the_end;
}
/* check and assign mode, speed */
if (opt.mode != -1) {
err = set_mode(hdev, &opt);
if (err)
goto the_end;
}
if (opt.speed != -1) {
err = set_speed(hdev, &opt);
if (err)
goto the_end;
}
/* do data transfer */
if (fdir == DIR_READ) {
err = perform_read(hdev, &opt);
}
else if (fdir == DIR_WRITE) {
err = perform_write(hdev, &opt);
}
else if (fdir == DIR_READWRITE) {
err = perform_readwrite(hdev, &opt);
}
the_end:
if (hdev != -1)
close(hdev);
free(opt.pcmd);
return (err);
}
static int
interpret_command_bytes(const char *parg, struct spi_options *popt)
{
int ch, ch2, ctr, cbcmd, err;
const char *ppos;
void *ptemp;
uint8_t *pcur;
err = 0;
cbcmd = DEFAULT_BUFFER_SIZE; /* initial cmd buffer size */
popt->pcmd = (uint8_t *)malloc(cbcmd);
if (!popt->pcmd)
return 1;
pcur = popt->pcmd;
ctr = 0;
ppos = parg;
while (*ppos) {
while (*ppos && *ppos <= ' ') {
ppos++; /* skip (optional) leading white space */
}
if (!*ppos)
break; /* I am done */
ch = hexval(*(ppos++));
if (ch < 0 || !*ppos) { /* must be valid pair of hex characters */
err = 1;
goto the_end;
}
ch2 = hexval(*(ppos++));
if (ch2 < 0) {
err = 1;
goto the_end;
}
ch = (ch * 16 + ch2) & 0xff; /* convert to byte */
if (ctr >= cbcmd) { /* need re-alloc buffer? (unlikely) */
cbcmd += 8192; /* increase by additional 8k */
ptemp = realloc(popt->pcmd, cbcmd);
if (!ptemp) {
err = 1;
fprintf(stderr,
"Not enough memory to interpret command bytes, errno=%d\n",
errno);
goto the_end;
}
popt->pcmd = (uint8_t *)ptemp;
pcur = popt->pcmd + ctr;
}
if (popt->lsb)
*pcur = reversebits[ch];
else
*pcur = (uint8_t)ch;
pcur++;
ctr++;
}
popt->ncmd = ctr; /* record num bytes in '-C' argument */
the_end:
/* at this point popt->pcmd is NULL or a valid pointer */
return err;
}
static int
get_info(int hdev, const char *dev_name)
{
uint32_t fmode, fspeed;
int err;
char temp_buf[PATH_MAX], cpath[PATH_MAX];
if (!realpath(dev_name, cpath)) /* get canonical name for info purposes */
strlcpy(cpath, temp_buf, sizeof(cpath)); /* this shouldn't happen */
err = ioctl(hdev, SPIGENIOC_GET_SPI_MODE, &fmode);
if (err == 0)
err = ioctl(hdev, SPIGENIOC_GET_CLOCK_SPEED, &fspeed);
if (err == 0) {
fprintf(stderr,
"Device name: %s\n"
"Device mode: %d\n"
"Device speed: %d\n",
cpath, fmode, fspeed);//, max_cmd, max_data, temp_buf);
}
else
fprintf(stderr, "Unable to query info (err=%d), errno=%d\n",
err, errno);
return err;
}
static int
set_mode(int hdev, struct spi_options *popt)
{
uint32_t fmode = popt->mode;
if (popt->mode < 0) /* use default? */
return 0;
return ioctl(hdev, SPIGENIOC_SET_SPI_MODE, &fmode);
}
static int
set_speed(int hdev, struct spi_options *popt)
{
uint32_t clock_speed = popt->speed;
if (popt->speed < 0)
return 0;
return ioctl(hdev, SPIGENIOC_SET_CLOCK_SPEED, &clock_speed);
}
static int
hexval(char c)
{
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
} else if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
}
return -1;
}
static void *
prep_write_buffer(struct spi_options *popt)
{
int ch, ch2, ch3, ncmd, lsb, err;
uint8_t *pdata, *pdat2;
size_t cbdata, cbread;
const char *szbytes;
ncmd = popt->ncmd; /* num command bytes (can be zero) */
if (ncmd == 0 && popt->count == 0)
return NULL; /* always since it's an error if it happens
* now */
if (popt->count < 0) {
cbdata = DEFAULT_BUFFER_SIZE;
}
else {
cbdata = popt->count;
}
lsb = popt->lsb; /* non-zero if LSB order; else MSB */
pdata = malloc(cbdata + ncmd + 1);
cbread = 0;
err = 0;
if (!pdata)
return NULL;
if (popt->pcmd && ncmd > 0) {
memcpy(pdata, popt->pcmd, ncmd); /* copy command bytes */
pdat2 = pdata + ncmd;
}
else
pdat2 = pdata; /* no prepended command data */
/*
* read up to 'cbdata' bytes. If I get an EOF, do one of two things:
* a) change the data count to match how many bytes I read in b) fill
* the rest of the input buffer with zeros
*
* If the specified length is negative, I do 'a', else 'b'
*/
while (!err && cbread < cbdata && (ch = fgetc(stdin)) != EOF) {
if (popt->ASCII) {
/* skip consecutive white space */
while (ch <= ' ') {
if ((ch = fgetc(stdin)) == EOF)
break;
}
if (ch != EOF) {
ch2 = hexval(ch);
if (ch2 < 0) {
invalid_character:
fprintf(stderr,
"Invalid input character '%c'\n", ch);
err = 1;
break;
}
ch = fgetc(stdin);
if (ch != EOF) {
ch3 = hexval(ch);
if (ch3 < 0)
goto invalid_character;
ch = ch2 * 16 + ch3;
}
}
if (err || ch == EOF)
break;
}
/* for LSB, flip the bits - otherwise, just copy the value */
if (lsb)
pdat2[cbread] = reversebits[ch];
else
pdat2[cbread] = (uint8_t) ch;
cbread++; /* increment num bytes read so far */
}
/* if it was an error, not an EOF, that ended the I/O, return NULL */
if (err || ferror(stdin)) {
free(pdata);
return NULL;
}
if (popt->verbose > 0) {
const char *sz_bytes;
if (cbread != 1)
sz_bytes = "bytes"; /* correct plurality of 'byte|bytes' */
else
sz_bytes = "byte";
if (popt->ASCII)
fprintf(stderr, "ASCII input of %zd %s\n", cbread,
sz_bytes);
else
fprintf(stderr, "Binary input of %zd %s\n", cbread,
sz_bytes);
}
/*
* if opt.count is negative, copy actual byte count to opt.count which does
* not include any of the 'command' bytes that are being sent. Can be zero.
*/
if (popt->count < 0) {
popt->count = cbread;
}
/*
* for everything else, fill the rest of the read buffer with '0'
* bytes, as per the standard practice for SPI
*/
else {
while (cbread < cbdata)
pdat2[cbread++] = 0;
}
/*
* popt->count bytes will be sent and read from the SPI, preceded by the
* 'popt->ncmd' command bytes (if any).
* So we must use 'popt->count' and 'popt->ncmd' from this point on in
* the code.
*/
if (popt->verbose > 0 && popt->count + popt->ncmd) {
if ((popt->count + popt->ncmd) == 1)
szbytes = "byte";
else
szbytes = "bytes";
fprintf(stderr, "Writing %d %s to SPI device\n",
popt->count + popt->ncmd, szbytes);
verbose_dump_buffer(pdata, popt->count + popt->ncmd, lsb);
}
return pdata;
}
static int
_read_write(int hdev, void *bufw, void *bufr, int cbrw, int lsb)
{
int err, ctr;
struct spigen_transfer spi;
if (!cbrw)
return 0;
if (!bufr)
bufr = bufw;
else
memcpy(bufr, bufw, cbrw); /* transaction uses bufr for
* both R and W */
bzero(&spi, sizeof(spi)); /* zero structure first */
/* spigen code seems to suggest there must be at least 1 command byte */
spi.st_command.iov_base = bufr;
spi.st_command.iov_len = cbrw;
/*
* The remaining members for spi.st_data are zero - all bytes are
* 'command' for this. The driver doesn't really do anything different
* for 'command' vs 'data' and at least one command byte must be sent in
* the transaction.
*/
err = ioctl(hdev, SPIGENIOC_TRANSFER, &spi) < 0 ? -1 : 0;
if (!err && lsb) {
/* flip the bits for 'lsb' mode */
for (ctr = 0; ctr < cbrw; ctr++) {
((uint8_t *) bufr)[ctr] =
reversebits[((uint8_t *)bufr)[ctr]];
}
}
if (err)
fprintf(stderr, "Error performing SPI transaction, errno=%d\n",
errno);
return err;
}
static int
_do_data_output(void *pr, struct spi_options *popt)
{
int err, idx, icount;
const char *sz_bytes, *sz_byte2;
const uint8_t *pbuf;
pbuf = (uint8_t *)pr + popt->ncmd; /* only the data we want */
icount = popt->count;
err = 0;
if (icount <= 0) {
return -1; /* should not but could happen */
}
if (icount != 1)
sz_bytes = "bytes"; /* correct plurality of 'byte|bytes' */
else
sz_bytes = "byte";
if (popt->ncmd != 1)
sz_byte2 = "bytes";
else
sz_byte2 = "byte";
/* binary on stdout */
if (popt->binary || !popt->ASCII) {
if (popt->verbose > 0)
fprintf(stderr, "Binary output of %d %s\n", icount,
sz_bytes);
err = (int)fwrite(pbuf, 1, icount, stdout) != icount;
}
else if (icount > 0) {
if (popt->verbose > 0)
fprintf(stderr, "ASCII output of %d %s\n", icount,
sz_bytes);
/* ASCII output */
for (idx = 0; !err && idx < icount; idx++) {
if (idx) {
/*
* not the first time, insert separating space
*/
err = fputc(' ', stdout) == EOF;
}
if (!err)
err = fprintf(stdout, "%02hhx", pbuf[idx]) < 0;
}
if (!err)
err = fputc('\n', stdout) == EOF;
}
/* verbose text out on stderr */
if (err)
fprintf(stderr, "Error writing to stdout, errno=%d\n", errno);
else if (popt->verbose > 0 && icount) {
fprintf(stderr,
"%d command %s and %d data %s read from SPI device\n",
popt->ncmd, sz_byte2, icount, sz_bytes);
/* verbose output will show the command bytes as well */
verbose_dump_buffer(pr, icount + popt->ncmd, popt->lsb);
}
return err;
}
static int
perform_read(int hdev, struct spi_options *popt)
{
int icount, err;
void *pr, *pw;
pr = NULL;
icount = popt->count + popt->ncmd;
/* prep write buffer filled with 0 bytes */
pw = malloc(icount);
if (!pw) {
err = -1;
goto the_end;
}
bzero(pw, icount);
/* if I included a command sequence, copy bytes to the write buf */
if (popt->pcmd && popt->ncmd > 0)
memcpy(pw, popt->pcmd, popt->ncmd);
pr = malloc(icount + 1);
if (!pr) {
err = -2;
goto the_end;
}
bzero(pr, icount);
err = _read_write(hdev, pw, pr, icount, popt->lsb);
if (!err && popt->count > 0)
err = _do_data_output(pr, popt);
the_end:
free(pr);
free(pw);
return err;
}
static int
perform_write(int hdev, struct spi_options *popt)
{
int err;
void *pw;
/* read data from cmd buf and stdin and write to 'write' buffer */
pw = prep_write_buffer(popt);
if (!pw) {
err = -1;
goto the_end;
}
err = _read_write(hdev, pw, NULL, popt->count + popt->ncmd, popt->lsb);
the_end:
free(pw);
return err;
}
static int
perform_readwrite(int hdev, struct spi_options *popt)
{
int icount, err;
void *pr, *pw;
pr = NULL;
pw = prep_write_buffer(popt);
icount = popt->count + popt->ncmd; /* assign after fn call */
if (!pw) {
err = -1;
goto the_end;
}
pr = malloc(icount + 1);
if (!pr) {
err = -2;
goto the_end;
}
bzero(pr, icount);
err = _read_write(hdev, pw, pr, icount, popt->lsb);
if (!err)
err = _do_data_output(pr, popt);
the_end:
free(pr);
free(pw);
return err;
}
static void
verbose_dump_buffer(void *pbuf, int icount, int lsb)
{
uint8_t ch;
int ictr, ictr2, idx;
fputs(" | 0 1 2 3 4 5 6 7 8 9 A B C D E F "
"| |\n", stderr);
for (ictr = 0; ictr < icount; ictr += 16) {
fprintf(stderr, " %6x | ", ictr & 0xfffff0);
for (ictr2 = 0; ictr2 < 16; ictr2++) {
idx = ictr + ictr2;
if (idx < icount) {
ch = ((uint8_t *) pbuf)[idx];
if (lsb)
ch = reversebits[ch];
fprintf(stderr, "%02hhx ", ch);
}
else {
fputs(" ", stderr);
}
}
fputs("| ", stderr);
for (ictr2 = 0; ictr2 < 16; ictr2++) {
idx = ictr + ictr2;
if (idx < icount) {
ch = ((uint8_t *) pbuf)[idx];
if (lsb)
ch = reversebits[ch];
if (ch < ' ' || ch > 127)
goto out_of_range;
fprintf(stderr, "%c", ch);
}
else if (idx < icount) {
out_of_range:
fputc('.', stderr);
}
else {
fputc(' ', stderr);
}
}
fputs(" |\n", stderr);
}
fflush(stderr);
}