xref: /illumos-gate/usr/src/cmd/kbd/kbd.c (revision 4fceebdf03eeac0d7c58a4f70cc19b00a8c40a73)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 
29 /*
30  *	Usage: kbd [-r] [-t] [-l] [-i] [-c on|off] [-a enable|disable|alternate]
31  *		    [-d keyboard device]
32  *	-r			reset the keyboard as if power-up
33  *	-t			return the type of the keyboard being used
34  *	-l			return the layout of the keyboard being used,
35  *				and the Autorepeat settings
36  *	-i			read in the default configuration file
37  *	-c on|off		turn on|off clicking
38  *	-a enable|disable|alternate	sets abort sequence
39  *	-D autorepeat delay	sets autorepeat dealy, unit in ms
40  *	-R autorepeat rate	sets autorepeat rate, unit in ms
41  *	-d keyboard device	chooses the kbd device, default /dev/kbd.
42  *	-s keyboard layout	sets keyboard layout
43  */
44 
45 #include <sys/types.h>
46 #include <sys/ioctl.h>
47 #include <sys/kbio.h>
48 #include <sys/kbd.h>
49 #include <stdio.h>
50 #include <fcntl.h>
51 #include <deflt.h>
52 #include <unistd.h>
53 #include <string.h>
54 #include <stdlib.h>
55 #include <stropts.h>
56 #include <libintl.h>
57 #include <locale.h>
58 
59 #define	KBD_DEVICE	"/dev/kbd"		/* default keyboard device */
60 #define	DEF_FILE	"/etc/default/kbd"	/* kbd defaults file	*/
61 #define	DEF_ABORT	"KEYBOARD_ABORT="
62 #define	DEF_CLICK	"KEYCLICK="
63 #define	DEF_RPTDELAY	"REPEAT_DELAY="
64 #define	DEF_RPTRATE	"REPEAT_RATE="
65 #define	DEF_LAYOUT	"LAYOUT="
66 
67 #define	KBD_LAYOUT_FILE  "/usr/share/lib/keytables/type_6/kbd_layouts"
68 #define	MAX_LAYOUT_NUM		128
69 #define	MAX_LINE_SIZE		256
70 #define	DEFAULT_KBD_LAYOUT	33
71 
72 char *layout_names[MAX_LAYOUT_NUM];
73 int layout_numbers[MAX_LAYOUT_NUM];
74 static int layout_count;
75 static int default_layout_number = 0;
76 
77 static void reset(int);
78 static void get_type(int);
79 static void get_layout(int);
80 static void kbd_defaults(int);
81 static void usage(void);
82 
83 static int click(char *, int);
84 static int abort_enable(char *, int);
85 static int set_repeat_delay(char *, int);
86 static int set_repeat_rate(char *, int);
87 
88 static int get_layout_number(char *);
89 static int set_layout(int, int);
90 static int get_layouts(void);
91 static int set_kbd_layout(int, char *);
92 
93 int
94 main(int argc, char **argv)
95 {
96 	int c, error;
97 	int rflag, tflag, lflag, cflag, dflag, aflag, iflag, errflag,
98 	    Dflag, Rflag, rtlacDRflag, sflag;
99 	char *copt, *aopt, *delay, *rate, *layout_name;
100 	char *kbdname = KBD_DEVICE;
101 	int kbd;
102 	extern char *optarg;
103 	extern int optind;
104 
105 	rflag = tflag = cflag = dflag = aflag = iflag = errflag = lflag =
106 	    Dflag = Rflag = sflag = 0;
107 	copt = aopt = (char *)0;
108 
109 	(void) setlocale(LC_ALL, "");
110 #if !defined(TEXT_DOMAIN)
111 #define	TEXT_DOMAIN	"SYS_TEST"
112 #endif
113 	(void) textdomain(TEXT_DOMAIN);
114 
115 	while ((c = getopt(argc, argv, "rtlisc:a:d:D:R:")) != EOF) {
116 		switch (c) {
117 		case 'r':
118 			rflag++;
119 			break;
120 		case 't':
121 			tflag++;
122 			break;
123 		case 'l':
124 			lflag++;
125 			break;
126 		case 'i':
127 			iflag++;
128 			break;
129 		case 's':
130 			sflag++;
131 			break;
132 		case 'c':
133 			copt = optarg;
134 			cflag++;
135 			break;
136 		case 'a':
137 			aopt = optarg;
138 			aflag++;
139 			break;
140 		case 'd':
141 			kbdname = optarg;
142 			dflag++;
143 			break;
144 		case 'D':
145 			delay = optarg;
146 			Dflag++;
147 			break;
148 		case 'R':
149 			rate = optarg;
150 			Rflag++;
151 			break;
152 		case '?':
153 			errflag++;
154 			break;
155 		}
156 	}
157 
158 	/*
159 	 * Check for valid arguments:
160 	 *
161 	 * If argument parsing failed or if there are left-over
162 	 * command line arguments(except -s option), then we're done now.
163 	 */
164 	if (errflag != 0 || ((sflag == 0) && (argc != optind))) {
165 		usage();
166 		exit(1);
167 	}
168 
169 	/*
170 	 * kbd requires that the user specify either "-i" or "-s" or at
171 	 * least one of -[rtlacDR].  The "-d" option is, well, optional.
172 	 * We don't care if it's there or not.
173 	 */
174 	rtlacDRflag = rflag + tflag + lflag + aflag + cflag + Dflag + Rflag;
175 	if (!((iflag != 0 && sflag == 0 && rtlacDRflag == 0) ||
176 	    (iflag == 0 && sflag != 0 && dflag == 0 && rtlacDRflag == 0) ||
177 	    (iflag == 0 && sflag == 0 && rtlacDRflag != 0))) {
178 		usage();
179 		exit(1);
180 	}
181 
182 	if (Dflag && atoi(delay) <= 0) {
183 		(void) fprintf(stderr, "Invalid arguments: -D %s\n", delay);
184 		usage();
185 		exit(1);
186 	}
187 
188 	if (Rflag && atoi(rate) <= 0) {
189 		(void) fprintf(stderr, "Invalid arguments: -R %s\n", rate);
190 		usage();
191 		exit(1);
192 	}
193 
194 	/*
195 	 * Open the keyboard device
196 	 */
197 	if ((kbd = open(kbdname, O_RDWR)) < 0) {
198 		perror("opening the keyboard");
199 		(void) fprintf(stderr, "kbd: Cannot open %s\n", kbdname);
200 		exit(1);
201 	}
202 
203 	if (iflag) {
204 		kbd_defaults(kbd);
205 		exit(0);	/* A mutually exclusive option */
206 		/*NOTREACHED*/
207 	}
208 
209 	if (tflag)
210 		get_type(kbd);
211 
212 	if (lflag)
213 		get_layout(kbd);
214 
215 	if (cflag && (error = click(copt, kbd)) != 0)
216 		exit(error);
217 
218 	if (rflag)
219 		reset(kbd);
220 
221 	if (aflag && (error = abort_enable(aopt, kbd)) != 0)
222 		exit(error);
223 
224 	if (Dflag && (error = set_repeat_delay(delay, kbd)) != 0)
225 		exit(error);
226 
227 	if (Rflag && (error = set_repeat_rate(rate, kbd)) != 0)
228 		exit(error);
229 
230 	if (sflag) {
231 		if (argc == optind) {
232 			layout_name = NULL;
233 		} else if (argc == (optind + 1)) {
234 			layout_name = argv[optind];
235 		} else {
236 			usage();
237 			exit(1);
238 		}
239 
240 		if ((error = set_kbd_layout(kbd, layout_name)) != 0)
241 			exit(error);
242 	}
243 
244 	return (0);
245 }
246 
247 /*
248  * this routine gets the type of the keyboard being used
249  */
250 static int
251 set_kbd_layout(int kbd, char *layout_name)
252 {
253 	int layout_num;
254 	int error = 1;
255 
256 	/* get the language info from the layouts file */
257 	if (get_layouts() != 0)
258 		return (error);
259 
260 	if (layout_name != NULL) {
261 		if ((layout_num = get_layout_number(layout_name)) == -1) {
262 			(void) fprintf(stderr, "%s: unknown layout name\n"
263 				    "Please refer to 'kbd -s' to get the "
264 				    "supported layouts.\n", layout_name);
265 			return (error);
266 		}
267 	} else {
268 			int i, j, print_cnt, input_num;
269 			boolean_t input_right = B_TRUE;
270 			boolean_t default_input = B_FALSE;
271 			char input[8]; /* 8 chars is enough for numbers */
272 
273 			print_cnt = (layout_count % 2) ?
274 				layout_count/2+1 : layout_count/2;
275 
276 			for (i = 1; i <= print_cnt; i++) {
277 				(void) printf("%2d. %-30s", i,
278 					    layout_names[i-1]);
279 				j = i + print_cnt;
280 				if (j <= layout_count) {
281 					(void) printf("%-2d. %-30s\n", j,
282 						    layout_names[j-1]);
283 				}
284 			}
285 			(void) printf(gettext("\nTo select the keyboard layout,"
286 				    " enter a number [default %d]:"),
287 				    default_layout_number+1);
288 
289 			for (;;) {
290 				if (input_right == B_FALSE)
291 					(void) printf(gettext("Invalid input. "
292 					    "Please input a number "
293 					    "(1,2,...):"));
294 				(void) memset(input, 0, 8);
295 				(void) fflush(stdin);
296 				(void) fgets(input, 8, stdin);
297 				if (strlen(input) > 4) {
298 					input_right = B_FALSE;
299 					continue;
300 				}
301 				if (input[0] == '\n') {
302 					default_input = B_TRUE;
303 					break;
304 				}
305 				input_right = B_TRUE;
306 				/* check if the inputs are numbers 0~9 */
307 				for (i = 0; i < (strlen(input) - 1); i++) {
308 					if ((input[i] < '0') ||
309 					    (input[i] > '9')) {
310 						input_right = B_FALSE;
311 						break;
312 					}
313 				}
314 				if (input_right == B_FALSE)
315 					continue;
316 				input_num = atoi(input);
317 				if ((input_num > 0) &&
318 				    (input_num <= layout_count))
319 					break;
320 				else
321 					input_right = B_FALSE;
322 			}
323 			if (default_input == B_TRUE)
324 				layout_num = DEFAULT_KBD_LAYOUT;
325 			else
326 				layout_num = layout_numbers[--input_num];
327 	}
328 
329 	if ((error = set_layout(kbd, layout_num)) != 0)
330 		return (error);
331 
332 	return (0);
333 }
334 
335 /*
336  * this routine resets the state of the keyboard as if power-up
337  */
338 static void
339 reset(int kbd)
340 {
341 	int cmd;
342 
343 	cmd = KBD_CMD_RESET;
344 
345 	if (ioctl(kbd, KIOCCMD, &cmd)) {
346 		perror("kbd: ioctl error");
347 		exit(1);
348 	}
349 
350 }
351 
352 /*
353  * this routine gets the type of the keyboard being used
354  */
355 static void
356 get_type(int kbd)
357 {
358 	int kbd_type;
359 
360 	if (ioctl(kbd, KIOCTYPE, &kbd_type)) {
361 		perror("ioctl (kbd type)");
362 		exit(1);
363 	}
364 
365 	switch (kbd_type) {
366 
367 	case KB_SUN3:
368 		(void) printf("Type 3 Sun keyboard\n");
369 		break;
370 
371 	case KB_SUN4:
372 		(void) printf("Type 4 Sun keyboard\n");
373 		break;
374 
375 	case KB_ASCII:
376 		(void) printf("ASCII\n");
377 		break;
378 
379 	case KB_PC:
380 		(void) printf("PC\n");
381 		break;
382 
383 	case KB_USB:
384 		(void) printf("USB keyboard\n");
385 		break;
386 
387 	default:
388 		(void) printf("Unknown keyboard type\n");
389 		break;
390 	}
391 }
392 
393 /*
394  * this routine gets the layout of the keyboard being used
395  * also, included the autorepeat delay and rate being used
396  */
397 static void
398 get_layout(int kbd)
399 {
400 	int kbd_type;
401 	int kbd_layout;
402 	/* these two variables are used for getting delay&rate */
403 	int delay, rate;
404 	delay = rate = 0;
405 
406 	if (ioctl(kbd, KIOCTYPE, &kbd_type)) {
407 		perror("ioctl (kbd type)");
408 		exit(1);
409 	}
410 
411 	if (ioctl(kbd, KIOCLAYOUT, &kbd_layout)) {
412 		perror("ioctl (kbd layout)");
413 		exit(1);
414 	}
415 
416 	(void) printf("type=%d\nlayout=%d (0x%.2x)\n",
417 	    kbd_type, kbd_layout, kbd_layout);
418 
419 	/* below code is used to get the autorepeat delay and rate */
420 	if (ioctl(kbd, KIOCGRPTDELAY, &delay)) {
421 		perror("ioctl (kbd get repeat delay)");
422 		exit(1);
423 	}
424 
425 	if (ioctl(kbd, KIOCGRPTRATE, &rate)) {
426 		perror("ioctl (kbd get repeat rate)");
427 		exit(1);
428 	}
429 
430 	(void) printf("delay(ms)=%d\n", delay);
431 	(void) printf("rate(ms)=%d\n", rate);
432 }
433 
434 /*
435  * this routine enables or disables clicking of the keyboard
436  */
437 static int
438 click(char *copt, int kbd)
439 {
440 	int cmd;
441 
442 	if (strcmp(copt, "on") == 0)
443 		cmd = KBD_CMD_CLICK;
444 	else if (strcmp(copt, "off") == 0)
445 		cmd = KBD_CMD_NOCLICK;
446 	else {
447 		(void) fprintf(stderr, "wrong option -- %s\n", copt);
448 		usage();
449 		return (1);
450 	}
451 
452 	if (ioctl(kbd, KIOCCMD, &cmd)) {
453 		perror("kbd ioctl (keyclick)");
454 		return (1);
455 	}
456 	return (0);
457 }
458 
459 /*
460  * this routine enables/disables/sets BRK or abort sequence feature
461  */
462 static int
463 abort_enable(char *aopt, int kbd)
464 {
465 	int enable;
466 
467 	if (strcmp(aopt, "alternate") == 0)
468 		enable = KIOCABORTALTERNATE;
469 	else if (strcmp(aopt, "enable") == 0)
470 		enable = KIOCABORTENABLE;
471 	else if (strcmp(aopt, "disable") == 0)
472 		enable = KIOCABORTDISABLE;
473 	else {
474 		(void) fprintf(stderr, "wrong option -- %s\n", aopt);
475 		usage();
476 		return (1);
477 	}
478 
479 	if (ioctl(kbd, KIOCSKABORTEN, &enable)) {
480 		perror("kbd ioctl (abort enable)");
481 		return (1);
482 	}
483 	return (0);
484 }
485 
486 /*
487  * this routine set autorepeat delay
488  */
489 static int
490 set_repeat_delay(char *delay_str, int kbd)
491 {
492 	int delay = atoi(delay_str);
493 
494 	/*
495 	 * The error message depends on the different inputs.
496 	 * a. the input is a invalid integer(unit in ms)
497 	 * b. the input is a integer less than the minimal delay setting.
498 	 * The condition (a) has been covered by main function and set_default
499 	 * function.
500 	 */
501 	if (ioctl(kbd, KIOCSRPTDELAY, &delay) == -1) {
502 		if (delay < KIOCRPTDELAY_MIN)
503 			(void) fprintf(stderr, "kbd: specified delay %d is "
504 			    "less than minimum %d\n", delay, KIOCRPTDELAY_MIN);
505 		else
506 			perror("kbd: set repeat delay");
507 		return (1);
508 	}
509 
510 	return (0);
511 }
512 
513 /*
514  * this routine set autorepeat rate
515  */
516 static int
517 set_repeat_rate(char *rate_str, int kbd)
518 {
519 	int rate = atoi(rate_str);
520 
521 	/*
522 	 * The error message depends on the different inputs.
523 	 * a. the input is a invalid integer(unit in ms)
524 	 * b. the input is a integer less than the minimal rate setting.
525 	 * The condition (a) has been covered by main function and set_default
526 	 * function.
527 	 */
528 	if (ioctl(kbd, KIOCSRPTRATE, &rate) == -1) {
529 		if (rate < KIOCRPTRATE_MIN)
530 			(void) fprintf(stderr, "kbd: specified rate %d is "
531 			    "less than minimum %d\n", rate, KIOCRPTRATE_MIN);
532 		else
533 			perror("kbd: set repeat rate");
534 		return (1);
535 	}
536 
537 	return (0);
538 }
539 
540 #define	BAD_DEFAULT	"kbd: bad default value for %s: %s\n"
541 
542 static void
543 kbd_defaults(int kbd)
544 {
545 	char *p;
546 	int layout_num;
547 
548 	if (defopen(DEF_FILE) != 0) {
549 		(void) fprintf(stderr, "Can't open default file: %s\n",
550 		    DEF_FILE);
551 		exit(1);
552 	}
553 
554 	p = defread(DEF_CLICK);
555 	if (p != NULL) {
556 		/*
557 		 * KEYCLICK must equal "on" or "off"
558 		 */
559 		if ((strcmp(p, "on") == 0) || (strcmp(p, "off") == 0))
560 			(void) click(p, kbd);
561 		else
562 			(void) fprintf(stderr, BAD_DEFAULT, DEF_CLICK, p);
563 	}
564 
565 	p = defread(DEF_ABORT);
566 	if (p != NULL) {
567 		/*
568 		 * ABORT must equal "enable", "disable" or "alternate"
569 		 */
570 		if ((strcmp(p, "enable") == 0) ||
571 		    (strcmp(p, "alternate") == 0) ||
572 		    (strcmp(p, "disable") == 0))
573 			(void) abort_enable(p, kbd);
574 		else
575 			(void) fprintf(stderr, BAD_DEFAULT, DEF_ABORT, p);
576 	}
577 
578 	p = defread(DEF_RPTDELAY);
579 	if (p != NULL) {
580 		/*
581 		 * REPEAT_DELAY unit in ms
582 		 */
583 		if (atoi(p) > 0)
584 			(void) set_repeat_delay(p, kbd);
585 		else
586 			(void) fprintf(stderr, BAD_DEFAULT, DEF_RPTDELAY, p);
587 	}
588 
589 	p = defread(DEF_RPTRATE);
590 	if (p != NULL) {
591 		/*
592 		 * REPEAT_RATE unit in ms
593 		 */
594 		if (atoi(p) > 0)
595 			(void) set_repeat_rate(p, kbd);
596 		else
597 			(void) fprintf(stderr, BAD_DEFAULT, DEF_RPTRATE, p);
598 	}
599 
600 	p = defread(DEF_LAYOUT);
601 	if (p != NULL) {
602 		/*
603 		 * LAYOUT must be one of the layouts supported in kbd_layouts
604 		 */
605 		if (get_layouts() != 0)
606 			return;
607 
608 		if ((layout_num = get_layout_number(p)) == -1) {
609 			(void) fprintf(stderr, BAD_DEFAULT, DEF_LAYOUT, p);
610 			return;
611 		}
612 
613 		(void) set_layout(kbd, layout_num);
614 	}
615 }
616 
617 static int
618 get_layout_number(char *layout)
619 {
620 	int i;
621 	int layout_number = -1;
622 
623 	for (i = 0; i < layout_count; i ++) {
624 		if (strcmp(layout, layout_names[i]) == 0) {
625 			layout_number = layout_numbers[i];
626 			break;
627 		}
628 	}
629 
630 	return (layout_number);
631 }
632 
633 static int
634 get_layouts()
635 {
636 	FILE *stream;
637 	char buffer[MAX_LINE_SIZE];
638 	char *result = NULL;
639 	int  i = 0;
640 	char *tmpbuf;
641 
642 	if ((stream = fopen(KBD_LAYOUT_FILE, "r")) == 0) {
643 		perror(KBD_LAYOUT_FILE);
644 		return (1);
645 	}
646 
647 	while ((fgets(buffer, MAX_LINE_SIZE, stream) != NULL) &&
648 	    (i < MAX_LAYOUT_NUM)) {
649 		if (buffer[0] == '#')
650 			continue;
651 		if ((result = strtok(buffer, "=")) == NULL)
652 			continue;
653 		if ((tmpbuf = strdup(result)) != NULL) {
654 			layout_names[i] = tmpbuf;
655 		} else {
656 			perror("out of memory getting layout names");
657 			return (1);
658 		}
659 		if ((result = strtok(NULL, "\n")) == NULL)
660 			continue;
661 		layout_numbers[i] = atoi(result);
662 		if (strcmp(tmpbuf, "US-English") == 0)
663 			default_layout_number = i;
664 		i++;
665 	}
666 	layout_count = i;
667 
668 	return (0);
669 }
670 
671 /*
672  * this routine sets the layout of the keyboard being used
673  */
674 static int
675 set_layout(int kbd, int layout_num)
676 {
677 
678 	if (ioctl(kbd, KIOCSLAYOUT, layout_num)) {
679 		perror("ioctl (set kbd layout)");
680 		return (1);
681 	}
682 
683 	return (0);
684 }
685 
686 static char *usage1 = "kbd [-r] [-t] [-l] [-a enable|disable|alternate]";
687 static char *usage2 = "    [-c on|off][-D delay][-R rate][-d keyboard device]";
688 static char *usage3 = "kbd -i [-d keyboard device]";
689 static char *usage4 = "kbd -s [language]";
690 
691 static void
692 usage(void)
693 {
694 	(void) fprintf(stderr, "Usage:\t%s\n\t%s\n\t%s\n\t%s\n", usage1, usage2,
695 	    usage3, usage4);
696 }
697