/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Usage: kbd [-r] [-t] [-l] [-c on|off] [-a enable|disable|alternate] * [-d keyboard device] [-A autorepeat count] * [-D autorepeat delay] [-R autorepeat rate] * kbd [-i] [-d keyboard device] * kbd -s [language] * kbd -b [keyboard|console] frequency * -r reset the keyboard as if power-up * -t return the type of the keyboard being used * -l return the layout of the keyboard being used, * and the Autorepeat settings * -i read in the default configuration file * -c on|off turn on|off clicking * -a enable|disable|alternate sets abort sequence * -A autorepeat count sets autorepeat sequence length in chars. * -D autorepeat delay sets autorepeat delay, unit in ms * -R autorepeat rate sets autorepeat rate, unit in ms * -d keyboard device chooses the kbd device, default /dev/kbd. * -s keyboard layout sets keyboard layout * -b [keyboard| console] frequency * sets keyboard or console beeper frequency */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KBD_DEVICE "/dev/kbd" /* default keyboard device */ #define KBD_LAYOUT_FILE "/usr/share/lib/keytables/type_6/kbd_layouts" #define MAX_LAYOUT_NUM 128 #define MAX_LINE_SIZE 256 #define DEFAULT_KBD_LAYOUT 33 #define KBD_FMRI "svc:/system/keymap:default" #define KBD_PG "keymap" #define KBD_PROP_LAYOUT "layout" #define KBD_PROP_KEYCLICK "keyclick" #define KBD_PROP_KEYBOARD_ABORT "keyboard_abort" #define KBD_PROP_RPTDELAY "repeat_delay" #define KBD_PROP_RPTRATE "repeat_rate" #define KBD_PROP_RPTCOUNT "repeat_count" #define KBD_PROP_FREQ "kbd_beeper_freq" #define KBD_PROP_CONSFREQ "console_beeper_freq" #define KBD_MAX_NAME_LEN 1024 char *layout_names[MAX_LAYOUT_NUM]; int layout_numbers[MAX_LAYOUT_NUM]; static int layout_count; static int default_layout_number = 0; static void reset(int); static int get_type(int); static void get_layout(int); static void kbd_defaults(int); static void usage(void); static int click(char *, int); static int abort_enable(char *, int); static int set_repeat_count(char *, int); static int set_rptcount(int, int); static int set_repeat_delay(char *, int); static int set_rptdelay(int, int); static int set_repeat_rate(char *, int); static int set_rptrate(int, int); static int get_layout_number(char *); static int set_layout(int, int); static int get_layouts(void); static int set_kbd_layout(int, char *); static int set_beep_freq(int, char *, int); int main(int argc, char **argv) { int c, error; int rflag, tflag, lflag, cflag, dflag, aflag, iflag, errflag, Aflag, Dflag, Rflag, rtlacADRflag, sflag, bflag; char *copt, *aopt, *count, *delay, *rate, *layout_name, *b_type; char *freq_str; char *kbdname = KBD_DEVICE, *endptr = NULL; int kbd, freq_val; extern char *optarg; extern int optind; rflag = tflag = cflag = dflag = aflag = iflag = errflag = lflag = Aflag = Dflag = Rflag = sflag = bflag = 0; copt = aopt = NULL; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); while ((c = getopt(argc, argv, "rtlisc:a:d:A:D:R:b:")) != EOF) { switch (c) { case 'r': rflag++; break; case 't': tflag++; break; case 'l': lflag++; break; case 'i': iflag++; break; case 's': sflag++; break; case 'c': copt = optarg; cflag++; break; case 'a': aopt = optarg; aflag++; break; case 'd': kbdname = optarg; dflag++; break; case 'A': count = optarg; Aflag++; break; case 'D': delay = optarg; Dflag++; break; case 'R': rate = optarg; Rflag++; break; case 'b': bflag++; break; case '?': errflag++; break; } } /* * Check for valid arguments: * * If argument parsing failed or if there are left-over * command line arguments(except -s and -b option), * then we're done now. */ if (errflag != 0 || (sflag == 0 && bflag == 0 && argc != optind)) { usage(); exit(1); } /* * kbd requires that the user specify either "-i" or "-s" or "-b" or * at least one of -[rtlacADR]. The "-d" option is, well, optional. * We don't care if it's there or not. */ rtlacADRflag = rflag + tflag + lflag + aflag + cflag + Aflag + Dflag + Rflag; if (!((iflag != 0 && sflag == 0 && bflag == 0 && rtlacADRflag == 0) || (iflag == 0 && sflag != 0 && bflag == 0 && dflag == 0 && rtlacADRflag == 0) || (iflag == 0 && sflag == 0 && bflag == 0 && rtlacADRflag != 0) || (iflag == 0 && sflag == 0 && bflag != 0 && rtlacADRflag == 0))) { usage(); exit(1); } if (Aflag && atoi(count) < -1) { (void) fprintf(stderr, "Invalid arguments: -A %s\n", count); usage(); exit(1); } if (Dflag && atoi(delay) <= 0) { (void) fprintf(stderr, "Invalid arguments: -D %s\n", delay); usage(); exit(1); } if (Rflag && atoi(rate) <= 0) { (void) fprintf(stderr, "Invalid arguments: -R %s\n", rate); usage(); exit(1); } /* * Open the keyboard device */ if ((kbd = open(kbdname, O_RDWR)) < 0) { perror("opening the keyboard"); (void) fprintf(stderr, "kbd: Cannot open %s\n", kbdname); exit(1); } if (iflag) { kbd_defaults(kbd); exit(0); /* A mutually exclusive option */ /*NOTREACHED*/ } if (tflag) (void) get_type(kbd); if (lflag) get_layout(kbd); if (cflag && (error = click(copt, kbd)) != 0) exit(error); if (rflag) reset(kbd); if (aflag && (error = abort_enable(aopt, kbd)) != 0) exit(error); if (Aflag && (error = set_repeat_count(count, kbd)) != 0) exit(error); if (Dflag && (error = set_repeat_delay(delay, kbd)) != 0) exit(error); if (Rflag && (error = set_repeat_rate(rate, kbd)) != 0) exit(error); if (sflag) { if (argc == optind) { layout_name = NULL; } else if (argc == (optind + 1)) { layout_name = argv[optind]; } else { usage(); exit(1); } if ((error = set_kbd_layout(kbd, layout_name)) != 0) exit(error); } if (bflag) { if (argc == optind) { b_type = "keyboard"; } else if (argc == (optind + 1)) { b_type = argv[argc - 2]; } else { usage(); exit(1); } if (strcmp(b_type, "keyboard") && strcmp(b_type, "console")) { usage(); exit(1); } freq_str = argv[argc - 1]; errno = 0; freq_val = (int)strtol(freq_str, &endptr, 10); if (errno != 0 || endptr[0] != '\0') { usage(); exit(1); } if (freq_val < 0 || freq_val > INT16_MAX) { (void) fprintf(stderr, "Invalid arguments: -b %s\n", freq_str); (void) fprintf(stderr, "Frequency range: [0 - %d]\n", INT16_MAX); exit(1); } if ((error = set_beep_freq(kbd, b_type, freq_val)) != 0) exit(1); } return (0); } /* * this routine gets the type of the keyboard being used */ static int set_kbd_layout(int kbd, char *layout_name) { int layout_num; int error = 1; /* layout setting is possible only for USB type keyboards */ if (get_type(kbd) != KB_USB) { (void) fprintf(stderr, "The -s option does not apply for this" " keyboard type.\n" "Only USB/PS2 type keyboards support this option.\n"); return (error); } /* get the language info from the layouts file */ if (get_layouts() != 0) return (error); if (layout_name != NULL) { if ((layout_num = get_layout_number(layout_name)) == -1) { (void) fprintf(stderr, "%s: unknown layout name\n" "Please refer to 'kbd -s' to get the " "supported layouts.\n", layout_name); return (error); } } else { int i, j, print_cnt, input_num; boolean_t input_right = B_TRUE; boolean_t default_input = B_FALSE; char input[8]; /* 8 chars is enough for numbers */ print_cnt = (layout_count % 2) ? layout_count/2+1 : layout_count/2; for (i = 1; i <= print_cnt; i++) { (void) printf("%2d. %-30s", i, layout_names[i-1]); j = i + print_cnt; if (j <= layout_count) { (void) printf("%-2d. %-30s\n", j, layout_names[j-1]); } } (void) printf(gettext("\nTo select the keyboard layout," " enter a number [default %d]:"), default_layout_number+1); for (;;) { if (input_right == B_FALSE) (void) printf(gettext("Invalid input. " "Please input a number " "(1,2,...):")); (void) memset(input, 0, 8); (void) fflush(stdin); (void) fgets(input, 8, stdin); if (strlen(input) > 4) { input_right = B_FALSE; continue; } if (input[0] == '\n') { default_input = B_TRUE; break; } input_right = B_TRUE; /* check if the inputs are numbers 0~9 */ for (i = 0; i < (strlen(input) - 1); i++) { if ((input[i] < '0') || (input[i] > '9')) { input_right = B_FALSE; break; } } if (input_right == B_FALSE) continue; input_num = atoi(input); if ((input_num > 0) && (input_num <= layout_count)) break; else input_right = B_FALSE; } if (default_input == B_TRUE) layout_num = DEFAULT_KBD_LAYOUT; else layout_num = layout_numbers[--input_num]; } if ((error = set_layout(kbd, layout_num)) != 0) return (error); return (0); } /* * This routine sets keyboard or console beeper frequency */ static int set_beep_freq(int fd, char *type, int freq) { struct freq_request fr_struct; if (strcmp(type, "keyboard") == 0) fr_struct.type = KBD_BEEP; else if (strcmp(type, "console") == 0) fr_struct.type = CONSOLE_BEEP; else return (EINVAL); fr_struct.freq = (int16_t)freq; return (ioctl(fd, KIOCSETFREQ, &fr_struct)); } /* * this routine resets the state of the keyboard as if power-up */ static void reset(int kbd) { int cmd; cmd = KBD_CMD_RESET; if (ioctl(kbd, KIOCCMD, &cmd)) { perror("kbd: ioctl error"); exit(1); } } /* * this routine gets the type of the keyboard being used */ static int get_type(int kbd) { int kbd_type; if (ioctl(kbd, KIOCTYPE, &kbd_type)) { perror("ioctl (kbd type)"); exit(1); } switch (kbd_type) { case KB_SUN3: (void) printf("Type 3 Sun keyboard\n"); break; case KB_SUN4: (void) printf("Type 4 Sun keyboard\n"); break; case KB_ASCII: (void) printf("ASCII\n"); break; case KB_PC: (void) printf("PC\n"); break; case KB_USB: (void) printf("USB keyboard\n"); break; default: (void) printf("Unknown keyboard type\n"); break; } return (kbd_type); } /* * this routine gets the layout of the keyboard being used * also, included the autorepeat delay and rate being used */ static void get_layout(int kbd) { int kbd_type; int kbd_layout; /* these three variables are used for getting delay&rate&count */ int delay, rate, count = -1; delay = rate = 0; if (ioctl(kbd, KIOCTYPE, &kbd_type)) { perror("ioctl (kbd type)"); exit(1); } if (ioctl(kbd, KIOCLAYOUT, &kbd_layout)) { perror("ioctl (kbd layout)"); exit(1); } (void) printf("type=%d\nlayout=%d (0x%.2x)\n", kbd_type, kbd_layout, kbd_layout); /* below code is used to get the autorepeat delay and rate */ if (ioctl(kbd, KIOCGRPTDELAY, &delay)) { perror("ioctl (kbd get repeat delay)"); exit(1); } if (ioctl(kbd, KIOCGRPTRATE, &rate)) { perror("ioctl (kbd get repeat rate)"); exit(1); } if (ioctl(kbd, KIOCGRPTCOUNT, &count)) { perror("ioctl (kbd get repeat count)"); exit(1); } (void) printf("delay(ms)=%d\n", delay); (void) printf("rate(ms)=%d\n", rate); if (count == -1) (void) printf("count=unlimited\n"); else (void) printf("count=%d\n", count); } /* * this routine enables or disables clicking of the keyboard */ static int click(char *copt, int kbd) { int cmd; if (strcmp(copt, "on") == 0) cmd = KBD_CMD_CLICK; else if (strcmp(copt, "off") == 0) cmd = KBD_CMD_NOCLICK; else { (void) fprintf(stderr, "wrong option -- %s\n", copt); usage(); return (1); } if (ioctl(kbd, KIOCCMD, &cmd)) { perror("kbd ioctl (keyclick)"); return (1); } return (0); } /* * this routine enables/disables/sets BRK or abort sequence feature */ static int abort_enable(char *aopt, int kbd) { int enable; if (strcmp(aopt, "alternate") == 0) enable = KIOCABORTALTERNATE; else if (strcmp(aopt, "enable") == 0) enable = KIOCABORTENABLE; else if (strcmp(aopt, "disable") == 0) enable = KIOCABORTDISABLE; else { (void) fprintf(stderr, "wrong option -- %s\n", aopt); usage(); return (1); } if (ioctl(kbd, KIOCSKABORTEN, &enable)) { perror("kbd ioctl (abort enable)"); return (1); } return (0); } static int set_rptcount(int count, int kbd) { if (ioctl(kbd, KIOCSRPTCOUNT, &count) == -1) { perror("kbd: set repeat count"); return (1); } return (0); } static int set_repeat_count(char *count_str, int kbd) { int count = atoi(count_str); return (set_rptcount(count, kbd)); } static int set_rptdelay(int delay, int kbd) { /* * The error message depends on the different inputs. * a. the input is a invalid integer(unit in ms) * b. the input is a integer less than the minimal delay setting. * The condition (a) has been covered by main function and kbd_defaults * function. */ if (ioctl(kbd, KIOCSRPTDELAY, &delay) == -1) { if (delay < KIOCRPTDELAY_MIN) (void) fprintf(stderr, "kbd: specified delay %d is " "less than minimum %d\n", delay, KIOCRPTDELAY_MIN); else perror("kbd: set repeat delay"); return (1); } return (0); } /* * this routine set autorepeat delay */ static int set_repeat_delay(char *delay_str, int kbd) { int delay = atoi(delay_str); return (set_rptdelay(delay, kbd)); } static int set_rptrate(int rate, int kbd) { /* * The input validation check has been covered by main function * and kbd_defaults function.Here just give an error message if * the ioctl fails. */ if (ioctl(kbd, KIOCSRPTRATE, &rate) == -1) { perror("kbd: set repeat rate"); return (1); } return (0); } /* * this routine set autorepeat rate */ static int set_repeat_rate(char *rate_str, int kbd) { int rate = atoi(rate_str); return (set_rptrate(rate, kbd)); } #define BAD_DEFAULT_STR "kbd: bad default value for %s: %s\n" #define BAD_DEFAULT_INT "kbd: bad default value for %s: %d\n" #define BAD_DEFAULT_LLINT "kbd: bad default value for %s: %lld\n" static void kbd_defaults(int kbd) { scf_handle_t *h = NULL; scf_snapshot_t *snap = NULL; scf_instance_t *inst = NULL; scf_propertygroup_t *pg = NULL; scf_property_t *prop = NULL; scf_value_t *val = NULL; int layout_num; char *val_layout = NULL, *val_abort = NULL; uint8_t val_click; int64_t val_delay, val_rate, val_count; int64_t val_kbd_beeper, val_console_beeper; if ((h = scf_handle_create(SCF_VERSION)) == NULL || scf_handle_bind(h) != 0 || (inst = scf_instance_create(h)) == NULL || (snap = scf_snapshot_create(h)) == NULL || (pg = scf_pg_create(h)) == NULL || (prop = scf_property_create(h)) == NULL || (val = scf_value_create(h)) == NULL) { goto out; } if (scf_handle_decode_fmri(h, KBD_FMRI, NULL, NULL, inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { goto out; } if (scf_instance_get_snapshot(inst, "running", snap) != 0) { scf_snapshot_destroy(snap); snap = NULL; } if (scf_instance_get_pg_composed(inst, snap, KBD_PG, pg) != 0) { goto out; } if ((val_abort = malloc(KBD_MAX_NAME_LEN)) == NULL) { (void) fprintf(stderr, "Can not alloc memory for keyboard properties\n"); goto out; } if ((val_layout = malloc(KBD_MAX_NAME_LEN)) == NULL) { (void) fprintf(stderr, "Can not alloc memory for keyboard properties\n"); goto out; } if (scf_pg_get_property(pg, KBD_PROP_KEYCLICK, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_boolean(val, &val_click) == -1) { (void) fprintf(stderr, "Can not get KEYCLICK\n"); } if (val_click == 1) (void) click("on", kbd); else if (val_click == 0) (void) click("off", kbd); else (void) fprintf(stderr, BAD_DEFAULT_INT, KBD_PROP_KEYCLICK, val_click); if (scf_pg_get_property(pg, KBD_PROP_KEYBOARD_ABORT, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_astring(val, val_abort, KBD_MAX_NAME_LEN) == -1) { (void) fprintf(stderr, "Can not get KEYBOARD_ABORT\n"); } if (*val_abort != '\0') { /* * ABORT must equal "enable", "disable" or "alternate" */ if ((strcmp(val_abort, "enable") == 0) || (strcmp(val_abort, "alternate") == 0) || (strcmp(val_abort, "disable") == 0)) (void) abort_enable(val_abort, kbd); else (void) fprintf(stderr, BAD_DEFAULT_STR, KBD_PROP_KEYBOARD_ABORT, val_abort); } if (scf_pg_get_property(pg, KBD_PROP_RPTCOUNT, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_integer(val, &val_count) == -1) { (void) fprintf(stderr, "Can not get RPTCOUNT\n"); } if (val_count == -1 || (val_count > 0 && val_count < INT_MAX)) (void) set_rptcount(val_count, kbd); else (void) fprintf(stderr, BAD_DEFAULT_LLINT, KBD_PROP_RPTCOUNT, val_count); if (scf_pg_get_property(pg, KBD_PROP_RPTDELAY, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_integer(val, &val_delay) == -1) { (void) fprintf(stderr, "Can not get RPTDELAY\n"); } if (val_delay > 0) (void) set_rptdelay(val_delay, kbd); else (void) fprintf(stderr, BAD_DEFAULT_LLINT, KBD_PROP_RPTDELAY, val_delay); if (scf_pg_get_property(pg, KBD_PROP_RPTRATE, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_integer(val, &val_rate) == -1) { (void) fprintf(stderr, "Can not get RPTRATE\n"); } if (val_rate > 0) (void) set_rptrate(val_rate, kbd); else (void) fprintf(stderr, BAD_DEFAULT_LLINT, KBD_PROP_RPTRATE, val_rate); if (scf_pg_get_property(pg, KBD_PROP_LAYOUT, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_astring(val, val_layout, KBD_MAX_NAME_LEN) == -1) { (void) fprintf(stderr, "Can not get LAYOUT\n"); } if (*val_layout != '\0') { /* * LAYOUT must be one of the layouts supported in kbd_layouts */ if (get_layouts() != 0) goto out; if ((layout_num = get_layout_number(val_layout)) == -1) { (void) fprintf(stderr, BAD_DEFAULT_STR, KBD_PROP_LAYOUT, val_layout); goto out; } (void) set_layout(kbd, layout_num); } if (scf_pg_get_property(pg, KBD_PROP_FREQ, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_integer(val, &val_kbd_beeper) == -1) { (void) fprintf(stderr, "Can not get FREQ\n"); } if (val_kbd_beeper >= 0 && val_kbd_beeper <= INT16_MAX) (void) set_beep_freq(kbd, "keyboard", val_kbd_beeper); else (void) fprintf(stderr, BAD_DEFAULT_LLINT, KBD_PROP_FREQ, val_kbd_beeper); if (scf_pg_get_property(pg, KBD_PROP_CONSFREQ, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_integer(val, &val_console_beeper) == -1) { (void) fprintf(stderr, "Can not get CONSFREQ\n"); } if (val_console_beeper >= 0 && val_console_beeper <= INT16_MAX) (void) set_beep_freq(kbd, "console", val_console_beeper); else (void) fprintf(stderr, BAD_DEFAULT_LLINT, KBD_PROP_CONSFREQ, val_console_beeper); out: if (val_layout != NULL) free(val_layout); if (val_abort != NULL) free(val_abort); if (snap != NULL) scf_snapshot_destroy(snap); scf_value_destroy(val); scf_property_destroy(prop); scf_pg_destroy(pg); scf_instance_destroy(inst); scf_handle_destroy(h); } static int get_layout_number(char *layout) { int i; int layout_number = -1; for (i = 0; i < layout_count; i ++) { if (strcmp(layout, layout_names[i]) == 0) { layout_number = layout_numbers[i]; break; } } return (layout_number); } static int get_layouts() { FILE *stream; char buffer[MAX_LINE_SIZE]; char *result = NULL; int i = 0; char *tmpbuf; if ((stream = fopen(KBD_LAYOUT_FILE, "r")) == 0) { perror(KBD_LAYOUT_FILE); return (1); } while ((fgets(buffer, MAX_LINE_SIZE, stream) != NULL) && (i < MAX_LAYOUT_NUM)) { if (buffer[0] == '#') continue; if ((result = strtok(buffer, "=")) == NULL) continue; if ((tmpbuf = strdup(result)) != NULL) { layout_names[i] = tmpbuf; } else { perror("out of memory getting layout names"); return (1); } if ((result = strtok(NULL, "\n")) == NULL) continue; layout_numbers[i] = atoi(result); if (strcmp(tmpbuf, "US-English") == 0) default_layout_number = i; i++; } layout_count = i; return (0); } /* * this routine sets the layout of the keyboard being used */ static int set_layout(int kbd, int layout_num) { if (ioctl(kbd, KIOCSLAYOUT, layout_num)) { perror("ioctl (set kbd layout)"); return (1); } (void) system("/usr/bin/loadkeys"); return (0); } static char *usage1 = "kbd [-r] [-t] [-l] [-a enable|disable|alternate] [-c on|off]\n" "\t [-d keyboard device] [-A count] [-D delay] [-R rate]"; static char *usage2 = "kbd -i [-d keyboard device]"; static char *usage3 = "kbd -s [language]"; static char *usage4 = "kbd -b [keyboard|console] frequency"; static void usage(void) { (void) fprintf(stderr, "Usage:\t%s\n\t%s\n\t%s\n\t%s\n", usage1, usage2, usage3, usage4); }