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