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