Home | History | Annotate | Line # | Download | only in scmdctl
      1 /*	$NetBSD: scmdctl.c,v 1.3 2024/12/01 10:32:48 rillig Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2021 Brad Spencer <brad (at) anduin.eldar.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/cdefs.h>
     20 #ifdef __RCSID
     21 __RCSID("$NetBSD: scmdctl.c,v 1.3 2024/12/01 10:32:48 rillig Exp $");
     22 #endif
     23 
     24 /* Main userland program that knows how to talk to the Sparkfun
     25  * Serial Controlled Motor Driver (SCMD).  The device provides
     26  * 127 registers that are used to interact with the motors.
     27  * This program provides some convience commands to work with most
     28  * of the abilities of the SCMD device.
     29  *
     30  * This knows how to talk to a SCMD device via:
     31  *
     32  * 1) The uart tty interface that is provided by the SCMD device
     33  * 2) Userland SPI talking to something like /dev/spi0 directly
     34  *    In most ways this acts like talking to the tty uart.
     35  * 3) Using the scmd(4) i2c or spi driver.  This is, by far, the
     36  *    fastest way to access the driver.  The other methods have
     37  *    increased latency.
     38  */
     39 
     40 #include <inttypes.h>
     41 #include <stdbool.h>
     42 #include <stdio.h>
     43 #include <stdlib.h>
     44 #include <unistd.h>
     45 #include <err.h>
     46 #include <fcntl.h>
     47 #include <string.h>
     48 #include <limits.h>
     49 #include <termios.h>
     50 #include <sys/ioctl.h>
     51 #include <sys/time.h>
     52 #include <dev/spi/spi_io.h>
     53 
     54 #include <dev/ic/scmdreg.h>
     55 
     56 #define EXTERN extern
     57 #include "common.h"
     58 #include "scmdctl.h"
     59 #include "uart.h"
     60 #include "i2cspi.h"
     61 #include "printscmd.h"
     62 #include "responses.h"
     63 #include "scmdctlconst.h"
     64 
     65 int	ul_spisetup(int, int);
     66 int	ttysetup(int, speed_t);
     67 int	valid_cmd(const struct scmdcmd[], long unsigned int, char *);
     68 
     69 
     70 static void
     71 usage(void)
     72 {
     73 	const char *p = getprogname();
     74 
     75 	fprintf(stderr, "Usage: %s [-dlh] [-b baud rate] [-s SPI slave addr] device cmd args\n\n",
     76 	    p);
     77 
     78 	for(long unsigned int i = 0;i < __arraycount(scmdcmds);i++) {
     79 		fprintf(stderr,"%s [-dlh] [-b baud rate] [-s SPI slave addr] device %s %s\n",
     80 		    p,scmdcmds[i].cmd,scmdcmds[i].helpargs);
     81 	}
     82 }
     83 
     84 int
     85 valid_cmd(const struct scmdcmd c[], long unsigned int csize, char *cmdtocheck)
     86 {
     87 	int r = -1;
     88 
     89 	for(long unsigned int i = 0;i < csize;i++) {
     90 		if (strncmp(cmdtocheck,c[i].cmd,16) == 0) {
     91 			r = i;
     92 			break;
     93 		}
     94 	}
     95 
     96 	return r;
     97 }
     98 
     99 /* This is expected to fail if the device is not a classic tty */
    100 int
    101 ttysetup(int fd, speed_t spd)
    102 {
    103         struct termios  cntrl;
    104 
    105         (void)tcgetattr(fd, &cntrl);
    106         (void)cfsetospeed(&cntrl, spd);
    107         (void)cfsetispeed(&cntrl, spd);
    108         cntrl.c_cflag &= ~(CSIZE|PARENB);
    109         cntrl.c_cflag |= CS8;
    110 	cntrl.c_cflag |= CLOCAL;
    111         cntrl.c_iflag &= ~(ISTRIP|ICRNL);
    112         cntrl.c_oflag &= ~OPOST;
    113         cntrl.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
    114         cntrl.c_cc[VMIN] = 1;
    115         cntrl.c_cc[VTIME] = 0;
    116 	cntrl.c_iflag &= ~(IXOFF|IXON);
    117         return tcsetattr(fd, TCSAFLUSH, &cntrl);
    118 }
    119 
    120 /* This is for userland SPI and is expected to fail if the device is
    121  * not a /dev/spiN
    122  */
    123 int
    124 ul_spisetup(int fd, int slave_addr)
    125 {
    126 	struct timespec ts;
    127 	struct spi_ioctl_configure spi_c;
    128 	int e;
    129 
    130 	spi_c.sic_addr = slave_addr;
    131 #define SPI_MODE_0 0
    132 #define SPI_MODE_1 1
    133 #define SPI_MODE_2 2
    134 #define SPI_MODE_3 3
    135 	spi_c.sic_mode = SPI_MODE_0;
    136 	spi_c.sic_speed = 1000000;
    137 
    138 	e = ioctl(fd,SPI_IOCTL_CONFIGURE,&spi_c);
    139 	if (e != -1) {
    140 		ts.tv_sec = 0;
    141 		ts.tv_nsec = 50;
    142 		nanosleep(&ts,NULL);
    143 	}
    144 
    145 	return e;
    146 }
    147 
    148 int
    149 main(int argc, char *argv[])
    150 {
    151 	int c;
    152 	bool debug = false;
    153 	int fd = -1, error, ttyerror = 0, ul_spierror = 0, valid, validsub = -1;
    154 	long baud_rate = 9600;
    155 	long slave_a = 0;
    156 	bool dev_is_uart = true;
    157 	int uart_s = UART_IS_PURE_UART;
    158 	struct scmd_identify_response ir;
    159 	struct scmd_diag_response diag;
    160 	struct scmd_motor_response motors;
    161 	long module;
    162 	char motor;
    163 	int8_t reg_value;
    164 	uint8_t reg = 0, reg_e = 0, ur, ebus_s, lock_state;
    165 	uint8_t register_shadow[SCMD_REG_SIZE];
    166 	int lock_type = -1;
    167 	bool list_names = false;
    168 	struct function_block func_block;
    169 
    170 	while ((c = getopt(argc, argv, "db:s:lh")) != -1 ) {
    171 		switch (c) {
    172 		case 'd':
    173 			debug = true;
    174 			break;
    175 		case 'b':
    176 			baud_rate = (long)strtoi(optarg, NULL, 0, 1, LONG_MAX, &error);
    177 			if (error)
    178 				warnc(error, "Conversion of `%s' to a baud rate "
    179 				    "failed, using %ld", optarg, baud_rate);
    180 			break;
    181 		case 's':
    182 			slave_a = (long)strtoi(optarg, NULL, 0, 0, LONG_MAX, &error);
    183 			if (error)
    184 				warnc(error, "Conversion of `%s' to a SPI slave address "
    185 				    "failed, using %ld", optarg, slave_a);
    186 			break;
    187 		case 'l':
    188 			list_names = true;
    189 			break;
    190 		case 'h':
    191 		default:
    192 			usage();
    193 			exit(0);
    194 		}
    195 	}
    196 
    197 	argc -= optind;
    198 	argv += optind;
    199 
    200 	if (debug) {
    201 		fprintf(stderr,"ARGC: %d\n", argc);
    202 		fprintf(stderr,"ARGV[0]: %s ; ARGV[1]: %s ; ARGV[2]: %s ; ARGV[3]: %s; ARGV[4]: %s; ARGV[5]: %s\n",
    203 		    argv[0],argv[1],argv[2],argv[3],argv[4],argv[5]);
    204 	}
    205 
    206 	if (list_names) {
    207 		for(c = 0x00; c < SCMD_REG_SIZE;c++)
    208 			printf("Register %d (0x%02X): %s\n",c,c,scmdregisternames[c]);
    209 		exit(0);
    210 	}
    211 
    212 	if (argc <= 1) {
    213 		usage();
    214 		exit(0);
    215 	}
    216 
    217 	fd = open(argv[0], O_RDWR, 0);
    218 	if (fd == -1) {
    219 		err(EXIT_FAILURE, "open %s", argv[0]);
    220 	}
    221 
    222 	/* Figure out what the device is.  First try uart tty,
    223 	 * then SPI userland and the if those two fail, assume
    224 	 * scmd(4).
    225 	 */
    226 	ttyerror = ttysetup(fd,(speed_t)baud_rate);
    227 
    228 	if (ttyerror) {
    229 		ul_spierror = ul_spisetup(fd, slave_a);
    230 		if (ul_spierror) {
    231 			dev_is_uart = false;
    232 		} else {
    233 			uart_s = UART_IS_SPI_USERLAND;
    234 		}
    235 	}
    236 	uart_set_subtype(uart_s, slave_a);
    237 
    238 	if (debug) {
    239 		fprintf(stderr, "ttysetup: error return %d\n", ttyerror);
    240 		fprintf(stderr, "ul_spisetup: error return %d\n", ul_spierror);
    241 	}
    242 
    243 	/* A UART here is either a tty uart or a SPI userland device.
    244 	 * They mostly end up working the same.
    245 	 */
    246 	if (dev_is_uart) {
    247 		func_block.func_clear = &uart_clear;
    248 		func_block.func_phy_read = &uart_read_register;
    249 		func_block.func_phy_write = &uart_write_register;
    250 	} else {
    251 		func_block.func_clear = &i2cspi_clear;
    252 		func_block.func_phy_read = &i2cspi_read_register;
    253 		func_block.func_phy_write = &i2cspi_write_register;
    254 	}
    255 
    256 	valid = valid_cmd(scmdcmds,__arraycount(scmdcmds),argv[1]);
    257 
    258 	if (valid != -1) {
    259 		switch (scmdcmds[valid].id) {
    260 		case SCMD_IDENTIFY:
    261 			module = 0;
    262 			if (argc == 3) {
    263 				module = (long)strtoi(argv[2], NULL, 10, 0, 16, &error);
    264 				if (error)
    265 					warnc(error, "Conversion of '%s' module failed,"
    266 					    " using %ld", argv[2], module);
    267 			}
    268 			error = common_identify(&func_block, fd, debug, module, &ir);
    269 			break;
    270 		case SCMD_DIAG:
    271 			module = 0;
    272 			if (argc == 3) {
    273 				module = (long)strtoi(argv[2], NULL, 10, 0, 16, &error);
    274 				if (error)
    275 					warnc(error, "Conversion of '%s' module failed,"
    276 					    " using %ld", argv[2], module);
    277 			}
    278 			error = common_diag(&func_block, fd, debug, module, &diag);
    279 			break;
    280 		case SCMD_MOTOR:
    281 			if (argc >= 3) {
    282 				validsub = valid_cmd(motorsubcmds,__arraycount(motorsubcmds),argv[2]);
    283 				if (validsub != -1) {
    284 					switch (motorsubcmds[validsub].id) {
    285 					case SCMD_SUBMOTORGET:
    286 						module = SCMD_ANY_MODULE;
    287 						if (argc == 4) {
    288 							module = (long)strtoi(argv[3], NULL, 10, 0, 16, &error);
    289 							if (error)
    290 								warnc(error, "Conversion of '%s' module failed,"
    291 								    " using %ld", argv[3], module);
    292 						}
    293 						error = common_get_motor(&func_block, fd, debug, (int)module, &motors);
    294 						break;
    295 					case SCMD_SUBMOTORSET:
    296 						if (argc == 6) {
    297 							module = (long)strtoi(argv[3], NULL, 10, 0, 16, &error);
    298 							if (error)
    299 								warnc(error, "Conversion of '%s' module failed,"
    300 								    " using %ld", argv[3], module);
    301 							motor = argv[4][0];
    302 							reg_value = (int8_t)strtoi(argv[5], NULL, 0, -127, 127, &error);
    303 							if (error)
    304 								err(EXIT_FAILURE,"Bad conversion for set motor for reg_value: %s", argv[5]);
    305 						} else {
    306 							fprintf(stderr,"Missing arguments to set motor command\n\n");
    307 							usage();
    308 							exit(1);
    309 						}
    310 						error = common_set_motor(&func_block, fd, debug, (int)module, motor, reg_value);
    311 						break;
    312 					case SCMD_SUBMOTORINVERT:
    313 						if (argc == 5) {
    314 							module = (long)strtoi(argv[3], NULL, 10, 0, 16, &error);
    315 							if (error)
    316 								warnc(error, "Conversion of '%s' module failed,"
    317 								    " using %ld", argv[3], module);
    318 							motor = argv[4][0];
    319 						} else {
    320 							fprintf(stderr,"Missing arguments to invert motor command\n\n");
    321 							usage();
    322 							exit(1);
    323 						}
    324 						error = common_invert_motor(&func_block, fd, debug, (int)module, motor);
    325 						break;
    326 					case SCMD_SUBMOTORBRIDGE:
    327 						if (argc == 4) {
    328 							module = (long)strtoi(argv[3], NULL, 10, 0, 16, &error);
    329 							if (error)
    330 								warnc(error, "Conversion of '%s' module failed,"
    331 								    " using %ld", argv[3], module);
    332 						} else {
    333 							fprintf(stderr,"Missing arguments to bridge motor command\n\n");
    334 							usage();
    335 							exit(1);
    336 						}
    337 						error = common_bridge_motor(&func_block, fd, debug, (int)module);
    338 						break;
    339 					case SCMD_SUBMOTORDISABLE:
    340 						error = common_enable_disable(&func_block, fd, debug, SCMD_DISABLE);
    341 						break;
    342 					case SCMD_SUBMOTORENABLE:
    343 						error = common_enable_disable(&func_block, fd, debug, SCMD_ENABLE);
    344 						break;
    345 					default:
    346 						fprintf(stderr,"Unhandled subcommand to motor: %s %d\n\n", argv[2], validsub);
    347 						usage();
    348 						exit(1);
    349 					}
    350 				} else {
    351 					fprintf(stderr,"Unknown subcommand to motor: %s\n\n", argv[2]);
    352 					usage();
    353 					exit(1);
    354 				}
    355 			} else {
    356 				fprintf(stderr,"Missing arguments to motor command\n\n");
    357 				usage();
    358 				exit(1);
    359 			}
    360 			break;
    361 		case SCMD_READ:
    362 			memset(register_shadow,SCMD_HOLE_VALUE + 1,SCMD_REG_SIZE);
    363 			if (argc >= 4) {
    364 				module = (long)strtoi(argv[2], NULL, 10, 0, 16, &error);
    365 				if (error)
    366 					warnc(error, "Conversion of '%s' module failed,"
    367 					    " using %ld", argv[2], module);
    368 				reg = (uint8_t)strtoi(argv[3], NULL, 0, 0, 0x7e, &error);
    369 				if (error) {
    370 					for(c = 0x00; c < SCMD_REG_SIZE;c++)
    371 						if (strncmp(argv[3],scmdregisternames[c],15) == 0)
    372 							break;
    373 					if (c == SCMD_REG_SIZE) {
    374 						fprintf(stderr,"Bad conversion for read register start: %s\n", argv[3]);
    375 						exit(1);
    376 					}
    377 					reg = c;
    378 				}
    379 				reg_e = reg;
    380 				if (argc == 5) {
    381 					reg_e = (uint8_t)strtoi(argv[4], NULL, 0, 0, 0x7e, &error);
    382 					if (error) {
    383 						for(c = 0x00; c < SCMD_REG_SIZE;c++)
    384 							if (strncmp(argv[4],scmdregisternames[c],15) == 0)
    385 								break;
    386 						if (c == SCMD_REG_SIZE) {
    387 							fprintf(stderr,"Bad conversion for read register end: %s\n", argv[4]);
    388 							exit(1);
    389 						}
    390 						reg_e = c;
    391 					}
    392 				}
    393 				if (reg_e < reg) {
    394 					fprintf(stderr,"Register end can not be less than register start: %d %d\n\n", reg, reg_e);
    395 					usage();
    396 					exit(1);
    397 				}
    398 				if (dev_is_uart) {
    399 					error = uart_read_register(fd,debug,module,reg,reg_e,&register_shadow[reg]);
    400 				} else {
    401 					error = i2cspi_read_register(fd,debug,module,reg,reg_e,&register_shadow[reg]);
    402 				}
    403 			} else {
    404 				fprintf(stderr,"Missing arguments to read_register command\n\n");
    405 				usage();
    406 				exit(1);
    407 			}
    408 			break;
    409 		case SCMD_WRITE:
    410 			if (argc == 5) {
    411 				module = (long)strtoi(argv[2], NULL, 10, 0, 16, &error);
    412 				if (error)
    413 					warnc(error, "Conversion of '%s' module failed,"
    414 					    " using %ld", argv[2], module);
    415 				reg = (uint8_t)strtoi(argv[3], NULL, 0, 0, 0x7e, &error);
    416 				if (error) {
    417 					for(c = 0x00; c < SCMD_REG_SIZE;c++)
    418 						if (strncmp(argv[3],scmdregisternames[c],15) == 0)
    419 							break;
    420 					if (c == SCMD_REG_SIZE) {
    421 						fprintf(stderr,"Bad conversion for write register start: %s\n", argv[3]);
    422 						exit(1);
    423 					}
    424 					reg = c;
    425 				}
    426 				reg_value = (int8_t)strtoi(argv[4], NULL, 0, 0, 0xff, &error);
    427 				if (error)
    428 					err(EXIT_FAILURE,"Bad conversion for write register for reg_value: %s", argv[4]);
    429 				if (dev_is_uart) {
    430 					error = uart_write_register(fd,debug,module,reg,reg_value);
    431 				} else {
    432 					error = i2cspi_write_register(fd,debug,module,reg,reg_value);
    433 				}
    434 			} else {
    435 				fprintf(stderr,"Missing arguments to write_register command\n\n");
    436 				usage();
    437 				exit(1);
    438 			}
    439 			break;
    440 		case SCMD_RESTART:
    441 		case SCMD_ENUMERATE:
    442 			error = common_control_1(&func_block, fd, debug, scmdcmds[valid].id);
    443 			break;
    444 		case SCMD_UPDATERATE:
    445 			if (argc >= 3) {
    446 				validsub = valid_cmd(updateratesubcmds,__arraycount(updateratesubcmds),argv[2]);
    447 				if (validsub != -1) {
    448 					switch (updateratesubcmds[validsub].id) {
    449 					case SCMD_SUBURGET:
    450 						error = common_get_update_rate(&func_block, fd, debug, &ur);
    451 						break;
    452 					case SCMD_SUBURSET:
    453 						if (argc == 4) {
    454 							ur = (uint8_t)strtoi(argv[3], NULL, 0, 0, 0xff, &error);
    455 							if (error)
    456 								err(EXIT_FAILURE,"Bad conversion for update_rate: %s", argv[3]);
    457 							error = common_set_update_rate(&func_block, fd, debug, ur);
    458 						} else {
    459 							fprintf(stderr,"Missing arguments to set update_rate command\n\n");
    460 							usage();
    461 							exit(1);
    462 						}
    463 						break;
    464 					case SCMD_SUBURFORCE:
    465 						error = common_force_update(&func_block, fd, debug);
    466 						break;
    467 					default:
    468 						fprintf(stderr,"Unhandled subcommand to updaterate: %s %d\n\n", argv[2], validsub);
    469 						usage();
    470 						exit(1);
    471 					}
    472 				} else {
    473 					fprintf(stderr,"Unknown subcommand to updaterate: %s\n\n", argv[2]);
    474 					usage();
    475 					exit(1);
    476 				}
    477 			} else {
    478 				fprintf(stderr,"Missing arguments to update_rate command\n\n");
    479 				usage();
    480 				exit(1);
    481 			}
    482 			break;
    483 		case SCMD_EBUS:
    484 			if (argc >= 3) {
    485 				validsub = valid_cmd(ebussubcmds,__arraycount(ebussubcmds),argv[2]);
    486 				if (validsub != -1) {
    487 					switch (ebussubcmds[validsub].id) {
    488 					case SCMD_SUBEBUSGET:
    489 						error = common_get_ebus_speed(&func_block, fd, debug, &ebus_s);
    490 						break;
    491 					case SCMD_SUBEBUSSET:
    492 						if (argc == 4) {
    493 							for(ebus_s = 0; ebus_s < __arraycount(ebus_speeds);ebus_s++)
    494 								if (strncmp(argv[3],ebus_speeds[ebus_s],8) == 0)
    495 									break;
    496 							if (ebus_s == __arraycount(ebus_speeds)) {
    497 								fprintf(stderr,"Bad conversion for set expansion bus speed: %s\n", argv[3]);
    498 								exit(1);
    499 							}
    500 							error = common_set_ebus_speed(&func_block, fd, debug, ebus_s);
    501 						} else {
    502 							fprintf(stderr,"Missing arguments to set expansion_bus command\n\n");
    503 							usage();
    504 							exit(1);
    505 						}
    506 						break;
    507 					default:
    508 						fprintf(stderr,"Unhandled subcommand to expansion_bus: %s %d\n\n", argv[2], validsub);
    509 						usage();
    510 						exit(1);
    511 					}
    512 				} else {
    513 					fprintf(stderr,"Unknown subcommand to expansion_bus: %s\n\n", argv[2]);
    514 					usage();
    515 					exit(1);
    516 				}
    517 			} else {
    518 				fprintf(stderr,"Missing arguments to expansion_bus_speed command\n\n");
    519 				usage();
    520 				exit(1);
    521 			}
    522 			break;
    523 		case SCMD_LOCK:
    524 			if (argc == 4) {
    525 				validsub = valid_cmd(locksubcmds,__arraycount(locksubcmds),argv[2]);
    526 				if (validsub != -1) {
    527 					lock_type = valid_cmd(lockcmdtypes,__arraycount(lockcmdtypes),argv[3]);
    528 					if (lock_type == -1) {
    529 						fprintf(stderr,"Unknown lock type: %s\n\n", argv[3]);
    530 						usage();
    531 						exit(1);
    532 					}
    533 					lock_type = lockcmdtypes[lock_type].id;
    534 
    535 					if (debug)
    536 						fprintf(stderr,"Lock type in lock command: %d\n",lock_type);
    537 
    538 					switch (locksubcmds[validsub].id) {
    539 					case SCMD_SUBLOCKGET:
    540 						error = common_get_lock_state(&func_block, fd, debug, lock_type, &lock_state);
    541 						break;
    542 					case SCMD_SUBLOCKLOCK:
    543 						error = common_set_lock_state(&func_block, fd, debug, lock_type, SCMD_LOCK_LOCKED);
    544 						break;
    545 					case SCMD_SUBLOCKUNLOCK:
    546 						error = common_set_lock_state(&func_block, fd, debug, lock_type, SCMD_LOCK_UNLOCK);
    547 						break;
    548 					default:
    549 						fprintf(stderr,"Unhandled subcommand to lock: %s %d\n\n", argv[2], validsub);
    550 						usage();
    551 						exit(1);
    552 					}
    553 				} else {
    554 					fprintf(stderr,"Unknown subcommand to lock: %s\n\n", argv[2]);
    555 					usage();
    556 					exit(1);
    557 				}
    558 			} else {
    559 				fprintf(stderr,"Missing arguments to lock command\n\n");
    560 				usage();
    561 				exit(1);
    562 			}
    563 			break;
    564 		case SCMD_SPIREADONE:
    565 			error = 0;
    566 			if (dev_is_uart &&
    567 			    uart_s == UART_IS_SPI_USERLAND) {
    568 				error = uart_ul_spi_read_one(fd,debug);
    569 			}
    570 			break;
    571 		default:
    572 			fprintf(stderr,"Unknown handling of command: %d\n",valid);
    573 			exit(2);
    574 		}
    575 
    576 		if (! error) {
    577 			switch (scmdcmds[valid].id) {
    578 			case SCMD_IDENTIFY:
    579 				print_identify(&ir);
    580 				break;
    581 			case SCMD_DIAG:
    582 				print_diag(&diag);
    583 				break;
    584 			case SCMD_MOTOR:
    585 				if (validsub != -1 &&
    586 				    motorsubcmds[validsub].id == SCMD_SUBMOTORGET)
    587 					print_motor(&motors);
    588 				break;
    589 			case SCMD_READ:
    590 				for(int g = reg; g <= reg_e; g++)
    591 					printf("Register %d (0x%02X) (%s): %d (0x%02X)\n",g,g,scmdregisternames[g],register_shadow[g],register_shadow[g]);
    592 				break;
    593 			case SCMD_UPDATERATE:
    594 				if (validsub != -1 &&
    595 				    updateratesubcmds[validsub].id == SCMD_SUBURGET)
    596 					printf("Update rate: %dms\n",ur);
    597 				break;
    598 			case SCMD_EBUS:
    599 				if (validsub != -1 &&
    600 				    ebussubcmds[validsub].id == SCMD_SUBEBUSGET)
    601 					printf("Expansion bus speed: %s (0x%02X)\n",(ebus_s <= 0x03) ? ebus_speeds[ebus_s] : "Unknown",ebus_s);
    602 				break;
    603 			case SCMD_LOCK:
    604 				if (validsub != -1 &&
    605 				    locksubcmds[validsub].id == SCMD_SUBLOCKGET) {
    606 					int x = SCMD_MASTER_LOCK_UNLOCKED;
    607 
    608 					if (lock_type == SCMD_LOCAL_USER_LOCK ||
    609 					    lock_type == SCMD_GLOBAL_USER_LOCK)
    610 						x = SCMD_USER_LOCK_UNLOCKED;
    611 					printf("Lock state: %s (0x%02X)\n",(lock_state == x ? "Unlocked" : "Locked"),lock_state);
    612 				}
    613 				break;
    614 			case SCMD_WRITE:
    615 			case SCMD_RESTART:
    616 			case SCMD_ENUMERATE:
    617 			case SCMD_SPIREADONE:
    618 				break;
    619 			default:
    620 				fprintf(stderr,"Unknown printing of command: %d\n",valid);
    621 				exit(2);
    622 			}
    623 		} else {
    624 			fprintf(stderr,"Error: %d\n", error);
    625 			exit(1);
    626 		}
    627 	} else {
    628 		fprintf(stderr,"Unknown command: %s\n\n",argv[1]);
    629 		usage();
    630 		exit(1);
    631 	}
    632 
    633 	close(fd);
    634 	exit(0);
    635 }
    636