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