xref: /netbsd/usr.bin/scmdctl/scmdctl.c (revision 32757b83)
1 /*	$NetBSD: scmdctl.c,v 1.1 2021/12/07 17:39:55 brad Exp $	*/
2 
3 /*
4  * Copyright (c) 2021 Brad Spencer <brad@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.1 2021/12/07 17:39:55 brad 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
usage(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
valid_cmd(const struct scmdcmd c[],long unsigned int csize,char * cmdtocheck)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
ttysetup(int fd,speed_t spd)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
ul_spisetup(int fd,int slave_addr)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
main(int argc,char * argv[])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 	extern char *optarg;
170 	extern int optind;
171 
172 	while ((c = getopt(argc, argv, "db:s:lh")) != -1 ) {
173 		switch (c) {
174 		case 'd':
175 			debug = true;
176 			break;
177 		case 'b':
178 			baud_rate = (long)strtoi(optarg, NULL, 0, 1, LONG_MAX, &error);
179 			if (error)
180 				warnc(error, "Conversion of `%s' to a baud rate "
181 				    "failed, using %ld", optarg, baud_rate);
182 			break;
183 		case 's':
184 			slave_a = (long)strtoi(optarg, NULL, 0, 0, LONG_MAX, &error);
185 			if (error)
186 				warnc(error, "Conversion of `%s' to a SPI slave address "
187 				    "failed, using %ld", optarg, slave_a);
188 			break;
189 		case 'l':
190 			list_names = true;
191 			break;
192 		case 'h':
193 		default:
194 			usage();
195 			exit(0);
196 		}
197 	}
198 
199 	argc -= optind;
200 	argv += optind;
201 
202 	if (debug) {
203 		fprintf(stderr,"ARGC: %d\n", argc);
204 		fprintf(stderr,"ARGV[0]: %s ; ARGV[1]: %s ; ARGV[2]: %s ; ARGV[3]: %s; ARGV[4]: %s; ARGV[5]: %s\n",
205 		    argv[0],argv[1],argv[2],argv[3],argv[4],argv[5]);
206 	}
207 
208 	if (list_names) {
209 		for(c = 0x00; c < SCMD_REG_SIZE;c++)
210 			printf("Register %d (0x%02X): %s\n",c,c,scmdregisternames[c]);
211 		exit(0);
212 	}
213 
214 	if (argc <= 1) {
215 		usage();
216 		exit(0);
217 	}
218 
219 	fd = open(argv[0], O_RDWR, 0);
220 	if (fd == -1) {
221 		err(EXIT_FAILURE, "open %s", argv[0]);
222 	}
223 
224 	/* Figure out what the device is.  First try uart tty,
225 	 * then SPI userland and the if those two fail, assume
226 	 * scmd(4).
227 	 */
228 	ttyerror = ttysetup(fd,(speed_t)baud_rate);
229 
230 	if (ttyerror) {
231 		ul_spierror = ul_spisetup(fd, slave_a);
232 		if (ul_spierror) {
233 			dev_is_uart = false;
234 		} else {
235 			uart_s = UART_IS_SPI_USERLAND;
236 		}
237 	}
238 	uart_set_subtype(uart_s, slave_a);
239 
240 	if (debug) {
241 		fprintf(stderr, "ttysetup: error return %d\n", ttyerror);
242 		fprintf(stderr, "ul_spisetup: error return %d\n", ul_spierror);
243 	}
244 
245 	/* A UART here is either a tty uart or a SPI userland device.
246 	 * They mostly end up working the same.
247 	 */
248 	if (dev_is_uart) {
249 		func_block.func_clear = &uart_clear;
250 		func_block.func_phy_read = &uart_read_register;
251 		func_block.func_phy_write = &uart_write_register;
252 	} else {
253 		func_block.func_clear = &i2cspi_clear;
254 		func_block.func_phy_read = &i2cspi_read_register;
255 		func_block.func_phy_write = &i2cspi_write_register;
256 	}
257 
258 	valid = valid_cmd(scmdcmds,__arraycount(scmdcmds),argv[1]);
259 
260 	if (valid != -1) {
261 		switch (scmdcmds[valid].id) {
262 		case SCMD_IDENTIFY:
263 			module = 0;
264 			if (argc == 3) {
265 				module = (long)strtoi(argv[2], NULL, 10, 0, 16, &error);
266 				if (error)
267 					warnc(error, "Conversion of '%s' module failed,"
268 					    " using %ld", argv[2], module);
269 			}
270 			error = common_identify(&func_block, fd, debug, module, &ir);
271 			break;
272 		case SCMD_DIAG:
273 			module = 0;
274 			if (argc == 3) {
275 				module = (long)strtoi(argv[2], NULL, 10, 0, 16, &error);
276 				if (error)
277 					warnc(error, "Conversion of '%s' module failed,"
278 					    " using %ld", argv[2], module);
279 			}
280 			error = common_diag(&func_block, fd, debug, module, &diag);
281 			break;
282 		case SCMD_MOTOR:
283 			if (argc >= 3) {
284 				validsub = valid_cmd(motorsubcmds,__arraycount(motorsubcmds),argv[2]);
285 				if (validsub != -1) {
286 					switch (motorsubcmds[validsub].id) {
287 					case SCMD_SUBMOTORGET:
288 						module = SCMD_ANY_MODULE;
289 						if (argc == 4) {
290 							module = (long)strtoi(argv[3], NULL, 10, 0, 16, &error);
291 							if (error)
292 								warnc(error, "Conversion of '%s' module failed,"
293 								    " using %ld", argv[3], module);
294 						}
295 						error = common_get_motor(&func_block, fd, debug, (int)module, &motors);
296 						break;
297 					case SCMD_SUBMOTORSET:
298 						if (argc == 6) {
299 							module = (long)strtoi(argv[3], NULL, 10, 0, 16, &error);
300 							if (error)
301 								warnc(error, "Conversion of '%s' module failed,"
302 								    " using %ld", argv[3], module);
303 							motor = argv[4][0];
304 							reg_value = (int8_t)strtoi(argv[5], NULL, 0, -127, 127, &error);
305 							if (error)
306 								err(EXIT_FAILURE,"Bad conversion for set motor for reg_value: %s", argv[5]);
307 						} else {
308 							fprintf(stderr,"Missing arguments to set motor command\n\n");
309 							usage();
310 							exit(1);
311 						}
312 						error = common_set_motor(&func_block, fd, debug, (int)module, motor, reg_value);
313 						break;
314 					case SCMD_SUBMOTORINVERT:
315 						if (argc == 5) {
316 							module = (long)strtoi(argv[3], NULL, 10, 0, 16, &error);
317 							if (error)
318 								warnc(error, "Conversion of '%s' module failed,"
319 								    " using %ld", argv[3], module);
320 							motor = argv[4][0];
321 						} else {
322 							fprintf(stderr,"Missing arguments to invert motor command\n\n");
323 							usage();
324 							exit(1);
325 						}
326 						error = common_invert_motor(&func_block, fd, debug, (int)module, motor);
327 						break;
328 					case SCMD_SUBMOTORBRIDGE:
329 						if (argc == 4) {
330 							module = (long)strtoi(argv[3], NULL, 10, 0, 16, &error);
331 							if (error)
332 								warnc(error, "Conversion of '%s' module failed,"
333 								    " using %ld", argv[3], module);
334 						} else {
335 							fprintf(stderr,"Missing arguments to bridge motor command\n\n");
336 							usage();
337 							exit(1);
338 						}
339 						error = common_bridge_motor(&func_block, fd, debug, (int)module);
340 						break;
341 					case SCMD_SUBMOTORDISABLE:
342 						error = common_enable_disable(&func_block, fd, debug, SCMD_DISABLE);
343 						break;
344 					case SCMD_SUBMOTORENABLE:
345 						error = common_enable_disable(&func_block, fd, debug, SCMD_ENABLE);
346 						break;
347 					default:
348 						fprintf(stderr,"Unhandled subcommand to motor: %s %d\n\n", argv[2], validsub);
349 						usage();
350 						exit(1);
351 						break;
352 					}
353 				} else {
354 					fprintf(stderr,"Unknown subcommand to motor: %s\n\n", argv[2]);
355 					usage();
356 					exit(1);
357 				}
358 			} else {
359 				fprintf(stderr,"Missing arguments to motor command\n\n");
360 				usage();
361 				exit(1);
362 			}
363 			break;
364 		case SCMD_READ:
365 			memset(register_shadow,SCMD_HOLE_VALUE + 1,SCMD_REG_SIZE);
366 			if (argc >= 4) {
367 				module = (long)strtoi(argv[2], NULL, 10, 0, 16, &error);
368 				if (error)
369 					warnc(error, "Conversion of '%s' module failed,"
370 					    " using %ld", argv[2], module);
371 				reg = (uint8_t)strtoi(argv[3], NULL, 0, 0, 0x7e, &error);
372 				if (error) {
373 					for(c = 0x00; c < SCMD_REG_SIZE;c++)
374 						if (strncmp(argv[3],scmdregisternames[c],15) == 0)
375 							break;
376 					if (c == SCMD_REG_SIZE) {
377 						fprintf(stderr,"Bad conversion for read register start: %s\n", argv[3]);
378 						exit(1);
379 					}
380 					reg = c;
381 				}
382 				reg_e = reg;
383 				if (argc == 5) {
384 					reg_e = (uint8_t)strtoi(argv[4], NULL, 0, 0, 0x7e, &error);
385 					if (error) {
386 						for(c = 0x00; c < SCMD_REG_SIZE;c++)
387 							if (strncmp(argv[4],scmdregisternames[c],15) == 0)
388 								break;
389 						if (c == SCMD_REG_SIZE) {
390 							fprintf(stderr,"Bad conversion for read register end: %s\n", argv[4]);
391 							exit(1);
392 						}
393 						reg_e = c;
394 					}
395 				}
396 				if (reg_e < reg) {
397 					fprintf(stderr,"Register end can not be less than register start: %d %d\n\n", reg, reg_e);
398 					usage();
399 					exit(1);
400 				}
401 				if (dev_is_uart) {
402 					error = uart_read_register(fd,debug,module,reg,reg_e,&register_shadow[reg]);
403 				} else {
404 					error = i2cspi_read_register(fd,debug,module,reg,reg_e,&register_shadow[reg]);
405 				}
406 			} else {
407 				fprintf(stderr,"Missing arguments to read_register command\n\n");
408 				usage();
409 				exit(1);
410 			}
411 			break;
412 		case SCMD_WRITE:
413 			if (argc == 5) {
414 				module = (long)strtoi(argv[2], NULL, 10, 0, 16, &error);
415 				if (error)
416 					warnc(error, "Conversion of '%s' module failed,"
417 					    " using %ld", argv[2], module);
418 				reg = (uint8_t)strtoi(argv[3], NULL, 0, 0, 0x7e, &error);
419 				if (error) {
420 					for(c = 0x00; c < SCMD_REG_SIZE;c++)
421 						if (strncmp(argv[3],scmdregisternames[c],15) == 0)
422 							break;
423 					if (c == SCMD_REG_SIZE) {
424 						fprintf(stderr,"Bad conversion for write register start: %s\n", argv[3]);
425 						exit(1);
426 					}
427 					reg = c;
428 				}
429 				reg_value = (int8_t)strtoi(argv[4], NULL, 0, 0, 0xff, &error);
430 				if (error)
431 					err(EXIT_FAILURE,"Bad conversion for write register for reg_value: %s", argv[4]);
432 				if (dev_is_uart) {
433 					error = uart_write_register(fd,debug,module,reg,reg_value);
434 				} else {
435 					error = i2cspi_write_register(fd,debug,module,reg,reg_value);
436 				}
437 			} else {
438 				fprintf(stderr,"Missing arguments to write_register command\n\n");
439 				usage();
440 				exit(1);
441 			}
442 			break;
443 		case SCMD_RESTART:
444 		case SCMD_ENUMERATE:
445 			error = common_control_1(&func_block, fd, debug, scmdcmds[valid].id);
446 			break;
447 		case SCMD_UPDATERATE:
448 			if (argc >= 3) {
449 				validsub = valid_cmd(updateratesubcmds,__arraycount(updateratesubcmds),argv[2]);
450 				if (validsub != -1) {
451 					switch (updateratesubcmds[validsub].id) {
452 					case SCMD_SUBURGET:
453 						error = common_get_update_rate(&func_block, fd, debug, &ur);
454 						break;
455 					case SCMD_SUBURSET:
456 						if (argc == 4) {
457 							ur = (uint8_t)strtoi(argv[3], NULL, 0, 0, 0xff, &error);
458 							if (error)
459 								err(EXIT_FAILURE,"Bad conversion for update_rate: %s", argv[3]);
460 							error = common_set_update_rate(&func_block, fd, debug, ur);
461 						} else {
462 							fprintf(stderr,"Missing arguments to set update_rate command\n\n");
463 							usage();
464 							exit(1);
465 						}
466 						break;
467 					case SCMD_SUBURFORCE:
468 						error = common_force_update(&func_block, fd, debug);
469 						break;
470 					default:
471 						fprintf(stderr,"Unhandled subcommand to updaterate: %s %d\n\n", argv[2], validsub);
472 						usage();
473 						exit(1);
474 						break;
475 					}
476 				} else {
477 					fprintf(stderr,"Unknown subcommand to updaterate: %s\n\n", argv[2]);
478 					usage();
479 					exit(1);
480 				}
481 			} else {
482 				fprintf(stderr,"Missing arguments to update_rate command\n\n");
483 				usage();
484 				exit(1);
485 			}
486 			break;
487 		case SCMD_EBUS:
488 			if (argc >= 3) {
489 				validsub = valid_cmd(ebussubcmds,__arraycount(ebussubcmds),argv[2]);
490 				if (validsub != -1) {
491 					switch (ebussubcmds[validsub].id) {
492 					case SCMD_SUBEBUSGET:
493 						error = common_get_ebus_speed(&func_block, fd, debug, &ebus_s);
494 						break;
495 					case SCMD_SUBEBUSSET:
496 						if (argc == 4) {
497 							for(ebus_s = 0; ebus_s < __arraycount(ebus_speeds);ebus_s++)
498 								if (strncmp(argv[3],ebus_speeds[ebus_s],8) == 0)
499 									break;
500 							if (ebus_s == __arraycount(ebus_speeds)) {
501 								fprintf(stderr,"Bad conversion for set expansion bus speed: %s\n", argv[3]);
502 								exit(1);
503 							}
504 							error = common_set_ebus_speed(&func_block, fd, debug, ebus_s);
505 						} else {
506 							fprintf(stderr,"Missing arguments to set expansion_bus command\n\n");
507 							usage();
508 							exit(1);
509 						}
510 						break;
511 					default:
512 						fprintf(stderr,"Unhandled subcommand to expansion_bus: %s %d\n\n", argv[2], validsub);
513 						usage();
514 						exit(1);
515 						break;
516 					}
517 				} else {
518 					fprintf(stderr,"Unknown subcommand to expansion_bus: %s\n\n", argv[2]);
519 					usage();
520 					exit(1);
521 				}
522 			} else {
523 				fprintf(stderr,"Missing arguments to expansion_bus_speed command\n\n");
524 				usage();
525 				exit(1);
526 			}
527 			break;
528 		case SCMD_LOCK:
529 			if (argc == 4) {
530 				validsub = valid_cmd(locksubcmds,__arraycount(locksubcmds),argv[2]);
531 				if (validsub != -1) {
532 					lock_type = valid_cmd(lockcmdtypes,__arraycount(lockcmdtypes),argv[3]);
533 					if (lock_type == -1) {
534 						fprintf(stderr,"Unknown lock type: %s\n\n", argv[3]);
535 						usage();
536 						exit(1);
537 					}
538 					lock_type = lockcmdtypes[lock_type].id;
539 
540 					if (debug)
541 						fprintf(stderr,"Lock type in lock command: %d\n",lock_type);
542 
543 					switch (locksubcmds[validsub].id) {
544 					case SCMD_SUBLOCKGET:
545 						error = common_get_lock_state(&func_block, fd, debug, lock_type, &lock_state);
546 						break;
547 					case SCMD_SUBLOCKLOCK:
548 						error = common_set_lock_state(&func_block, fd, debug, lock_type, SCMD_LOCK_LOCKED);
549 						break;
550 					case SCMD_SUBLOCKUNLOCK:
551 						error = common_set_lock_state(&func_block, fd, debug, lock_type, SCMD_LOCK_UNLOCK);
552 						break;
553 					default:
554 						fprintf(stderr,"Unhandled subcommand to lock: %s %d\n\n", argv[2], validsub);
555 						usage();
556 						exit(1);
557 						break;
558 					}
559 				} else {
560 					fprintf(stderr,"Unknown subcommand to lock: %s\n\n", argv[2]);
561 					usage();
562 					exit(1);
563 				}
564 			} else {
565 				fprintf(stderr,"Missing arguments to lock command\n\n");
566 				usage();
567 				exit(1);
568 			}
569 			break;
570 		case SCMD_SPIREADONE:
571 			error = 0;
572 			if (dev_is_uart &&
573 			    uart_s == UART_IS_SPI_USERLAND) {
574 				error = uart_ul_spi_read_one(fd,debug);
575 			}
576 			break;
577 		default:
578 			fprintf(stderr,"Unknown handling of command: %d\n",valid);
579 			exit(2);
580 			break;
581 		}
582 
583 		if (! error) {
584 			switch (scmdcmds[valid].id) {
585 			case SCMD_IDENTIFY:
586 				print_identify(&ir);
587 				break;
588 			case SCMD_DIAG:
589 				print_diag(&diag);
590 				break;
591 			case SCMD_MOTOR:
592 				if (validsub != -1 &&
593 				    motorsubcmds[validsub].id == SCMD_SUBMOTORGET)
594 					print_motor(&motors);
595 				break;
596 			case SCMD_READ:
597 				for(int g = reg; g <= reg_e; g++)
598 					printf("Register %d (0x%02X) (%s): %d (0x%02X)\n",g,g,scmdregisternames[g],register_shadow[g],register_shadow[g]);
599 				break;
600 			case SCMD_UPDATERATE:
601 				if (validsub != -1 &&
602 				    updateratesubcmds[validsub].id == SCMD_SUBURGET)
603 					printf("Update rate: %dms\n",ur);
604 				break;
605 			case SCMD_EBUS:
606 				if (validsub != -1 &&
607 				    ebussubcmds[validsub].id == SCMD_SUBEBUSGET)
608 					printf("Expansion bus speed: %s (0x%02X)\n",(ebus_s <= 0x03) ? ebus_speeds[ebus_s] : "Unknown",ebus_s);
609 				break;
610 			case SCMD_LOCK:
611 				if (validsub != -1 &&
612 				    locksubcmds[validsub].id == SCMD_SUBLOCKGET) {
613 					int x = SCMD_MASTER_LOCK_UNLOCKED;
614 
615 					if (lock_type == SCMD_LOCAL_USER_LOCK ||
616 					    lock_type == SCMD_GLOBAL_USER_LOCK)
617 						x = SCMD_USER_LOCK_UNLOCKED;
618 					printf("Lock state: %s (0x%02X)\n",(lock_state == x ? "Unlocked" : "Locked"),lock_state);
619 				}
620 				break;
621 			case SCMD_WRITE:
622 			case SCMD_RESTART:
623 			case SCMD_ENUMERATE:
624 			case SCMD_SPIREADONE:
625 				break;
626 			default:
627 				fprintf(stderr,"Unknown printing of command: %d\n",valid);
628 				exit(2);
629 				break;
630 			}
631 		} else {
632 			fprintf(stderr,"Error: %d\n", error);
633 			exit(1);
634 		}
635 	} else {
636 		fprintf(stderr,"Unknown command: %s\n\n",argv[1]);
637 		usage();
638 		exit(1);
639 	}
640 
641 	close(fd);
642 	exit(0);
643 }
644