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