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 void 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 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 /* get the language info from the layouts file */ 309 if (get_layouts() != 0) 310 return (error); 311 312 if (layout_name != NULL) { 313 if ((layout_num = get_layout_number(layout_name)) == -1) { 314 (void) fprintf(stderr, "%s: unknown layout name\n" 315 "Please refer to 'kbd -s' to get the " 316 "supported layouts.\n", layout_name); 317 return (error); 318 } 319 } else { 320 int i, j, print_cnt, input_num; 321 boolean_t input_right = B_TRUE; 322 boolean_t default_input = B_FALSE; 323 char input[8]; /* 8 chars is enough for numbers */ 324 325 print_cnt = (layout_count % 2) ? 326 layout_count/2+1 : layout_count/2; 327 328 for (i = 1; i <= print_cnt; i++) { 329 (void) printf("%2d. %-30s", i, 330 layout_names[i-1]); 331 j = i + print_cnt; 332 if (j <= layout_count) { 333 (void) printf("%-2d. %-30s\n", j, 334 layout_names[j-1]); 335 } 336 } 337 (void) printf(gettext("\nTo select the keyboard layout," 338 " enter a number [default %d]:"), 339 default_layout_number+1); 340 341 for (;;) { 342 if (input_right == B_FALSE) 343 (void) printf(gettext("Invalid input. " 344 "Please input a number " 345 "(1,2,...):")); 346 (void) memset(input, 0, 8); 347 (void) fflush(stdin); 348 (void) fgets(input, 8, stdin); 349 if (strlen(input) > 4) { 350 input_right = B_FALSE; 351 continue; 352 } 353 if (input[0] == '\n') { 354 default_input = B_TRUE; 355 break; 356 } 357 input_right = B_TRUE; 358 /* check if the inputs are numbers 0~9 */ 359 for (i = 0; i < (strlen(input) - 1); i++) { 360 if ((input[i] < '0') || 361 (input[i] > '9')) { 362 input_right = B_FALSE; 363 break; 364 } 365 } 366 if (input_right == B_FALSE) 367 continue; 368 input_num = atoi(input); 369 if ((input_num > 0) && 370 (input_num <= layout_count)) 371 break; 372 else 373 input_right = B_FALSE; 374 } 375 if (default_input == B_TRUE) 376 layout_num = DEFAULT_KBD_LAYOUT; 377 else 378 layout_num = layout_numbers[--input_num]; 379 } 380 381 if ((error = set_layout(kbd, layout_num)) != 0) 382 return (error); 383 384 return (0); 385 } 386 387 /* 388 * This routine sets keyboard or console beeper frequency 389 */ 390 static int 391 set_beep_freq(int fd, char *type, int freq) 392 { 393 struct freq_request fr_struct; 394 395 if (strcmp(type, "keyboard") == 0) 396 fr_struct.type = KBD_BEEP; 397 else if (strcmp(type, "console") == 0) 398 fr_struct.type = CONSOLE_BEEP; 399 else 400 return (EINVAL); 401 402 fr_struct.freq = (int16_t)freq; 403 404 return (ioctl(fd, KIOCSETFREQ, &fr_struct)); 405 } 406 407 /* 408 * this routine resets the state of the keyboard as if power-up 409 */ 410 static void 411 reset(int kbd) 412 { 413 int cmd; 414 415 cmd = KBD_CMD_RESET; 416 417 if (ioctl(kbd, KIOCCMD, &cmd)) { 418 perror("kbd: ioctl error"); 419 exit(1); 420 } 421 422 } 423 424 /* 425 * this routine gets the type of the keyboard being used 426 */ 427 static void 428 get_type(int kbd) 429 { 430 int kbd_type; 431 432 if (ioctl(kbd, KIOCTYPE, &kbd_type)) { 433 perror("ioctl (kbd type)"); 434 exit(1); 435 } 436 437 switch (kbd_type) { 438 439 case KB_SUN3: 440 (void) printf("Type 3 Sun keyboard\n"); 441 break; 442 443 case KB_SUN4: 444 (void) printf("Type 4 Sun keyboard\n"); 445 break; 446 447 case KB_ASCII: 448 (void) printf("ASCII\n"); 449 break; 450 451 case KB_PC: 452 (void) printf("PC\n"); 453 break; 454 455 case KB_USB: 456 (void) printf("USB keyboard\n"); 457 break; 458 459 default: 460 (void) printf("Unknown keyboard type\n"); 461 break; 462 } 463 } 464 465 /* 466 * this routine gets the layout of the keyboard being used 467 * also, included the autorepeat delay and rate being used 468 */ 469 static void 470 get_layout(int kbd) 471 { 472 int kbd_type; 473 int kbd_layout; 474 /* these two variables are used for getting delay&rate */ 475 int delay, rate; 476 delay = rate = 0; 477 478 if (ioctl(kbd, KIOCTYPE, &kbd_type)) { 479 perror("ioctl (kbd type)"); 480 exit(1); 481 } 482 483 if (ioctl(kbd, KIOCLAYOUT, &kbd_layout)) { 484 perror("ioctl (kbd layout)"); 485 exit(1); 486 } 487 488 (void) printf("type=%d\nlayout=%d (0x%.2x)\n", 489 kbd_type, kbd_layout, kbd_layout); 490 491 /* below code is used to get the autorepeat delay and rate */ 492 if (ioctl(kbd, KIOCGRPTDELAY, &delay)) { 493 perror("ioctl (kbd get repeat delay)"); 494 exit(1); 495 } 496 497 if (ioctl(kbd, KIOCGRPTRATE, &rate)) { 498 perror("ioctl (kbd get repeat rate)"); 499 exit(1); 500 } 501 502 (void) printf("delay(ms)=%d\n", delay); 503 (void) printf("rate(ms)=%d\n", rate); 504 } 505 506 /* 507 * this routine enables or disables clicking of the keyboard 508 */ 509 static int 510 click(char *copt, int kbd) 511 { 512 int cmd; 513 514 if (strcmp(copt, "on") == 0) 515 cmd = KBD_CMD_CLICK; 516 else if (strcmp(copt, "off") == 0) 517 cmd = KBD_CMD_NOCLICK; 518 else { 519 (void) fprintf(stderr, "wrong option -- %s\n", copt); 520 usage(); 521 return (1); 522 } 523 524 if (ioctl(kbd, KIOCCMD, &cmd)) { 525 perror("kbd ioctl (keyclick)"); 526 return (1); 527 } 528 return (0); 529 } 530 531 /* 532 * this routine enables/disables/sets BRK or abort sequence feature 533 */ 534 static int 535 abort_enable(char *aopt, int kbd) 536 { 537 int enable; 538 539 if (strcmp(aopt, "alternate") == 0) 540 enable = KIOCABORTALTERNATE; 541 else if (strcmp(aopt, "enable") == 0) 542 enable = KIOCABORTENABLE; 543 else if (strcmp(aopt, "disable") == 0) 544 enable = KIOCABORTDISABLE; 545 else { 546 (void) fprintf(stderr, "wrong option -- %s\n", aopt); 547 usage(); 548 return (1); 549 } 550 551 if (ioctl(kbd, KIOCSKABORTEN, &enable)) { 552 perror("kbd ioctl (abort enable)"); 553 return (1); 554 } 555 return (0); 556 } 557 558 /* 559 * this routine set autorepeat delay 560 */ 561 static int 562 set_repeat_delay(char *delay_str, int kbd) 563 { 564 int delay = atoi(delay_str); 565 566 /* 567 * The error message depends on the different inputs. 568 * a. the input is a invalid integer(unit in ms) 569 * b. the input is a integer less than the minimal delay setting. 570 * The condition (a) has been covered by main function and kbd_defaults 571 * function. 572 */ 573 if (ioctl(kbd, KIOCSRPTDELAY, &delay) == -1) { 574 if (delay < KIOCRPTDELAY_MIN) 575 (void) fprintf(stderr, "kbd: specified delay %d is " 576 "less than minimum %d\n", delay, KIOCRPTDELAY_MIN); 577 else 578 perror("kbd: set repeat delay"); 579 return (1); 580 } 581 582 return (0); 583 } 584 585 /* 586 * this routine set autorepeat rate 587 */ 588 static int 589 set_repeat_rate(char *rate_str, int kbd) 590 { 591 int rate = atoi(rate_str); 592 593 /* 594 * The input validation check has been covered by main function 595 * and kbd_defaults function.Here just give an error message if 596 * the ioctl fails. 597 */ 598 if (ioctl(kbd, KIOCSRPTRATE, &rate) == -1) { 599 perror("kbd: set repeat rate"); 600 return (1); 601 } 602 return (0); 603 } 604 605 #define BAD_DEFAULT "kbd: bad default value for %s: %s\n" 606 607 static void 608 kbd_defaults(int kbd) 609 { 610 char *p, *endptr; 611 int layout_num, freq; 612 613 if (defopen(DEF_FILE) != 0) { 614 (void) fprintf(stderr, "Can't open default file: %s\n", 615 DEF_FILE); 616 exit(1); 617 } 618 619 p = defread(DEF_CLICK); 620 if (p != NULL) { 621 /* 622 * KEYCLICK must equal "on" or "off" 623 */ 624 if ((strcmp(p, "on") == 0) || (strcmp(p, "off") == 0)) 625 (void) click(p, kbd); 626 else 627 (void) fprintf(stderr, BAD_DEFAULT, DEF_CLICK, p); 628 } 629 630 p = defread(DEF_ABORT); 631 if (p != NULL) { 632 /* 633 * ABORT must equal "enable", "disable" or "alternate" 634 */ 635 if ((strcmp(p, "enable") == 0) || 636 (strcmp(p, "alternate") == 0) || 637 (strcmp(p, "disable") == 0)) 638 (void) abort_enable(p, kbd); 639 else 640 (void) fprintf(stderr, BAD_DEFAULT, DEF_ABORT, p); 641 } 642 643 p = defread(DEF_RPTDELAY); 644 if (p != NULL) { 645 /* 646 * REPEAT_DELAY unit in ms 647 */ 648 if (atoi(p) > 0) 649 (void) set_repeat_delay(p, kbd); 650 else 651 (void) fprintf(stderr, BAD_DEFAULT, DEF_RPTDELAY, p); 652 } 653 654 p = defread(DEF_RPTRATE); 655 if (p != NULL) { 656 /* 657 * REPEAT_RATE unit in ms 658 */ 659 if (atoi(p) > 0) 660 (void) set_repeat_rate(p, kbd); 661 else 662 (void) fprintf(stderr, BAD_DEFAULT, DEF_RPTRATE, p); 663 } 664 665 p = defread(DEF_LAYOUT); 666 if (p != NULL) { 667 /* 668 * LAYOUT must be one of the layouts supported in kbd_layouts 669 */ 670 if (get_layouts() != 0) 671 return; 672 673 if ((layout_num = get_layout_number(p)) == -1) { 674 (void) fprintf(stderr, BAD_DEFAULT, DEF_LAYOUT, p); 675 return; 676 } 677 678 (void) set_layout(kbd, layout_num); 679 } 680 681 p = defread(DEF_KBDFREQ); 682 if (p != NULL) { 683 /* 684 * Keyboard beeper frequency unit in Hz 685 */ 686 endptr = NULL; 687 errno = 0; 688 freq = (int)strtol(p, &endptr, 10); 689 if (errno == 0 && endptr[0] == '\0' && 690 freq >= 0 && freq <= INT16_MAX) 691 (void) set_beep_freq(kbd, "keyboard", freq); 692 else 693 (void) fprintf(stderr, BAD_DEFAULT, DEF_KBDFREQ, p); 694 } 695 696 p = defread(DEF_CONSFREQ); 697 if (p != NULL) { 698 /* 699 * Console beeper frequency unit in Hz 700 */ 701 endptr = NULL; 702 errno = 0; 703 freq = (int)strtol(p, &endptr, 10); 704 if (errno == 0 && endptr[0] == '\0' && 705 freq >= 0 && freq <= INT16_MAX) 706 (void) set_beep_freq(kbd, "console", freq); 707 else 708 (void) fprintf(stderr, BAD_DEFAULT, DEF_CONSFREQ, p); 709 } 710 } 711 712 static int 713 get_layout_number(char *layout) 714 { 715 int i; 716 int layout_number = -1; 717 718 for (i = 0; i < layout_count; i ++) { 719 if (strcmp(layout, layout_names[i]) == 0) { 720 layout_number = layout_numbers[i]; 721 break; 722 } 723 } 724 725 return (layout_number); 726 } 727 728 static int 729 get_layouts() 730 { 731 FILE *stream; 732 char buffer[MAX_LINE_SIZE]; 733 char *result = NULL; 734 int i = 0; 735 char *tmpbuf; 736 737 if ((stream = fopen(KBD_LAYOUT_FILE, "r")) == 0) { 738 perror(KBD_LAYOUT_FILE); 739 return (1); 740 } 741 742 while ((fgets(buffer, MAX_LINE_SIZE, stream) != NULL) && 743 (i < MAX_LAYOUT_NUM)) { 744 if (buffer[0] == '#') 745 continue; 746 if ((result = strtok(buffer, "=")) == NULL) 747 continue; 748 if ((tmpbuf = strdup(result)) != NULL) { 749 layout_names[i] = tmpbuf; 750 } else { 751 perror("out of memory getting layout names"); 752 return (1); 753 } 754 if ((result = strtok(NULL, "\n")) == NULL) 755 continue; 756 layout_numbers[i] = atoi(result); 757 if (strcmp(tmpbuf, "US-English") == 0) 758 default_layout_number = i; 759 i++; 760 } 761 layout_count = i; 762 763 return (0); 764 } 765 766 /* 767 * this routine sets the layout of the keyboard being used 768 */ 769 static int 770 set_layout(int kbd, int layout_num) 771 { 772 773 if (ioctl(kbd, KIOCSLAYOUT, layout_num)) { 774 perror("ioctl (set kbd layout)"); 775 return (1); 776 } 777 778 return (0); 779 } 780 781 static char *usage1 = "kbd [-r] [-t] [-l] [-a enable|disable|alternate]"; 782 static char *usage2 = " [-c on|off][-D delay][-R rate][-d keyboard device]"; 783 static char *usage3 = "kbd -i [-d keyboard device]"; 784 static char *usage4 = "kbd -s [language]"; 785 static char *usage5 = "kbd -b [keyboard|console] frequency"; 786 787 static void 788 usage(void) 789 { 790 (void) fprintf(stderr, "Usage:\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n", usage1, 791 usage2, usage3, usage4, usage5); 792 } 793