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