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