Home | History | Annotate | Line # | Download | only in scmdctl
      1  1.3  andvar /*	$NetBSD: uart.c,v 1.3 2022/04/11 21:23:07 andvar Exp $	*/
      2  1.1    brad 
      3  1.1    brad /*
      4  1.1    brad  * Copyright (c) 2021 Brad Spencer <brad (at) anduin.eldar.org>
      5  1.1    brad  *
      6  1.1    brad  * Permission to use, copy, modify, and distribute this software for any
      7  1.1    brad  * purpose with or without fee is hereby granted, provided that the above
      8  1.1    brad  * copyright notice and this permission notice appear in all copies.
      9  1.1    brad  *
     10  1.1    brad  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  1.1    brad  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  1.1    brad  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  1.1    brad  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  1.1    brad  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  1.1    brad  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  1.1    brad  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  1.1    brad  */
     18  1.1    brad 
     19  1.1    brad #ifdef __RCSID
     20  1.3  andvar __RCSID("$NetBSD: uart.c,v 1.3 2022/04/11 21:23:07 andvar Exp $");
     21  1.1    brad #endif
     22  1.1    brad 
     23  1.1    brad /* Functions that know how to talk to a SCMD using the uart tty
     24  1.1    brad  * mode or via SPI userland, which ends up being mostly the same.
     25  1.1    brad  *
     26  1.1    brad  * Some of this is the same stuff that the kernel scmd(4) driver
     27  1.1    brad  * ends up doing.
     28  1.1    brad  */
     29  1.1    brad 
     30  1.1    brad #include <inttypes.h>
     31  1.1    brad #include <stdbool.h>
     32  1.1    brad #include <stdio.h>
     33  1.1    brad #include <stdlib.h>
     34  1.1    brad #include <unistd.h>
     35  1.1    brad #include <err.h>
     36  1.1    brad #include <fcntl.h>
     37  1.1    brad #include <string.h>
     38  1.1    brad #include <limits.h>
     39  1.1    brad #include <termios.h>
     40  1.1    brad #include <errno.h>
     41  1.1    brad #include <sys/ioctl.h>
     42  1.1    brad #include <sys/time.h>
     43  1.1    brad #include <dev/spi/spi_io.h>
     44  1.1    brad 
     45  1.1    brad #include <dev/ic/scmdreg.h>
     46  1.1    brad 
     47  1.1    brad #include "scmdctl.h"
     48  1.1    brad #include "responses.h"
     49  1.1    brad 
     50  1.1    brad #define EXTERN
     51  1.1    brad #include "uart.h"
     52  1.1    brad 
     53  1.1    brad 
     54  1.1    brad static int uart_subtype = -1;
     55  1.1    brad static int uart_spi_slave_addr = -1;
     56  1.1    brad 
     57  1.1    brad /* The uart tty mode of the SCMD device is useful for human or
     58  1.1    brad  * machine use.  However you can't really know what state it is in
     59  1.1    brad  * so send some junk and look for '>' character indicating a new
     60  1.1    brad  * command can be entered.  Usually this won't be needed, but
     61  1.1    brad  * you can never know when it is.
     62  1.1    brad  */
     63  1.1    brad int
     64  1.1    brad uart_clear(int fd, bool debug)
     65  1.1    brad {
     66  1.1    brad 	const char jcmd[4] = "qq\r\n";
     67  1.1    brad 	char input;
     68  1.1    brad 	int i;
     69  1.1    brad 
     70  1.1    brad 	if (uart_subtype == UART_IS_PURE_UART) {
     71  1.1    brad 		i = write(fd,jcmd,4);
     72  1.1    brad 		if (i == 4) {
     73  1.1    brad 			i = read(fd,&input,1);
     74  1.1    brad 			while (input != '>') {
     75  1.1    brad 				if (debug)
     76  1.1    brad 					fprintf(stderr,"uart_clear: %c\n",input);
     77  1.1    brad 				i = read(fd,&input,1);
     78  1.1    brad 			}
     79  1.1    brad 		} else {
     80  1.1    brad 			return EINVAL;
     81  1.1    brad 		}
     82  1.1    brad 	}
     83  1.1    brad 
     84  1.1    brad 	return 0;
     85  1.1    brad }
     86  1.1    brad 
     87  1.1    brad /* The SCMD device will echo back the characters in uart tty mode.
     88  1.1    brad  * Eat them here.
     89  1.1    brad  */
     90  1.1    brad static int
     91  1.1    brad pure_uart_send_cmd(int fd, const char *s, char *ibuf, int len)
     92  1.1    brad {
     93  1.1    brad 	int i;
     94  1.1    brad 
     95  1.1    brad 	i = write(fd,s,len);
     96  1.1    brad 	if (i == len) {
     97  1.1    brad 		i = read(fd,ibuf,len);
     98  1.1    brad 		return 0;
     99  1.1    brad 	} else {
    100  1.1    brad 		return EINVAL;
    101  1.1    brad 	}
    102  1.1    brad }
    103  1.1    brad 
    104  1.1    brad /* In pure uart tty mode, the command is sent as text and we are
    105  1.1    brad  * looking for '>'.  There is not a lot that can go wrong, but
    106  1.1    brad  * noise on the line is one of them, and that really is not detected here.
    107  1.1    brad  * This is probably the least reliable method of speaking to a SCMD
    108  1.1    brad  * device.
    109  1.1    brad  */
    110  1.1    brad static int
    111  1.1    brad uart_get_response(int fd, bool debug, char *obuf, int len)
    112  1.1    brad {
    113  1.1    brad 	int n,i;
    114  1.1    brad 	char c;
    115  1.1    brad 
    116  1.1    brad 	memset(obuf,0,len);
    117  1.1    brad 	n = 0;
    118  1.1    brad 	i = read(fd,&c,1);
    119  1.1    brad 	if (i == -1)
    120  1.1    brad 		return EINVAL;
    121  1.1    brad 	while (c != '>' && c != '\r' && n < len) {
    122  1.1    brad 		obuf[n] = c;
    123  1.1    brad 		if (debug)
    124  1.1    brad 			fprintf(stderr,"uart_get_response: looking for EOL or NL: %d %d -%c-\n",i,n,c);
    125  1.1    brad 		n++;
    126  1.1    brad 		i = read(fd,&c,1);
    127  1.1    brad 	}
    128  1.1    brad 
    129  1.1    brad 	if (c != '>') {
    130  1.1    brad 		i = read(fd,&c,1);
    131  1.1    brad 		if (i == -1)
    132  1.1    brad 			return EINVAL;
    133  1.1    brad 		while (c != '>') {
    134  1.1    brad 			if (debug)
    135  1.2  andvar 				fprintf(stderr,"uart_get_response: draining: %d -%c-\n",i,c);
    136  1.1    brad 			i = read(fd,&c,1);
    137  1.1    brad 			if (i == -1)
    138  1.1    brad 				return EINVAL;
    139  1.1    brad 		}
    140  1.1    brad 	}
    141  1.1    brad 
    142  1.1    brad 	return 0;
    143  1.1    brad }
    144  1.1    brad 
    145  1.1    brad /* This handles the two uart cases.  Either pure tty uart or SPI
    146  1.1    brad  * userland.  The first uses text commands and the second is binary,
    147  1.1    brad  * but has the strange read situation that scmd(4) has.
    148  1.1    brad  */
    149  1.1    brad static int
    150  1.1    brad uart_phy_read_register(int fd, bool debug, uint8_t reg, uint8_t *buf)
    151  1.1    brad {
    152  1.1    brad 	int err;
    153  1.1    brad 	char cmdbuf[9];
    154  1.1    brad 	char qbuf[10];
    155  1.1    brad 	struct timespec ts;
    156  1.1    brad 	struct spi_ioctl_transfer spi_t;
    157  1.1    brad 	uint8_t b;
    158  1.1    brad 
    159  1.1    brad 	if (SCMD_IS_HOLE(reg)) {
    160  1.1    brad 		*buf = SCMD_HOLE_VALUE;
    161  1.1    brad 		return 0;
    162  1.1    brad 	}
    163  1.1    brad 
    164  1.1    brad 	switch (uart_subtype) {
    165  1.1    brad 	case UART_IS_PURE_UART:
    166  1.1    brad 		sprintf(cmdbuf, "R%02X\r\n", reg);
    167  1.1    brad 		err = pure_uart_send_cmd(fd, cmdbuf, qbuf, 5);
    168  1.1    brad 		if (! err) {
    169  1.1    brad 			err = uart_get_response(fd, debug, qbuf, 5);
    170  1.1    brad 			*buf = (uint8_t)strtol(qbuf,NULL,16);
    171  1.1    brad 		}
    172  1.1    brad 		break;
    173  1.1    brad 	case UART_IS_SPI_USERLAND:
    174  1.1    brad 		spi_t.sit_addr = uart_spi_slave_addr;
    175  1.1    brad 		reg = reg | 0x80;
    176  1.1    brad 		spi_t.sit_send = &reg;
    177  1.1    brad 		spi_t.sit_sendlen = 1;
    178  1.1    brad 		spi_t.sit_recv = NULL;
    179  1.1    brad 		spi_t.sit_recvlen = 0;
    180  1.1    brad 
    181  1.1    brad 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
    182  1.1    brad 		if (debug)
    183  1.1    brad 			fprintf(stderr,"uart_phy_read_register: IOCTL UL SPI send err: %d ; reg: %02x ; xreg: %02x\n",
    184  1.1    brad 			    err,reg,reg & 0x7f);
    185  1.1    brad 
    186  1.1    brad 		if (err == -1)
    187  1.1    brad 			return errno;
    188  1.1    brad 
    189  1.1    brad 		ts.tv_sec = 0;
    190  1.1    brad 		ts.tv_nsec = 50;
    191  1.1    brad 		nanosleep(&ts,NULL);
    192  1.1    brad 
    193  1.1    brad 		spi_t.sit_addr = uart_spi_slave_addr;
    194  1.1    brad 		spi_t.sit_send = NULL;
    195  1.1    brad 		spi_t.sit_sendlen = 0;
    196  1.1    brad 		b = SCMD_HOLE_VALUE;
    197  1.1    brad 		spi_t.sit_recv = &b;
    198  1.1    brad 		spi_t.sit_recvlen = 1;
    199  1.1    brad 
    200  1.1    brad 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
    201  1.1    brad 		if (debug)
    202  1.1    brad 			fprintf(stderr,"uart_phy_read_register: IOCTL UL SPI receive 1 err: %d ; b: %02x\n",
    203  1.1    brad 			    err,b);
    204  1.1    brad 
    205  1.1    brad 		if (err == -1)
    206  1.1    brad 			return errno;
    207  1.1    brad 
    208  1.1    brad 		ts.tv_sec = 0;
    209  1.1    brad 		ts.tv_nsec = 50;
    210  1.1    brad 		nanosleep(&ts,NULL);
    211  1.1    brad 
    212  1.1    brad 		*buf = (uint8_t)b;
    213  1.1    brad 
    214  1.1    brad 		/* Bogus read that is needed */
    215  1.1    brad 		spi_t.sit_addr = uart_spi_slave_addr;
    216  1.1    brad 		spi_t.sit_send = NULL;
    217  1.1    brad 		spi_t.sit_sendlen = 0;
    218  1.1    brad 		b = SCMD_HOLE_VALUE;
    219  1.1    brad 		spi_t.sit_recv = &b;
    220  1.1    brad 		spi_t.sit_recvlen = 1;
    221  1.1    brad 
    222  1.1    brad 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
    223  1.1    brad 		if (debug)
    224  1.1    brad 			fprintf(stderr,"uart_phy_read_register: IOCTL UL SPI receive 2 err: %d ; b: %02x\n",
    225  1.1    brad 			    err,b);
    226  1.1    brad 
    227  1.1    brad 		if (err == -1)
    228  1.1    brad 			return errno;
    229  1.1    brad 
    230  1.1    brad 		ts.tv_sec = 0;
    231  1.1    brad 		ts.tv_nsec = 50;
    232  1.1    brad 		nanosleep(&ts,NULL);
    233  1.1    brad 
    234  1.1    brad 		break;
    235  1.1    brad 	default:
    236  1.1    brad 		return EINVAL;
    237  1.1    brad 		break;
    238  1.1    brad 	}
    239  1.1    brad 
    240  1.1    brad 	return err;
    241  1.1    brad }
    242  1.1    brad 
    243  1.1    brad /* Like read, this handles the two uart cases. */
    244  1.1    brad static int
    245  1.1    brad uart_phy_write_register(int fd, bool debug, uint8_t reg, uint8_t buf)
    246  1.1    brad {
    247  1.1    brad 	int err;
    248  1.1    brad 	char cmdbuf[9];
    249  1.1    brad 	char qbuf[10];
    250  1.1    brad 	struct timespec ts;
    251  1.1    brad 	struct spi_ioctl_transfer spi_t;
    252  1.1    brad 
    253  1.1    brad 	if (SCMD_IS_HOLE(reg)) {
    254  1.1    brad 		return 0;
    255  1.1    brad 	}
    256  1.1    brad 
    257  1.1    brad 	switch (uart_subtype) {
    258  1.1    brad 	case UART_IS_PURE_UART:
    259  1.1    brad 		sprintf(cmdbuf, "W%02X%02X\r\n", reg, buf);
    260  1.1    brad 		err = pure_uart_send_cmd(fd, cmdbuf, qbuf, 7);
    261  1.1    brad 		if (! err) {
    262  1.1    brad 			err = uart_get_response(fd, debug, qbuf, 10);
    263  1.1    brad 		}
    264  1.1    brad 		break;
    265  1.1    brad 	case UART_IS_SPI_USERLAND:
    266  1.1    brad 		spi_t.sit_addr = uart_spi_slave_addr;
    267  1.1    brad 		reg = reg & 0x7f;
    268  1.1    brad 		spi_t.sit_send = &reg;
    269  1.1    brad 		spi_t.sit_sendlen = 1;
    270  1.1    brad 		spi_t.sit_recv = NULL;
    271  1.1    brad 		spi_t.sit_recvlen = 0;
    272  1.1    brad 
    273  1.1    brad 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
    274  1.1    brad 		if (debug)
    275  1.1    brad 			fprintf(stderr,"uart_phy_write_register: IOCTL UL SPI write send 1 err: %d ; reg: %02x ; xreg: %02x\n",
    276  1.1    brad 			    err,reg,reg & 0x7f);
    277  1.1    brad 
    278  1.1    brad 		if (err == -1)
    279  1.1    brad 			return errno;
    280  1.1    brad 
    281  1.1    brad 		spi_t.sit_addr = uart_spi_slave_addr;
    282  1.1    brad 		spi_t.sit_send = &buf;
    283  1.1    brad 		spi_t.sit_sendlen = 1;
    284  1.1    brad 		spi_t.sit_recv = NULL;
    285  1.1    brad 		spi_t.sit_recvlen = 0;
    286  1.1    brad 
    287  1.1    brad 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
    288  1.1    brad 		if (debug)
    289  1.1    brad 			fprintf(stderr,"uart_phy_write_register: IOCTL UL SPI write send 2 err: %d ; buf: %02x\n",
    290  1.1    brad 			    err,buf);
    291  1.1    brad 
    292  1.1    brad 		if (err == -1)
    293  1.1    brad 			return errno;
    294  1.1    brad 
    295  1.1    brad 		ts.tv_sec = 0;
    296  1.1    brad 		ts.tv_nsec = 50;
    297  1.1    brad 		nanosleep(&ts,NULL);
    298  1.1    brad 
    299  1.1    brad 		break;
    300  1.1    brad 	default:
    301  1.1    brad 		return EINVAL;
    302  1.1    brad 		break;
    303  1.1    brad 	}
    304  1.1    brad 
    305  1.1    brad 	return err;
    306  1.1    brad }
    307  1.1    brad 
    308  1.1    brad static int
    309  1.1    brad uart_local_read_register(int fd, bool debug, uint8_t reg, uint8_t reg_end, uint8_t *r)
    310  1.1    brad {
    311  1.1    brad 	uint8_t b;
    312  1.1    brad 	int err = 0;
    313  1.1    brad 
    314  1.1    brad 	for(int q = reg, g = 0; q <= reg_end; q++, g++) {
    315  1.1    brad 		err = uart_phy_read_register(fd, debug, q, &b);
    316  1.1    brad 		if (!err)
    317  1.1    brad 			r[g] = b;
    318  1.1    brad 	}
    319  1.1    brad 
    320  1.1    brad 	return err;
    321  1.1    brad }
    322  1.1    brad 
    323  1.1    brad /* When speaking to a SCMD device in any uart mode the view port for
    324  1.3  andvar  * chained slave modules has to be handled in userland.  This is similar
    325  1.1    brad  * to what the scmd(4) kernel driver ends up doing, but is much slower.
    326  1.1    brad  */
    327  1.1    brad static int
    328  1.1    brad uart_set_view_port(int fd, bool debug, int a_module, uint8_t vpi2creg)
    329  1.1    brad {
    330  1.1    brad 	int err;
    331  1.1    brad 	uint8_t vpi2caddr = (SCMD_REMOTE_ADDR_LOW + a_module) - 1;
    332  1.1    brad 
    333  1.1    brad 	if (debug)
    334  1.1    brad 		fprintf(stderr, "uart_set_view_port: View port addr: %02x ; View port register: %02x\n",
    335  1.1    brad 		    vpi2caddr, vpi2creg);
    336  1.1    brad 
    337  1.1    brad 	err = uart_phy_write_register(fd, debug, SCMD_REG_REM_ADDR, vpi2caddr);
    338  1.1    brad 	if (! err)
    339  1.1    brad 		err = uart_phy_write_register(fd, debug, SCMD_REG_REM_OFFSET, vpi2creg);
    340  1.1    brad 
    341  1.1    brad 	return err;
    342  1.1    brad }
    343  1.1    brad 
    344  1.1    brad static int
    345  1.1    brad uart_remote_read_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_end, uint8_t *r)
    346  1.1    brad {
    347  1.1    brad 	int err;
    348  1.1    brad 	int c;
    349  1.1    brad 	uint8_t b;
    350  1.1    brad 
    351  1.1    brad 	for(int q = reg, g = 0; q <= reg_end; q++, g++) {
    352  1.1    brad 		err = uart_set_view_port(fd, debug, a_module, q);
    353  1.1    brad 		if (err)
    354  1.1    brad 			break;
    355  1.1    brad 
    356  1.1    brad 		b = 0xff; /* you can write anything here.. it doesn't matter */
    357  1.1    brad 		err = uart_phy_write_register(fd, debug, SCMD_REG_REM_READ, b);
    358  1.1    brad 		if (err)
    359  1.1    brad 			break;
    360  1.1    brad 
    361  1.1    brad 		/* So ...  there is no way to really know that the data is ready and
    362  1.1    brad 		 * there is no way to know if there was an error in the master module reading
    363  1.1    brad 		 * the data from the slave module.  The data sheet says wait 5ms.. so we will
    364  1.1    brad 		 * wait a bit and see if the register cleared, but don't wait forever...  I
    365  1.1    brad 		 * can't see how it would not be possible to read junk at times.
    366  1.1    brad 		 */
    367  1.1    brad 		c = 0;
    368  1.1    brad 		do {
    369  1.1    brad 			sleep(1);
    370  1.1    brad 			err = uart_phy_read_register(fd, debug, SCMD_REG_REM_READ, &b);
    371  1.1    brad 			c++;
    372  1.1    brad 		} while ((c < 10) && (b != 0x00) && (!err));
    373  1.1    brad 
    374  1.1    brad 		/* We can only hope that whatever was read from the slave module is there */
    375  1.1    brad 		if (err)
    376  1.1    brad 			break;
    377  1.1    brad 		err = uart_phy_read_register(fd, debug, SCMD_REG_REM_DATA_RD, &b);
    378  1.1    brad 		if (err)
    379  1.1    brad 			break;
    380  1.1    brad 		r[g] = b;
    381  1.1    brad 	}
    382  1.1    brad 
    383  1.1    brad 	return err;
    384  1.1    brad }
    385  1.1    brad 
    386  1.1    brad void
    387  1.1    brad uart_set_subtype(int subt, int spi_s_addr)
    388  1.1    brad {
    389  1.1    brad 	uart_subtype = subt;
    390  1.1    brad 	uart_spi_slave_addr = spi_s_addr;
    391  1.1    brad 
    392  1.1    brad 	return;
    393  1.1    brad }
    394  1.1    brad 
    395  1.1    brad /* Unlike scmd(4) local reads and remote module reads are done very
    396  1.1    brad  * differently.
    397  1.1    brad  */
    398  1.1    brad int
    399  1.1    brad uart_read_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_end, uint8_t *r)
    400  1.1    brad {
    401  1.1    brad 	int err;
    402  1.1    brad 
    403  1.1    brad 	if (reg > SCMD_LAST_REG ||
    404  1.1    brad 	    reg_end > SCMD_LAST_REG)
    405  1.1    brad 		return EINVAL;
    406  1.1    brad 
    407  1.1    brad 	if (reg_end < reg)
    408  1.1    brad 		return EINVAL;
    409  1.1    brad 
    410  1.1    brad 	err = uart_clear(fd, debug);
    411  1.1    brad 	if (! err) {
    412  1.1    brad 		if (a_module == 0) {
    413  1.1    brad 			err = uart_local_read_register(fd, debug, reg, reg_end, r);
    414  1.1    brad 		} else {
    415  1.1    brad 			err = uart_remote_read_register(fd, debug, a_module, reg, reg_end, r);
    416  1.1    brad 		}
    417  1.1    brad 	}
    418  1.1    brad 
    419  1.1    brad 	return err;
    420  1.1    brad }
    421  1.1    brad 
    422  1.1    brad static int
    423  1.1    brad uart_remote_write_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_v)
    424  1.1    brad {
    425  1.1    brad 	int err;
    426  1.1    brad 	int c;
    427  1.1    brad 	uint8_t b;
    428  1.1    brad 
    429  1.1    brad 	err = uart_set_view_port(fd, debug, a_module, reg);
    430  1.1    brad 	if (! err) {
    431  1.1    brad 		/* We just sort of send this write off and wait to see if the register
    432  1.1    brad 		 * clears.  There really isn't any indication that the data made it to the
    433  1.1    brad 		 * slave modules.
    434  1.1    brad 		 */
    435  1.1    brad 		err = uart_phy_write_register(fd, debug, SCMD_REG_REM_DATA_WR, reg_v);
    436  1.1    brad 		if (! err) {
    437  1.1    brad 			b = 0xff; /* you can write anything here.. it doesn't matter */
    438  1.1    brad 			err = uart_phy_write_register(fd, debug, SCMD_REG_REM_WRITE, b);
    439  1.1    brad 			if (! err) {
    440  1.1    brad 				c = 0;
    441  1.1    brad 				do {
    442  1.1    brad 					sleep(1);
    443  1.1    brad 					err = uart_phy_read_register(fd, debug, SCMD_REG_REM_WRITE, &b);
    444  1.1    brad 					c++;
    445  1.1    brad 				} while ((c < 10) && (b != 0x00) && (!err));
    446  1.1    brad 			}
    447  1.1    brad 		}
    448  1.1    brad 	}
    449  1.1    brad 
    450  1.1    brad 	return err;
    451  1.1    brad }
    452  1.1    brad 
    453  1.1    brad /* Like reads, writes are done very differently between scmd(4) and
    454  1.1    brad  * the uart modes.
    455  1.1    brad  */
    456  1.1    brad int
    457  1.1    brad uart_write_register(int fd, bool debug, int a_module, uint8_t reg, uint8_t reg_v)
    458  1.1    brad {
    459  1.1    brad 	int err;
    460  1.1    brad 
    461  1.1    brad 	if (reg > SCMD_LAST_REG)
    462  1.1    brad 		return EINVAL;
    463  1.1    brad 
    464  1.1    brad 	err = uart_clear(fd, debug);
    465  1.1    brad 	if (! err) {
    466  1.1    brad 		if (a_module == 0) {
    467  1.1    brad 			err = uart_phy_write_register(fd, debug, reg, reg_v);
    468  1.1    brad 		} else {
    469  1.1    brad 			err = uart_remote_write_register(fd, debug, a_module, reg, reg_v);
    470  1.1    brad 		}
    471  1.1    brad 	}
    472  1.1    brad 
    473  1.1    brad 	return err;
    474  1.1    brad }
    475  1.1    brad 
    476  1.1    brad /* This is a special ability to do a single SPI receive that has the
    477  1.1    brad  * hope of resyncing the device should it get out of sync in SPI mode.
    478  1.1    brad  * This will work for either SPI userland mode or scmd(4) when attached
    479  1.1    brad  * to the SPI bus as you can still write to /dev/spiN then too.
    480  1.1    brad  */
    481  1.1    brad int
    482  1.1    brad uart_ul_spi_read_one(int fd, bool debug)
    483  1.1    brad {
    484  1.1    brad 	int err = 0;
    485  1.1    brad 	struct timespec ts;
    486  1.1    brad 	struct spi_ioctl_transfer spi_t;
    487  1.1    brad 	uint8_t b;
    488  1.1    brad 
    489  1.1    brad 	if (uart_subtype == UART_IS_SPI_USERLAND) {
    490  1.1    brad 		spi_t.sit_addr = uart_spi_slave_addr;
    491  1.1    brad 		spi_t.sit_send = NULL;
    492  1.1    brad 		spi_t.sit_sendlen = 0;
    493  1.1    brad 		b = SCMD_HOLE_VALUE;
    494  1.1    brad 		spi_t.sit_recv = &b;
    495  1.1    brad 		spi_t.sit_recvlen = 1;
    496  1.1    brad 
    497  1.1    brad 		err = ioctl(fd,SPI_IOCTL_TRANSFER,&spi_t);
    498  1.1    brad 		if (debug)
    499  1.1    brad 			fprintf(stderr,"uart_ul_spi_read_one: IOCTL UL SPI receive 1 err: %d ; b: %02x\n",
    500  1.1    brad 			    err,b);
    501  1.1    brad 
    502  1.1    brad 		if (err == -1)
    503  1.1    brad 			return errno;
    504  1.1    brad 
    505  1.1    brad 		ts.tv_sec = 0;
    506  1.1    brad 		ts.tv_nsec = 50;
    507  1.1    brad 		nanosleep(&ts,NULL);
    508  1.1    brad 	}
    509  1.1    brad 
    510  1.1    brad 	return err;
    511  1.1    brad }
    512