Index: head/sys/dev/atkbdc/atkbd.c =================================================================== --- head/sys/dev/atkbdc/atkbd.c (revision 56835) +++ head/sys/dev/atkbdc/atkbd.c (revision 56836) @@ -1,1397 +1,1392 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "atkbd.h" #include "opt_kbd.h" #include "opt_atkbd.h" -#if NATKBD > 0 - #include #include #include #include #include #include #ifdef __i386__ #include #include #include #include #include #include #endif /* __i386__ */ #include #include #include #include static timeout_t atkbd_timeout; int atkbd_probe_unit(int unit, int port, int irq, int flags) { keyboard_switch_t *sw; int args[2]; int error; sw = kbd_get_switch(ATKBD_DRIVER_NAME); if (sw == NULL) return ENXIO; args[0] = port; args[1] = irq; error = (*sw->probe)(unit, args, flags); if (error) return error; return 0; } int atkbd_attach_unit(int unit, keyboard_t **kbd, int port, int irq, int flags) { keyboard_switch_t *sw; int args[2]; int error; sw = kbd_get_switch(ATKBD_DRIVER_NAME); if (sw == NULL) return ENXIO; /* reset, initialize and enable the device */ args[0] = port; args[1] = irq; *kbd = NULL; error = (*sw->probe)(unit, args, flags); if (error) return error; error = (*sw->init)(unit, kbd, args, flags); if (error) return error; (*sw->enable)(*kbd); #ifdef KBD_INSTALL_CDEV /* attach a virtual keyboard cdev */ error = kbd_attach(*kbd); if (error) return error; #endif /* * This is a kludge to compensate for lost keyboard interrupts. * A similar code used to be in syscons. See below. XXX */ atkbd_timeout(*kbd); if (bootverbose) (*sw->diag)(*kbd, bootverbose); return 0; } static void atkbd_timeout(void *arg) { keyboard_t *kbd; int s; /* * The original text of the following comments are extracted * from syscons.c (1.287) * * With release 2.1 of the Xaccel server, the keyboard is left * hanging pretty often. Apparently an interrupt from the * keyboard is lost, and I don't know why (yet). * This ugly hack calls the low-level interrupt routine if input * is ready for the keyboard and conveniently hides the problem. XXX * * Try removing anything stuck in the keyboard controller; whether * it's a keyboard scan code or mouse data. The low-level * interrupt routine doesn't read the mouse data directly, * but the keyboard controller driver will, as a side effect. */ /* * And here is bde's original comment about this: * * This is necessary to handle edge triggered interrupts - if we * returned when our IRQ is high due to unserviced input, then there * would be no more keyboard IRQs until the keyboard is reset by * external powers. * * The keyboard apparently unwedges the irq in most cases. */ s = spltty(); kbd = (keyboard_t *)arg; if ((*kbdsw[kbd->kb_index]->lock)(kbd, TRUE)) { /* * We have seen the lock flag is not set. Let's reset * the flag early, otherwise the LED update routine fails * which may want the lock during the interrupt routine. */ (*kbdsw[kbd->kb_index]->lock)(kbd, FALSE); if ((*kbdsw[kbd->kb_index]->check_char)(kbd)) (*kbdsw[kbd->kb_index]->intr)(kbd, NULL); } splx(s); timeout(atkbd_timeout, arg, hz/10); } /* LOW-LEVEL */ #include #include #include #define ATKBD_DEFAULT 0 typedef struct atkbd_state { KBDC kbdc; /* keyboard controller */ /* XXX: don't move this field; pcvt * expects `kbdc' to be the first * field in this structure. */ int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int ks_flags; /* flags */ #define COMPOSE (1 << 0) int ks_polling; int ks_state; /* shift/lock key state */ int ks_accents; /* accent key index (> 0) */ u_int ks_composed_char; /* composed char code (> 0) */ u_char ks_prefix; /* AT scan code prefix */ } atkbd_state_t; /* keyboard driver declaration */ static int atkbd_configure(int flags); static kbd_probe_t atkbd_probe; static kbd_init_t atkbd_init; static kbd_term_t atkbd_term; static kbd_intr_t atkbd_intr; static kbd_test_if_t atkbd_test_if; static kbd_enable_t atkbd_enable; static kbd_disable_t atkbd_disable; static kbd_read_t atkbd_read; static kbd_check_t atkbd_check; static kbd_read_char_t atkbd_read_char; static kbd_check_char_t atkbd_check_char; static kbd_ioctl_t atkbd_ioctl; static kbd_lock_t atkbd_lock; static kbd_clear_state_t atkbd_clear_state; static kbd_get_state_t atkbd_get_state; static kbd_set_state_t atkbd_set_state; static kbd_poll_mode_t atkbd_poll; keyboard_switch_t atkbdsw = { atkbd_probe, atkbd_init, atkbd_term, atkbd_intr, atkbd_test_if, atkbd_enable, atkbd_disable, atkbd_read, atkbd_check, atkbd_read_char, atkbd_check_char, atkbd_ioctl, atkbd_lock, atkbd_clear_state, atkbd_get_state, atkbd_set_state, genkbd_get_fkeystr, atkbd_poll, genkbd_diag, }; KEYBOARD_DRIVER(atkbd, atkbdsw, atkbd_configure); /* local functions */ static int get_typematic(keyboard_t *kbd); static int setup_kbd_port(KBDC kbdc, int port, int intr); static int get_kbd_echo(KBDC kbdc); static int probe_keyboard(KBDC kbdc, int flags); static int init_keyboard(KBDC kbdc, int *type, int flags); static int write_kbd(KBDC kbdc, int command, int data); static int get_kbd_id(KBDC kbdc); static int typematic(int delay, int rate); static int typematic_delay(int delay); static int typematic_rate(int rate); /* local variables */ /* the initial key map, accent map and fkey strings */ #ifdef ATKBD_DFLT_KEYMAP #define KBD_DFLT_KEYMAP #include "atkbdmap.h" #endif #include /* structures for the default keyboard */ static keyboard_t default_kbd; static atkbd_state_t default_kbd_state; static keymap_t default_keymap; static accentmap_t default_accentmap; static fkeytab_t default_fkeytab[NUM_FKEYS]; /* * The back door to the keyboard driver! * This function is called by the console driver, via the kbdio module, * to tickle keyboard drivers when the low-level console is being initialized. * Almost nothing in the kernel has been initialied yet. Try to probe * keyboards if possible. * NOTE: because of the way the low-level conole is initialized, this routine * may be called more than once!! */ static int atkbd_configure(int flags) { keyboard_t *kbd; int arg[2]; int i; /* probe the keyboard controller */ atkbdc_configure(); /* if the driver is disabled, unregister the keyboard if any */ if ((resource_int_value("atkbd", ATKBD_DEFAULT, "disabled", &i) == 0) && i != 0) { i = kbd_find_keyboard(ATKBD_DRIVER_NAME, ATKBD_DEFAULT); if (i >= 0) { kbd = kbd_get_keyboard(i); kbd_unregister(kbd); kbd->kb_flags &= ~KB_REGISTERED; } return 0; } /* XXX: a kludge to obtain the device configuration flags */ if (resource_int_value("atkbd", ATKBD_DEFAULT, "flags", &i) == 0) flags |= i; /* probe the default keyboard */ arg[0] = -1; arg[1] = -1; kbd = NULL; if (atkbd_probe(ATKBD_DEFAULT, arg, flags)) return 0; if (atkbd_init(ATKBD_DEFAULT, &kbd, arg, flags)) return 0; /* return the number of found keyboards */ return 1; } /* low-level functions */ /* detect a keyboard */ static int atkbd_probe(int unit, void *arg, int flags) { KBDC kbdc; int *data = (int *)arg; /* XXX */ if (unit == ATKBD_DEFAULT) { if (KBD_IS_PROBED(&default_kbd)) return 0; } kbdc = kbdc_open(data[0]); if (kbdc == NULL) return ENXIO; if (probe_keyboard(kbdc, flags)) { if (flags & KB_CONF_FAIL_IF_NO_KBD) return ENXIO; } return 0; } /* reset and initialize the device */ static int atkbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) { keyboard_t *kbd; atkbd_state_t *state; keymap_t *keymap; accentmap_t *accmap; fkeytab_t *fkeymap; int fkeymap_size; int delay[2]; int *data = (int *)arg; /* XXX */ if (unit == ATKBD_DEFAULT) { *kbdp = kbd = &default_kbd; if (KBD_IS_INITIALIZED(kbd) && KBD_IS_CONFIGURED(kbd)) return 0; state = &default_kbd_state; keymap = &default_keymap; accmap = &default_accentmap; fkeymap = default_fkeytab; fkeymap_size = sizeof(default_fkeytab)/sizeof(default_fkeytab[0]); } else if (*kbdp == NULL) { *kbdp = kbd = malloc(sizeof(*kbd), M_DEVBUF, M_NOWAIT); if (kbd == NULL) return ENOMEM; bzero(kbd, sizeof(*kbd)); state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT); keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT); accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT); fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT); fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]); if ((state == NULL) || (keymap == NULL) || (accmap == NULL) || (fkeymap == NULL)) { if (state != NULL) free(state, M_DEVBUF); if (keymap != NULL) free(keymap, M_DEVBUF); if (accmap != NULL) free(accmap, M_DEVBUF); if (fkeymap != NULL) free(fkeymap, M_DEVBUF); free(kbd, M_DEVBUF); return ENOMEM; } bzero(state, sizeof(*state)); } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) { return 0; } else { kbd = *kbdp; state = (atkbd_state_t *)kbd->kb_data; bzero(state, sizeof(*state)); keymap = kbd->kb_keymap; accmap = kbd->kb_accentmap; fkeymap = kbd->kb_fkeytab; fkeymap_size = kbd->kb_fkeytab_size; } if (!KBD_IS_PROBED(kbd)) { state->kbdc = kbdc_open(data[0]); if (state->kbdc == NULL) return ENXIO; kbd_init_struct(kbd, ATKBD_DRIVER_NAME, KB_OTHER, unit, flags, data[0], IO_KBDSIZE); bcopy(&key_map, keymap, sizeof(key_map)); bcopy(&accent_map, accmap, sizeof(accent_map)); bcopy(fkey_tab, fkeymap, imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab))); kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size); kbd->kb_data = (void *)state; if (probe_keyboard(state->kbdc, flags)) { /* shouldn't happen */ if (flags & KB_CONF_FAIL_IF_NO_KBD) return ENXIO; } else { KBD_FOUND_DEVICE(kbd); } atkbd_clear_state(kbd); state->ks_mode = K_XLATE; /* * FIXME: set the initial value for lock keys in ks_state * according to the BIOS data? */ KBD_PROBE_DONE(kbd); } if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) { kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY; if (KBD_HAS_DEVICE(kbd) && init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config) && (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD)) return ENXIO; atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); get_typematic(kbd); delay[0] = kbd->kb_delay1; delay[1] = kbd->kb_delay2; atkbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay); KBD_INIT_DONE(kbd); } if (!KBD_IS_CONFIGURED(kbd)) { if (kbd_register(kbd) < 0) return ENXIO; KBD_CONFIG_DONE(kbd); } return 0; } /* finish using this keyboard */ static int atkbd_term(keyboard_t *kbd) { kbd_unregister(kbd); return 0; } /* keyboard interrupt routine */ static int atkbd_intr(keyboard_t *kbd, void *arg) { atkbd_state_t *state; int delay[2]; int c; if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) { /* let the callback function to process the input */ (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT, kbd->kb_callback.kc_arg); } else { /* read and discard the input; no one is waiting for input */ do { c = atkbd_read_char(kbd, FALSE); } while (c != NOKEY); if (!KBD_HAS_DEVICE(kbd)) { /* * The keyboard was not detected before; * it must have been reconnected! */ state = (atkbd_state_t *)kbd->kb_data; init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config); atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); get_typematic(kbd); delay[0] = kbd->kb_delay1; delay[1] = kbd->kb_delay2; atkbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay); KBD_FOUND_DEVICE(kbd); } } return 0; } /* test the interface to the device */ static int atkbd_test_if(keyboard_t *kbd) { int error; int s; error = 0; empty_both_buffers(((atkbd_state_t *)kbd->kb_data)->kbdc, 10); s = spltty(); if (!test_controller(((atkbd_state_t *)kbd->kb_data)->kbdc)) error = EIO; else if (test_kbd_port(((atkbd_state_t *)kbd->kb_data)->kbdc) != 0) error = EIO; splx(s); return error; } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int atkbd_enable(keyboard_t *kbd) { int s; s = spltty(); KBD_ACTIVATE(kbd); splx(s); return 0; } /* disallow the access to the device */ static int atkbd_disable(keyboard_t *kbd) { int s; s = spltty(); KBD_DEACTIVATE(kbd); splx(s); return 0; } /* read one byte from the keyboard if it's allowed */ static int atkbd_read(keyboard_t *kbd, int wait) { int c; if (wait) c = read_kbd_data(((atkbd_state_t *)kbd->kb_data)->kbdc); else c = read_kbd_data_no_wait(((atkbd_state_t *)kbd->kb_data)->kbdc); if (c != -1) ++kbd->kb_count; return (KBD_IS_ACTIVE(kbd) ? c : -1); } /* check if data is waiting */ static int atkbd_check(keyboard_t *kbd) { if (!KBD_IS_ACTIVE(kbd)) return FALSE; return kbdc_data_ready(((atkbd_state_t *)kbd->kb_data)->kbdc); } /* read char from the keyboard */ static u_int atkbd_read_char(keyboard_t *kbd, int wait) { atkbd_state_t *state; u_int action; int scancode; int keycode; state = (atkbd_state_t *)kbd->kb_data; next_code: /* do we have a composed char to return? */ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) { action = state->ks_composed_char; state->ks_composed_char = 0; if (action > UCHAR_MAX) return ERRKEY; return action; } /* see if there is something in the keyboard port */ if (wait) { do { scancode = read_kbd_data(state->kbdc); } while (scancode == -1); } else { scancode = read_kbd_data_no_wait(state->kbdc); if (scancode == -1) return NOKEY; } ++kbd->kb_count; #if KBDIO_DEBUG >= 10 printf("atkbd_read_char(): scancode:0x%x\n", scancode); #endif /* return the byte as is for the K_RAW mode */ if (state->ks_mode == K_RAW) return scancode; /* translate the scan code into a keycode */ keycode = scancode & 0x7F; switch (state->ks_prefix) { case 0x00: /* normal scancode */ switch(scancode) { case 0xB8: /* left alt (compose key) released */ if (state->ks_flags & COMPOSE) { state->ks_flags &= ~COMPOSE; if (state->ks_composed_char > UCHAR_MAX) state->ks_composed_char = 0; } break; case 0x38: /* left alt (compose key) pressed */ if (!(state->ks_flags & COMPOSE)) { state->ks_flags |= COMPOSE; state->ks_composed_char = 0; } break; case 0xE0: case 0xE1: state->ks_prefix = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ state->ks_prefix = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; case 0x35: /* keypad divide key */ keycode = 0x5B; break; case 0x37: /* print scrn key */ keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ keycode = 0x5D; break; case 0x46: /* ctrl-pause/break on AT 101 (see below) */ keycode = 0x68; break; case 0x47: /* grey home key */ keycode = 0x5E; break; case 0x48: /* grey up arrow key */ keycode = 0x5F; break; case 0x49: /* grey page up key */ keycode = 0x60; break; case 0x4B: /* grey left arrow key */ keycode = 0x61; break; case 0x4D: /* grey right arrow key */ keycode = 0x62; break; case 0x4F: /* grey end key */ keycode = 0x63; break; case 0x50: /* grey down arrow key */ keycode = 0x64; break; case 0x51: /* grey page down key */ keycode = 0x65; break; case 0x52: /* grey insert key */ keycode = 0x66; break; case 0x53: /* grey delete key */ keycode = 0x67; break; /* the following 3 are only used on the MS "Natural" keyboard */ case 0x5b: /* left Window key */ keycode = 0x69; break; case 0x5c: /* right Window key */ keycode = 0x6a; break; case 0x5d: /* menu key */ keycode = 0x6b; break; default: /* ignore everything else */ goto next_code; } break; case 0xE1: /* 0xE1 prefix */ /* * The pause/break key on the 101 keyboard produces: * E1-1D-45 E1-9D-C5 * Ctrl-pause/break produces: * E0-46 E0-C6 (See above.) */ state->ks_prefix = 0; if (keycode == 0x1D) state->ks_prefix = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ state->ks_prefix = 0; if (keycode != 0x45) goto next_code; keycode = 0x68; break; } if (kbd->kb_type == KB_84) { switch (keycode) { case 0x37: /* *(numpad)/print screen */ if (state->ks_flags & SHIFTS) keycode = 0x5c; /* print screen */ break; case 0x45: /* num lock/pause */ if (state->ks_flags & CTLS) keycode = 0x68; /* pause */ break; case 0x46: /* scroll lock/break */ if (state->ks_flags & CTLS) keycode = 0x6c; /* break */ break; } } else if (kbd->kb_type == KB_101) { switch (keycode) { case 0x5c: /* print screen */ if (state->ks_flags & ALTS) keycode = 0x54; /* sysrq */ break; case 0x68: /* pause/break */ if (state->ks_flags & CTLS) keycode = 0x6c; /* break */ break; } } /* return the key code in the K_CODE mode */ if (state->ks_mode == K_CODE) return (keycode | (scancode & 0x80)); /* compose a character code */ if (state->ks_flags & COMPOSE) { switch (keycode | (scancode & 0x80)) { /* key pressed, process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ state->ks_composed_char *= 10; state->ks_composed_char += keycode - 0x40; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ state->ks_composed_char *= 10; state->ks_composed_char += keycode - 0x47; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ state->ks_composed_char *= 10; state->ks_composed_char += keycode - 0x4E; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x52: /* keypad 0 */ state->ks_composed_char *= 10; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; /* key released, no interest here */ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ case 0xD2: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (state->ks_composed_char > 0) { state->ks_flags &= ~COMPOSE; state->ks_composed_char = 0; return ERRKEY; } break; } } /* keycode to key action */ action = genkbd_keyaction(kbd, keycode, scancode & 0x80, &state->ks_state, &state->ks_accents); if (action == NOKEY) goto next_code; else return action; } /* check if char is waiting */ static int atkbd_check_char(keyboard_t *kbd) { atkbd_state_t *state; if (!KBD_IS_ACTIVE(kbd)) return FALSE; state = (atkbd_state_t *)kbd->kb_data; if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) return TRUE; return kbdc_data_ready(state->kbdc); } /* some useful control functions */ static int atkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { /* trasnlate LED_XXX bits into the device specific bits */ static u_char ledmap[8] = { 0, 4, 2, 6, 1, 5, 3, 7, }; atkbd_state_t *state = kbd->kb_data; int error; int s; int i; s = spltty(); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = state->ks_mode; break; case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (state->ks_mode != K_XLATE) { /* make lock key state and LED state match */ state->ks_state &= ~LOCK_MASK; state->ks_state |= KBD_LED_VAL(kbd); } /* FALL THROUGH */ case K_RAW: case K_CODE: if (state->ks_mode != *(int *)arg) { atkbd_clear_state(kbd); state->ks_mode = *(int *)arg; } break; default: splx(s); return EINVAL; } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in ks_state won't be changed */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } if (KBD_HAS_DEVICE(kbd)) { error = write_kbd(state->kbdc, KBDC_SET_LEDS, ledmap[i & LED_MASK]); if (error) { splx(s); return error; } } KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = state->ks_state & LOCK_MASK; break; case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } state->ks_state &= ~LOCK_MASK; state->ks_state |= *(int *)arg; splx(s); /* set LEDs and quit */ return atkbd_ioctl(kbd, KDSETLED, arg); case KDSETREPEAT: /* set keyboard repeat rate (new interface) */ splx(s); if (!KBD_HAS_DEVICE(kbd)) return 0; i = typematic(((int *)arg)[0], ((int *)arg)[1]); error = write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, i); if (error == 0) { kbd->kb_delay1 = typematic_delay(i); kbd->kb_delay2 = typematic_rate(i); } return error; case KDSETRAD: /* set keyboard repeat rate (old interface) */ splx(s); if (!KBD_HAS_DEVICE(kbd)) return 0; error = write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, *(int *)arg); if (error == 0) { kbd->kb_delay1 = typematic_delay(*(int *)arg); kbd->kb_delay2 = typematic_rate(*(int *)arg); } return error; case PIO_KEYMAP: /* set keyboard translation table */ case PIO_KEYMAPENT: /* set keyboard translation table entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ state->ks_accents = 0; /* FALL THROUGH */ default: splx(s); return genkbd_commonioctl(kbd, cmd, arg); } splx(s); return 0; } /* lock the access to the keyboard */ static int atkbd_lock(keyboard_t *kbd, int lock) { return kbdc_lock(((atkbd_state_t *)kbd->kb_data)->kbdc, lock); } /* clear the internal state of the keyboard */ static void atkbd_clear_state(keyboard_t *kbd) { atkbd_state_t *state; state = (atkbd_state_t *)kbd->kb_data; state->ks_flags = 0; state->ks_polling = 0; state->ks_state &= LOCK_MASK; /* preserve locking key state */ state->ks_accents = 0; state->ks_composed_char = 0; #if 0 state->ks_prefix = 0; /* XXX */ #endif } /* save the internal state */ static int atkbd_get_state(keyboard_t *kbd, void *buf, size_t len) { if (len == 0) return sizeof(atkbd_state_t); if (len < sizeof(atkbd_state_t)) return -1; bcopy(kbd->kb_data, buf, sizeof(atkbd_state_t)); return 0; } /* set the internal state */ static int atkbd_set_state(keyboard_t *kbd, void *buf, size_t len) { if (len < sizeof(atkbd_state_t)) return ENOMEM; if (((atkbd_state_t *)kbd->kb_data)->kbdc != ((atkbd_state_t *)buf)->kbdc) return ENOMEM; bcopy(buf, kbd->kb_data, sizeof(atkbd_state_t)); return 0; } static int atkbd_poll(keyboard_t *kbd, int on) { atkbd_state_t *state; int s; state = (atkbd_state_t *)kbd->kb_data; s = spltty(); if (on) ++state->ks_polling; else --state->ks_polling; splx(s); return 0; } /* local functions */ static int get_typematic(keyboard_t *kbd) { #ifdef __i386__ /* * Only some systems allow us to retrieve the keyboard repeat * rate previously set via the BIOS... */ struct vm86frame vmf; u_int32_t p; bzero(&vmf, sizeof(vmf)); vmf.vmf_ax = 0xc000; vm86_intcall(0x15, &vmf); if ((vmf.vmf_eflags & PSL_C) || vmf.vmf_ah) return ENODEV; p = BIOS_PADDRTOVADDR(((u_int32_t)vmf.vmf_es << 4) + vmf.vmf_bx); if ((readb(p + 6) & 0x40) == 0) /* int 16, function 0x09 supported? */ return ENODEV; vmf.vmf_ax = 0x0900; vm86_intcall(0x16, &vmf); if ((vmf.vmf_al & 0x08) == 0) /* int 16, function 0x0306 supported? */ return ENODEV; vmf.vmf_ax = 0x0306; vm86_intcall(0x16, &vmf); kbd->kb_delay1 = typematic_delay(vmf.vmf_bh << 5); kbd->kb_delay2 = typematic_rate(vmf.vmf_bl); return 0; #else return ENODEV; #endif /* __i386__ */ } static int setup_kbd_port(KBDC kbdc, int port, int intr) { if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS, ((port) ? KBD_ENABLE_KBD_PORT : KBD_DISABLE_KBD_PORT) | ((intr) ? KBD_ENABLE_KBD_INT : KBD_DISABLE_KBD_INT))) return 1; return 0; } static int get_kbd_echo(KBDC kbdc) { /* enable the keyboard port, but disable the keyboard intr. */ if (setup_kbd_port(kbdc, TRUE, FALSE)) /* CONTROLLER ERROR: there is very little we can do... */ return ENXIO; /* see if something is present */ write_kbd_command(kbdc, KBDC_ECHO); if (read_kbd_data(kbdc) != KBD_ECHO) { empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); return ENXIO; } /* enable the keyboard port and intr. */ if (setup_kbd_port(kbdc, TRUE, TRUE)) { /* * CONTROLLER ERROR * This is serious; the keyboard intr is left disabled! */ return ENXIO; } return 0; } static int probe_keyboard(KBDC kbdc, int flags) { /* * Don't try to print anything in this function. The low-level * console may not have been initialized yet... */ int err; int c; int m; if (!kbdc_lock(kbdc, TRUE)) { /* driver error? */ return ENXIO; } /* flush any noise in the buffer */ empty_both_buffers(kbdc, 10); /* save the current keyboard controller command byte */ m = kbdc_get_device_mask(kbdc) & ~KBD_KBD_CONTROL_BITS; c = get_controller_command_byte(kbdc); if (c == -1) { /* CONTROLLER ERROR */ kbdc_set_device_mask(kbdc, m); kbdc_lock(kbdc, FALSE); return ENXIO; } /* * The keyboard may have been screwed up by the boot block. * We may just be able to recover from error by testing the controller * and the keyboard port. The controller command byte needs to be * saved before this recovery operation, as some controllers seem * to set the command byte to particular values. */ test_controller(kbdc); test_kbd_port(kbdc); err = get_kbd_echo(kbdc); if (err == 0) { kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS); } else { if (c != -1) /* try to restore the command byte as before */ set_controller_command_byte(kbdc, 0xff, c); kbdc_set_device_mask(kbdc, m); } kbdc_lock(kbdc, FALSE); return err; } static int init_keyboard(KBDC kbdc, int *type, int flags) { int codeset; int id; int c; if (!kbdc_lock(kbdc, TRUE)) { /* driver error? */ return EIO; } /* save the current controller command byte */ empty_both_buffers(kbdc, 200); c = get_controller_command_byte(kbdc); if (c == -1) { /* CONTROLLER ERROR */ kbdc_lock(kbdc, FALSE); printf("atkbd: unable to get the current command byte value.\n"); return EIO; } if (bootverbose) printf("atkbd: the current kbd controller command byte %04x\n", c); #if 0 /* override the keyboard lock switch */ c |= KBD_OVERRIDE_KBD_LOCK; #endif /* enable the keyboard port, but disable the keyboard intr. */ if (setup_kbd_port(kbdc, TRUE, FALSE)) { /* CONTROLLER ERROR: there is very little we can do... */ printf("atkbd: unable to set the command byte.\n"); kbdc_lock(kbdc, FALSE); return EIO; } /* * Check if we have an XT keyboard before we attempt to reset it. * The procedure assumes that the keyboard and the controller have * been set up properly by BIOS and have not been messed up * during the boot process. */ codeset = -1; if (flags & KB_CONF_ALT_SCANCODESET) /* the user says there is a XT keyboard */ codeset = 1; #ifdef KBD_DETECT_XT_KEYBOARD else if ((c & KBD_TRANSLATION) == 0) { /* SET_SCANCODE_SET is not always supported; ignore error */ if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, 0) == KBD_ACK) codeset = read_kbd_data(kbdc); } if (bootverbose) printf("atkbd: scancode set %d\n", codeset); #endif /* KBD_DETECT_XT_KEYBOARD */ *type = KB_OTHER; id = get_kbd_id(kbdc); switch(id) { case 0x41ab: /* 101/102/... Enhanced */ case 0x83ab: /* ditto */ case 0x54ab: /* SpaceSaver */ case 0x84ab: /* ditto */ #if 0 case 0x90ab: /* 'G' */ case 0x91ab: /* 'P' */ case 0x92ab: /* 'A' */ #endif *type = KB_101; break; case -1: /* AT 84 keyboard doesn't return ID */ *type = KB_84; break; default: break; } if (bootverbose) printf("atkbd: keyboard ID 0x%x (%d)\n", id, *type); /* reset keyboard hardware */ if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) { /* * KEYBOARD ERROR * Keyboard reset may fail either because the keyboard * doen't exist, or because the keyboard doesn't pass * the self-test, or the keyboard controller on the * motherboard and the keyboard somehow fail to shake hands. * It is just possible, particularly in the last case, * that the keyoard controller may be left in a hung state. * test_controller() and test_kbd_port() appear to bring * the keyboard controller back (I don't know why and how, * though.) */ empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); /* * We could disable the keyboard port and interrupt... but, * the keyboard may still exist (see above). */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); if (bootverbose) printf("atkbd: failed to reset the keyboard.\n"); return EIO; } /* * Allow us to set the XT_KEYBD flag in UserConfig so that keyboards * such as those on the IBM ThinkPad laptop computers can be used * with the standard console driver. */ if (codeset == 1) { if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, codeset) == KBD_ACK) { /* XT kbd doesn't need scan code translation */ c &= ~KBD_TRANSLATION; } else { /* * KEYBOARD ERROR * The XT kbd isn't usable unless the proper scan * code set is selected. */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); printf("atkbd: unable to set the XT keyboard mode.\n"); return EIO; } } #ifdef __alpha__ if (send_kbd_command_and_data( kbdc, KBDC_SET_SCANCODE_SET, 2) != KBD_ACK) { printf("atkbd: can't set translation.\n"); } c |= KBD_TRANSLATION; #endif /* enable the keyboard port and intr. */ if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK, (c & (KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK)) | KBD_ENABLE_KBD_PORT | KBD_ENABLE_KBD_INT)) { /* * CONTROLLER ERROR * This is serious; we are left with the disabled * keyboard intr. */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); printf("atkbd: unable to enable the keyboard port and intr.\n"); return EIO; } kbdc_lock(kbdc, FALSE); return 0; } static int write_kbd(KBDC kbdc, int command, int data) { int s; /* prevent the timeout routine from polling the keyboard */ if (!kbdc_lock(kbdc, TRUE)) return EBUSY; /* disable the keyboard and mouse interrupt */ s = spltty(); #if 0 c = get_controller_command_byte(kbdc); if ((c == -1) || !set_controller_command_byte(kbdc, kbdc_get_device_mask(kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ kbdc_lock(kbdc, FALSE); splx(s); return EIO; } /* * Now that the keyboard controller is told not to generate * the keyboard and mouse interrupts, call `splx()' to allow * the other tty interrupts. The clock interrupt may also occur, * but the timeout routine (`scrn_timer()') will be blocked * by the lock flag set via `kbdc_lock()' */ splx(s); #endif if (send_kbd_command_and_data(kbdc, command, data) != KBD_ACK) send_kbd_command(kbdc, KBDC_ENABLE_KBD); #if 0 /* restore the interrupts */ if (!set_controller_command_byte(kbdc, kbdc_get_device_mask(kbdc), c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR */ } #else splx(s); #endif kbdc_lock(kbdc, FALSE); return 0; } static int get_kbd_id(KBDC kbdc) { int id1, id2; empty_both_buffers(kbdc, 10); id1 = id2 = -1; if (send_kbd_command(kbdc, KBDC_SEND_DEV_ID) != KBD_ACK) return -1; DELAY(10000); /* 10 msec delay */ id1 = read_kbd_data(kbdc); if (id1 != -1) id2 = read_kbd_data(kbdc); if ((id1 == -1) || (id2 == -1)) { empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); return -1; } return ((id2 << 8) | id1); } static int delays[] = { 250, 500, 750, 1000 }; static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504 }; static int typematic_delay(int i) { return delays[(i >> 5) & 3]; } static int typematic_rate(int i) { return rates[i & 0x1f]; } static int typematic(int delay, int rate) { int value; int i; for (i = sizeof(delays)/sizeof(delays[0]) - 1; i > 0; --i) { if (delay >= delays[i]) break; } value = i << 5; for (i = sizeof(rates)/sizeof(rates[0]) - 1; i > 0; --i) { if (rate >= rates[i]) break; } value |= i; return value; } - -#endif /* NATKBD > 0 */ Index: head/sys/dev/atkbdc/atkbd_atkbdc.c =================================================================== --- head/sys/dev/atkbdc/atkbd_atkbdc.c (revision 56835) +++ head/sys/dev/atkbdc/atkbd_atkbdc.c (revision 56836) @@ -1,128 +1,123 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "atkbd.h" #include "opt_kbd.h" -#if NATKBD > 0 - #include #include #include #include #include #include #include #include #include #include #include #include #include #include devclass_t atkbd_devclass; static int atkbdprobe(device_t dev); static int atkbdattach(device_t dev); static void atkbd_isa_intr(void *arg); static device_method_t atkbd_methods[] = { DEVMETHOD(device_probe, atkbdprobe), DEVMETHOD(device_attach, atkbdattach), { 0, 0 } }; static driver_t atkbd_driver = { ATKBD_DRIVER_NAME, atkbd_methods, 1, }; static int atkbdprobe(device_t dev) { uintptr_t port; uintptr_t irq; uintptr_t flags; device_set_desc(dev, "AT Keyboard"); /* obtain parameters */ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); /* probe the device */ return atkbd_probe_unit(device_get_unit(dev), port, irq, flags); } static int atkbdattach(device_t dev) { keyboard_t *kbd; uintptr_t port; uintptr_t irq; uintptr_t flags; struct resource *res; void *ih; int zero = 0; int error; BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); error = atkbd_attach_unit(device_get_unit(dev), &kbd, port, irq, flags); if (error) return error; /* declare our interrupt handler */ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, INTR_TYPE_TTY, atkbd_isa_intr, kbd, &ih); return 0; } static void atkbd_isa_intr(void *arg) { keyboard_t *kbd; kbd = (keyboard_t *)arg; (*kbdsw[kbd->kb_index]->intr)(kbd, NULL); } DRIVER_MODULE(atkbd, atkbdc, atkbd_driver, atkbd_devclass, 0, 0); - -#endif /* NATKBD > 0 */ Index: head/sys/dev/atkbdc/atkbd_isa.c =================================================================== --- head/sys/dev/atkbdc/atkbd_isa.c (revision 56835) +++ head/sys/dev/atkbdc/atkbd_isa.c (revision 56836) @@ -1,128 +1,123 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "atkbd.h" #include "opt_kbd.h" -#if NATKBD > 0 - #include #include #include #include #include #include #include #include #include #include #include #include #include #include devclass_t atkbd_devclass; static int atkbdprobe(device_t dev); static int atkbdattach(device_t dev); static void atkbd_isa_intr(void *arg); static device_method_t atkbd_methods[] = { DEVMETHOD(device_probe, atkbdprobe), DEVMETHOD(device_attach, atkbdattach), { 0, 0 } }; static driver_t atkbd_driver = { ATKBD_DRIVER_NAME, atkbd_methods, 1, }; static int atkbdprobe(device_t dev) { uintptr_t port; uintptr_t irq; uintptr_t flags; device_set_desc(dev, "AT Keyboard"); /* obtain parameters */ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); /* probe the device */ return atkbd_probe_unit(device_get_unit(dev), port, irq, flags); } static int atkbdattach(device_t dev) { keyboard_t *kbd; uintptr_t port; uintptr_t irq; uintptr_t flags; struct resource *res; void *ih; int zero = 0; int error; BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); error = atkbd_attach_unit(device_get_unit(dev), &kbd, port, irq, flags); if (error) return error; /* declare our interrupt handler */ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, INTR_TYPE_TTY, atkbd_isa_intr, kbd, &ih); return 0; } static void atkbd_isa_intr(void *arg) { keyboard_t *kbd; kbd = (keyboard_t *)arg; (*kbdsw[kbd->kb_index]->intr)(kbd, NULL); } DRIVER_MODULE(atkbd, atkbdc, atkbd_driver, atkbd_devclass, 0, 0); - -#endif /* NATKBD > 0 */ Index: head/sys/dev/atkbdc/atkbdc.c =================================================================== --- head/sys/dev/atkbdc/atkbdc.c (revision 56835) +++ head/sys/dev/atkbdc/atkbdc.c (revision 56836) @@ -1,1021 +1,1017 @@ /*- * Copyright (c) 1996-1999 * Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * from kbdio.c,v 1.13 1998/09/25 11:55:46 yokota Exp */ #include "atkbdc.h" #include "opt_kbd.h" #include #include #include #include #include #include #include #include /* constants */ -#define MAXKBDC MAX(NATKBDC, 1) +#define MAXKBDC MAX(NATKBDC, 1) /* XXX */ /* macros */ #ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif #define kbdcp(p) ((atkbdc_softc_t *)(p)) #define nextq(i) (((i) + 1) % KBDQ_BUFSIZE) #define availq(q) ((q)->head != (q)->tail) #if KBDIO_DEBUG >= 2 #define emptyq(q) ((q)->tail = (q)->head = (q)->qcount = 0) #else #define emptyq(q) ((q)->tail = (q)->head = 0) #endif /* local variables */ /* * We always need at least one copy of the kbdc_softc struct for the * low-level console. As the low-level console accesses the keyboard * controller before kbdc, and all other devices, is probed, we * statically allocate one entry. XXX */ static atkbdc_softc_t default_kbdc; static atkbdc_softc_t *atkbdc_softc[MAXKBDC] = { &default_kbdc }; static int verbose = KBDIO_DEBUG; /* function prototypes */ static int atkbdc_setup(atkbdc_softc_t *sc, int port); static int addq(kqueue *q, int c); static int removeq(kqueue *q); static int wait_while_controller_busy(atkbdc_softc_t *kbdc); static int wait_for_data(atkbdc_softc_t *kbdc); static int wait_for_kbd_data(atkbdc_softc_t *kbdc); static int wait_for_kbd_ack(atkbdc_softc_t *kbdc); static int wait_for_aux_data(atkbdc_softc_t *kbdc); static int wait_for_aux_ack(atkbdc_softc_t *kbdc); -#if NATKBDC > 0 - atkbdc_softc_t *atkbdc_get_softc(int unit) { atkbdc_softc_t *sc; if (unit >= sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0])) return NULL; sc = atkbdc_softc[unit]; if (sc == NULL) { sc = atkbdc_softc[unit] = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); if (sc == NULL) return NULL; bzero(sc, sizeof(*sc)); sc->port = -1; /* XXX */ } return sc; } int atkbdc_probe_unit(int unit, int port) { if (port <= 0) return ENXIO; return 0; } int atkbdc_attach_unit(int unit, atkbdc_softc_t *sc, int port) { return atkbdc_setup(sc, port); } - -#endif /* NATKBDC > 0 */ /* the backdoor to the keyboard controller! XXX */ int atkbdc_configure(void) { return atkbdc_setup(atkbdc_softc[0], -1); } static int atkbdc_setup(atkbdc_softc_t *sc, int port) { if (port <= 0) port = IO_KBD; if (sc->port <= 0) { sc->command_byte = -1; sc->command_mask = 0; sc->lock = FALSE; sc->kbd.head = sc->kbd.tail = 0; sc->aux.head = sc->aux.tail = 0; #if KBDIO_DEBUG >= 2 sc->kbd.call_count = 0; sc->kbd.qcount = sc->kbd.max_qcount = 0; sc->aux.call_count = 0; sc->aux.qcount = sc->aux.max_qcount = 0; #endif } sc->port = port; /* may override the previous value */ return 0; } /* associate a port number with a KBDC */ KBDC kbdc_open(int port) { int s; int i; if (port <= 0) port = IO_KBD; s = spltty(); for (i = 0; i < sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0]); ++i) { if (atkbdc_softc[i] == NULL) continue; if (atkbdc_softc[i]->port == port) { splx(s); return (KBDC)atkbdc_softc[i]; } if (atkbdc_softc[i]->port <= 0) { if (atkbdc_setup(atkbdc_softc[i], port)) break; splx(s); return (KBDC)atkbdc_softc[i]; } } splx(s); return NULL; } /* * I/O access arbitration in `kbdio' * * The `kbdio' module uses a simplistic convention to arbitrate * I/O access to the controller/keyboard/mouse. The convention requires * close cooperation of the calling device driver. * * The device driver which utilizes the `kbdio' module are assumed to * have the following set of routines. * a. An interrupt handler (the bottom half of the driver). * b. Timeout routines which may briefly polls the keyboard controller. * c. Routines outside interrupt context (the top half of the driver). * They should follow the rules below: * 1. The interrupt handler may assume that it always has full access * to the controller/keyboard/mouse. * 2. The other routines must issue `spltty()' if they wish to * prevent the interrupt handler from accessing * the controller/keyboard/mouse. * 3. The timeout routines and the top half routines of the device driver * arbitrate I/O access by observing the lock flag in `kbdio'. * The flag is manipulated via `kbdc_lock()'; when one wants to * perform I/O, call `kbdc_lock(kbdc, TRUE)' and proceed only if * the call returns with TRUE. Otherwise the caller must back off. * Call `kbdc_lock(kbdc, FALSE)' when necessary I/O operaion * is finished. This mechanism does not prevent the interrupt * handler from being invoked at any time and carrying out I/O. * Therefore, `spltty()' must be strategically placed in the device * driver code. Also note that the timeout routine may interrupt * `kbdc_lock()' called by the top half of the driver, but this * interruption is OK so long as the timeout routine observes the * the rule 4 below. * 4. The interrupt and timeout routines should not extend I/O operation * across more than one interrupt or timeout; they must complete * necessary I/O operation within one invokation of the routine. * This measns that if the timeout routine acquires the lock flag, * it must reset the flag to FALSE before it returns. */ /* set/reset polling lock */ int kbdc_lock(KBDC p, int lock) { int prevlock; prevlock = kbdcp(p)->lock; kbdcp(p)->lock = lock; return (prevlock != lock); } /* check if any data is waiting to be processed */ int kbdc_data_ready(KBDC p) { return (availq(&kbdcp(p)->kbd) || availq(&kbdcp(p)->aux) || (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)); } /* queuing functions */ static int addq(kqueue *q, int c) { if (nextq(q->tail) != q->head) { q->q[q->tail] = c; q->tail = nextq(q->tail); #if KBDIO_DEBUG >= 2 ++q->call_count; ++q->qcount; if (q->qcount > q->max_qcount) q->max_qcount = q->qcount; #endif return TRUE; } return FALSE; } static int removeq(kqueue *q) { int c; if (q->tail != q->head) { c = q->q[q->head]; q->head = nextq(q->head); #if KBDIO_DEBUG >= 2 --q->qcount; #endif return c; } return -1; } /* * device I/O routines */ static int wait_while_controller_busy(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 100msec at most */ int retry = 5000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT)) & KBDS_INPUT_BUFFER_FULL) { if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->kbd, inb(port + KBD_DATA_PORT)); } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->aux, inb(port + KBD_DATA_PORT)); } DELAY(KBDC_DELAYTIME); if (--retry < 0) return FALSE; } return TRUE; } /* * wait for any data; whether it's from the controller, * the keyboard, or the aux device. */ static int wait_for_data(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) == 0) { DELAY(KBDC_DELAYTIME); if (--retry < 0) return 0; } DELAY(KBDD_DELAYTIME); return f; } /* wait for data from the keyboard */ static int wait_for_kbd_data(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) != KBDS_KBD_BUFFER_FULL) { if (f == KBDS_AUX_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->aux, inb(port + KBD_DATA_PORT)); } DELAY(KBDC_DELAYTIME); if (--retry < 0) return 0; } DELAY(KBDD_DELAYTIME); return f; } /* * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the keyboard. * queue anything else. */ static int wait_for_kbd_ack(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; int b; while (retry-- > 0) { if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); b = inb(port + KBD_DATA_PORT); if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { if ((b == KBD_ACK) || (b == KBD_RESEND) || (b == KBD_RESET_FAIL)) return b; addq(&kbdc->kbd, b); } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { addq(&kbdc->aux, b); } } DELAY(KBDC_DELAYTIME); } return -1; } /* wait for data from the aux device */ static int wait_for_aux_data(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) != KBDS_AUX_BUFFER_FULL) { if (f == KBDS_KBD_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->kbd, inb(port + KBD_DATA_PORT)); } DELAY(KBDC_DELAYTIME); if (--retry < 0) return 0; } DELAY(KBDD_DELAYTIME); return f; } /* * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the aux device. * queue anything else. */ static int wait_for_aux_ack(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; int b; while (retry-- > 0) { if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); b = inb(port + KBD_DATA_PORT); if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { if ((b == PSM_ACK) || (b == PSM_RESEND) || (b == PSM_RESET_FAIL)) return b; addq(&kbdc->aux, b); } else if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { addq(&kbdc->kbd, b); } } DELAY(KBDC_DELAYTIME); } return -1; } /* write a one byte command to the controller */ int write_controller_command(KBDC p, int c) { if (!wait_while_controller_busy(kbdcp(p))) return FALSE; outb(kbdcp(p)->port + KBD_COMMAND_PORT, c); return TRUE; } /* write a one byte data to the controller */ int write_controller_data(KBDC p, int c) { if (!wait_while_controller_busy(kbdcp(p))) return FALSE; outb(kbdcp(p)->port + KBD_DATA_PORT, c); return TRUE; } /* write a one byte keyboard command */ int write_kbd_command(KBDC p, int c) { if (!wait_while_controller_busy(kbdcp(p))) return FALSE; outb(kbdcp(p)->port + KBD_DATA_PORT, c); return TRUE; } /* write a one byte auxiliary device command */ int write_aux_command(KBDC p, int c) { if (!write_controller_command(p, KBDC_WRITE_TO_AUX)) return FALSE; return write_controller_data(p, c); } /* send a command to the keyboard and wait for ACK */ int send_kbd_command(KBDC p, int c) { int retry = KBD_MAXRETRY; int res = -1; while (retry-- > 0) { if (!write_kbd_command(p, c)) continue; res = wait_for_kbd_ack(kbdcp(p)); if (res == KBD_ACK) break; } return res; } /* send a command to the auxiliary device and wait for ACK */ int send_aux_command(KBDC p, int c) { int retry = KBD_MAXRETRY; int res = -1; while (retry-- > 0) { if (!write_aux_command(p, c)) continue; /* * FIXME: XXX * The aux device may have already sent one or two bytes of * status data, when a command is received. It will immediately * stop data transmission, thus, leaving an incomplete data * packet in our buffer. We have to discard any unprocessed * data in order to remove such packets. Well, we may remove * unprocessed, but necessary data byte as well... */ emptyq(&kbdcp(p)->aux); res = wait_for_aux_ack(kbdcp(p)); if (res == PSM_ACK) break; } return res; } /* send a command and a data to the keyboard, wait for ACKs */ int send_kbd_command_and_data(KBDC p, int c, int d) { int retry; int res = -1; for (retry = KBD_MAXRETRY; retry > 0; --retry) { if (!write_kbd_command(p, c)) continue; res = wait_for_kbd_ack(kbdcp(p)); if (res == KBD_ACK) break; else if (res != KBD_RESEND) return res; } if (retry <= 0) return res; for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) { if (!write_kbd_command(p, d)) continue; res = wait_for_kbd_ack(kbdcp(p)); if (res != KBD_RESEND) break; } return res; } /* send a command and a data to the auxiliary device, wait for ACKs */ int send_aux_command_and_data(KBDC p, int c, int d) { int retry; int res = -1; for (retry = KBD_MAXRETRY; retry > 0; --retry) { if (!write_aux_command(p, c)) continue; emptyq(&kbdcp(p)->aux); res = wait_for_aux_ack(kbdcp(p)); if (res == PSM_ACK) break; else if (res != PSM_RESEND) return res; } if (retry <= 0) return res; for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) { if (!write_aux_command(p, d)) continue; res = wait_for_aux_ack(kbdcp(p)); if (res != PSM_RESEND) break; } return res; } /* * read one byte from any source; whether from the controller, * the keyboard, or the aux device */ int read_controller_data(KBDC p) { if (availq(&kbdcp(p)->kbd)) return removeq(&kbdcp(p)->kbd); if (availq(&kbdcp(p)->aux)) return removeq(&kbdcp(p)->aux); if (!wait_for_data(kbdcp(p))) return -1; /* timeout */ return inb(kbdcp(p)->port + KBD_DATA_PORT); } #if KBDIO_DEBUG >= 2 static int call = 0; #endif /* read one byte from the keyboard */ int read_kbd_data(KBDC p) { #if KBDIO_DEBUG >= 2 if (++call > 2000) { call = 0; log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, " "aux q: %d calls, max %d chars\n", kbdcp(p)->kbd.call_count, kbdcp(p)->kbd.max_qcount, kbdcp(p)->aux.call_count, kbdcp(p)->aux.max_qcount); } #endif if (availq(&kbdcp(p)->kbd)) return removeq(&kbdcp(p)->kbd); if (!wait_for_kbd_data(kbdcp(p))) return -1; /* timeout */ return inb(kbdcp(p)->port + KBD_DATA_PORT); } /* read one byte from the keyboard, but return immediately if * no data is waiting */ int read_kbd_data_no_wait(KBDC p) { int f; #if KBDIO_DEBUG >= 2 if (++call > 2000) { call = 0; log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, " "aux q: %d calls, max %d chars\n", kbdcp(p)->kbd.call_count, kbdcp(p)->kbd.max_qcount, kbdcp(p)->aux.call_count, kbdcp(p)->aux.max_qcount); } #endif if (availq(&kbdcp(p)->kbd)) return removeq(&kbdcp(p)->kbd); f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; if (f == KBDS_AUX_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdcp(p)->aux, inb(kbdcp(p)->port + KBD_DATA_PORT)); f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; } if (f == KBDS_KBD_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); return inb(kbdcp(p)->port + KBD_DATA_PORT); } return -1; /* no data */ } /* read one byte from the aux device */ int read_aux_data(KBDC p) { if (availq(&kbdcp(p)->aux)) return removeq(&kbdcp(p)->aux); if (!wait_for_aux_data(kbdcp(p))) return -1; /* timeout */ return inb(kbdcp(p)->port + KBD_DATA_PORT); } /* read one byte from the aux device, but return immediately if * no data is waiting */ int read_aux_data_no_wait(KBDC p) { int f; if (availq(&kbdcp(p)->aux)) return removeq(&kbdcp(p)->aux); f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; if (f == KBDS_KBD_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdcp(p)->kbd, inb(kbdcp(p)->port + KBD_DATA_PORT)); f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; } if (f == KBDS_AUX_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); return inb(kbdcp(p)->port + KBD_DATA_PORT); } return -1; /* no data */ } /* discard data from the keyboard */ void empty_kbd_buffer(KBDC p, int wait) { int t; int b; int f; #if KBDIO_DEBUG >= 2 int c1 = 0; int c2 = 0; #endif int delta = 2; for (t = wait; t > 0; ) { if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); b = inb(kbdcp(p)->port + KBD_DATA_PORT); if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { addq(&kbdcp(p)->aux, b); #if KBDIO_DEBUG >= 2 ++c2; } else { ++c1; #endif } t = wait; } else { t -= delta; } DELAY(delta*1000); } #if KBDIO_DEBUG >= 2 if ((c1 > 0) || (c2 > 0)) log(LOG_DEBUG, "kbdc: %d:%d char read (empty_kbd_buffer)\n", c1, c2); #endif emptyq(&kbdcp(p)->kbd); } /* discard data from the aux device */ void empty_aux_buffer(KBDC p, int wait) { int t; int b; int f; #if KBDIO_DEBUG >= 2 int c1 = 0; int c2 = 0; #endif int delta = 2; for (t = wait; t > 0; ) { if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); b = inb(kbdcp(p)->port + KBD_DATA_PORT); if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { addq(&kbdcp(p)->kbd, b); #if KBDIO_DEBUG >= 2 ++c1; } else { ++c2; #endif } t = wait; } else { t -= delta; } DELAY(delta*1000); } #if KBDIO_DEBUG >= 2 if ((c1 > 0) || (c2 > 0)) log(LOG_DEBUG, "kbdc: %d:%d char read (empty_aux_buffer)\n", c1, c2); #endif emptyq(&kbdcp(p)->aux); } /* discard any data from the keyboard or the aux device */ void empty_both_buffers(KBDC p, int wait) { int t; int f; #if KBDIO_DEBUG >= 2 int c1 = 0; int c2 = 0; #endif int delta = 2; for (t = wait; t > 0; ) { if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); (void)inb(kbdcp(p)->port + KBD_DATA_PORT); #if KBDIO_DEBUG >= 2 if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) ++c1; else ++c2; #endif t = wait; } else { t -= delta; } DELAY(delta*1000); } #if KBDIO_DEBUG >= 2 if ((c1 > 0) || (c2 > 0)) log(LOG_DEBUG, "kbdc: %d:%d char read (empty_both_buffers)\n", c1, c2); #endif emptyq(&kbdcp(p)->kbd); emptyq(&kbdcp(p)->aux); } /* keyboard and mouse device control */ /* NOTE: enable the keyboard port but disable the keyboard * interrupt before calling "reset_kbd()". */ int reset_kbd(KBDC p) { int retry = KBD_MAXRETRY; int again = KBD_MAXWAIT; int c = KBD_RESEND; /* keep the compiler happy */ while (retry-- > 0) { empty_both_buffers(p, 10); if (!write_kbd_command(p, KBDC_RESET_KBD)) continue; emptyq(&kbdcp(p)->kbd); c = read_controller_data(p); if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: RESET_KBD return code:%04x\n", c); if (c == KBD_ACK) /* keyboard has agreed to reset itself... */ break; } if (retry < 0) return FALSE; while (again-- > 0) { /* wait awhile, well, in fact we must wait quite loooooooooooong */ DELAY(KBD_RESETDELAY*1000); c = read_controller_data(p); /* RESET_DONE/RESET_FAIL */ if (c != -1) /* wait again if the controller is not ready */ break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: RESET_KBD status:%04x\n", c); if (c != KBD_RESET_DONE) return FALSE; return TRUE; } /* NOTE: enable the aux port but disable the aux interrupt * before calling `reset_aux_dev()'. */ int reset_aux_dev(KBDC p) { int retry = KBD_MAXRETRY; int again = KBD_MAXWAIT; int c = PSM_RESEND; /* keep the compiler happy */ while (retry-- > 0) { empty_both_buffers(p, 10); if (!write_aux_command(p, PSMC_RESET_DEV)) continue; emptyq(&kbdcp(p)->aux); /* NOTE: Compaq Armada laptops require extra delay here. XXX */ for (again = KBD_MAXWAIT; again > 0; --again) { DELAY(KBD_RESETDELAY*1000); c = read_aux_data_no_wait(p); if (c != -1) break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: RESET_AUX return code:%04x\n", c); if (c == PSM_ACK) /* aux dev is about to reset... */ break; } if (retry < 0) return FALSE; for (again = KBD_MAXWAIT; again > 0; --again) { /* wait awhile, well, quite looooooooooooong */ DELAY(KBD_RESETDELAY*1000); c = read_aux_data_no_wait(p); /* RESET_DONE/RESET_FAIL */ if (c != -1) /* wait again if the controller is not ready */ break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: RESET_AUX status:%04x\n", c); if (c != PSM_RESET_DONE) /* reset status */ return FALSE; c = read_aux_data(p); /* device ID */ if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: RESET_AUX ID:%04x\n", c); /* NOTE: we could check the device ID now, but leave it later... */ return TRUE; } /* controller diagnostics and setup */ int test_controller(KBDC p) { int retry = KBD_MAXRETRY; int again = KBD_MAXWAIT; int c = KBD_DIAG_FAIL; while (retry-- > 0) { empty_both_buffers(p, 10); if (write_controller_command(p, KBDC_DIAGNOSE)) break; } if (retry < 0) return FALSE; emptyq(&kbdcp(p)->kbd); while (again-- > 0) { /* wait awhile */ DELAY(KBD_RESETDELAY*1000); c = read_controller_data(p); /* DIAG_DONE/DIAG_FAIL */ if (c != -1) /* wait again if the controller is not ready */ break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: DIAGNOSE status:%04x\n", c); return (c == KBD_DIAG_DONE); } int test_kbd_port(KBDC p) { int retry = KBD_MAXRETRY; int again = KBD_MAXWAIT; int c = -1; while (retry-- > 0) { empty_both_buffers(p, 10); if (write_controller_command(p, KBDC_TEST_KBD_PORT)) break; } if (retry < 0) return FALSE; emptyq(&kbdcp(p)->kbd); while (again-- > 0) { c = read_controller_data(p); if (c != -1) /* try again if the controller is not ready */ break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: TEST_KBD_PORT status:%04x\n", c); return c; } int test_aux_port(KBDC p) { int retry = KBD_MAXRETRY; int again = KBD_MAXWAIT; int c = -1; while (retry-- > 0) { empty_both_buffers(p, 10); if (write_controller_command(p, KBDC_TEST_AUX_PORT)) break; } if (retry < 0) return FALSE; emptyq(&kbdcp(p)->kbd); while (again-- > 0) { c = read_controller_data(p); if (c != -1) /* try again if the controller is not ready */ break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: TEST_AUX_PORT status:%04x\n", c); return c; } int kbdc_get_device_mask(KBDC p) { return kbdcp(p)->command_mask; } void kbdc_set_device_mask(KBDC p, int mask) { kbdcp(p)->command_mask = mask & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS); } int get_controller_command_byte(KBDC p) { if (kbdcp(p)->command_byte != -1) return kbdcp(p)->command_byte; if (!write_controller_command(p, KBDC_GET_COMMAND_BYTE)) return -1; emptyq(&kbdcp(p)->kbd); kbdcp(p)->command_byte = read_controller_data(p); return kbdcp(p)->command_byte; } int set_controller_command_byte(KBDC p, int mask, int command) { if (get_controller_command_byte(p) == -1) return FALSE; command = (kbdcp(p)->command_byte & ~mask) | (command & mask); if (command & KBD_DISABLE_KBD_PORT) { if (!write_controller_command(p, KBDC_DISABLE_KBD_PORT)) return FALSE; } if (!write_controller_command(p, KBDC_SET_COMMAND_BYTE)) return FALSE; if (!write_controller_data(p, command)) return FALSE; kbdcp(p)->command_byte = command; if (verbose) log(LOG_DEBUG, "kbdc: new command byte:%04x (set_controller...)\n", command); return TRUE; } Index: head/sys/dev/atkbdc/atkbdc_isa.c =================================================================== --- head/sys/dev/atkbdc/atkbdc_isa.c (revision 56835) +++ head/sys/dev/atkbdc/atkbdc_isa.c (revision 56836) @@ -1,269 +1,264 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "atkbdc.h" #include "opt_kbd.h" -#if NATKBDC > 0 - #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device"); /* children */ typedef struct atkbdc_device { int flags; /* configuration flags */ int port; /* port number (same as the controller's) */ int irq; /* ISA IRQ mask */ } atkbdc_device_t; /* kbdc */ devclass_t atkbdc_devclass; static int atkbdc_probe(device_t dev); static int atkbdc_attach(device_t dev); static int atkbdc_print_child(device_t bus, device_t dev); static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val); static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val); static device_method_t atkbdc_methods[] = { DEVMETHOD(device_probe, atkbdc_probe), DEVMETHOD(device_attach, atkbdc_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(bus_print_child, atkbdc_print_child), DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t atkbdc_driver = { ATKBDC_DRIVER_NAME, atkbdc_methods, sizeof(atkbdc_softc_t *), }; static int atkbdc_probe(device_t dev) { int error; int rid; struct resource *port; /* Check isapnp ids */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "keyboard controller (i8042)"); rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_probe_unit(device_get_unit(dev), rman_get_start(port)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return error; } static void atkbdc_add_device(device_t dev, const char *name, int unit) { atkbdc_softc_t *sc = *(atkbdc_softc_t **)device_get_softc(dev); atkbdc_device_t *kdev; device_t child; int t; kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT); if (!kdev) return; bzero(kdev, sizeof *kdev); kdev->port = sc->port; if (resource_int_value(name, unit, "irq", &t) == 0) kdev->irq = t; else kdev->irq = -1; if (resource_int_value(name, unit, "flags", &t) == 0) kdev->flags = t; else kdev->flags = 0; child = device_add_child(dev, name, unit); device_set_ivars(child, kdev); } static int atkbdc_attach(device_t dev) { atkbdc_softc_t *sc; struct resource *port; int unit; int error; int rid; int i; unit = device_get_unit(dev); sc = *(atkbdc_softc_t **)device_get_softc(dev); if (sc == NULL) { /* * We have to maintain two copies of the kbdc_softc struct, * as the low-level console needs to have access to the * keyboard controller before kbdc is probed and attached. * kbdc_soft[] contains the default entry for that purpose. * See atkbdc.c. XXX */ sc = atkbdc_get_softc(unit); if (sc == NULL) return ENOMEM; } /* XXX should track resource in softc */ rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_attach_unit(unit, sc, rman_get_start(port)); if (error) return error; *(atkbdc_softc_t **)device_get_softc(dev) = sc; /* * Add all devices configured to be attached to atkbdc0. */ for (i = resource_query_string(-1, "at", "atkbdc0"); i != -1; i = resource_query_string(i, "at", "atkbdc0")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } /* * and atkbdc? */ for (i = resource_query_string(-1, "at", "atkbdc"); i != -1; i = resource_query_string(i, "at", "atkbdc")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } bus_generic_attach(dev); return 0; } static int atkbdc_print_child(device_t bus, device_t dev) { atkbdc_device_t *kbdcdev; int retval = 0; kbdcdev = (atkbdc_device_t *)device_get_ivars(dev); retval += bus_print_child_header(bus, dev); if (kbdcdev->flags != 0) retval += printf(" flags 0x%x", kbdcdev->flags); if (kbdcdev->irq != -1) retval += printf(" irq %d", kbdcdev->irq); retval += bus_print_child_footer(bus, dev); return (retval); } static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: *val = (u_long)ivar->port; break; case KBDC_IVAR_IRQ: *val = (u_long)ivar->irq; break; case KBDC_IVAR_FLAGS: *val = (u_long)ivar->flags; break; default: return ENOENT; } return 0; } static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: ivar->port = (int)val; break; case KBDC_IVAR_IRQ: ivar->irq = (int)val; break; case KBDC_IVAR_FLAGS: ivar->flags = (int)val; break; default: return ENOENT; } return 0; } DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0); - -#endif /* NATKBDC > 0 */ Index: head/sys/dev/atkbdc/atkbdc_subr.c =================================================================== --- head/sys/dev/atkbdc/atkbdc_subr.c (revision 56835) +++ head/sys/dev/atkbdc/atkbdc_subr.c (revision 56836) @@ -1,269 +1,264 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "atkbdc.h" #include "opt_kbd.h" -#if NATKBDC > 0 - #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device"); /* children */ typedef struct atkbdc_device { int flags; /* configuration flags */ int port; /* port number (same as the controller's) */ int irq; /* ISA IRQ mask */ } atkbdc_device_t; /* kbdc */ devclass_t atkbdc_devclass; static int atkbdc_probe(device_t dev); static int atkbdc_attach(device_t dev); static int atkbdc_print_child(device_t bus, device_t dev); static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val); static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val); static device_method_t atkbdc_methods[] = { DEVMETHOD(device_probe, atkbdc_probe), DEVMETHOD(device_attach, atkbdc_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(bus_print_child, atkbdc_print_child), DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t atkbdc_driver = { ATKBDC_DRIVER_NAME, atkbdc_methods, sizeof(atkbdc_softc_t *), }; static int atkbdc_probe(device_t dev) { int error; int rid; struct resource *port; /* Check isapnp ids */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "keyboard controller (i8042)"); rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_probe_unit(device_get_unit(dev), rman_get_start(port)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return error; } static void atkbdc_add_device(device_t dev, const char *name, int unit) { atkbdc_softc_t *sc = *(atkbdc_softc_t **)device_get_softc(dev); atkbdc_device_t *kdev; device_t child; int t; kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT); if (!kdev) return; bzero(kdev, sizeof *kdev); kdev->port = sc->port; if (resource_int_value(name, unit, "irq", &t) == 0) kdev->irq = t; else kdev->irq = -1; if (resource_int_value(name, unit, "flags", &t) == 0) kdev->flags = t; else kdev->flags = 0; child = device_add_child(dev, name, unit); device_set_ivars(child, kdev); } static int atkbdc_attach(device_t dev) { atkbdc_softc_t *sc; struct resource *port; int unit; int error; int rid; int i; unit = device_get_unit(dev); sc = *(atkbdc_softc_t **)device_get_softc(dev); if (sc == NULL) { /* * We have to maintain two copies of the kbdc_softc struct, * as the low-level console needs to have access to the * keyboard controller before kbdc is probed and attached. * kbdc_soft[] contains the default entry for that purpose. * See atkbdc.c. XXX */ sc = atkbdc_get_softc(unit); if (sc == NULL) return ENOMEM; } /* XXX should track resource in softc */ rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_attach_unit(unit, sc, rman_get_start(port)); if (error) return error; *(atkbdc_softc_t **)device_get_softc(dev) = sc; /* * Add all devices configured to be attached to atkbdc0. */ for (i = resource_query_string(-1, "at", "atkbdc0"); i != -1; i = resource_query_string(i, "at", "atkbdc0")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } /* * and atkbdc? */ for (i = resource_query_string(-1, "at", "atkbdc"); i != -1; i = resource_query_string(i, "at", "atkbdc")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } bus_generic_attach(dev); return 0; } static int atkbdc_print_child(device_t bus, device_t dev) { atkbdc_device_t *kbdcdev; int retval = 0; kbdcdev = (atkbdc_device_t *)device_get_ivars(dev); retval += bus_print_child_header(bus, dev); if (kbdcdev->flags != 0) retval += printf(" flags 0x%x", kbdcdev->flags); if (kbdcdev->irq != -1) retval += printf(" irq %d", kbdcdev->irq); retval += bus_print_child_footer(bus, dev); return (retval); } static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: *val = (u_long)ivar->port; break; case KBDC_IVAR_IRQ: *val = (u_long)ivar->irq; break; case KBDC_IVAR_FLAGS: *val = (u_long)ivar->flags; break; default: return ENOENT; } return 0; } static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: ivar->port = (int)val; break; case KBDC_IVAR_IRQ: ivar->irq = (int)val; break; case KBDC_IVAR_FLAGS: ivar->flags = (int)val; break; default: return ENOENT; } return 0; } DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0); - -#endif /* NATKBDC > 0 */ Index: head/sys/dev/atkbdc/psm.c =================================================================== --- head/sys/dev/atkbdc/psm.c (revision 56835) +++ head/sys/dev/atkbdc/psm.c (revision 56836) @@ -1,2454 +1,2445 @@ /*- * Copyright (c) 1992, 1993 Erik Forsberg. * Copyright (c) 1996, 1997 Kazutaka YOKOTA. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * Ported to 386bsd Oct 17, 1992 * Sandi Donno, Computer Science, University of Cape Town, South Africa * Please send bug reports to sandi@cs.uct.ac.za * * Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca - * although I was only partially successful in getting the alpha release * of his "driver for the Logitech and ATI Inport Bus mice for use with * 386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless * found his code to be an invaluable reference when porting this driver * to 386bsd. * * Further modifications for latest 386BSD+patchkit and port to NetBSD, * Andrew Herbert - 8 June 1993 * * Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by * Andrew Herbert - 12 June 1993 * * Modified for PS/2 mouse by Charles Hannum * - 13 June 1993 * * Modified for PS/2 AUX mouse by Shoji Yuen * - 24 October 1993 * * Hardware access routines and probe logic rewritten by * Kazutaka Yokota * - 3, 14, 22 October 1996. * - 12 November 1996. IOCTLs and rearranging `psmread', `psmioctl'... * - 14, 30 November 1996. Uses `kbdio.c'. * - 13 December 1996. Uses queuing version of `kbdio.c'. * - January/February 1997. Tweaked probe logic for * HiNote UltraII/Latitude/Armada laptops. * - 30 July 1997. Added APM support. * - 5 March 1997. Defined driver configuration flags (PSM_CONFIG_XXX). * Improved sync check logic. * Vendor specific support routines. */ -#include "psm.h" #include "opt_psm.h" -#if NPSM > 0 - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Driver specific options: the following options may be set by * `options' statements in the kernel configuration file. */ /* debugging */ #ifndef PSM_DEBUG #define PSM_DEBUG 0 /* logging: 0: none, 1: brief, 2: verbose */ #endif /* features */ /* #define PSM_HOOKRESUME hook the system resume event */ /* #define PSM_RESETAFTERSUSPEND reset the device at the resume event */ #ifdef PSM_HOOKAPM #undef PSM_HOOKRESUME #define PSM_HOOKRESUME 1 #endif /* PSM_HOOKAPM */ #ifndef PSM_HOOKRESUME #undef PSM_RESETAFTERSUSPEND #endif /* PSM_HOOKRESUME */ /* end of driver specific options */ /* input queue */ #define PSM_BUFSIZE 960 #define PSM_SMALLBUFSIZE 240 /* operation levels */ #define PSM_LEVEL_BASE 0 #define PSM_LEVEL_STANDARD 1 #define PSM_LEVEL_NATIVE 2 #define PSM_LEVEL_MIN PSM_LEVEL_BASE #define PSM_LEVEL_MAX PSM_LEVEL_NATIVE /* Logitech PS2++ protocol */ #define MOUSE_PS2PLUS_CHECKBITS(b) \ ((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f)) #define MOUSE_PS2PLUS_PACKET_TYPE(b) \ (((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4)) /* some macros */ #define PSM_UNIT(dev) (minor(dev) >> 1) #define PSM_NBLOCKIO(dev) (minor(dev) & 1) #define PSM_MKMINOR(unit,block) (((unit) << 1) | ((block) ? 0:1)) #ifndef max #define max(x,y) ((x) > (y) ? (x) : (y)) #endif #ifndef min #define min(x,y) ((x) < (y) ? (x) : (y)) #endif #define abs(x) (((x) < 0) ? -(x) : (x)) /* ring buffer */ typedef struct ringbuf { int count; /* # of valid elements in the buffer */ int head; /* head pointer */ int tail; /* tail poiner */ unsigned char buf[PSM_BUFSIZE]; } ringbuf_t; /* driver control block */ struct psm_softc { /* Driver status information */ struct selinfo rsel; /* Process selecting for Input */ unsigned char state; /* Mouse driver state */ int config; /* driver configuration flags */ int flags; /* other flags */ KBDC kbdc; /* handle to access the keyboard controller */ int addr; /* I/O port address */ mousehw_t hw; /* hardware information */ mousemode_t mode; /* operation mode */ mousemode_t dflt_mode; /* default operation mode */ mousestatus_t status; /* accumulated mouse movement */ ringbuf_t queue; /* mouse status queue */ unsigned char ipacket[16]; /* interim input buffer */ int inputbytes; /* # of bytes in the input buffer */ int button; /* the latest button state */ int xold; /* previous absolute X position */ int yold; /* previous absolute Y position */ }; devclass_t psm_devclass; #define PSM_SOFTC(unit) ((struct psm_softc*)devclass_get_softc(psm_devclass, unit)) /* driver state flags (state) */ #define PSM_VALID 0x80 #define PSM_OPEN 1 /* Device is open */ #define PSM_ASLP 2 /* Waiting for mouse data */ /* driver configuration flags (config) */ #define PSM_CONFIG_RESOLUTION 0x000f /* resolution */ #define PSM_CONFIG_ACCEL 0x00f0 /* acceleration factor */ #define PSM_CONFIG_NOCHECKSYNC 0x0100 /* disable sync. test */ #define PSM_CONFIG_NOIDPROBE 0x0200 /* disable mouse model probe */ #define PSM_CONFIG_NORESET 0x0400 /* don't reset the mouse */ #define PSM_CONFIG_FORCETAP 0x0800 /* assume `tap' action exists */ #define PSM_CONFIG_IGNPORTERROR 0x1000 /* ignore error in aux port test */ #define PSM_CONFIG_FLAGS (PSM_CONFIG_RESOLUTION \ | PSM_CONFIG_ACCEL \ | PSM_CONFIG_NOCHECKSYNC \ | PSM_CONFIG_NOIDPROBE \ | PSM_CONFIG_NORESET \ | PSM_CONFIG_FORCETAP \ | PSM_CONFIG_IGNPORTERROR) /* other flags (flags) */ #define PSM_FLAGS_FINGERDOWN 0x0001 /* VersaPad finger down */ /* for backward compatibility */ #define OLD_MOUSE_GETHWINFO _IOR('M', 1, old_mousehw_t) #define OLD_MOUSE_GETMODE _IOR('M', 2, old_mousemode_t) #define OLD_MOUSE_SETMODE _IOW('M', 3, old_mousemode_t) typedef struct old_mousehw { int buttons; int iftype; int type; int hwid; } old_mousehw_t; typedef struct old_mousemode { int protocol; int rate; int resolution; int accelfactor; } old_mousemode_t; /* packet formatting function */ typedef int packetfunc_t __P((struct psm_softc *, unsigned char *, int *, int, mousestatus_t *)); /* function prototypes */ static int psmprobe __P((device_t)); static int psmattach __P((device_t)); static int psmresume __P((device_t)); static d_open_t psmopen; static d_close_t psmclose; static d_read_t psmread; static d_ioctl_t psmioctl; static d_poll_t psmpoll; static int enable_aux_dev __P((KBDC)); static int disable_aux_dev __P((KBDC)); static int get_mouse_status __P((KBDC, int *, int, int)); static int get_aux_id __P((KBDC)); static int set_mouse_sampling_rate __P((KBDC, int)); static int set_mouse_scaling __P((KBDC, int)); static int set_mouse_resolution __P((KBDC, int)); #ifdef PSM_RESETAFTERSUSPEND static int set_mouse_mode __P((KBDC)); #endif /* PSM_RESETAFTERSUSPEND */ static int get_mouse_buttons __P((KBDC)); static int is_a_mouse __P((int)); static void recover_from_error __P((KBDC)); static int restore_controller __P((KBDC, int)); #ifdef PSM_RESETAFTERSUSPEND static int reinitialize __P((int, mousemode_t *)); #endif static int doopen __P((int, int)); static char *model_name(int); static void psmintr(void*); /* vendor specific features */ typedef int probefunc_t __P((struct psm_softc *)); static int mouse_id_proc1 __P((KBDC, int, int, int *)); static probefunc_t enable_groller; static probefunc_t enable_gmouse; static probefunc_t enable_aglide; static probefunc_t enable_kmouse; static probefunc_t enable_msintelli; static probefunc_t enable_mmanplus; static probefunc_t enable_versapad; static int tame_mouse __P((struct psm_softc *, mousestatus_t *, unsigned char *)); static struct { int model; unsigned char syncmask; int packetsize; probefunc_t *probefunc; } vendortype[] = { { MOUSE_MODEL_NET, /* Genius NetMouse */ 0xc8, MOUSE_INTELLI_PACKETSIZE, enable_gmouse, }, { MOUSE_MODEL_NETSCROLL, /* Genius NetScroll */ 0xc8, 6, enable_groller, }, { MOUSE_MODEL_GLIDEPOINT, /* ALPS GlidePoint */ 0xc0, MOUSE_PS2_PACKETSIZE, enable_aglide, }, { MOUSE_MODEL_MOUSEMANPLUS, /* Logitech MouseMan+ */ 0x08, MOUSE_PS2_PACKETSIZE, enable_mmanplus, }, { MOUSE_MODEL_THINK, /* Kensignton ThinkingMouse */ 0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse, }, { MOUSE_MODEL_INTELLI, /* Microsoft IntelliMouse */ 0xc8, MOUSE_INTELLI_PACKETSIZE, enable_msintelli, }, { MOUSE_MODEL_VERSAPAD, /* Interlink electronics VersaPad */ 0xe8, MOUSE_PS2VERSA_PACKETSIZE, enable_versapad, }, { MOUSE_MODEL_GENERIC, 0xc0, MOUSE_PS2_PACKETSIZE, NULL, }, }; #define GENERIC_MOUSE_ENTRY 7 /* device driver declarateion */ static device_method_t psm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, psmprobe), DEVMETHOD(device_attach, psmattach), DEVMETHOD(device_resume, psmresume), { 0, 0 } }; static driver_t psm_driver = { "psm", psm_methods, sizeof(struct psm_softc), }; #define CDEV_MAJOR 21 static struct cdevsw psm_cdevsw = { /* open */ psmopen, /* close */ psmclose, /* read */ psmread, /* write */ nowrite, /* ioctl */ psmioctl, /* poll */ psmpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "psm", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; /* debug message level */ static int verbose = PSM_DEBUG; /* device I/O routines */ static int enable_aux_dev(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_ENABLE_DEV); if (verbose >= 2) log(LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res); return (res == PSM_ACK); } static int disable_aux_dev(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_DISABLE_DEV); if (verbose >= 2) log(LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res); return (res == PSM_ACK); } static int get_mouse_status(KBDC kbdc, int *status, int flag, int len) { int cmd; int res; int i; switch (flag) { case 0: default: cmd = PSMC_SEND_DEV_STATUS; break; case 1: cmd = PSMC_SEND_DEV_DATA; break; } empty_aux_buffer(kbdc, 5); res = send_aux_command(kbdc, cmd); if (verbose >= 2) log(LOG_DEBUG, "psm: SEND_AUX_DEV_%s return code:%04x\n", (flag == 1) ? "DATA" : "STATUS", res); if (res != PSM_ACK) return 0; for (i = 0; i < len; ++i) { status[i] = read_aux_data(kbdc); if (status[i] < 0) break; } if (verbose) { log(LOG_DEBUG, "psm: %s %02x %02x %02x\n", (flag == 1) ? "data" : "status", status[0], status[1], status[2]); } return i; } static int get_aux_id(KBDC kbdc) { int res; int id; empty_aux_buffer(kbdc, 5); res = send_aux_command(kbdc, PSMC_SEND_DEV_ID); if (verbose >= 2) log(LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res); if (res != PSM_ACK) return (-1); /* 10ms delay */ DELAY(10000); id = read_aux_data(kbdc); if (verbose >= 2) log(LOG_DEBUG, "psm: device ID: %04x\n", id); return id; } static int set_mouse_sampling_rate(KBDC kbdc, int rate) { int res; res = send_aux_command_and_data(kbdc, PSMC_SET_SAMPLING_RATE, rate); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res); return ((res == PSM_ACK) ? rate : -1); } static int set_mouse_scaling(KBDC kbdc, int scale) { int res; switch (scale) { case 1: default: scale = PSMC_SET_SCALING11; break; case 2: scale = PSMC_SET_SCALING21; break; } res = send_aux_command(kbdc, scale); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_SCALING%s return code:%04x\n", (scale == PSMC_SET_SCALING21) ? "21" : "11", res); return (res == PSM_ACK); } /* `val' must be 0 through PSMD_MAX_RESOLUTION */ static int set_mouse_resolution(KBDC kbdc, int val) { int res; res = send_aux_command_and_data(kbdc, PSMC_SET_RESOLUTION, val); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n", val, res); return ((res == PSM_ACK) ? val : -1); } #ifdef PSM_RESETAFTERSUSPEND /* * NOTE: once `set_mouse_mode()' is called, the mouse device must be * re-enabled by calling `enable_aux_dev()' */ static int set_mouse_mode(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_SET_STREAM_MODE); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res); return (res == PSM_ACK); } #endif /* PSM_RESETAFTERSUSPEND */ static int get_mouse_buttons(KBDC kbdc) { int c = 2; /* assume two buttons by default */ int status[3]; /* * NOTE: a special sequence to obtain Logitech Mouse specific * information: set resolution to 25 ppi, set scaling to 1:1, set * scaling to 1:1, set scaling to 1:1. Then the second byte of the * mouse status bytes is the number of available buttons. * Some manufactures also support this sequence. */ if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW) return c; if (set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1) && (get_mouse_status(kbdc, status, 0, 3) >= 3)) { if (status[1] != 0) return status[1]; } return c; } /* misc subroutines */ /* * Someday, I will get the complete list of valid pointing devices and * their IDs... XXX */ static int is_a_mouse(int id) { #if 0 static int valid_ids[] = { PSM_MOUSE_ID, /* mouse */ PSM_BALLPOINT_ID, /* ballpoint device */ PSM_INTELLI_ID, /* Intellimouse */ -1 /* end of table */ }; int i; for (i = 0; valid_ids[i] >= 0; ++i) if (valid_ids[i] == id) return TRUE; return FALSE; #else return TRUE; #endif } static char * model_name(int model) { static struct { int model_code; char *model_name; } models[] = { { MOUSE_MODEL_NETSCROLL, "NetScroll Mouse" }, { MOUSE_MODEL_NET, "NetMouse" }, { MOUSE_MODEL_GLIDEPOINT, "GlidePoint" }, { MOUSE_MODEL_THINK, "ThinkingMouse" }, { MOUSE_MODEL_INTELLI, "IntelliMouse" }, { MOUSE_MODEL_MOUSEMANPLUS, "MouseMan+" }, { MOUSE_MODEL_VERSAPAD, "VersaPad" }, { MOUSE_MODEL_GENERIC, "Generic PS/2 mouse" }, { MOUSE_MODEL_UNKNOWN, NULL }, }; int i; for (i = 0; models[i].model_code != MOUSE_MODEL_UNKNOWN; ++i) { if (models[i].model_code == model) return models[i].model_name; } return "Unknown"; } static void recover_from_error(KBDC kbdc) { /* discard anything left in the output buffer */ empty_both_buffers(kbdc, 10); #if 0 /* * NOTE: KBDC_RESET_KBD may not restore the communication between the * keyboard and the controller. */ reset_kbd(kbdc); #else /* * NOTE: somehow diagnostic and keyboard port test commands bring the * keyboard back. */ if (!test_controller(kbdc)) log(LOG_ERR, "psm: keyboard controller failed.\n"); /* if there isn't a keyboard in the system, the following error is OK */ if (test_kbd_port(kbdc) != 0) { if (verbose) log(LOG_ERR, "psm: keyboard port failed.\n"); } #endif } static int restore_controller(KBDC kbdc, int command_byte) { empty_both_buffers(kbdc, 10); if (!set_controller_command_byte(kbdc, 0xff, command_byte)) { log(LOG_ERR, "psm: failed to restore the keyboard controller " "command byte.\n"); return FALSE; } else { return TRUE; } } #ifdef PSM_RESETAFTERSUSPEND /* * Re-initialize the aux port and device. The aux port must be enabled * and its interrupt must be disabled before calling this routine. * The aux device will be disabled before returning. * The keyboard controller must be locked via `kbdc_lock()' before * calling this routine. */ static int reinitialize(int unit, mousemode_t *mode) { struct psm_softc *sc = PSM_SOFTC(unit); KBDC kbdc = sc->kbdc; int stat[3]; int i; switch((i = test_aux_port(kbdc))) { case 1: /* ignore this error */ case PSM_ACK: if (verbose) log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n", unit, i); /* fall though */ case 0: /* no error */ break; case -1: /* time out */ default: /* error */ recover_from_error(kbdc); if (sc->config & PSM_CONFIG_IGNPORTERROR) break; log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n", unit, i); return FALSE; } if (sc->config & PSM_CONFIG_NORESET) { /* * Don't try to reset the pointing device. It may possibly be * left in the unknown state, though... */ } else { /* * NOTE: some controllers appears to hang the `keyboard' when * the aux port doesn't exist and `PSMC_RESET_DEV' is issued. */ if (!reset_aux_dev(kbdc)) { recover_from_error(kbdc); log(LOG_ERR, "psm%d: failed to reset the aux device.\n", unit); return FALSE; } } /* * both the aux port and the aux device is functioning, see * if the device can be enabled. */ if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) { log(LOG_ERR, "psm%d: failed to enable the aux device.\n", unit); return FALSE; } empty_both_buffers(kbdc, 10); /* remove stray data if any */ if (sc->config & PSM_CONFIG_NOIDPROBE) { i = GENERIC_MOUSE_ENTRY; } else { /* FIXME: hardware ID, mouse buttons? */ /* other parameters */ for (i = 0; vendortype[i].probefunc != NULL; ++i) { if ((*vendortype[i].probefunc)(sc)) { if (verbose >= 2) log(LOG_ERR, "psm%d: found %s\n", unit, model_name(vendortype[i].model)); break; } } } sc->hw.model = vendortype[i].model; sc->mode.packetsize = vendortype[i].packetsize; /* set mouse parameters */ if (mode != (mousemode_t *)NULL) { if (mode->rate > 0) mode->rate = set_mouse_sampling_rate(kbdc, mode->rate); if (mode->resolution >= 0) mode->resolution = set_mouse_resolution(kbdc, mode->resolution); set_mouse_scaling(kbdc, 1); set_mouse_mode(kbdc); } /* request a data packet and extract sync. bits */ if (get_mouse_status(kbdc, stat, 1, 3) < 3) { log(LOG_DEBUG, "psm%d: failed to get data (reinitialize).\n", unit); sc->mode.syncmask[0] = 0; } else { sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */ /* the NetScroll Mouse will send three more bytes... Ignore them */ empty_aux_buffer(kbdc, 5); } /* just check the status of the mouse */ if (get_mouse_status(kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (reinitialize).\n", unit); return TRUE; } #endif /* PSM_RESETAFTERSUSPEND */ static int doopen(int unit, int command_byte) { struct psm_softc *sc = PSM_SOFTC(unit); int stat[3]; /* enable the mouse device */ if (!enable_aux_dev(sc->kbdc)) { /* MOUSE ERROR: failed to enable the mouse because: * 1) the mouse is faulty, * 2) the mouse has been removed(!?) * In the latter case, the keyboard may have hung, and need * recovery procedure... */ recover_from_error(sc->kbdc); #if 0 /* FIXME: we could reset the mouse here and try to enable * it again. But it will take long time and it's not a good * idea to disable the keyboard that long... */ if (!reinitialize(unit, &sc->mode) || !enable_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); #else { #endif restore_controller(sc->kbdc, command_byte); /* mark this device is no longer available */ sc->state &= ~PSM_VALID; log(LOG_ERR, "psm%d: failed to enable the device (doopen).\n", unit); return (EIO); } } if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", unit); /* enable the aux port and interrupt */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (command_byte & KBD_KBD_CONTROL_BITS) | KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) { /* CONTROLLER ERROR */ disable_aux_dev(sc->kbdc); restore_controller(sc->kbdc, command_byte); log(LOG_ERR, "psm%d: failed to enable the aux interrupt (doopen).\n", unit); return (EIO); } return (0); } /* psm driver entry points */ #define endprobe(v) { if (bootverbose) \ --verbose; \ kbdc_set_device_mask(sc->kbdc, mask); \ kbdc_lock(sc->kbdc, FALSE); \ free(sc, M_DEVBUF); \ return (v); \ } static int psmprobe(device_t dev) { int unit = device_get_unit(dev); struct psm_softc *sc = device_get_softc(dev); uintptr_t port; uintptr_t flags; int stat[3]; int command_byte; int mask; int i; #if 0 kbdc_debug(TRUE); #endif BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); sc->addr = port; sc->kbdc = kbdc_open(sc->addr); sc->config = flags & PSM_CONFIG_FLAGS; sc->flags = 0; if (bootverbose) ++verbose; device_set_desc(dev, "PS/2 Mouse"); if (!kbdc_lock(sc->kbdc, TRUE)) { printf("psm%d: unable to lock the controller.\n", unit); if (bootverbose) --verbose; return (ENXIO); } /* * NOTE: two bits in the command byte controls the operation of the * aux port (mouse port): the aux port disable bit (bit 5) and the aux * port interrupt (IRQ 12) enable bit (bit 2). */ /* discard anything left after the keyboard initialization */ empty_both_buffers(sc->kbdc, 10); /* save the current command byte; it will be used later */ mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS; command_byte = get_controller_command_byte(sc->kbdc); if (verbose) printf("psm%d: current command byte:%04x\n", unit, command_byte); if (command_byte == -1) { /* CONTROLLER ERROR */ printf("psm%d: unable to get the current command byte value.\n", unit); endprobe(ENXIO); } /* * disable the keyboard port while probing the aux port, which must be * enabled during this routine */ if (!set_controller_command_byte(sc->kbdc, KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS, KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * this is CONTROLLER ERROR; I don't know how to recover * from this error... */ restore_controller(sc->kbdc, command_byte); printf("psm%d: unable to set the command byte.\n", unit); endprobe(ENXIO); } write_controller_command(sc->kbdc, KBDC_ENABLE_AUX_PORT); /* * NOTE: `test_aux_port()' is designed to return with zero if the aux * port exists and is functioning. However, some controllers appears * to respond with zero even when the aux port doesn't exist. (It may * be that this is only the case when the controller DOES have the aux * port but the port is not wired on the motherboard.) The keyboard * controllers without the port, such as the original AT, are * supporsed to return with an error code or simply time out. In any * case, we have to continue probing the port even when the controller * passes this test. * * XXX: some controllers erroneously return the error code 1 when * it has the perfectly functional aux port. We have to ignore this * error code. Even if the controller HAS error with the aux port, * it will be detected later... * XXX: another incompatible controller returns PSM_ACK (0xfa)... */ switch ((i = test_aux_port(sc->kbdc))) { case 1: /* ignore this error */ case PSM_ACK: if (verbose) printf("psm%d: strange result for test aux port (%d).\n", unit, i); /* fall though */ case 0: /* no error */ break; case -1: /* time out */ default: /* error */ recover_from_error(sc->kbdc); if (sc->config & PSM_CONFIG_IGNPORTERROR) break; restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: the aux port is not functioning (%d).\n", unit, i); endprobe(ENXIO); } if (sc->config & PSM_CONFIG_NORESET) { /* * Don't try to reset the pointing device. It may possibly be * left in the unknown state, though... */ } else { /* * NOTE: some controllers appears to hang the `keyboard' when the aux * port doesn't exist and `PSMC_RESET_DEV' is issued. */ if (!reset_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: failed to reset the aux device.\n", unit); endprobe(ENXIO); } } /* * both the aux port and the aux device is functioning, see if the * device can be enabled. NOTE: when enabled, the device will start * sending data; we shall immediately disable the device once we know * the device can be enabled. */ if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) { /* MOUSE ERROR */ recover_from_error(sc->kbdc); restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: failed to enable the aux device.\n", unit); endprobe(ENXIO); } /* save the default values after reset */ if (get_mouse_status(sc->kbdc, stat, 0, 3) >= 3) { sc->dflt_mode.rate = sc->mode.rate = stat[2]; sc->dflt_mode.resolution = sc->mode.resolution = stat[1]; } else { sc->dflt_mode.rate = sc->mode.rate = -1; sc->dflt_mode.resolution = sc->mode.resolution = -1; } /* hardware information */ sc->hw.iftype = MOUSE_IF_PS2; /* verify the device is a mouse */ sc->hw.hwid = get_aux_id(sc->kbdc); if (!is_a_mouse(sc->hw.hwid)) { restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid); endprobe(ENXIO); } switch (sc->hw.hwid) { case PSM_BALLPOINT_ID: sc->hw.type = MOUSE_TRACKBALL; break; case PSM_MOUSE_ID: case PSM_INTELLI_ID: sc->hw.type = MOUSE_MOUSE; break; default: sc->hw.type = MOUSE_UNKNOWN; break; } if (sc->config & PSM_CONFIG_NOIDPROBE) { sc->hw.buttons = 2; i = GENERIC_MOUSE_ENTRY; } else { /* # of buttons */ sc->hw.buttons = get_mouse_buttons(sc->kbdc); /* other parameters */ for (i = 0; vendortype[i].probefunc != NULL; ++i) { if ((*vendortype[i].probefunc)(sc)) { if (verbose >= 2) printf("psm%d: found %s\n", unit, model_name(vendortype[i].model)); break; } } } sc->hw.model = vendortype[i].model; sc->dflt_mode.level = PSM_LEVEL_BASE; sc->dflt_mode.packetsize = MOUSE_PS2_PACKETSIZE; sc->dflt_mode.accelfactor = (sc->config & PSM_CONFIG_ACCEL) >> 4; if (sc->config & PSM_CONFIG_NOCHECKSYNC) sc->dflt_mode.syncmask[0] = 0; else sc->dflt_mode.syncmask[0] = vendortype[i].syncmask; if (sc->config & PSM_CONFIG_FORCETAP) sc->mode.syncmask[0] &= ~MOUSE_PS2_TAP; sc->dflt_mode.syncmask[1] = 0; /* syncbits */ sc->mode = sc->dflt_mode; sc->mode.packetsize = vendortype[i].packetsize; /* set mouse parameters */ #if 0 /* * A version of Logitech FirstMouse+ won't report wheel movement, * if SET_DEFAULTS is sent... Don't use this command. * This fix was found by Takashi Nishida. */ i = send_aux_command(sc->kbdc, PSMC_SET_DEFAULTS); if (verbose >= 2) printf("psm%d: SET_DEFAULTS return code:%04x\n", unit, i); #endif if (sc->config & PSM_CONFIG_RESOLUTION) { sc->mode.resolution = set_mouse_resolution(sc->kbdc, (sc->config & PSM_CONFIG_RESOLUTION) - 1); } else if (sc->mode.resolution >= 0) { sc->mode.resolution = set_mouse_resolution(sc->kbdc, sc->dflt_mode.resolution); } if (sc->mode.rate > 0) { sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, sc->dflt_mode.rate); } set_mouse_scaling(sc->kbdc, 1); /* request a data packet and extract sync. bits */ if (get_mouse_status(sc->kbdc, stat, 1, 3) < 3) { printf("psm%d: failed to get data.\n", unit); sc->mode.syncmask[0] = 0; } else { sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */ /* the NetScroll Mouse will send three more bytes... Ignore them */ empty_aux_buffer(sc->kbdc, 5); } /* just check the status of the mouse */ /* * NOTE: XXX there are some arcane controller/mouse combinations out * there, which hung the controller unless there is data transmission * after ACK from the mouse. */ if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) { printf("psm%d: failed to get status.\n", unit); } else { /* * When in its native mode, some mice operate with different * default parameters than in the PS/2 compatible mode. */ sc->dflt_mode.rate = sc->mode.rate = stat[2]; sc->dflt_mode.resolution = sc->mode.resolution = stat[1]; } /* disable the aux port for now... */ if (!set_controller_command_byte(sc->kbdc, KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS, (command_byte & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * this is CONTROLLER ERROR; I don't know the proper way to * recover from this error... */ restore_controller(sc->kbdc, command_byte); printf("psm%d: unable to set the command byte.\n", unit); endprobe(ENXIO); } /* done */ kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS); kbdc_lock(sc->kbdc, FALSE); return (0); } static int psmattach(device_t dev) { int unit = device_get_unit(dev); struct psm_softc *sc = device_get_softc(dev); void *ih; struct resource *res; uintptr_t irq; int zero = 0; if (sc == NULL) /* shouldn't happen */ return (ENXIO); /* Setup initial state */ sc->state = PSM_VALID; /* Done */ make_dev(&psm_cdevsw, PSM_MKMINOR(unit, FALSE), 0, 0, 0666, "psm%d", unit); make_dev(&psm_cdevsw, PSM_MKMINOR(unit, TRUE), 0, 0, 0666, "bpsm%d", unit); if (!verbose) { printf("psm%d: model %s, device ID %d\n", unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff); } else { printf("psm%d: model %s, device ID %d-%02x, %d buttons\n", unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff, sc->hw.hwid >> 8, sc->hw.buttons); printf("psm%d: config:%08x, flags:%08x, packet size:%d\n", unit, sc->config, sc->flags, sc->mode.packetsize); printf("psm%d: syncmask:%02x, syncbits:%02x\n", unit, sc->mode.syncmask[0], sc->mode.syncmask[1]); } if (bootverbose) --verbose; BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, INTR_TYPE_TTY, psmintr, sc, &ih); return (0); } static int psmopen(dev_t dev, int flag, int fmt, struct proc *p) { int unit = PSM_UNIT(dev); struct psm_softc *sc; int command_byte; int err; int s; - /* Validate unit number */ - if (unit >= NPSM) - return (ENXIO); - /* Get device data */ sc = PSM_SOFTC(unit); if ((sc == NULL) || (sc->state & PSM_VALID) == 0) /* the device is no longer valid/functioning */ return (ENXIO); /* Disallow multiple opens */ if (sc->state & PSM_OPEN) return (EBUSY); device_busy(devclass_get_device(psm_devclass, unit)); /* Initialize state */ sc->rsel.si_flags = 0; sc->rsel.si_pid = 0; sc->mode.level = sc->dflt_mode.level; sc->mode.protocol = sc->dflt_mode.protocol; /* flush the event queue */ sc->queue.count = 0; sc->queue.head = 0; sc->queue.tail = 0; sc->status.flags = 0; sc->status.button = 0; sc->status.obutton = 0; sc->status.dx = 0; sc->status.dy = 0; sc->status.dz = 0; sc->button = 0; /* empty input buffer */ bzero(sc->ipacket, sizeof(sc->ipacket)); sc->inputbytes = 0; /* don't let timeout routines in the keyboard driver to poll the kbdc */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); /* save the current controller command byte */ s = spltty(); command_byte = get_controller_command_byte(sc->kbdc); /* enable the aux port and temporalily disable the keyboard */ if ((command_byte == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR; do you know how to get out of this? */ kbdc_lock(sc->kbdc, FALSE); splx(s); log(LOG_ERR, "psm%d: unable to set the command byte (psmopen).\n", unit); return (EIO); } /* * Now that the keyboard controller is told not to generate * the keyboard and mouse interrupts, call `splx()' to allow * the other tty interrupts. The clock interrupt may also occur, * but timeout routines will be blocked by the poll flag set * via `kbdc_lock()' */ splx(s); /* enable the mouse device */ err = doopen(unit, command_byte); /* done */ if (err == 0) sc->state |= PSM_OPEN; kbdc_lock(sc->kbdc, FALSE); return (err); } static int psmclose(dev_t dev, int flag, int fmt, struct proc *p) { int unit = PSM_UNIT(dev); struct psm_softc *sc = PSM_SOFTC(unit); int stat[3]; int command_byte; int s; /* don't let timeout routines in the keyboard driver to poll the kbdc */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); /* save the current controller command byte */ s = spltty(); command_byte = get_controller_command_byte(sc->kbdc); if (command_byte == -1) { kbdc_lock(sc->kbdc, FALSE); splx(s); return (EIO); } /* disable the aux interrupt and temporalily disable the keyboard */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { log(LOG_ERR, "psm%d: failed to disable the aux int (psmclose).\n", PSM_UNIT(dev)); /* CONTROLLER ERROR; * NOTE: we shall force our way through. Because the only * ill effect we shall see is that we may not be able * to read ACK from the mouse, and it doesn't matter much * so long as the mouse will accept the DISABLE command. */ } splx(s); /* remove anything left in the output buffer */ empty_aux_buffer(sc->kbdc, 10); /* disable the aux device, port and interrupt */ if (sc->state & PSM_VALID) { if (!disable_aux_dev(sc->kbdc)) { /* MOUSE ERROR; * NOTE: we don't return error and continue, pretending * we have successfully disabled the device. It's OK because * the interrupt routine will discard any data from the mouse * hereafter. */ log(LOG_ERR, "psm%d: failed to disable the device (psmclose).\n", PSM_UNIT(dev)); } if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (psmclose).\n", PSM_UNIT(dev)); } if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (command_byte & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR; * we shall ignore this error; see the above comment. */ log(LOG_ERR, "psm%d: failed to disable the aux port (psmclose).\n", PSM_UNIT(dev)); } /* remove anything left in the output buffer */ empty_aux_buffer(sc->kbdc, 10); /* close is almost always successful */ sc->state &= ~PSM_OPEN; kbdc_lock(sc->kbdc, FALSE); device_unbusy(devclass_get_device(psm_devclass, unit)); return (0); } static int tame_mouse(struct psm_softc *sc, mousestatus_t *status, unsigned char *buf) { static unsigned char butmapps2[8] = { 0, MOUSE_PS2_BUTTON1DOWN, MOUSE_PS2_BUTTON2DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN, MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN, }; static unsigned char butmapmsc[8] = { MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON1UP, 0, }; int mapped; int i; if (sc->mode.level == PSM_LEVEL_BASE) { mapped = status->button & ~MOUSE_BUTTON4DOWN; if (status->button & MOUSE_BUTTON4DOWN) mapped |= MOUSE_BUTTON1DOWN; status->button = mapped; buf[0] = MOUSE_PS2_SYNC | butmapps2[mapped & MOUSE_STDBUTTONS]; i = max(min(status->dx, 255), -256); if (i < 0) buf[0] |= MOUSE_PS2_XNEG; buf[1] = i; i = max(min(status->dy, 255), -256); if (i < 0) buf[0] |= MOUSE_PS2_YNEG; buf[2] = i; return MOUSE_PS2_PACKETSIZE; } else if (sc->mode.level == PSM_LEVEL_STANDARD) { buf[0] = MOUSE_MSC_SYNC | butmapmsc[status->button & MOUSE_STDBUTTONS]; i = max(min(status->dx, 255), -256); buf[1] = i >> 1; buf[3] = i - buf[1]; i = max(min(status->dy, 255), -256); buf[2] = i >> 1; buf[4] = i - buf[2]; i = max(min(status->dz, 127), -128); buf[5] = (i >> 1) & 0x7f; buf[6] = (i - (i >> 1)) & 0x7f; buf[7] = (~status->button >> 3) & 0x7f; return MOUSE_SYS_PACKETSIZE; } return sc->inputbytes;; } static int psmread(dev_t dev, struct uio *uio, int flag) { register struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); unsigned char buf[PSM_SMALLBUFSIZE]; int error = 0; int s; int l; if ((sc->state & PSM_VALID) == 0) return EIO; /* block until mouse activity occured */ s = spltty(); while (sc->queue.count <= 0) { if (PSM_NBLOCKIO(dev)) { splx(s); return EWOULDBLOCK; } sc->state |= PSM_ASLP; error = tsleep((caddr_t) sc, PZERO | PCATCH, "psmrea", 0); sc->state &= ~PSM_ASLP; if (error) { splx(s); return error; } else if ((sc->state & PSM_VALID) == 0) { /* the device disappeared! */ splx(s); return EIO; } } splx(s); /* copy data to the user land */ while ((sc->queue.count > 0) && (uio->uio_resid > 0)) { s = spltty(); l = min(sc->queue.count, uio->uio_resid); if (l > sizeof(buf)) l = sizeof(buf); if (l > sizeof(sc->queue.buf) - sc->queue.head) { bcopy(&sc->queue.buf[sc->queue.head], &buf[0], sizeof(sc->queue.buf) - sc->queue.head); bcopy(&sc->queue.buf[0], &buf[sizeof(sc->queue.buf) - sc->queue.head], l - (sizeof(sc->queue.buf) - sc->queue.head)); } else { bcopy(&sc->queue.buf[sc->queue.head], &buf[0], l); } sc->queue.count -= l; sc->queue.head = (sc->queue.head + l) % sizeof(sc->queue.buf); splx(s); error = uiomove(buf, l, uio); if (error) break; } return error; } static int block_mouse_data(struct psm_softc *sc, int *c) { int s; if (!kbdc_lock(sc->kbdc, TRUE)) return EIO; s = spltty(); *c = get_controller_command_byte(sc->kbdc); if ((*c == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* this is CONTROLLER ERROR */ splx(s); kbdc_lock(sc->kbdc, FALSE); return EIO; } /* * The device may be in the middle of status data transmission. * The transmission will be interrupted, thus, incomplete status * data must be discarded. Although the aux interrupt is disabled * at the keyboard controller level, at most one aux interrupt * may have already been pending and a data byte is in the * output buffer; throw it away. Note that the second argument * to `empty_aux_buffer()' is zero, so that the call will just * flush the internal queue. * `psmintr()' will be invoked after `splx()' if an interrupt is * pending; it will see no data and returns immediately. */ empty_aux_buffer(sc->kbdc, 0); /* flush the queue */ read_aux_data_no_wait(sc->kbdc); /* throw away data if any */ sc->inputbytes = 0; splx(s); return 0; } static int unblock_mouse_data(struct psm_softc *sc, int c) { int error = 0; /* * We may have seen a part of status data during `set_mouse_XXX()'. * they have been queued; flush it. */ empty_aux_buffer(sc->kbdc, 0); /* restore ports and interrupt */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR; this is serious, we may have * been left with the inaccessible keyboard and * the disabled mouse interrupt. */ error = EIO; } kbdc_lock(sc->kbdc, FALSE); return error; } static int psmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); mousemode_t mode; mousestatus_t status; #if (defined(MOUSE_GETVARS)) mousevar_t *var; #endif mousedata_t *data; int stat[3]; int command_byte; int error = 0; int s; /* Perform IOCTL command */ switch (cmd) { case OLD_MOUSE_GETHWINFO: s = spltty(); ((old_mousehw_t *)addr)->buttons = sc->hw.buttons; ((old_mousehw_t *)addr)->iftype = sc->hw.iftype; ((old_mousehw_t *)addr)->type = sc->hw.type; ((old_mousehw_t *)addr)->hwid = sc->hw.hwid & 0x00ff; splx(s); break; case MOUSE_GETHWINFO: s = spltty(); *(mousehw_t *)addr = sc->hw; if (sc->mode.level == PSM_LEVEL_BASE) ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC; splx(s); break; case OLD_MOUSE_GETMODE: s = spltty(); switch (sc->mode.level) { case PSM_LEVEL_BASE: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; case PSM_LEVEL_STANDARD: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; break; case PSM_LEVEL_NATIVE: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; } ((old_mousemode_t *)addr)->rate = sc->mode.rate; ((old_mousemode_t *)addr)->resolution = sc->mode.resolution; ((old_mousemode_t *)addr)->accelfactor = sc->mode.accelfactor; splx(s); break; case MOUSE_GETMODE: s = spltty(); *(mousemode_t *)addr = sc->mode; ((mousemode_t *)addr)->resolution = MOUSE_RES_LOW - sc->mode.resolution; switch (sc->mode.level) { case PSM_LEVEL_BASE: ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; ((mousemode_t *)addr)->packetsize = MOUSE_PS2_PACKETSIZE; break; case PSM_LEVEL_STANDARD: ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; ((mousemode_t *)addr)->packetsize = MOUSE_SYS_PACKETSIZE; ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK; ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC; break; case PSM_LEVEL_NATIVE: /* FIXME: this isn't quite correct... XXX */ ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; } splx(s); break; case OLD_MOUSE_SETMODE: case MOUSE_SETMODE: if (cmd == OLD_MOUSE_SETMODE) { mode.rate = ((old_mousemode_t *)addr)->rate; /* * resolution old I/F new I/F * default 0 0 * low 1 -2 * medium low 2 -3 * medium high 3 -4 * high 4 -5 */ if (((old_mousemode_t *)addr)->resolution > 0) mode.resolution = -((old_mousemode_t *)addr)->resolution - 1; mode.accelfactor = ((old_mousemode_t *)addr)->accelfactor; mode.level = -1; } else { mode = *(mousemode_t *)addr; } /* adjust and validate parameters. */ if (mode.rate > UCHAR_MAX) return EINVAL; if (mode.rate == 0) mode.rate = sc->dflt_mode.rate; else if (mode.rate == -1) /* don't change the current setting */ ; else if (mode.rate < 0) return EINVAL; if (mode.resolution >= UCHAR_MAX) return EINVAL; if (mode.resolution >= 200) mode.resolution = MOUSE_RES_HIGH; else if (mode.resolution >= 100) mode.resolution = MOUSE_RES_MEDIUMHIGH; else if (mode.resolution >= 50) mode.resolution = MOUSE_RES_MEDIUMLOW; else if (mode.resolution > 0) mode.resolution = MOUSE_RES_LOW; if (mode.resolution == MOUSE_RES_DEFAULT) mode.resolution = sc->dflt_mode.resolution; else if (mode.resolution == -1) /* don't change the current setting */ ; else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */ mode.resolution = MOUSE_RES_LOW - mode.resolution; if (mode.level == -1) /* don't change the current setting */ mode.level = sc->mode.level; else if ((mode.level < PSM_LEVEL_MIN) || (mode.level > PSM_LEVEL_MAX)) return EINVAL; if (mode.accelfactor == -1) /* don't change the current setting */ mode.accelfactor = sc->mode.accelfactor; else if (mode.accelfactor < 0) return EINVAL; /* don't allow anybody to poll the keyboard controller */ error = block_mouse_data(sc, &command_byte); if (error) return error; /* set mouse parameters */ if (mode.rate > 0) mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate); if (mode.resolution >= 0) mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution); set_mouse_scaling(sc->kbdc, 1); get_mouse_status(sc->kbdc, stat, 0, 3); s = spltty(); sc->mode.rate = mode.rate; sc->mode.resolution = mode.resolution; sc->mode.accelfactor = mode.accelfactor; sc->mode.level = mode.level; splx(s); unblock_mouse_data(sc, command_byte); break; case MOUSE_GETLEVEL: *(int *)addr = sc->mode.level; break; case MOUSE_SETLEVEL: if ((*(int *)addr < PSM_LEVEL_MIN) || (*(int *)addr > PSM_LEVEL_MAX)) return EINVAL; sc->mode.level = *(int *)addr; break; case MOUSE_GETSTATUS: s = spltty(); status = sc->status; sc->status.flags = 0; sc->status.obutton = sc->status.button; sc->status.button = 0; sc->status.dx = 0; sc->status.dy = 0; sc->status.dz = 0; splx(s); *(mousestatus_t *)addr = status; break; #if (defined(MOUSE_GETVARS)) case MOUSE_GETVARS: var = (mousevar_t *)addr; bzero(var, sizeof(*var)); s = spltty(); var->var[0] = MOUSE_VARS_PS2_SIG; var->var[1] = sc->config; var->var[2] = sc->flags; splx(s); break; case MOUSE_SETVARS: return ENODEV; #endif /* MOUSE_GETVARS */ case MOUSE_READSTATE: case MOUSE_READDATA: data = (mousedata_t *)addr; if (data->len > sizeof(data->buf)/sizeof(data->buf[0])) return EINVAL; error = block_mouse_data(sc, &command_byte); if (error) return error; if ((data->len = get_mouse_status(sc->kbdc, data->buf, (cmd == MOUSE_READDATA) ? 1 : 0, data->len)) <= 0) error = EIO; unblock_mouse_data(sc, command_byte); break; #if (defined(MOUSE_SETRESOLUTION)) case MOUSE_SETRESOLUTION: mode.resolution = *(int *)addr; if (mode.resolution >= UCHAR_MAX) return EINVAL; else if (mode.resolution >= 200) mode.resolution = MOUSE_RES_HIGH; else if (mode.resolution >= 100) mode.resolution = MOUSE_RES_MEDIUMHIGH; else if (mode.resolution >= 50) mode.resolution = MOUSE_RES_MEDIUMLOW; else if (mode.resolution > 0) mode.resolution = MOUSE_RES_LOW; if (mode.resolution == MOUSE_RES_DEFAULT) mode.resolution = sc->dflt_mode.resolution; else if (mode.resolution == -1) mode.resolution = sc->mode.resolution; else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */ mode.resolution = MOUSE_RES_LOW - mode.resolution; error = block_mouse_data(sc, &command_byte); if (error) return error; sc->mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution); if (sc->mode.resolution != mode.resolution) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETRESOLUTION */ #if (defined(MOUSE_SETRATE)) case MOUSE_SETRATE: mode.rate = *(int *)addr; if (mode.rate > UCHAR_MAX) return EINVAL; if (mode.rate == 0) mode.rate = sc->dflt_mode.rate; else if (mode.rate < 0) mode.rate = sc->mode.rate; error = block_mouse_data(sc, &command_byte); if (error) return error; sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate); if (sc->mode.rate != mode.rate) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETRATE */ #if (defined(MOUSE_SETSCALING)) case MOUSE_SETSCALING: if ((*(int *)addr <= 0) || (*(int *)addr > 2)) return EINVAL; error = block_mouse_data(sc, &command_byte); if (error) return error; if (!set_mouse_scaling(sc->kbdc, *(int *)addr)) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETSCALING */ #if (defined(MOUSE_GETHWID)) case MOUSE_GETHWID: error = block_mouse_data(sc, &command_byte); if (error) return error; sc->hw.hwid &= ~0x00ff; sc->hw.hwid |= get_aux_id(sc->kbdc); *(int *)addr = sc->hw.hwid & 0x00ff; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_GETHWID */ default: return ENOTTY; } return error; } static void psmintr(void *arg) { /* * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN) * into `mousestatus' button bits (MOUSE_BUTTON?DOWN). */ static int butmap[8] = { 0, MOUSE_BUTTON1DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN }; static int butmap_versapad[8] = { 0, MOUSE_BUTTON3DOWN, 0, MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN }; register struct psm_softc *sc = arg; mousestatus_t ms; int x, y, z; int c; int l; int x0, y0; /* read until there is nothing to read */ while((c = read_aux_data_no_wait(sc->kbdc)) != -1) { /* discard the byte if the device is not open */ if ((sc->state & PSM_OPEN) == 0) continue; /* * Check sync bits. We check for overflow bits and the bit 3 * for most mice. True, the code doesn't work if overflow * condition occurs. But we expect it rarely happens... */ if ((sc->inputbytes == 0) && ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1])) { log(LOG_DEBUG, "psmintr: out of sync (%04x != %04x).\n", c & sc->mode.syncmask[0], sc->mode.syncmask[1]); continue; } sc->ipacket[sc->inputbytes++] = c; if (sc->inputbytes < sc->mode.packetsize) continue; #if 0 log(LOG_DEBUG, "psmintr: %02x %02x %02x %02x %02x %02x\n", sc->ipacket[0], sc->ipacket[1], sc->ipacket[2], sc->ipacket[3], sc->ipacket[4], sc->ipacket[5]); #endif c = sc->ipacket[0]; /* * A kludge for Kensington device! * The MSB of the horizontal count appears to be stored in * a strange place. This kludge doesn't affect other mice * because the bit is the overflow bit which is, in most cases, * expected to be zero when we reach here. XXX */ if (sc->hw.model != MOUSE_MODEL_VERSAPAD) sc->ipacket[1] |= (c & MOUSE_PS2_XOVERFLOW) ? 0x80 : 0; /* ignore the overflow bits... */ x = (c & MOUSE_PS2_XNEG) ? sc->ipacket[1] - 256 : sc->ipacket[1]; y = (c & MOUSE_PS2_YNEG) ? sc->ipacket[2] - 256 : sc->ipacket[2]; z = 0; ms.obutton = sc->button; /* previous button state */ ms.button = butmap[c & MOUSE_PS2_BUTTONS]; /* `tapping' action */ if (sc->config & PSM_CONFIG_FORCETAP) ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN; switch (sc->hw.model) { case MOUSE_MODEL_INTELLI: case MOUSE_MODEL_NET: /* wheel data is in the fourth byte */ z = (char)sc->ipacket[3]; break; case MOUSE_MODEL_MOUSEMANPLUS: /* * PS2++ protocl packet * * b7 b6 b5 b4 b3 b2 b1 b0 * byte 1: * 1 p3 p2 1 * * * * byte 2: c1 c2 p1 p0 d1 d0 1 0 * * p3-p0: packet type * c1, c2: c1 & c2 == 1, if p2 == 0 * c1 & c2 == 0, if p2 == 1 * * packet type: 0 (device type) * See comments in enable_mmanplus() below. * * packet type: 1 (wheel data) * * b7 b6 b5 b4 b3 b2 b1 b0 * byte 3: h * B5 B4 s d2 d1 d0 * * h: 1, if horizontal roller data * 0, if vertical roller data * B4, B5: button 4 and 5 * s: sign bit * d2-d0: roller data * * packet type: 2 (reserved) */ if (((c & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC) && (abs(x) > 191) && MOUSE_PS2PLUS_CHECKBITS(sc->ipacket)) { /* the extended data packet encodes button and wheel events */ switch (MOUSE_PS2PLUS_PACKET_TYPE(sc->ipacket)) { case 1: /* wheel data packet */ x = y = 0; if (sc->ipacket[2] & 0x80) { /* horizontal roller count - ignore it XXX*/ } else { /* vertical roller count */ z = (sc->ipacket[2] & MOUSE_PS2PLUS_ZNEG) ? (sc->ipacket[2] & 0x0f) - 16 : (sc->ipacket[2] & 0x0f); } ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON4DOWN) ? MOUSE_BUTTON4DOWN : 0; ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON5DOWN) ? MOUSE_BUTTON5DOWN : 0; break; case 2: /* this packet type is reserved, and currently ignored */ /* FALL THROUGH */ case 0: /* device type packet - shouldn't happen */ /* FALL THROUGH */ default: x = y = 0; ms.button = ms.obutton; log(LOG_DEBUG, "psmintr: unknown PS2++ packet type %d: " "0x%02x 0x%02x 0x%02x\n", MOUSE_PS2PLUS_PACKET_TYPE(sc->ipacket), sc->ipacket[0], sc->ipacket[1], sc->ipacket[2]); break; } } else { /* preserve button states */ ms.button |= ms.obutton & MOUSE_EXTBUTTONS; } break; case MOUSE_MODEL_GLIDEPOINT: /* `tapping' action */ ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN; break; case MOUSE_MODEL_NETSCROLL: /* three addtional bytes encode button and wheel events */ ms.button |= (sc->ipacket[3] & MOUSE_PS2_BUTTON3DOWN) ? MOUSE_BUTTON4DOWN : 0; z = (sc->ipacket[3] & MOUSE_PS2_XNEG) ? sc->ipacket[4] - 256 : sc->ipacket[4]; break; case MOUSE_MODEL_THINK: /* the fourth button state in the first byte */ ms.button |= (c & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0; break; case MOUSE_MODEL_VERSAPAD: /* VersaPad PS/2 absolute mode message format * * [packet1] 7 6 5 4 3 2 1 0(LSB) * ipacket[0]: 1 1 0 A 1 L T R * ipacket[1]: H7 H6 H5 H4 H3 H2 H1 H0 * ipacket[2]: V7 V6 V5 V4 V3 V2 V1 V0 * ipacket[3]: 1 1 1 A 1 L T R * ipacket[4]:V11 V10 V9 V8 H11 H10 H9 H8 * ipacket[5]: 0 P6 P5 P4 P3 P2 P1 P0 * * [note] * R: right physical mouse button (1=on) * T: touch pad virtual button (1=tapping) * L: left physical mouse button (1=on) * A: position data is valid (1=valid) * H: horizontal data (12bit signed integer. H11 is sign bit.) * V: vertical data (12bit signed integer. V11 is sign bit.) * P: pressure data * * Tapping is mapped to MOUSE_BUTTON4. */ ms.button = butmap_versapad[c & MOUSE_PS2VERSA_BUTTONS]; ms.button |= (c & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0; x = y = 0; if (c & MOUSE_PS2VERSA_IN_USE) { x0 = sc->ipacket[1] | (((sc->ipacket[4]) & 0x0f) << 8); y0 = sc->ipacket[2] | (((sc->ipacket[4]) & 0xf0) << 4); if (x0 & 0x800) x0 -= 0x1000; if (y0 & 0x800) y0 -= 0x1000; if (sc->flags & PSM_FLAGS_FINGERDOWN) { x = sc->xold - x0; y = y0 - sc->yold; if (x < 0) /* XXX */ x++; else if (x) x--; if (y < 0) y++; else if (y) y--; } else { sc->flags |= PSM_FLAGS_FINGERDOWN; } sc->xold = x0; sc->yold = y0; } else { sc->flags &= ~PSM_FLAGS_FINGERDOWN; } c = ((x < 0) ? MOUSE_PS2_XNEG : 0) | ((y < 0) ? MOUSE_PS2_YNEG : 0); break; case MOUSE_MODEL_GENERIC: default: break; } /* scale values */ if (sc->mode.accelfactor >= 1) { if (x != 0) { x = x * x / sc->mode.accelfactor; if (x == 0) x = 1; if (c & MOUSE_PS2_XNEG) x = -x; } if (y != 0) { y = y * y / sc->mode.accelfactor; if (y == 0) y = 1; if (c & MOUSE_PS2_YNEG) y = -y; } } ms.dx = x; ms.dy = y; ms.dz = z; ms.flags = ((x || y || z) ? MOUSE_POSCHANGED : 0) | (ms.obutton ^ ms.button); if (sc->mode.level < PSM_LEVEL_NATIVE) sc->inputbytes = tame_mouse(sc, &ms, sc->ipacket); sc->status.flags |= ms.flags; sc->status.dx += ms.dx; sc->status.dy += ms.dy; sc->status.dz += ms.dz; sc->status.button = ms.button; sc->button = ms.button; /* queue data */ if (sc->queue.count + sc->inputbytes < sizeof(sc->queue.buf)) { l = min(sc->inputbytes, sizeof(sc->queue.buf) - sc->queue.tail); bcopy(&sc->ipacket[0], &sc->queue.buf[sc->queue.tail], l); if (sc->inputbytes > l) bcopy(&sc->ipacket[l], &sc->queue.buf[0], sc->inputbytes - l); sc->queue.tail = (sc->queue.tail + sc->inputbytes) % sizeof(sc->queue.buf); sc->queue.count += sc->inputbytes; } sc->inputbytes = 0; if (sc->state & PSM_ASLP) { sc->state &= ~PSM_ASLP; wakeup((caddr_t) sc); } selwakeup(&sc->rsel); } } static int psmpoll(dev_t dev, int events, struct proc *p) { struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); int s; int revents = 0; /* Return true if a mouse event available */ s = spltty(); if (events & (POLLIN | POLLRDNORM)) { if (sc->queue.count > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sc->rsel); } splx(s); return (revents); } /* vendor/model specific routines */ static int mouse_id_proc1(KBDC kbdc, int res, int scale, int *status) { if (set_mouse_resolution(kbdc, res) != res) return FALSE; if (set_mouse_scaling(kbdc, scale) && set_mouse_scaling(kbdc, scale) && set_mouse_scaling(kbdc, scale) && (get_mouse_status(kbdc, status, 0, 3) >= 3)) return TRUE; return FALSE; } #if notyet /* Logitech MouseMan Cordless II */ static int enable_lcordless(struct psm_softc *sc) { int status[3]; int ch; if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 2, status)) return FALSE; if (status[1] == PSMD_RES_HIGH) return FALSE; ch = (status[0] & 0x07) - 1; /* channel # */ if ((ch <= 0) || (ch > 4)) return FALSE; /* * status[1]: always one? * status[2]: battery status? (0-100) */ return TRUE; } #endif /* notyet */ /* Genius NetScroll Mouse */ static int enable_groller(struct psm_softc *sc) { int status[3]; /* * The special sequence to enable the fourth button and the * roller. Immediately after this sequence check status bytes. * if the mouse is NetScroll, the second and the third bytes are * '3' and 'D'. */ /* * If the mouse is an ordinary PS/2 mouse, the status bytes should * look like the following. * * byte 1 bit 7 always 0 * bit 6 stream mode (0) * bit 5 disabled (0) * bit 4 1:1 scaling (0) * bit 3 always 0 * bit 0-2 button status * byte 2 resolution (PSMD_RES_HIGH) * byte 3 report rate (?) */ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status)) return FALSE; if ((status[1] != '3') || (status[2] != 'D')) return FALSE; /* FIXME!! */ sc->hw.buttons = get_mouse_buttons(sc->kbdc); sc->hw.buttons = 4; return TRUE; } /* Genius NetMouse/NetMouse Pro */ static int enable_gmouse(struct psm_softc *sc) { int status[3]; /* * The special sequence to enable the middle, "rubber" button. * Immediately after this sequence check status bytes. * if the mouse is NetMouse, NetMouse Pro, or ASCII MIE Mouse, * the second and the third bytes are '3' and 'U'. * NOTE: NetMouse reports that it has three buttons although it has * two buttons and a rubber button. NetMouse Pro and MIE Mouse * say they have three buttons too and they do have a button on the * side... */ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status)) return FALSE; if ((status[1] != '3') || (status[2] != 'U')) return FALSE; return TRUE; } /* ALPS GlidePoint */ static int enable_aglide(struct psm_softc *sc) { int status[3]; /* * The special sequence to obtain ALPS GlidePoint specific * information. Immediately after this sequence, status bytes will * contain something interesting. * NOTE: ALPS produces several models of GlidePoint. Some of those * do not respond to this sequence, thus, cannot be detected this way. */ if (set_mouse_sampling_rate(sc->kbdc, 100) != 100) return FALSE; if (!mouse_id_proc1(sc->kbdc, PSMD_RES_LOW, 2, status)) return FALSE; if ((status[1] == PSMD_RES_LOW) || (status[2] == 100)) return FALSE; return TRUE; } /* Kensington ThinkingMouse/Trackball */ static int enable_kmouse(struct psm_softc *sc) { static unsigned char rate[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; KBDC kbdc = sc->kbdc; int status[3]; int id1; int id2; int i; id1 = get_aux_id(kbdc); if (set_mouse_sampling_rate(kbdc, 10) != 10) return FALSE; /* * The device is now in the native mode? It returns a different * ID value... */ id2 = get_aux_id(kbdc); if ((id1 == id2) || (id2 != 2)) return FALSE; if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW) return FALSE; #if PSM_DEBUG >= 2 /* at this point, resolution is LOW, sampling rate is 10/sec */ if (get_mouse_status(kbdc, status, 0, 3) < 3) return FALSE; #endif /* * The special sequence to enable the third and fourth buttons. * Otherwise they behave like the first and second buttons. */ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) { if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i]) return FALSE; } /* * At this point, the device is using default resolution and * sampling rate for the native mode. */ if (get_mouse_status(kbdc, status, 0, 3) < 3) return FALSE; if ((status[1] == PSMD_RES_LOW) || (status[2] == rate[i - 1])) return FALSE; /* the device appears be enabled by this sequence, diable it for now */ disable_aux_dev(kbdc); empty_aux_buffer(kbdc, 5); return TRUE; } /* Logitech MouseMan+/FirstMouse+ */ static int enable_mmanplus(struct psm_softc *sc) { static char res[] = { -1, PSMD_RES_LOW, PSMD_RES_HIGH, PSMD_RES_MEDIUM_HIGH, PSMD_RES_MEDIUM_LOW, -1, PSMD_RES_HIGH, PSMD_RES_MEDIUM_LOW, PSMD_RES_MEDIUM_HIGH, PSMD_RES_HIGH, }; KBDC kbdc = sc->kbdc; int data[3]; int i; /* the special sequence to enable the fourth button and the roller. */ for (i = 0; i < sizeof(res)/sizeof(res[0]); ++i) { if (res[i] < 0) { if (!set_mouse_scaling(kbdc, 1)) return FALSE; } else { if (set_mouse_resolution(kbdc, res[i]) != res[i]) return FALSE; } } if (get_mouse_status(kbdc, data, 1, 3) < 3) return FALSE; /* * PS2++ protocl, packet type 0 * * b7 b6 b5 b4 b3 b2 b1 b0 * byte 1: * 1 p3 p2 1 * * * * byte 2: 1 1 p1 p0 m1 m0 1 0 * byte 3: m7 m6 m5 m4 m3 m2 m1 m0 * * p3-p0: packet type: 0 * m7-m0: model ID: MouseMan+:0x50, FirstMouse+:0x51,... */ /* check constant bits */ if ((data[0] & MOUSE_PS2PLUS_SYNCMASK) != MOUSE_PS2PLUS_SYNC) return FALSE; if ((data[1] & 0xc3) != 0xc2) return FALSE; /* check d3-d0 in byte 2 */ if (!MOUSE_PS2PLUS_CHECKBITS(data)) return FALSE; /* check p3-p0 */ if (MOUSE_PS2PLUS_PACKET_TYPE(data) != 0) return FALSE; sc->hw.hwid &= 0x00ff; sc->hw.hwid |= data[2] << 8; /* save model ID */ /* * MouseMan+ (or FirstMouse+) is now in its native mode, in which * the wheel and the fourth button events are encoded in the * special data packet. The mouse may be put in the IntelliMouse mode * if it is initialized by the IntelliMouse's method. */ return TRUE; } /* MS IntelliMouse */ static int enable_msintelli(struct psm_softc *sc) { /* * Logitech MouseMan+ and FirstMouse+ will also respond to this * probe routine and act like IntelliMouse. */ static unsigned char rate[] = { 200, 100, 80, }; KBDC kbdc = sc->kbdc; int id; int i; /* the special sequence to enable the third button and the roller. */ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) { if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i]) return FALSE; } /* the device will give the genuine ID only after the above sequence */ id = get_aux_id(kbdc); if (id != PSM_INTELLI_ID) return FALSE; sc->hw.hwid = id; sc->hw.buttons = 3; return TRUE; } /* Interlink electronics VersaPad */ static int enable_versapad(struct psm_softc *sc) { KBDC kbdc = sc->kbdc; int data[3]; set_mouse_resolution(kbdc, PSMD_RES_MEDIUM_HIGH); /* set res. 2 */ set_mouse_sampling_rate(kbdc, 100); /* set rate 100 */ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */ if (get_mouse_status(kbdc, data, 0, 3) < 3) /* get status */ return FALSE; if (data[2] != 0xa || data[1] != 0 ) /* rate == 0xa && res. == 0 */ return FALSE; set_mouse_scaling(kbdc, 1); /* set scale 1:1 */ return TRUE; /* PS/2 absolute mode */ } static int psmresume(device_t dev) { #ifdef PSM_HOOKRESUME struct psm_softc *sc = device_get_softc(dev); int unit = device_get_unit(dev); int err = 0; int s; int c; if (verbose >= 2) log(LOG_NOTICE, "psm%d: system resume hook called.\n", unit); /* don't let anybody mess with the aux device */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); s = spltty(); /* save the current controller command byte */ empty_both_buffers(sc->kbdc, 10); c = get_controller_command_byte(sc->kbdc); if (verbose >= 2) log(LOG_DEBUG, "psm%d: current command byte: %04x (psmresume).\n", unit, c); /* enable the aux port but disable the aux interrupt and the keyboard */ if ((c == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ splx(s); kbdc_lock(sc->kbdc, FALSE); log(LOG_ERR, "psm%d: unable to set the command byte (psmresume).\n", unit); return (EIO); } /* flush any data */ if (sc->state & PSM_VALID) { disable_aux_dev(sc->kbdc); /* this may fail; but never mind... */ empty_aux_buffer(sc->kbdc, 10); } sc->inputbytes = 0; #ifdef PSM_RESETAFTERSUSPEND /* try to detect the aux device; are you still there? */ if (reinitialize(unit, &sc->mode)) { /* yes */ sc->state |= PSM_VALID; } else { /* the device has gone! */ restore_controller(sc->kbdc, c); sc->state &= ~PSM_VALID; log(LOG_ERR, "psm%d: the aux device has gone! (psmresume).\n", unit); err = ENXIO; } #endif /* PSM_RESETAFTERSUSPEND */ splx(s); /* restore the driver state */ if ((sc->state & PSM_OPEN) && (err == 0)) { /* enable the aux device and the port again */ err = doopen(unit, c); if (err != 0) log(LOG_ERR, "psm%d: failed to enable the device (psmresume).\n", unit); } else { /* restore the keyboard port and disable the aux port */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (c & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ log(LOG_ERR, "psm%d: failed to disable the aux port (psmresume).\n", unit); err = EIO; } } /* done */ kbdc_lock(sc->kbdc, FALSE); if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) { /* * Release the blocked process; it must be notified that the device * cannot be accessed anymore. */ sc->state &= ~PSM_ASLP; wakeup((caddr_t)sc); } if (verbose >= 2) log(LOG_DEBUG, "psm%d: system resume hook exiting.\n", unit); return (err); #else /* !PSM_HOOKRESUME */ return (0); #endif /* PSM_HOOKRESUME */ } DRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass, 0, 0); - -#endif /* NPSM > 0 */ Index: head/sys/dev/kbd/atkbd.c =================================================================== --- head/sys/dev/kbd/atkbd.c (revision 56835) +++ head/sys/dev/kbd/atkbd.c (revision 56836) @@ -1,1397 +1,1392 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "atkbd.h" #include "opt_kbd.h" #include "opt_atkbd.h" -#if NATKBD > 0 - #include #include #include #include #include #include #ifdef __i386__ #include #include #include #include #include #include #endif /* __i386__ */ #include #include #include #include static timeout_t atkbd_timeout; int atkbd_probe_unit(int unit, int port, int irq, int flags) { keyboard_switch_t *sw; int args[2]; int error; sw = kbd_get_switch(ATKBD_DRIVER_NAME); if (sw == NULL) return ENXIO; args[0] = port; args[1] = irq; error = (*sw->probe)(unit, args, flags); if (error) return error; return 0; } int atkbd_attach_unit(int unit, keyboard_t **kbd, int port, int irq, int flags) { keyboard_switch_t *sw; int args[2]; int error; sw = kbd_get_switch(ATKBD_DRIVER_NAME); if (sw == NULL) return ENXIO; /* reset, initialize and enable the device */ args[0] = port; args[1] = irq; *kbd = NULL; error = (*sw->probe)(unit, args, flags); if (error) return error; error = (*sw->init)(unit, kbd, args, flags); if (error) return error; (*sw->enable)(*kbd); #ifdef KBD_INSTALL_CDEV /* attach a virtual keyboard cdev */ error = kbd_attach(*kbd); if (error) return error; #endif /* * This is a kludge to compensate for lost keyboard interrupts. * A similar code used to be in syscons. See below. XXX */ atkbd_timeout(*kbd); if (bootverbose) (*sw->diag)(*kbd, bootverbose); return 0; } static void atkbd_timeout(void *arg) { keyboard_t *kbd; int s; /* * The original text of the following comments are extracted * from syscons.c (1.287) * * With release 2.1 of the Xaccel server, the keyboard is left * hanging pretty often. Apparently an interrupt from the * keyboard is lost, and I don't know why (yet). * This ugly hack calls the low-level interrupt routine if input * is ready for the keyboard and conveniently hides the problem. XXX * * Try removing anything stuck in the keyboard controller; whether * it's a keyboard scan code or mouse data. The low-level * interrupt routine doesn't read the mouse data directly, * but the keyboard controller driver will, as a side effect. */ /* * And here is bde's original comment about this: * * This is necessary to handle edge triggered interrupts - if we * returned when our IRQ is high due to unserviced input, then there * would be no more keyboard IRQs until the keyboard is reset by * external powers. * * The keyboard apparently unwedges the irq in most cases. */ s = spltty(); kbd = (keyboard_t *)arg; if ((*kbdsw[kbd->kb_index]->lock)(kbd, TRUE)) { /* * We have seen the lock flag is not set. Let's reset * the flag early, otherwise the LED update routine fails * which may want the lock during the interrupt routine. */ (*kbdsw[kbd->kb_index]->lock)(kbd, FALSE); if ((*kbdsw[kbd->kb_index]->check_char)(kbd)) (*kbdsw[kbd->kb_index]->intr)(kbd, NULL); } splx(s); timeout(atkbd_timeout, arg, hz/10); } /* LOW-LEVEL */ #include #include #include #define ATKBD_DEFAULT 0 typedef struct atkbd_state { KBDC kbdc; /* keyboard controller */ /* XXX: don't move this field; pcvt * expects `kbdc' to be the first * field in this structure. */ int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int ks_flags; /* flags */ #define COMPOSE (1 << 0) int ks_polling; int ks_state; /* shift/lock key state */ int ks_accents; /* accent key index (> 0) */ u_int ks_composed_char; /* composed char code (> 0) */ u_char ks_prefix; /* AT scan code prefix */ } atkbd_state_t; /* keyboard driver declaration */ static int atkbd_configure(int flags); static kbd_probe_t atkbd_probe; static kbd_init_t atkbd_init; static kbd_term_t atkbd_term; static kbd_intr_t atkbd_intr; static kbd_test_if_t atkbd_test_if; static kbd_enable_t atkbd_enable; static kbd_disable_t atkbd_disable; static kbd_read_t atkbd_read; static kbd_check_t atkbd_check; static kbd_read_char_t atkbd_read_char; static kbd_check_char_t atkbd_check_char; static kbd_ioctl_t atkbd_ioctl; static kbd_lock_t atkbd_lock; static kbd_clear_state_t atkbd_clear_state; static kbd_get_state_t atkbd_get_state; static kbd_set_state_t atkbd_set_state; static kbd_poll_mode_t atkbd_poll; keyboard_switch_t atkbdsw = { atkbd_probe, atkbd_init, atkbd_term, atkbd_intr, atkbd_test_if, atkbd_enable, atkbd_disable, atkbd_read, atkbd_check, atkbd_read_char, atkbd_check_char, atkbd_ioctl, atkbd_lock, atkbd_clear_state, atkbd_get_state, atkbd_set_state, genkbd_get_fkeystr, atkbd_poll, genkbd_diag, }; KEYBOARD_DRIVER(atkbd, atkbdsw, atkbd_configure); /* local functions */ static int get_typematic(keyboard_t *kbd); static int setup_kbd_port(KBDC kbdc, int port, int intr); static int get_kbd_echo(KBDC kbdc); static int probe_keyboard(KBDC kbdc, int flags); static int init_keyboard(KBDC kbdc, int *type, int flags); static int write_kbd(KBDC kbdc, int command, int data); static int get_kbd_id(KBDC kbdc); static int typematic(int delay, int rate); static int typematic_delay(int delay); static int typematic_rate(int rate); /* local variables */ /* the initial key map, accent map and fkey strings */ #ifdef ATKBD_DFLT_KEYMAP #define KBD_DFLT_KEYMAP #include "atkbdmap.h" #endif #include /* structures for the default keyboard */ static keyboard_t default_kbd; static atkbd_state_t default_kbd_state; static keymap_t default_keymap; static accentmap_t default_accentmap; static fkeytab_t default_fkeytab[NUM_FKEYS]; /* * The back door to the keyboard driver! * This function is called by the console driver, via the kbdio module, * to tickle keyboard drivers when the low-level console is being initialized. * Almost nothing in the kernel has been initialied yet. Try to probe * keyboards if possible. * NOTE: because of the way the low-level conole is initialized, this routine * may be called more than once!! */ static int atkbd_configure(int flags) { keyboard_t *kbd; int arg[2]; int i; /* probe the keyboard controller */ atkbdc_configure(); /* if the driver is disabled, unregister the keyboard if any */ if ((resource_int_value("atkbd", ATKBD_DEFAULT, "disabled", &i) == 0) && i != 0) { i = kbd_find_keyboard(ATKBD_DRIVER_NAME, ATKBD_DEFAULT); if (i >= 0) { kbd = kbd_get_keyboard(i); kbd_unregister(kbd); kbd->kb_flags &= ~KB_REGISTERED; } return 0; } /* XXX: a kludge to obtain the device configuration flags */ if (resource_int_value("atkbd", ATKBD_DEFAULT, "flags", &i) == 0) flags |= i; /* probe the default keyboard */ arg[0] = -1; arg[1] = -1; kbd = NULL; if (atkbd_probe(ATKBD_DEFAULT, arg, flags)) return 0; if (atkbd_init(ATKBD_DEFAULT, &kbd, arg, flags)) return 0; /* return the number of found keyboards */ return 1; } /* low-level functions */ /* detect a keyboard */ static int atkbd_probe(int unit, void *arg, int flags) { KBDC kbdc; int *data = (int *)arg; /* XXX */ if (unit == ATKBD_DEFAULT) { if (KBD_IS_PROBED(&default_kbd)) return 0; } kbdc = kbdc_open(data[0]); if (kbdc == NULL) return ENXIO; if (probe_keyboard(kbdc, flags)) { if (flags & KB_CONF_FAIL_IF_NO_KBD) return ENXIO; } return 0; } /* reset and initialize the device */ static int atkbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) { keyboard_t *kbd; atkbd_state_t *state; keymap_t *keymap; accentmap_t *accmap; fkeytab_t *fkeymap; int fkeymap_size; int delay[2]; int *data = (int *)arg; /* XXX */ if (unit == ATKBD_DEFAULT) { *kbdp = kbd = &default_kbd; if (KBD_IS_INITIALIZED(kbd) && KBD_IS_CONFIGURED(kbd)) return 0; state = &default_kbd_state; keymap = &default_keymap; accmap = &default_accentmap; fkeymap = default_fkeytab; fkeymap_size = sizeof(default_fkeytab)/sizeof(default_fkeytab[0]); } else if (*kbdp == NULL) { *kbdp = kbd = malloc(sizeof(*kbd), M_DEVBUF, M_NOWAIT); if (kbd == NULL) return ENOMEM; bzero(kbd, sizeof(*kbd)); state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT); keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT); accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT); fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT); fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]); if ((state == NULL) || (keymap == NULL) || (accmap == NULL) || (fkeymap == NULL)) { if (state != NULL) free(state, M_DEVBUF); if (keymap != NULL) free(keymap, M_DEVBUF); if (accmap != NULL) free(accmap, M_DEVBUF); if (fkeymap != NULL) free(fkeymap, M_DEVBUF); free(kbd, M_DEVBUF); return ENOMEM; } bzero(state, sizeof(*state)); } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) { return 0; } else { kbd = *kbdp; state = (atkbd_state_t *)kbd->kb_data; bzero(state, sizeof(*state)); keymap = kbd->kb_keymap; accmap = kbd->kb_accentmap; fkeymap = kbd->kb_fkeytab; fkeymap_size = kbd->kb_fkeytab_size; } if (!KBD_IS_PROBED(kbd)) { state->kbdc = kbdc_open(data[0]); if (state->kbdc == NULL) return ENXIO; kbd_init_struct(kbd, ATKBD_DRIVER_NAME, KB_OTHER, unit, flags, data[0], IO_KBDSIZE); bcopy(&key_map, keymap, sizeof(key_map)); bcopy(&accent_map, accmap, sizeof(accent_map)); bcopy(fkey_tab, fkeymap, imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab))); kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size); kbd->kb_data = (void *)state; if (probe_keyboard(state->kbdc, flags)) { /* shouldn't happen */ if (flags & KB_CONF_FAIL_IF_NO_KBD) return ENXIO; } else { KBD_FOUND_DEVICE(kbd); } atkbd_clear_state(kbd); state->ks_mode = K_XLATE; /* * FIXME: set the initial value for lock keys in ks_state * according to the BIOS data? */ KBD_PROBE_DONE(kbd); } if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) { kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY; if (KBD_HAS_DEVICE(kbd) && init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config) && (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD)) return ENXIO; atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); get_typematic(kbd); delay[0] = kbd->kb_delay1; delay[1] = kbd->kb_delay2; atkbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay); KBD_INIT_DONE(kbd); } if (!KBD_IS_CONFIGURED(kbd)) { if (kbd_register(kbd) < 0) return ENXIO; KBD_CONFIG_DONE(kbd); } return 0; } /* finish using this keyboard */ static int atkbd_term(keyboard_t *kbd) { kbd_unregister(kbd); return 0; } /* keyboard interrupt routine */ static int atkbd_intr(keyboard_t *kbd, void *arg) { atkbd_state_t *state; int delay[2]; int c; if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) { /* let the callback function to process the input */ (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT, kbd->kb_callback.kc_arg); } else { /* read and discard the input; no one is waiting for input */ do { c = atkbd_read_char(kbd, FALSE); } while (c != NOKEY); if (!KBD_HAS_DEVICE(kbd)) { /* * The keyboard was not detected before; * it must have been reconnected! */ state = (atkbd_state_t *)kbd->kb_data; init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config); atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); get_typematic(kbd); delay[0] = kbd->kb_delay1; delay[1] = kbd->kb_delay2; atkbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay); KBD_FOUND_DEVICE(kbd); } } return 0; } /* test the interface to the device */ static int atkbd_test_if(keyboard_t *kbd) { int error; int s; error = 0; empty_both_buffers(((atkbd_state_t *)kbd->kb_data)->kbdc, 10); s = spltty(); if (!test_controller(((atkbd_state_t *)kbd->kb_data)->kbdc)) error = EIO; else if (test_kbd_port(((atkbd_state_t *)kbd->kb_data)->kbdc) != 0) error = EIO; splx(s); return error; } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int atkbd_enable(keyboard_t *kbd) { int s; s = spltty(); KBD_ACTIVATE(kbd); splx(s); return 0; } /* disallow the access to the device */ static int atkbd_disable(keyboard_t *kbd) { int s; s = spltty(); KBD_DEACTIVATE(kbd); splx(s); return 0; } /* read one byte from the keyboard if it's allowed */ static int atkbd_read(keyboard_t *kbd, int wait) { int c; if (wait) c = read_kbd_data(((atkbd_state_t *)kbd->kb_data)->kbdc); else c = read_kbd_data_no_wait(((atkbd_state_t *)kbd->kb_data)->kbdc); if (c != -1) ++kbd->kb_count; return (KBD_IS_ACTIVE(kbd) ? c : -1); } /* check if data is waiting */ static int atkbd_check(keyboard_t *kbd) { if (!KBD_IS_ACTIVE(kbd)) return FALSE; return kbdc_data_ready(((atkbd_state_t *)kbd->kb_data)->kbdc); } /* read char from the keyboard */ static u_int atkbd_read_char(keyboard_t *kbd, int wait) { atkbd_state_t *state; u_int action; int scancode; int keycode; state = (atkbd_state_t *)kbd->kb_data; next_code: /* do we have a composed char to return? */ if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) { action = state->ks_composed_char; state->ks_composed_char = 0; if (action > UCHAR_MAX) return ERRKEY; return action; } /* see if there is something in the keyboard port */ if (wait) { do { scancode = read_kbd_data(state->kbdc); } while (scancode == -1); } else { scancode = read_kbd_data_no_wait(state->kbdc); if (scancode == -1) return NOKEY; } ++kbd->kb_count; #if KBDIO_DEBUG >= 10 printf("atkbd_read_char(): scancode:0x%x\n", scancode); #endif /* return the byte as is for the K_RAW mode */ if (state->ks_mode == K_RAW) return scancode; /* translate the scan code into a keycode */ keycode = scancode & 0x7F; switch (state->ks_prefix) { case 0x00: /* normal scancode */ switch(scancode) { case 0xB8: /* left alt (compose key) released */ if (state->ks_flags & COMPOSE) { state->ks_flags &= ~COMPOSE; if (state->ks_composed_char > UCHAR_MAX) state->ks_composed_char = 0; } break; case 0x38: /* left alt (compose key) pressed */ if (!(state->ks_flags & COMPOSE)) { state->ks_flags |= COMPOSE; state->ks_composed_char = 0; } break; case 0xE0: case 0xE1: state->ks_prefix = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ state->ks_prefix = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; case 0x35: /* keypad divide key */ keycode = 0x5B; break; case 0x37: /* print scrn key */ keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ keycode = 0x5D; break; case 0x46: /* ctrl-pause/break on AT 101 (see below) */ keycode = 0x68; break; case 0x47: /* grey home key */ keycode = 0x5E; break; case 0x48: /* grey up arrow key */ keycode = 0x5F; break; case 0x49: /* grey page up key */ keycode = 0x60; break; case 0x4B: /* grey left arrow key */ keycode = 0x61; break; case 0x4D: /* grey right arrow key */ keycode = 0x62; break; case 0x4F: /* grey end key */ keycode = 0x63; break; case 0x50: /* grey down arrow key */ keycode = 0x64; break; case 0x51: /* grey page down key */ keycode = 0x65; break; case 0x52: /* grey insert key */ keycode = 0x66; break; case 0x53: /* grey delete key */ keycode = 0x67; break; /* the following 3 are only used on the MS "Natural" keyboard */ case 0x5b: /* left Window key */ keycode = 0x69; break; case 0x5c: /* right Window key */ keycode = 0x6a; break; case 0x5d: /* menu key */ keycode = 0x6b; break; default: /* ignore everything else */ goto next_code; } break; case 0xE1: /* 0xE1 prefix */ /* * The pause/break key on the 101 keyboard produces: * E1-1D-45 E1-9D-C5 * Ctrl-pause/break produces: * E0-46 E0-C6 (See above.) */ state->ks_prefix = 0; if (keycode == 0x1D) state->ks_prefix = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ state->ks_prefix = 0; if (keycode != 0x45) goto next_code; keycode = 0x68; break; } if (kbd->kb_type == KB_84) { switch (keycode) { case 0x37: /* *(numpad)/print screen */ if (state->ks_flags & SHIFTS) keycode = 0x5c; /* print screen */ break; case 0x45: /* num lock/pause */ if (state->ks_flags & CTLS) keycode = 0x68; /* pause */ break; case 0x46: /* scroll lock/break */ if (state->ks_flags & CTLS) keycode = 0x6c; /* break */ break; } } else if (kbd->kb_type == KB_101) { switch (keycode) { case 0x5c: /* print screen */ if (state->ks_flags & ALTS) keycode = 0x54; /* sysrq */ break; case 0x68: /* pause/break */ if (state->ks_flags & CTLS) keycode = 0x6c; /* break */ break; } } /* return the key code in the K_CODE mode */ if (state->ks_mode == K_CODE) return (keycode | (scancode & 0x80)); /* compose a character code */ if (state->ks_flags & COMPOSE) { switch (keycode | (scancode & 0x80)) { /* key pressed, process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ state->ks_composed_char *= 10; state->ks_composed_char += keycode - 0x40; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ state->ks_composed_char *= 10; state->ks_composed_char += keycode - 0x47; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ state->ks_composed_char *= 10; state->ks_composed_char += keycode - 0x4E; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; case 0x52: /* keypad 0 */ state->ks_composed_char *= 10; if (state->ks_composed_char > UCHAR_MAX) return ERRKEY; goto next_code; /* key released, no interest here */ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ case 0xD2: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (state->ks_composed_char > 0) { state->ks_flags &= ~COMPOSE; state->ks_composed_char = 0; return ERRKEY; } break; } } /* keycode to key action */ action = genkbd_keyaction(kbd, keycode, scancode & 0x80, &state->ks_state, &state->ks_accents); if (action == NOKEY) goto next_code; else return action; } /* check if char is waiting */ static int atkbd_check_char(keyboard_t *kbd) { atkbd_state_t *state; if (!KBD_IS_ACTIVE(kbd)) return FALSE; state = (atkbd_state_t *)kbd->kb_data; if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) return TRUE; return kbdc_data_ready(state->kbdc); } /* some useful control functions */ static int atkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { /* trasnlate LED_XXX bits into the device specific bits */ static u_char ledmap[8] = { 0, 4, 2, 6, 1, 5, 3, 7, }; atkbd_state_t *state = kbd->kb_data; int error; int s; int i; s = spltty(); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = state->ks_mode; break; case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (state->ks_mode != K_XLATE) { /* make lock key state and LED state match */ state->ks_state &= ~LOCK_MASK; state->ks_state |= KBD_LED_VAL(kbd); } /* FALL THROUGH */ case K_RAW: case K_CODE: if (state->ks_mode != *(int *)arg) { atkbd_clear_state(kbd); state->ks_mode = *(int *)arg; } break; default: splx(s); return EINVAL; } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in ks_state won't be changed */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } if (KBD_HAS_DEVICE(kbd)) { error = write_kbd(state->kbdc, KBDC_SET_LEDS, ledmap[i & LED_MASK]); if (error) { splx(s); return error; } } KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = state->ks_state & LOCK_MASK; break; case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { splx(s); return EINVAL; } state->ks_state &= ~LOCK_MASK; state->ks_state |= *(int *)arg; splx(s); /* set LEDs and quit */ return atkbd_ioctl(kbd, KDSETLED, arg); case KDSETREPEAT: /* set keyboard repeat rate (new interface) */ splx(s); if (!KBD_HAS_DEVICE(kbd)) return 0; i = typematic(((int *)arg)[0], ((int *)arg)[1]); error = write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, i); if (error == 0) { kbd->kb_delay1 = typematic_delay(i); kbd->kb_delay2 = typematic_rate(i); } return error; case KDSETRAD: /* set keyboard repeat rate (old interface) */ splx(s); if (!KBD_HAS_DEVICE(kbd)) return 0; error = write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, *(int *)arg); if (error == 0) { kbd->kb_delay1 = typematic_delay(*(int *)arg); kbd->kb_delay2 = typematic_rate(*(int *)arg); } return error; case PIO_KEYMAP: /* set keyboard translation table */ case PIO_KEYMAPENT: /* set keyboard translation table entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ state->ks_accents = 0; /* FALL THROUGH */ default: splx(s); return genkbd_commonioctl(kbd, cmd, arg); } splx(s); return 0; } /* lock the access to the keyboard */ static int atkbd_lock(keyboard_t *kbd, int lock) { return kbdc_lock(((atkbd_state_t *)kbd->kb_data)->kbdc, lock); } /* clear the internal state of the keyboard */ static void atkbd_clear_state(keyboard_t *kbd) { atkbd_state_t *state; state = (atkbd_state_t *)kbd->kb_data; state->ks_flags = 0; state->ks_polling = 0; state->ks_state &= LOCK_MASK; /* preserve locking key state */ state->ks_accents = 0; state->ks_composed_char = 0; #if 0 state->ks_prefix = 0; /* XXX */ #endif } /* save the internal state */ static int atkbd_get_state(keyboard_t *kbd, void *buf, size_t len) { if (len == 0) return sizeof(atkbd_state_t); if (len < sizeof(atkbd_state_t)) return -1; bcopy(kbd->kb_data, buf, sizeof(atkbd_state_t)); return 0; } /* set the internal state */ static int atkbd_set_state(keyboard_t *kbd, void *buf, size_t len) { if (len < sizeof(atkbd_state_t)) return ENOMEM; if (((atkbd_state_t *)kbd->kb_data)->kbdc != ((atkbd_state_t *)buf)->kbdc) return ENOMEM; bcopy(buf, kbd->kb_data, sizeof(atkbd_state_t)); return 0; } static int atkbd_poll(keyboard_t *kbd, int on) { atkbd_state_t *state; int s; state = (atkbd_state_t *)kbd->kb_data; s = spltty(); if (on) ++state->ks_polling; else --state->ks_polling; splx(s); return 0; } /* local functions */ static int get_typematic(keyboard_t *kbd) { #ifdef __i386__ /* * Only some systems allow us to retrieve the keyboard repeat * rate previously set via the BIOS... */ struct vm86frame vmf; u_int32_t p; bzero(&vmf, sizeof(vmf)); vmf.vmf_ax = 0xc000; vm86_intcall(0x15, &vmf); if ((vmf.vmf_eflags & PSL_C) || vmf.vmf_ah) return ENODEV; p = BIOS_PADDRTOVADDR(((u_int32_t)vmf.vmf_es << 4) + vmf.vmf_bx); if ((readb(p + 6) & 0x40) == 0) /* int 16, function 0x09 supported? */ return ENODEV; vmf.vmf_ax = 0x0900; vm86_intcall(0x16, &vmf); if ((vmf.vmf_al & 0x08) == 0) /* int 16, function 0x0306 supported? */ return ENODEV; vmf.vmf_ax = 0x0306; vm86_intcall(0x16, &vmf); kbd->kb_delay1 = typematic_delay(vmf.vmf_bh << 5); kbd->kb_delay2 = typematic_rate(vmf.vmf_bl); return 0; #else return ENODEV; #endif /* __i386__ */ } static int setup_kbd_port(KBDC kbdc, int port, int intr) { if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS, ((port) ? KBD_ENABLE_KBD_PORT : KBD_DISABLE_KBD_PORT) | ((intr) ? KBD_ENABLE_KBD_INT : KBD_DISABLE_KBD_INT))) return 1; return 0; } static int get_kbd_echo(KBDC kbdc) { /* enable the keyboard port, but disable the keyboard intr. */ if (setup_kbd_port(kbdc, TRUE, FALSE)) /* CONTROLLER ERROR: there is very little we can do... */ return ENXIO; /* see if something is present */ write_kbd_command(kbdc, KBDC_ECHO); if (read_kbd_data(kbdc) != KBD_ECHO) { empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); return ENXIO; } /* enable the keyboard port and intr. */ if (setup_kbd_port(kbdc, TRUE, TRUE)) { /* * CONTROLLER ERROR * This is serious; the keyboard intr is left disabled! */ return ENXIO; } return 0; } static int probe_keyboard(KBDC kbdc, int flags) { /* * Don't try to print anything in this function. The low-level * console may not have been initialized yet... */ int err; int c; int m; if (!kbdc_lock(kbdc, TRUE)) { /* driver error? */ return ENXIO; } /* flush any noise in the buffer */ empty_both_buffers(kbdc, 10); /* save the current keyboard controller command byte */ m = kbdc_get_device_mask(kbdc) & ~KBD_KBD_CONTROL_BITS; c = get_controller_command_byte(kbdc); if (c == -1) { /* CONTROLLER ERROR */ kbdc_set_device_mask(kbdc, m); kbdc_lock(kbdc, FALSE); return ENXIO; } /* * The keyboard may have been screwed up by the boot block. * We may just be able to recover from error by testing the controller * and the keyboard port. The controller command byte needs to be * saved before this recovery operation, as some controllers seem * to set the command byte to particular values. */ test_controller(kbdc); test_kbd_port(kbdc); err = get_kbd_echo(kbdc); if (err == 0) { kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS); } else { if (c != -1) /* try to restore the command byte as before */ set_controller_command_byte(kbdc, 0xff, c); kbdc_set_device_mask(kbdc, m); } kbdc_lock(kbdc, FALSE); return err; } static int init_keyboard(KBDC kbdc, int *type, int flags) { int codeset; int id; int c; if (!kbdc_lock(kbdc, TRUE)) { /* driver error? */ return EIO; } /* save the current controller command byte */ empty_both_buffers(kbdc, 200); c = get_controller_command_byte(kbdc); if (c == -1) { /* CONTROLLER ERROR */ kbdc_lock(kbdc, FALSE); printf("atkbd: unable to get the current command byte value.\n"); return EIO; } if (bootverbose) printf("atkbd: the current kbd controller command byte %04x\n", c); #if 0 /* override the keyboard lock switch */ c |= KBD_OVERRIDE_KBD_LOCK; #endif /* enable the keyboard port, but disable the keyboard intr. */ if (setup_kbd_port(kbdc, TRUE, FALSE)) { /* CONTROLLER ERROR: there is very little we can do... */ printf("atkbd: unable to set the command byte.\n"); kbdc_lock(kbdc, FALSE); return EIO; } /* * Check if we have an XT keyboard before we attempt to reset it. * The procedure assumes that the keyboard and the controller have * been set up properly by BIOS and have not been messed up * during the boot process. */ codeset = -1; if (flags & KB_CONF_ALT_SCANCODESET) /* the user says there is a XT keyboard */ codeset = 1; #ifdef KBD_DETECT_XT_KEYBOARD else if ((c & KBD_TRANSLATION) == 0) { /* SET_SCANCODE_SET is not always supported; ignore error */ if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, 0) == KBD_ACK) codeset = read_kbd_data(kbdc); } if (bootverbose) printf("atkbd: scancode set %d\n", codeset); #endif /* KBD_DETECT_XT_KEYBOARD */ *type = KB_OTHER; id = get_kbd_id(kbdc); switch(id) { case 0x41ab: /* 101/102/... Enhanced */ case 0x83ab: /* ditto */ case 0x54ab: /* SpaceSaver */ case 0x84ab: /* ditto */ #if 0 case 0x90ab: /* 'G' */ case 0x91ab: /* 'P' */ case 0x92ab: /* 'A' */ #endif *type = KB_101; break; case -1: /* AT 84 keyboard doesn't return ID */ *type = KB_84; break; default: break; } if (bootverbose) printf("atkbd: keyboard ID 0x%x (%d)\n", id, *type); /* reset keyboard hardware */ if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) { /* * KEYBOARD ERROR * Keyboard reset may fail either because the keyboard * doen't exist, or because the keyboard doesn't pass * the self-test, or the keyboard controller on the * motherboard and the keyboard somehow fail to shake hands. * It is just possible, particularly in the last case, * that the keyoard controller may be left in a hung state. * test_controller() and test_kbd_port() appear to bring * the keyboard controller back (I don't know why and how, * though.) */ empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); /* * We could disable the keyboard port and interrupt... but, * the keyboard may still exist (see above). */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); if (bootverbose) printf("atkbd: failed to reset the keyboard.\n"); return EIO; } /* * Allow us to set the XT_KEYBD flag in UserConfig so that keyboards * such as those on the IBM ThinkPad laptop computers can be used * with the standard console driver. */ if (codeset == 1) { if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, codeset) == KBD_ACK) { /* XT kbd doesn't need scan code translation */ c &= ~KBD_TRANSLATION; } else { /* * KEYBOARD ERROR * The XT kbd isn't usable unless the proper scan * code set is selected. */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); printf("atkbd: unable to set the XT keyboard mode.\n"); return EIO; } } #ifdef __alpha__ if (send_kbd_command_and_data( kbdc, KBDC_SET_SCANCODE_SET, 2) != KBD_ACK) { printf("atkbd: can't set translation.\n"); } c |= KBD_TRANSLATION; #endif /* enable the keyboard port and intr. */ if (!set_controller_command_byte(kbdc, KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK, (c & (KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK)) | KBD_ENABLE_KBD_PORT | KBD_ENABLE_KBD_INT)) { /* * CONTROLLER ERROR * This is serious; we are left with the disabled * keyboard intr. */ set_controller_command_byte(kbdc, 0xff, c); kbdc_lock(kbdc, FALSE); printf("atkbd: unable to enable the keyboard port and intr.\n"); return EIO; } kbdc_lock(kbdc, FALSE); return 0; } static int write_kbd(KBDC kbdc, int command, int data) { int s; /* prevent the timeout routine from polling the keyboard */ if (!kbdc_lock(kbdc, TRUE)) return EBUSY; /* disable the keyboard and mouse interrupt */ s = spltty(); #if 0 c = get_controller_command_byte(kbdc); if ((c == -1) || !set_controller_command_byte(kbdc, kbdc_get_device_mask(kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ kbdc_lock(kbdc, FALSE); splx(s); return EIO; } /* * Now that the keyboard controller is told not to generate * the keyboard and mouse interrupts, call `splx()' to allow * the other tty interrupts. The clock interrupt may also occur, * but the timeout routine (`scrn_timer()') will be blocked * by the lock flag set via `kbdc_lock()' */ splx(s); #endif if (send_kbd_command_and_data(kbdc, command, data) != KBD_ACK) send_kbd_command(kbdc, KBDC_ENABLE_KBD); #if 0 /* restore the interrupts */ if (!set_controller_command_byte(kbdc, kbdc_get_device_mask(kbdc), c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR */ } #else splx(s); #endif kbdc_lock(kbdc, FALSE); return 0; } static int get_kbd_id(KBDC kbdc) { int id1, id2; empty_both_buffers(kbdc, 10); id1 = id2 = -1; if (send_kbd_command(kbdc, KBDC_SEND_DEV_ID) != KBD_ACK) return -1; DELAY(10000); /* 10 msec delay */ id1 = read_kbd_data(kbdc); if (id1 != -1) id2 = read_kbd_data(kbdc); if ((id1 == -1) || (id2 == -1)) { empty_both_buffers(kbdc, 10); test_controller(kbdc); test_kbd_port(kbdc); return -1; } return ((id2 << 8) | id1); } static int delays[] = { 250, 500, 750, 1000 }; static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504 }; static int typematic_delay(int i) { return delays[(i >> 5) & 3]; } static int typematic_rate(int i) { return rates[i & 0x1f]; } static int typematic(int delay, int rate) { int value; int i; for (i = sizeof(delays)/sizeof(delays[0]) - 1; i > 0; --i) { if (delay >= delays[i]) break; } value = i << 5; for (i = sizeof(rates)/sizeof(rates[0]) - 1; i > 0; --i) { if (rate >= rates[i]) break; } value |= i; return value; } - -#endif /* NATKBD > 0 */ Index: head/sys/dev/kbd/atkbdc.c =================================================================== --- head/sys/dev/kbd/atkbdc.c (revision 56835) +++ head/sys/dev/kbd/atkbdc.c (revision 56836) @@ -1,1021 +1,1017 @@ /*- * Copyright (c) 1996-1999 * Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * from kbdio.c,v 1.13 1998/09/25 11:55:46 yokota Exp */ #include "atkbdc.h" #include "opt_kbd.h" #include #include #include #include #include #include #include #include /* constants */ -#define MAXKBDC MAX(NATKBDC, 1) +#define MAXKBDC MAX(NATKBDC, 1) /* XXX */ /* macros */ #ifndef MAX #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif #define kbdcp(p) ((atkbdc_softc_t *)(p)) #define nextq(i) (((i) + 1) % KBDQ_BUFSIZE) #define availq(q) ((q)->head != (q)->tail) #if KBDIO_DEBUG >= 2 #define emptyq(q) ((q)->tail = (q)->head = (q)->qcount = 0) #else #define emptyq(q) ((q)->tail = (q)->head = 0) #endif /* local variables */ /* * We always need at least one copy of the kbdc_softc struct for the * low-level console. As the low-level console accesses the keyboard * controller before kbdc, and all other devices, is probed, we * statically allocate one entry. XXX */ static atkbdc_softc_t default_kbdc; static atkbdc_softc_t *atkbdc_softc[MAXKBDC] = { &default_kbdc }; static int verbose = KBDIO_DEBUG; /* function prototypes */ static int atkbdc_setup(atkbdc_softc_t *sc, int port); static int addq(kqueue *q, int c); static int removeq(kqueue *q); static int wait_while_controller_busy(atkbdc_softc_t *kbdc); static int wait_for_data(atkbdc_softc_t *kbdc); static int wait_for_kbd_data(atkbdc_softc_t *kbdc); static int wait_for_kbd_ack(atkbdc_softc_t *kbdc); static int wait_for_aux_data(atkbdc_softc_t *kbdc); static int wait_for_aux_ack(atkbdc_softc_t *kbdc); -#if NATKBDC > 0 - atkbdc_softc_t *atkbdc_get_softc(int unit) { atkbdc_softc_t *sc; if (unit >= sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0])) return NULL; sc = atkbdc_softc[unit]; if (sc == NULL) { sc = atkbdc_softc[unit] = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); if (sc == NULL) return NULL; bzero(sc, sizeof(*sc)); sc->port = -1; /* XXX */ } return sc; } int atkbdc_probe_unit(int unit, int port) { if (port <= 0) return ENXIO; return 0; } int atkbdc_attach_unit(int unit, atkbdc_softc_t *sc, int port) { return atkbdc_setup(sc, port); } - -#endif /* NATKBDC > 0 */ /* the backdoor to the keyboard controller! XXX */ int atkbdc_configure(void) { return atkbdc_setup(atkbdc_softc[0], -1); } static int atkbdc_setup(atkbdc_softc_t *sc, int port) { if (port <= 0) port = IO_KBD; if (sc->port <= 0) { sc->command_byte = -1; sc->command_mask = 0; sc->lock = FALSE; sc->kbd.head = sc->kbd.tail = 0; sc->aux.head = sc->aux.tail = 0; #if KBDIO_DEBUG >= 2 sc->kbd.call_count = 0; sc->kbd.qcount = sc->kbd.max_qcount = 0; sc->aux.call_count = 0; sc->aux.qcount = sc->aux.max_qcount = 0; #endif } sc->port = port; /* may override the previous value */ return 0; } /* associate a port number with a KBDC */ KBDC kbdc_open(int port) { int s; int i; if (port <= 0) port = IO_KBD; s = spltty(); for (i = 0; i < sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0]); ++i) { if (atkbdc_softc[i] == NULL) continue; if (atkbdc_softc[i]->port == port) { splx(s); return (KBDC)atkbdc_softc[i]; } if (atkbdc_softc[i]->port <= 0) { if (atkbdc_setup(atkbdc_softc[i], port)) break; splx(s); return (KBDC)atkbdc_softc[i]; } } splx(s); return NULL; } /* * I/O access arbitration in `kbdio' * * The `kbdio' module uses a simplistic convention to arbitrate * I/O access to the controller/keyboard/mouse. The convention requires * close cooperation of the calling device driver. * * The device driver which utilizes the `kbdio' module are assumed to * have the following set of routines. * a. An interrupt handler (the bottom half of the driver). * b. Timeout routines which may briefly polls the keyboard controller. * c. Routines outside interrupt context (the top half of the driver). * They should follow the rules below: * 1. The interrupt handler may assume that it always has full access * to the controller/keyboard/mouse. * 2. The other routines must issue `spltty()' if they wish to * prevent the interrupt handler from accessing * the controller/keyboard/mouse. * 3. The timeout routines and the top half routines of the device driver * arbitrate I/O access by observing the lock flag in `kbdio'. * The flag is manipulated via `kbdc_lock()'; when one wants to * perform I/O, call `kbdc_lock(kbdc, TRUE)' and proceed only if * the call returns with TRUE. Otherwise the caller must back off. * Call `kbdc_lock(kbdc, FALSE)' when necessary I/O operaion * is finished. This mechanism does not prevent the interrupt * handler from being invoked at any time and carrying out I/O. * Therefore, `spltty()' must be strategically placed in the device * driver code. Also note that the timeout routine may interrupt * `kbdc_lock()' called by the top half of the driver, but this * interruption is OK so long as the timeout routine observes the * the rule 4 below. * 4. The interrupt and timeout routines should not extend I/O operation * across more than one interrupt or timeout; they must complete * necessary I/O operation within one invokation of the routine. * This measns that if the timeout routine acquires the lock flag, * it must reset the flag to FALSE before it returns. */ /* set/reset polling lock */ int kbdc_lock(KBDC p, int lock) { int prevlock; prevlock = kbdcp(p)->lock; kbdcp(p)->lock = lock; return (prevlock != lock); } /* check if any data is waiting to be processed */ int kbdc_data_ready(KBDC p) { return (availq(&kbdcp(p)->kbd) || availq(&kbdcp(p)->aux) || (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)); } /* queuing functions */ static int addq(kqueue *q, int c) { if (nextq(q->tail) != q->head) { q->q[q->tail] = c; q->tail = nextq(q->tail); #if KBDIO_DEBUG >= 2 ++q->call_count; ++q->qcount; if (q->qcount > q->max_qcount) q->max_qcount = q->qcount; #endif return TRUE; } return FALSE; } static int removeq(kqueue *q) { int c; if (q->tail != q->head) { c = q->q[q->head]; q->head = nextq(q->head); #if KBDIO_DEBUG >= 2 --q->qcount; #endif return c; } return -1; } /* * device I/O routines */ static int wait_while_controller_busy(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 100msec at most */ int retry = 5000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT)) & KBDS_INPUT_BUFFER_FULL) { if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->kbd, inb(port + KBD_DATA_PORT)); } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->aux, inb(port + KBD_DATA_PORT)); } DELAY(KBDC_DELAYTIME); if (--retry < 0) return FALSE; } return TRUE; } /* * wait for any data; whether it's from the controller, * the keyboard, or the aux device. */ static int wait_for_data(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) == 0) { DELAY(KBDC_DELAYTIME); if (--retry < 0) return 0; } DELAY(KBDD_DELAYTIME); return f; } /* wait for data from the keyboard */ static int wait_for_kbd_data(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) != KBDS_KBD_BUFFER_FULL) { if (f == KBDS_AUX_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->aux, inb(port + KBD_DATA_PORT)); } DELAY(KBDC_DELAYTIME); if (--retry < 0) return 0; } DELAY(KBDD_DELAYTIME); return f; } /* * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the keyboard. * queue anything else. */ static int wait_for_kbd_ack(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; int b; while (retry-- > 0) { if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); b = inb(port + KBD_DATA_PORT); if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { if ((b == KBD_ACK) || (b == KBD_RESEND) || (b == KBD_RESET_FAIL)) return b; addq(&kbdc->kbd, b); } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { addq(&kbdc->aux, b); } } DELAY(KBDC_DELAYTIME); } return -1; } /* wait for data from the aux device */ static int wait_for_aux_data(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) != KBDS_AUX_BUFFER_FULL) { if (f == KBDS_KBD_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->kbd, inb(port + KBD_DATA_PORT)); } DELAY(KBDC_DELAYTIME); if (--retry < 0) return 0; } DELAY(KBDD_DELAYTIME); return f; } /* * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the aux device. * queue anything else. */ static int wait_for_aux_ack(struct atkbdc_softc *kbdc) { /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; int b; while (retry-- > 0) { if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); b = inb(port + KBD_DATA_PORT); if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { if ((b == PSM_ACK) || (b == PSM_RESEND) || (b == PSM_RESET_FAIL)) return b; addq(&kbdc->aux, b); } else if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { addq(&kbdc->kbd, b); } } DELAY(KBDC_DELAYTIME); } return -1; } /* write a one byte command to the controller */ int write_controller_command(KBDC p, int c) { if (!wait_while_controller_busy(kbdcp(p))) return FALSE; outb(kbdcp(p)->port + KBD_COMMAND_PORT, c); return TRUE; } /* write a one byte data to the controller */ int write_controller_data(KBDC p, int c) { if (!wait_while_controller_busy(kbdcp(p))) return FALSE; outb(kbdcp(p)->port + KBD_DATA_PORT, c); return TRUE; } /* write a one byte keyboard command */ int write_kbd_command(KBDC p, int c) { if (!wait_while_controller_busy(kbdcp(p))) return FALSE; outb(kbdcp(p)->port + KBD_DATA_PORT, c); return TRUE; } /* write a one byte auxiliary device command */ int write_aux_command(KBDC p, int c) { if (!write_controller_command(p, KBDC_WRITE_TO_AUX)) return FALSE; return write_controller_data(p, c); } /* send a command to the keyboard and wait for ACK */ int send_kbd_command(KBDC p, int c) { int retry = KBD_MAXRETRY; int res = -1; while (retry-- > 0) { if (!write_kbd_command(p, c)) continue; res = wait_for_kbd_ack(kbdcp(p)); if (res == KBD_ACK) break; } return res; } /* send a command to the auxiliary device and wait for ACK */ int send_aux_command(KBDC p, int c) { int retry = KBD_MAXRETRY; int res = -1; while (retry-- > 0) { if (!write_aux_command(p, c)) continue; /* * FIXME: XXX * The aux device may have already sent one or two bytes of * status data, when a command is received. It will immediately * stop data transmission, thus, leaving an incomplete data * packet in our buffer. We have to discard any unprocessed * data in order to remove such packets. Well, we may remove * unprocessed, but necessary data byte as well... */ emptyq(&kbdcp(p)->aux); res = wait_for_aux_ack(kbdcp(p)); if (res == PSM_ACK) break; } return res; } /* send a command and a data to the keyboard, wait for ACKs */ int send_kbd_command_and_data(KBDC p, int c, int d) { int retry; int res = -1; for (retry = KBD_MAXRETRY; retry > 0; --retry) { if (!write_kbd_command(p, c)) continue; res = wait_for_kbd_ack(kbdcp(p)); if (res == KBD_ACK) break; else if (res != KBD_RESEND) return res; } if (retry <= 0) return res; for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) { if (!write_kbd_command(p, d)) continue; res = wait_for_kbd_ack(kbdcp(p)); if (res != KBD_RESEND) break; } return res; } /* send a command and a data to the auxiliary device, wait for ACKs */ int send_aux_command_and_data(KBDC p, int c, int d) { int retry; int res = -1; for (retry = KBD_MAXRETRY; retry > 0; --retry) { if (!write_aux_command(p, c)) continue; emptyq(&kbdcp(p)->aux); res = wait_for_aux_ack(kbdcp(p)); if (res == PSM_ACK) break; else if (res != PSM_RESEND) return res; } if (retry <= 0) return res; for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) { if (!write_aux_command(p, d)) continue; res = wait_for_aux_ack(kbdcp(p)); if (res != PSM_RESEND) break; } return res; } /* * read one byte from any source; whether from the controller, * the keyboard, or the aux device */ int read_controller_data(KBDC p) { if (availq(&kbdcp(p)->kbd)) return removeq(&kbdcp(p)->kbd); if (availq(&kbdcp(p)->aux)) return removeq(&kbdcp(p)->aux); if (!wait_for_data(kbdcp(p))) return -1; /* timeout */ return inb(kbdcp(p)->port + KBD_DATA_PORT); } #if KBDIO_DEBUG >= 2 static int call = 0; #endif /* read one byte from the keyboard */ int read_kbd_data(KBDC p) { #if KBDIO_DEBUG >= 2 if (++call > 2000) { call = 0; log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, " "aux q: %d calls, max %d chars\n", kbdcp(p)->kbd.call_count, kbdcp(p)->kbd.max_qcount, kbdcp(p)->aux.call_count, kbdcp(p)->aux.max_qcount); } #endif if (availq(&kbdcp(p)->kbd)) return removeq(&kbdcp(p)->kbd); if (!wait_for_kbd_data(kbdcp(p))) return -1; /* timeout */ return inb(kbdcp(p)->port + KBD_DATA_PORT); } /* read one byte from the keyboard, but return immediately if * no data is waiting */ int read_kbd_data_no_wait(KBDC p) { int f; #if KBDIO_DEBUG >= 2 if (++call > 2000) { call = 0; log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, " "aux q: %d calls, max %d chars\n", kbdcp(p)->kbd.call_count, kbdcp(p)->kbd.max_qcount, kbdcp(p)->aux.call_count, kbdcp(p)->aux.max_qcount); } #endif if (availq(&kbdcp(p)->kbd)) return removeq(&kbdcp(p)->kbd); f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; if (f == KBDS_AUX_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdcp(p)->aux, inb(kbdcp(p)->port + KBD_DATA_PORT)); f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; } if (f == KBDS_KBD_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); return inb(kbdcp(p)->port + KBD_DATA_PORT); } return -1; /* no data */ } /* read one byte from the aux device */ int read_aux_data(KBDC p) { if (availq(&kbdcp(p)->aux)) return removeq(&kbdcp(p)->aux); if (!wait_for_aux_data(kbdcp(p))) return -1; /* timeout */ return inb(kbdcp(p)->port + KBD_DATA_PORT); } /* read one byte from the aux device, but return immediately if * no data is waiting */ int read_aux_data_no_wait(KBDC p) { int f; if (availq(&kbdcp(p)->aux)) return removeq(&kbdcp(p)->aux); f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; if (f == KBDS_KBD_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdcp(p)->kbd, inb(kbdcp(p)->port + KBD_DATA_PORT)); f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; } if (f == KBDS_AUX_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); return inb(kbdcp(p)->port + KBD_DATA_PORT); } return -1; /* no data */ } /* discard data from the keyboard */ void empty_kbd_buffer(KBDC p, int wait) { int t; int b; int f; #if KBDIO_DEBUG >= 2 int c1 = 0; int c2 = 0; #endif int delta = 2; for (t = wait; t > 0; ) { if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); b = inb(kbdcp(p)->port + KBD_DATA_PORT); if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { addq(&kbdcp(p)->aux, b); #if KBDIO_DEBUG >= 2 ++c2; } else { ++c1; #endif } t = wait; } else { t -= delta; } DELAY(delta*1000); } #if KBDIO_DEBUG >= 2 if ((c1 > 0) || (c2 > 0)) log(LOG_DEBUG, "kbdc: %d:%d char read (empty_kbd_buffer)\n", c1, c2); #endif emptyq(&kbdcp(p)->kbd); } /* discard data from the aux device */ void empty_aux_buffer(KBDC p, int wait) { int t; int b; int f; #if KBDIO_DEBUG >= 2 int c1 = 0; int c2 = 0; #endif int delta = 2; for (t = wait; t > 0; ) { if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); b = inb(kbdcp(p)->port + KBD_DATA_PORT); if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { addq(&kbdcp(p)->kbd, b); #if KBDIO_DEBUG >= 2 ++c1; } else { ++c2; #endif } t = wait; } else { t -= delta; } DELAY(delta*1000); } #if KBDIO_DEBUG >= 2 if ((c1 > 0) || (c2 > 0)) log(LOG_DEBUG, "kbdc: %d:%d char read (empty_aux_buffer)\n", c1, c2); #endif emptyq(&kbdcp(p)->aux); } /* discard any data from the keyboard or the aux device */ void empty_both_buffers(KBDC p, int wait) { int t; int f; #if KBDIO_DEBUG >= 2 int c1 = 0; int c2 = 0; #endif int delta = 2; for (t = wait; t > 0; ) { if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); (void)inb(kbdcp(p)->port + KBD_DATA_PORT); #if KBDIO_DEBUG >= 2 if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) ++c1; else ++c2; #endif t = wait; } else { t -= delta; } DELAY(delta*1000); } #if KBDIO_DEBUG >= 2 if ((c1 > 0) || (c2 > 0)) log(LOG_DEBUG, "kbdc: %d:%d char read (empty_both_buffers)\n", c1, c2); #endif emptyq(&kbdcp(p)->kbd); emptyq(&kbdcp(p)->aux); } /* keyboard and mouse device control */ /* NOTE: enable the keyboard port but disable the keyboard * interrupt before calling "reset_kbd()". */ int reset_kbd(KBDC p) { int retry = KBD_MAXRETRY; int again = KBD_MAXWAIT; int c = KBD_RESEND; /* keep the compiler happy */ while (retry-- > 0) { empty_both_buffers(p, 10); if (!write_kbd_command(p, KBDC_RESET_KBD)) continue; emptyq(&kbdcp(p)->kbd); c = read_controller_data(p); if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: RESET_KBD return code:%04x\n", c); if (c == KBD_ACK) /* keyboard has agreed to reset itself... */ break; } if (retry < 0) return FALSE; while (again-- > 0) { /* wait awhile, well, in fact we must wait quite loooooooooooong */ DELAY(KBD_RESETDELAY*1000); c = read_controller_data(p); /* RESET_DONE/RESET_FAIL */ if (c != -1) /* wait again if the controller is not ready */ break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: RESET_KBD status:%04x\n", c); if (c != KBD_RESET_DONE) return FALSE; return TRUE; } /* NOTE: enable the aux port but disable the aux interrupt * before calling `reset_aux_dev()'. */ int reset_aux_dev(KBDC p) { int retry = KBD_MAXRETRY; int again = KBD_MAXWAIT; int c = PSM_RESEND; /* keep the compiler happy */ while (retry-- > 0) { empty_both_buffers(p, 10); if (!write_aux_command(p, PSMC_RESET_DEV)) continue; emptyq(&kbdcp(p)->aux); /* NOTE: Compaq Armada laptops require extra delay here. XXX */ for (again = KBD_MAXWAIT; again > 0; --again) { DELAY(KBD_RESETDELAY*1000); c = read_aux_data_no_wait(p); if (c != -1) break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: RESET_AUX return code:%04x\n", c); if (c == PSM_ACK) /* aux dev is about to reset... */ break; } if (retry < 0) return FALSE; for (again = KBD_MAXWAIT; again > 0; --again) { /* wait awhile, well, quite looooooooooooong */ DELAY(KBD_RESETDELAY*1000); c = read_aux_data_no_wait(p); /* RESET_DONE/RESET_FAIL */ if (c != -1) /* wait again if the controller is not ready */ break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: RESET_AUX status:%04x\n", c); if (c != PSM_RESET_DONE) /* reset status */ return FALSE; c = read_aux_data(p); /* device ID */ if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: RESET_AUX ID:%04x\n", c); /* NOTE: we could check the device ID now, but leave it later... */ return TRUE; } /* controller diagnostics and setup */ int test_controller(KBDC p) { int retry = KBD_MAXRETRY; int again = KBD_MAXWAIT; int c = KBD_DIAG_FAIL; while (retry-- > 0) { empty_both_buffers(p, 10); if (write_controller_command(p, KBDC_DIAGNOSE)) break; } if (retry < 0) return FALSE; emptyq(&kbdcp(p)->kbd); while (again-- > 0) { /* wait awhile */ DELAY(KBD_RESETDELAY*1000); c = read_controller_data(p); /* DIAG_DONE/DIAG_FAIL */ if (c != -1) /* wait again if the controller is not ready */ break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: DIAGNOSE status:%04x\n", c); return (c == KBD_DIAG_DONE); } int test_kbd_port(KBDC p) { int retry = KBD_MAXRETRY; int again = KBD_MAXWAIT; int c = -1; while (retry-- > 0) { empty_both_buffers(p, 10); if (write_controller_command(p, KBDC_TEST_KBD_PORT)) break; } if (retry < 0) return FALSE; emptyq(&kbdcp(p)->kbd); while (again-- > 0) { c = read_controller_data(p); if (c != -1) /* try again if the controller is not ready */ break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: TEST_KBD_PORT status:%04x\n", c); return c; } int test_aux_port(KBDC p) { int retry = KBD_MAXRETRY; int again = KBD_MAXWAIT; int c = -1; while (retry-- > 0) { empty_both_buffers(p, 10); if (write_controller_command(p, KBDC_TEST_AUX_PORT)) break; } if (retry < 0) return FALSE; emptyq(&kbdcp(p)->kbd); while (again-- > 0) { c = read_controller_data(p); if (c != -1) /* try again if the controller is not ready */ break; } if (verbose || bootverbose) log(LOG_DEBUG, "kbdc: TEST_AUX_PORT status:%04x\n", c); return c; } int kbdc_get_device_mask(KBDC p) { return kbdcp(p)->command_mask; } void kbdc_set_device_mask(KBDC p, int mask) { kbdcp(p)->command_mask = mask & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS); } int get_controller_command_byte(KBDC p) { if (kbdcp(p)->command_byte != -1) return kbdcp(p)->command_byte; if (!write_controller_command(p, KBDC_GET_COMMAND_BYTE)) return -1; emptyq(&kbdcp(p)->kbd); kbdcp(p)->command_byte = read_controller_data(p); return kbdcp(p)->command_byte; } int set_controller_command_byte(KBDC p, int mask, int command) { if (get_controller_command_byte(p) == -1) return FALSE; command = (kbdcp(p)->command_byte & ~mask) | (command & mask); if (command & KBD_DISABLE_KBD_PORT) { if (!write_controller_command(p, KBDC_DISABLE_KBD_PORT)) return FALSE; } if (!write_controller_command(p, KBDC_SET_COMMAND_BYTE)) return FALSE; if (!write_controller_data(p, command)) return FALSE; kbdcp(p)->command_byte = command; if (verbose) log(LOG_DEBUG, "kbdc: new command byte:%04x (set_controller...)\n", command); return TRUE; } Index: head/sys/dev/kbd/kbd.c =================================================================== --- head/sys/dev/kbd/kbd.c (revision 56835) +++ head/sys/dev/kbd/kbd.c (revision 56836) @@ -1,1247 +1,1246 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "kbd.h" #include "opt_kbd.h" #include #include #include #include #include #include #include #include #include #include #include #include #define KBD_INDEX(dev) minor(dev) typedef struct genkbd_softc { int gkb_flags; /* flag/status bits */ #define KB_ASLEEP (1 << 0) struct clist gkb_q; /* input queue */ struct selinfo gkb_rsel; } genkbd_softc_t; static SLIST_HEAD(, keyboard_driver) keyboard_drivers = SLIST_HEAD_INITIALIZER(keyboard_drivers); /* local arrays */ /* * We need at least one entry each in order to initialize a keyboard * for the kernel console. The arrays will be increased dynamically * when necessary. */ static int keyboards = 1; static keyboard_t *kbd_ini; static keyboard_t **keyboard = &kbd_ini; static keyboard_switch_t *kbdsw_ini; keyboard_switch_t **kbdsw = &kbdsw_ini; #define ARRAY_DELTA 4 static int kbd_realloc_array(void) { keyboard_t **new_kbd; keyboard_switch_t **new_kbdsw; int newsize; int s; s = spltty(); newsize = ((keyboards + ARRAY_DELTA)/ARRAY_DELTA)*ARRAY_DELTA; new_kbd = malloc(sizeof(*new_kbd)*newsize, M_DEVBUF, M_NOWAIT); if (new_kbd == NULL) { splx(s); return ENOMEM; } new_kbdsw = malloc(sizeof(*new_kbdsw)*newsize, M_DEVBUF, M_NOWAIT); if (new_kbdsw == NULL) { free(new_kbd, M_DEVBUF); splx(s); return ENOMEM; } bzero(new_kbd, sizeof(*new_kbd)*newsize); bzero(new_kbdsw, sizeof(*new_kbdsw)*newsize); bcopy(keyboard, new_kbd, sizeof(*keyboard)*keyboards); bcopy(kbdsw, new_kbdsw, sizeof(*kbdsw)*keyboards); if (keyboards > 1) { free(keyboard, M_DEVBUF); free(kbdsw, M_DEVBUF); } keyboard = new_kbd; kbdsw = new_kbdsw; keyboards = newsize; splx(s); if (bootverbose) printf("kbd: new array size %d\n", keyboards); return 0; } /* * Low-level keyboard driver functions * Keyboard subdrivers, such as the AT keyboard driver and the USB keyboard * driver, call these functions to initialize the keyboard_t structure * and register it to the virtual keyboard driver `kbd'. */ /* initialize the keyboard_t structure */ void kbd_init_struct(keyboard_t *kbd, char *name, int type, int unit, int config, int port, int port_size) { kbd->kb_flags = KB_NO_DEVICE; /* device has not been found */ kbd->kb_name = name; kbd->kb_type = type; kbd->kb_unit = unit; kbd->kb_config = config & ~KB_CONF_PROBE_ONLY; kbd->kb_led = 0; /* unknown */ kbd->kb_io_base = port; kbd->kb_io_size = port_size; kbd->kb_data = NULL; kbd->kb_keymap = NULL; kbd->kb_accentmap = NULL; kbd->kb_fkeytab = NULL; kbd->kb_fkeytab_size = 0; kbd->kb_delay1 = KB_DELAY1; /* these values are advisory only */ kbd->kb_delay2 = KB_DELAY2; kbd->kb_count = 0L; } void kbd_set_maps(keyboard_t *kbd, keymap_t *keymap, accentmap_t *accmap, fkeytab_t *fkeymap, int fkeymap_size) { kbd->kb_keymap = keymap; kbd->kb_accentmap = accmap; kbd->kb_fkeytab = fkeymap; kbd->kb_fkeytab_size = fkeymap_size; } /* declare a new keyboard driver */ int kbd_add_driver(keyboard_driver_t *driver) { if (SLIST_NEXT(driver, link)) return EINVAL; SLIST_INSERT_HEAD(&keyboard_drivers, driver, link); return 0; } int kbd_delete_driver(keyboard_driver_t *driver) { SLIST_REMOVE(&keyboard_drivers, driver, keyboard_driver, link); SLIST_NEXT(driver, link) = NULL; return 0; } /* register a keyboard and associate it with a function table */ int kbd_register(keyboard_t *kbd) { const keyboard_driver_t **list; const keyboard_driver_t *p; int index; for (index = 0; index < keyboards; ++index) { if (keyboard[index] == NULL) break; } if (index >= keyboards) { if (kbd_realloc_array()) return -1; } kbd->kb_index = index; KBD_UNBUSY(kbd); KBD_VALID(kbd); kbd->kb_active = 0; /* disabled until someone calls kbd_enable() */ kbd->kb_token = NULL; kbd->kb_callback.kc_func = NULL; kbd->kb_callback.kc_arg = NULL; SLIST_FOREACH(p, &keyboard_drivers, link) { if (strcmp(p->name, kbd->kb_name) == 0) { keyboard[index] = kbd; kbdsw[index] = p->kbdsw; return index; } } list = (const keyboard_driver_t **)kbddriver_set.ls_items; while ((p = *list++) != NULL) { if (strcmp(p->name, kbd->kb_name) == 0) { keyboard[index] = kbd; kbdsw[index] = p->kbdsw; return index; } } return -1; } int kbd_unregister(keyboard_t *kbd) { int error; int s; if ((kbd->kb_index < 0) || (kbd->kb_index >= keyboards)) return ENOENT; if (keyboard[kbd->kb_index] != kbd) return ENOENT; s = spltty(); if (KBD_IS_BUSY(kbd)) { error = (*kbd->kb_callback.kc_func)(kbd, KBDIO_UNLOADING, kbd->kb_callback.kc_arg); if (error) { splx(s); return error; } if (KBD_IS_BUSY(kbd)) { splx(s); return EBUSY; } } KBD_INVALID(kbd); keyboard[kbd->kb_index] = NULL; kbdsw[kbd->kb_index] = NULL; splx(s); return 0; } /* find a funciton table by the driver name */ keyboard_switch_t *kbd_get_switch(char *driver) { const keyboard_driver_t **list; const keyboard_driver_t *p; SLIST_FOREACH(p, &keyboard_drivers, link) { if (strcmp(p->name, driver) == 0) return p->kbdsw; } list = (const keyboard_driver_t **)kbddriver_set.ls_items; while ((p = *list++) != NULL) { if (strcmp(p->name, driver) == 0) return p->kbdsw; } return NULL; } /* * Keyboard client functions * Keyboard clients, such as the console driver `syscons' and the keyboard * cdev driver, use these functions to claim and release a keyboard for * exclusive use. */ /* find the keyboard specified by a driver name and a unit number */ int kbd_find_keyboard(char *driver, int unit) { int i; for (i = 0; i < keyboards; ++i) { if (keyboard[i] == NULL) continue; if (!KBD_IS_VALID(keyboard[i])) continue; if (strcmp("*", driver) && strcmp(keyboard[i]->kb_name, driver)) continue; if ((unit != -1) && (keyboard[i]->kb_unit != unit)) continue; return i; } return -1; } /* allocate a keyboard */ int kbd_allocate(char *driver, int unit, void *id, kbd_callback_func_t *func, void *arg) { int index; int s; if (func == NULL) return -1; s = spltty(); index = kbd_find_keyboard(driver, unit); if (index >= 0) { if (KBD_IS_BUSY(keyboard[index])) { splx(s); return -1; } keyboard[index]->kb_token = id; KBD_BUSY(keyboard[index]); keyboard[index]->kb_callback.kc_func = func; keyboard[index]->kb_callback.kc_arg = arg; (*kbdsw[index]->clear_state)(keyboard[index]); } splx(s); return index; } int kbd_release(keyboard_t *kbd, void *id) { int error; int s; s = spltty(); if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) { error = EINVAL; } else if (kbd->kb_token != id) { error = EPERM; } else { kbd->kb_token = NULL; KBD_UNBUSY(kbd); kbd->kb_callback.kc_func = NULL; kbd->kb_callback.kc_arg = NULL; (*kbdsw[kbd->kb_index]->clear_state)(kbd); error = 0; } splx(s); return error; } int kbd_change_callback(keyboard_t *kbd, void *id, kbd_callback_func_t *func, void *arg) { int error; int s; s = spltty(); if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) { error = EINVAL; } else if (kbd->kb_token != id) { error = EPERM; } else if (func == NULL) { error = EINVAL; } else { kbd->kb_callback.kc_func = func; kbd->kb_callback.kc_arg = arg; error = 0; } splx(s); return error; } /* get a keyboard structure */ keyboard_t *kbd_get_keyboard(int index) { if ((index < 0) || (index >= keyboards)) return NULL; if (keyboard[index] == NULL) return NULL; if (!KBD_IS_VALID(keyboard[index])) return NULL; return keyboard[index]; } /* * The back door for the console driver; configure keyboards * This function is for the kernel console to initialize keyboards * at very early stage. */ int kbd_configure(int flags) { const keyboard_driver_t **list; const keyboard_driver_t *p; SLIST_FOREACH(p, &keyboard_drivers, link) { if (p->configure != NULL) (*p->configure)(flags); } list = (const keyboard_driver_t **)kbddriver_set.ls_items; while ((p = *list++) != NULL) { if (p->configure != NULL) (*p->configure)(flags); } return 0; } #ifdef KBD_INSTALL_CDEV /* * Virtual keyboard cdev driver functions * The virtual keyboard driver dispatches driver functions to * appropriate subdrivers. */ #define KBD_UNIT(dev) minor(dev) static d_open_t genkbdopen; static d_close_t genkbdclose; static d_read_t genkbdread; static d_write_t genkbdwrite; static d_ioctl_t genkbdioctl; static d_poll_t genkbdpoll; #define CDEV_MAJOR 112 static struct cdevsw kbd_cdevsw = { /* open */ genkbdopen, /* close */ genkbdclose, /* read */ genkbdread, /* write */ genkbdwrite, /* ioctl */ genkbdioctl, /* poll */ genkbdpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "kbd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; int kbd_attach(keyboard_t *kbd) { dev_t dev; if (kbd->kb_index >= keyboards) return EINVAL; if (keyboard[kbd->kb_index] != kbd) return EINVAL; dev = make_dev(&kbd_cdevsw, kbd->kb_index, UID_ROOT, GID_WHEEL, 0600, "kbd%r", kbd->kb_index); if (dev->si_drv1 == NULL) dev->si_drv1 = malloc(sizeof(genkbd_softc_t), M_DEVBUF, M_WAITOK); bzero(dev->si_drv1, sizeof(genkbd_softc_t)); printf("kbd%d at %s%d\n", kbd->kb_index, kbd->kb_name, kbd->kb_unit); return 0; } int kbd_detach(keyboard_t *kbd) { dev_t dev; if (kbd->kb_index >= keyboards) return EINVAL; if (keyboard[kbd->kb_index] != kbd) return EINVAL; dev = makedev(kbd_cdevsw.d_maj, kbd->kb_index); if (dev->si_drv1) free(dev->si_drv1, M_DEVBUF); destroy_dev(dev); return 0; } /* * Generic keyboard cdev driver functions * Keyboard subdrivers may call these functions to implement common * driver functions. */ #define KB_QSIZE 512 #define KB_BUFSIZE 64 static kbd_callback_func_t genkbd_event; static int genkbdopen(dev_t dev, int mode, int flag, struct proc *p) { keyboard_t *kbd; genkbd_softc_t *sc; int s; int i; s = spltty(); sc = dev->si_drv1; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) { splx(s); return ENXIO; } i = kbd_allocate(kbd->kb_name, kbd->kb_unit, sc, genkbd_event, (void *)sc); if (i < 0) { splx(s); return EBUSY; } /* assert(i == kbd->kb_index) */ /* assert(kbd == kbd_get_keyboard(i)) */ /* * NOTE: even when we have successfully claimed a keyboard, * the device may still be missing (!KBD_HAS_DEVICE(kbd)). */ #if 0 bzero(&sc->gkb_q, sizeof(sc->gkb_q)); #endif clist_alloc_cblocks(&sc->gkb_q, KB_QSIZE, KB_QSIZE/2); /* XXX */ sc->gkb_rsel.si_flags = 0; sc->gkb_rsel.si_pid = 0; splx(s); return 0; } static int genkbdclose(dev_t dev, int mode, int flag, struct proc *p) { keyboard_t *kbd; genkbd_softc_t *sc; int s; /* * NOTE: the device may have already become invalid. * kbd == NULL || !KBD_IS_VALID(kbd) */ s = spltty(); sc = dev->si_drv1; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) { /* XXX: we shall be forgiving and don't report error... */ } else { kbd_release(kbd, (void *)sc); #if 0 clist_free_cblocks(&sc->gkb_q); #endif } splx(s); return 0; } static int genkbdread(dev_t dev, struct uio *uio, int flag) { keyboard_t *kbd; genkbd_softc_t *sc; u_char buffer[KB_BUFSIZE]; int len; int error; int s; /* wait for input */ s = spltty(); sc = dev->si_drv1; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) { splx(s); return ENXIO; } while (sc->gkb_q.c_cc == 0) { if (flag & IO_NDELAY) { splx(s); return EWOULDBLOCK; } sc->gkb_flags |= KB_ASLEEP; error = tsleep((caddr_t)sc, PZERO | PCATCH, "kbdrea", 0); kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((kbd == NULL) || !KBD_IS_VALID(kbd)) { splx(s); return ENXIO; /* our keyboard has gone... */ } if (error) { sc->gkb_flags &= ~KB_ASLEEP; splx(s); return error; } } splx(s); /* copy as much input as possible */ error = 0; while (uio->uio_resid > 0) { len = imin(uio->uio_resid, sizeof(buffer)); len = q_to_b(&sc->gkb_q, buffer, len); if (len <= 0) break; error = uiomove(buffer, len, uio); if (error) break; } return error; } static int genkbdwrite(dev_t dev, struct uio *uio, int flag) { keyboard_t *kbd; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((kbd == NULL) || !KBD_IS_VALID(kbd)) return ENXIO; return ENODEV; } static int genkbdioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) { keyboard_t *kbd; int error; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((kbd == NULL) || !KBD_IS_VALID(kbd)) return ENXIO; error = (*kbdsw[kbd->kb_index]->ioctl)(kbd, cmd, arg); if (error == ENOIOCTL) error = ENODEV; return error; } static int genkbdpoll(dev_t dev, int events, struct proc *p) { keyboard_t *kbd; genkbd_softc_t *sc; int revents; int s; revents = 0; s = spltty(); sc = dev->si_drv1; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) { revents = POLLHUP; /* the keyboard has gone */ } else if (events & (POLLIN | POLLRDNORM)) { if (sc->gkb_q.c_cc > 0) revents = events & (POLLIN | POLLRDNORM); else selrecord(p, &sc->gkb_rsel); } splx(s); return revents; } static int genkbd_event(keyboard_t *kbd, int event, void *arg) { genkbd_softc_t *sc; size_t len; u_char *cp; int mode; int c; /* assert(KBD_IS_VALID(kbd)) */ sc = (genkbd_softc_t *)arg; switch (event) { case KBDIO_KEYINPUT: break; case KBDIO_UNLOADING: /* the keyboard is going... */ kbd_release(kbd, (void *)sc); if (sc->gkb_flags & KB_ASLEEP) { sc->gkb_flags &= ~KB_ASLEEP; wakeup((caddr_t)sc); } selwakeup(&sc->gkb_rsel); return 0; default: return EINVAL; } /* obtain the current key input mode */ if ((*kbdsw[kbd->kb_index]->ioctl)(kbd, KDGKBMODE, (caddr_t)&mode)) mode = K_XLATE; /* read all pending input */ while ((*kbdsw[kbd->kb_index]->check_char)(kbd)) { c = (*kbdsw[kbd->kb_index]->read_char)(kbd, FALSE); if (c == NOKEY) continue; if (c == ERRKEY) /* XXX: ring bell? */ continue; if (!KBD_IS_BUSY(kbd)) /* the device is not open, discard the input */ continue; /* store the byte as is for K_RAW and K_CODE modes */ if (mode != K_XLATE) { putc(KEYCHAR(c), &sc->gkb_q); continue; } /* K_XLATE */ if (c & RELKEY) /* key release is ignored */ continue; /* process special keys; most of them are just ignored... */ if (c & SPCLKEY) { switch (KEYCHAR(c)) { default: /* ignore them... */ continue; case BTAB: /* a backtab: ESC [ Z */ putc(0x1b, &sc->gkb_q); putc('[', &sc->gkb_q); putc('Z', &sc->gkb_q); continue; } } /* normal chars, normal chars with the META, function keys */ switch (KEYFLAGS(c)) { case 0: /* a normal char */ putc(KEYCHAR(c), &sc->gkb_q); break; case MKEY: /* the META flag: prepend ESC */ putc(0x1b, &sc->gkb_q); putc(KEYCHAR(c), &sc->gkb_q); break; case FKEY | SPCLKEY: /* a function key, return string */ cp = (*kbdsw[kbd->kb_index]->get_fkeystr)(kbd, KEYCHAR(c), &len); if (cp != NULL) { while (len-- > 0) putc(*cp++, &sc->gkb_q); } break; } } /* wake up sleeping/polling processes */ if (sc->gkb_q.c_cc > 0) { if (sc->gkb_flags & KB_ASLEEP) { sc->gkb_flags &= ~KB_ASLEEP; wakeup((caddr_t)sc); } selwakeup(&sc->gkb_rsel); } return 0; } #endif /* KBD_INSTALL_CDEV */ /* * Generic low-level keyboard functions * The low-level functions in the keyboard subdriver may use these * functions. */ int genkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { keyarg_t *keyp; fkeyarg_t *fkeyp; int s; int i; s = spltty(); switch (cmd) { case KDGKBINFO: /* get keyboard information */ ((keyboard_info_t *)arg)->kb_index = kbd->kb_index; i = imin(strlen(kbd->kb_name) + 1, sizeof(((keyboard_info_t *)arg)->kb_name)); bcopy(kbd->kb_name, ((keyboard_info_t *)arg)->kb_name, i); ((keyboard_info_t *)arg)->kb_unit = kbd->kb_unit; ((keyboard_info_t *)arg)->kb_type = kbd->kb_type; ((keyboard_info_t *)arg)->kb_config = kbd->kb_config; ((keyboard_info_t *)arg)->kb_flags = kbd->kb_flags; break; case KDGKBTYPE: /* get keyboard type */ *(int *)arg = kbd->kb_type; break; case KDGETREPEAT: /* get keyboard repeat rate */ ((int *)arg)[0] = kbd->kb_delay1; ((int *)arg)[1] = kbd->kb_delay2; break; case GIO_KEYMAP: /* get keyboard translation table */ bcopy(kbd->kb_keymap, arg, sizeof(*kbd->kb_keymap)); break; case PIO_KEYMAP: /* set keyboard translation table */ #ifndef KBD_DISABLE_KEYMAP_LOAD bzero(kbd->kb_accentmap, sizeof(*kbd->kb_accentmap)); bcopy(arg, kbd->kb_keymap, sizeof(*kbd->kb_keymap)); break; #else splx(s); return ENODEV; #endif case GIO_KEYMAPENT: /* get keyboard translation table entry */ keyp = (keyarg_t *)arg; if (keyp->keynum >= sizeof(kbd->kb_keymap->key) /sizeof(kbd->kb_keymap->key[0])) { splx(s); return EINVAL; } bcopy(&kbd->kb_keymap->key[keyp->keynum], &keyp->key, sizeof(keyp->key)); break; case PIO_KEYMAPENT: /* set keyboard translation table entry */ #ifndef KBD_DISABLE_KEYMAP_LOAD keyp = (keyarg_t *)arg; if (keyp->keynum >= sizeof(kbd->kb_keymap->key) /sizeof(kbd->kb_keymap->key[0])) { splx(s); return EINVAL; } bcopy(&keyp->key, &kbd->kb_keymap->key[keyp->keynum], sizeof(keyp->key)); break; #else splx(s); return ENODEV; #endif case GIO_DEADKEYMAP: /* get accent key translation table */ bcopy(kbd->kb_accentmap, arg, sizeof(*kbd->kb_accentmap)); break; case PIO_DEADKEYMAP: /* set accent key translation table */ #ifndef KBD_DISABLE_KEYMAP_LOAD bcopy(arg, kbd->kb_accentmap, sizeof(*kbd->kb_accentmap)); break; #else splx(s); return ENODEV; #endif case GETFKEY: /* get functionkey string */ fkeyp = (fkeyarg_t *)arg; if (fkeyp->keynum >= kbd->kb_fkeytab_size) { splx(s); return EINVAL; } bcopy(kbd->kb_fkeytab[fkeyp->keynum].str, fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].len); fkeyp->flen = kbd->kb_fkeytab[fkeyp->keynum].len; break; case SETFKEY: /* set functionkey string */ #ifndef KBD_DISABLE_KEYMAP_LOAD fkeyp = (fkeyarg_t *)arg; if (fkeyp->keynum >= kbd->kb_fkeytab_size) { splx(s); return EINVAL; } kbd->kb_fkeytab[fkeyp->keynum].len = imin(fkeyp->flen, MAXFK); bcopy(fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].str, kbd->kb_fkeytab[fkeyp->keynum].len); break; #else splx(s); return ENODEV; #endif default: splx(s); return ENOIOCTL; } splx(s); return 0; } /* get a pointer to the string associated with the given function key */ u_char *genkbd_get_fkeystr(keyboard_t *kbd, int fkey, size_t *len) { if (kbd == NULL) return NULL; fkey -= F_FN; if (fkey > kbd->kb_fkeytab_size) return NULL; *len = kbd->kb_fkeytab[fkey].len; return kbd->kb_fkeytab[fkey].str; } /* diagnostic dump */ static char *get_kbd_type_name(int type) { static struct { int type; char *name; } name_table[] = { { KB_84, "AT 84" }, { KB_101, "AT 101/102" }, { KB_OTHER, "generic" }, }; int i; for (i = 0; i < sizeof(name_table)/sizeof(name_table[0]); ++i) { if (type == name_table[i].type) return name_table[i].name; } return "unknown"; } void genkbd_diag(keyboard_t *kbd, int level) { if (level > 0) { printf("kbd%d: %s%d, %s (%d), config:0x%x, flags:0x%x", kbd->kb_index, kbd->kb_name, kbd->kb_unit, get_kbd_type_name(kbd->kb_type), kbd->kb_type, kbd->kb_config, kbd->kb_flags); if (kbd->kb_io_base > 0) printf(", port:0x%x-0x%x", kbd->kb_io_base, kbd->kb_io_base + kbd->kb_io_size - 1); printf("\n"); } } #define set_lockkey_state(k, s, l) \ if (!((s) & l ## DOWN)) { \ int i; \ (s) |= l ## DOWN; \ (s) ^= l ## ED; \ i = (s) & LOCK_MASK; \ (*kbdsw[(k)->kb_index]->ioctl)((k), KDSETLED, (caddr_t)&i); \ } static u_int save_accent_key(keyboard_t *kbd, u_int key, int *accents) { int i; /* make an index into the accent map */ i = key - F_ACC + 1; if ((i > kbd->kb_accentmap->n_accs) || (kbd->kb_accentmap->acc[i - 1].accchar == 0)) { /* the index is out of range or pointing to an empty entry */ *accents = 0; return ERRKEY; } /* * If the same accent key has been hit twice, produce the accent char * itself. */ if (i == *accents) { key = kbd->kb_accentmap->acc[i - 1].accchar; *accents = 0; return key; } /* remember the index and wait for the next key */ *accents = i; return NOKEY; } static u_int make_accent_char(keyboard_t *kbd, u_int ch, int *accents) { struct acc_t *acc; int i; acc = &kbd->kb_accentmap->acc[*accents - 1]; *accents = 0; /* * If the accent key is followed by the space key, * produce the accent char itself. */ if (ch == ' ') return acc->accchar; /* scan the accent map */ for (i = 0; i < NUM_ACCENTCHARS; ++i) { if (acc->map[i][0] == 0) /* end of table */ break; if (acc->map[i][0] == ch) return acc->map[i][1]; } /* this char cannot be accented... */ return ERRKEY; } int genkbd_keyaction(keyboard_t *kbd, int keycode, int up, int *shiftstate, int *accents) { struct keyent_t *key; int state = *shiftstate; int action; int f; int i; i = keycode; f = state & (AGRS | ALKED); if ((f == AGRS1) || (f == AGRS2) || (f == ALKED)) i += ALTGR_OFFSET; key = &kbd->kb_keymap->key[i]; i = ((state & SHIFTS) ? 1 : 0) | ((state & CTLS) ? 2 : 0) | ((state & ALTS) ? 4 : 0); if (((key->flgs & FLAG_LOCK_C) && (state & CLKED)) || ((key->flgs & FLAG_LOCK_N) && (state & NLKED)) ) i ^= 1; action = key->map[i]; if (up) { /* break: key released */ if (key->spcl & (0x80 >> i)) { /* special keys */ switch (action) { case LSHA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = LSH; /* FALL THROUGH */ case LSH: state &= ~SHIFTS1; break; case RSHA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = RSH; /* FALL THROUGH */ case RSH: state &= ~SHIFTS2; break; case LCTRA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = LCTR; /* FALL THROUGH */ case LCTR: state &= ~CTLS1; break; case RCTRA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = RCTR; /* FALL THROUGH */ case RCTR: state &= ~CTLS2; break; case LALTA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = LALT; /* FALL THROUGH */ case LALT: state &= ~ALTS1; break; case RALTA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = RALT; /* FALL THROUGH */ case RALT: state &= ~ALTS2; break; case ASH: state &= ~AGRS1; break; case META: state &= ~METAS1; break; case NLK: state &= ~NLKDOWN; break; case CLK: #ifndef PC98 state &= ~CLKDOWN; #else state &= ~CLKED; i = state & LOCK_MASK; (*kbdsw[kbd->kb_index]->ioctl)(kbd, KDSETLED, (caddr_t)&i); #endif break; case SLK: state &= ~SLKDOWN; break; case ALK: state &= ~ALKDOWN; break; } *shiftstate = state & ~SHIFTAON; return (SPCLKEY | RELKEY | action); } /* release events of regular keys are not reported */ *shiftstate &= ~SHIFTAON; return NOKEY; } else { /* make: key pressed */ state &= ~SHIFTAON; if (key->spcl & (0x80 >> i)) { /* special keys */ switch (action) { /* LOCKING KEYS */ case NLK: set_lockkey_state(kbd, state, NLK); break; case CLK: #ifndef PC98 set_lockkey_state(kbd, state, CLK); #else state |= CLKED; i = state & LOCK_MASK; (*kbdsw[kbd->kb_index]->ioctl)(kbd, KDSETLED, (caddr_t)&i); #endif break; case SLK: set_lockkey_state(kbd, state, SLK); break; case ALK: set_lockkey_state(kbd, state, ALK); break; /* NON-LOCKING KEYS */ case SPSC: case RBT: case SUSP: case STBY: case DBG: case NEXT: case PREV: case PNC: *accents = 0; break; case BTAB: *accents = 0; action |= BKEY; break; case LSHA: state |= SHIFTAON; action = LSH; /* FALL THROUGH */ case LSH: state |= SHIFTS1; break; case RSHA: state |= SHIFTAON; action = RSH; /* FALL THROUGH */ case RSH: state |= SHIFTS2; break; case LCTRA: state |= SHIFTAON; action = LCTR; /* FALL THROUGH */ case LCTR: state |= CTLS1; break; case RCTRA: state |= SHIFTAON; action = RCTR; /* FALL THROUGH */ case RCTR: state |= CTLS2; break; case LALTA: state |= SHIFTAON; action = LALT; /* FALL THROUGH */ case LALT: state |= ALTS1; break; case RALTA: state |= SHIFTAON; action = RALT; /* FALL THROUGH */ case RALT: state |= ALTS2; break; case ASH: state |= AGRS1; break; case META: state |= METAS1; break; default: /* is this an accent (dead) key? */ *shiftstate = state; if (action >= F_ACC && action <= L_ACC) { action = save_accent_key(kbd, action, accents); switch (action) { case NOKEY: case ERRKEY: return action; default: if (state & METAS) return (action | MKEY); else return action; } /* NOT REACHED */ } /* other special keys */ if (*accents > 0) { *accents = 0; return ERRKEY; } if (action >= F_FN && action <= L_FN) action |= FKEY; /* XXX: return fkey string for the FKEY? */ return (SPCLKEY | action); } *shiftstate = state; return (SPCLKEY | action); } else { /* regular keys */ *shiftstate = state; if (*accents > 0) { /* make an accented char */ action = make_accent_char(kbd, action, accents); if (action == ERRKEY) return action; } if (state & METAS) action |= MKEY; return action; } } /* NOT REACHED */ } Index: head/sys/dev/syscons/scgfbrndr.c =================================================================== --- head/sys/dev/syscons/scgfbrndr.c (revision 56835) +++ head/sys/dev/syscons/scgfbrndr.c (revision 56836) @@ -1,830 +1,824 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" -#include "vga.h" #include "opt_syscons.h" #include "opt_vga.h" -#if NSC > 0 && NVGA > 0 - #include #include #include #include #include #include #include #include #ifndef SC_RENDER_DEBUG #define SC_RENDER_DEBUG 0 #endif static vr_clear_t vga_txtclear; static vr_draw_border_t vga_txtborder; static vr_draw_t vga_txtdraw; static vr_set_cursor_t vga_txtcursor_shape; static vr_draw_cursor_t vga_txtcursor; static vr_blink_cursor_t vga_txtblink; #ifndef SC_NO_CUTPASTE static vr_draw_mouse_t vga_txtmouse; #else #define vga_txtmouse (vr_draw_mouse_t *)vga_nop #endif #ifdef SC_PIXEL_MODE static vr_clear_t vga_pxlclear; static vr_draw_border_t vga_pxlborder; static vr_draw_t vga_egadraw; static vr_draw_t vga_vgadraw; static vr_set_cursor_t vga_pxlcursor_shape; static vr_draw_cursor_t vga_pxlcursor; static vr_blink_cursor_t vga_pxlblink; #ifndef SC_NO_CUTPASTE static vr_draw_mouse_t vga_pxlmouse; #else #define vga_pxlmouse (vr_draw_mouse_t *)vga_nop #endif #endif /* SC_PIXEL_MODE */ #ifndef SC_NO_MODE_CHANGE static vr_draw_border_t vga_grborder; #endif static void vga_nop(scr_stat *scp, ...); static struct linker_set vga_set; static sc_rndr_sw_t txtrndrsw = { vga_txtclear, vga_txtborder, vga_txtdraw, vga_txtcursor_shape, vga_txtcursor, vga_txtblink, (vr_set_mouse_t *)vga_nop, vga_txtmouse, }; RENDERER(mda, 0, txtrndrsw, vga_set); RENDERER(cga, 0, txtrndrsw, vga_set); RENDERER(ega, 0, txtrndrsw, vga_set); RENDERER(vga, 0, txtrndrsw, vga_set); #ifdef SC_PIXEL_MODE static sc_rndr_sw_t egarndrsw = { vga_pxlclear, vga_pxlborder, vga_egadraw, vga_pxlcursor_shape, vga_pxlcursor, vga_pxlblink, (vr_set_mouse_t *)vga_nop, vga_pxlmouse, }; RENDERER(ega, PIXEL_MODE, egarndrsw, vga_set); static sc_rndr_sw_t vgarndrsw = { vga_pxlclear, vga_pxlborder, vga_vgadraw, vga_pxlcursor_shape, vga_pxlcursor, vga_pxlblink, (vr_set_mouse_t *)vga_nop, vga_pxlmouse, }; RENDERER(vga, PIXEL_MODE, vgarndrsw, vga_set); #endif /* SC_PIXEL_MODE */ #ifndef SC_NO_MODE_CHANGE static sc_rndr_sw_t grrndrsw = { (vr_clear_t *)vga_nop, vga_grborder, (vr_draw_t *)vga_nop, (vr_set_cursor_t *)vga_nop, (vr_draw_cursor_t *)vga_nop, (vr_blink_cursor_t *)vga_nop, (vr_set_mouse_t *)vga_nop, (vr_draw_mouse_t *)vga_nop, }; RENDERER(cga, GRAPHICS_MODE, grrndrsw, vga_set); RENDERER(ega, GRAPHICS_MODE, grrndrsw, vga_set); RENDERER(vga, GRAPHICS_MODE, grrndrsw, vga_set); #endif /* SC_NO_MODE_CHANGE */ RENDERER_MODULE(vga, vga_set); #ifndef SC_NO_CUTPASTE static u_short mouse_and_mask[16] = { 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80, 0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000 }; static u_short mouse_or_mask[16] = { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800, 0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000 }; #endif static void vga_nop(scr_stat *scp, ...) { } /* text mode renderer */ static void vga_txtclear(scr_stat *scp, int c, int attr) { sc_vtb_clear(&scp->scr, c, attr); } static void vga_txtborder(scr_stat *scp, int color) { (*vidsw[scp->sc->adapter]->set_border)(scp->sc->adp, color); } static void vga_txtdraw(scr_stat *scp, int from, int count, int flip) { vm_offset_t p; int c; int a; if (from + count > scp->xsize*scp->ysize) count = scp->xsize*scp->ysize - from; if (flip) { for (p = sc_vtb_pointer(&scp->scr, from); count-- > 0; ++from) { c = sc_vtb_getc(&scp->vtb, from); a = sc_vtb_geta(&scp->vtb, from); a = (a & 0x8800) | ((a & 0x7000) >> 4) | ((a & 0x0700) << 4); p = sc_vtb_putchar(&scp->scr, p, c, a); } } else { sc_vtb_copy(&scp->vtb, from, &scp->scr, from, count); } } static void vga_txtcursor_shape(scr_stat *scp, int base, int height, int blink) { if (base < 0 || base >= scp->font_size) return; /* the caller may set height <= 0 in order to disable the cursor */ #if 0 scp->cursor_base = base; scp->cursor_height = height; #endif (*vidsw[scp->sc->adapter]->set_hw_cursor_shape)(scp->sc->adp, base, height, scp->font_size, blink); } static void vga_txtcursor(scr_stat *scp, int at, int blink, int on, int flip) { video_adapter_t *adp; int cursor_attr; if (scp->cursor_height <= 0) /* the text cursor is disabled */ return; adp = scp->sc->adp; if (blink) { scp->status |= VR_CURSOR_BLINK; if (on) { scp->status |= VR_CURSOR_ON; (*vidsw[adp->va_index]->set_hw_cursor)(adp, at%scp->xsize, at/scp->xsize); } else { if (scp->status & VR_CURSOR_ON) (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1); scp->status &= ~VR_CURSOR_ON; } } else { scp->status &= ~VR_CURSOR_BLINK; if (on) { scp->status |= VR_CURSOR_ON; cursor_attr = sc_vtb_geta(&scp->vtb, at); scp->cursor_saveunder_char = sc_vtb_getc(&scp->scr, at); scp->cursor_saveunder_attr = cursor_attr; if ((cursor_attr & 0x7000) == 0x7000) { cursor_attr &= 0x8f00; if ((cursor_attr & 0x0700) == 0) cursor_attr |= 0x0700; } else { cursor_attr |= 0x7000; if ((cursor_attr & 0x0700) == 0x0700) cursor_attr &= 0xf000; } if (flip) cursor_attr = (cursor_attr & 0x8800) | ((cursor_attr & 0x7000) >> 4) | ((cursor_attr & 0x0700) << 4); sc_vtb_putc(&scp->scr, at, sc_vtb_getc(&scp->scr, at), cursor_attr); } else { cursor_attr = scp->cursor_saveunder_attr; if (flip) cursor_attr = (cursor_attr & 0x8800) | ((cursor_attr & 0x7000) >> 4) | ((cursor_attr & 0x0700) << 4); if (scp->status & VR_CURSOR_ON) sc_vtb_putc(&scp->scr, at, scp->cursor_saveunder_char, cursor_attr); scp->status &= ~VR_CURSOR_ON; } } } static void vga_txtblink(scr_stat *scp, int at, int flip) { } #ifndef SC_NO_CUTPASTE static void draw_txtmouse(scr_stat *scp, int x, int y) { #ifndef SC_ALT_MOUSE_IMAGE u_char font_buf[128]; u_short cursor[32]; u_char c; int pos; int xoffset, yoffset; int crtc_addr; int i; /* prepare mousepointer char's bitmaps */ pos = (y/scp->font_size - scp->yoff)*scp->xsize + x/8 - scp->xoff; bcopy(scp->font + sc_vtb_getc(&scp->vtb, pos)*scp->font_size, &font_buf[0], scp->font_size); bcopy(scp->font + sc_vtb_getc(&scp->vtb, pos + 1)*scp->font_size, &font_buf[32], scp->font_size); bcopy(scp->font + sc_vtb_getc(&scp->vtb, pos + scp->xsize)*scp->font_size, &font_buf[64], scp->font_size); bcopy(scp->font + sc_vtb_getc(&scp->vtb, pos + scp->xsize + 1)*scp->font_size, &font_buf[96], scp->font_size); for (i = 0; i < scp->font_size; ++i) { cursor[i] = font_buf[i]<<8 | font_buf[i+32]; cursor[i + scp->font_size] = font_buf[i+64]<<8 | font_buf[i+96]; } /* now and-or in the mousepointer image */ xoffset = x%8; yoffset = y%scp->font_size; for (i = 0; i < 16; ++i) { cursor[i + yoffset] = (cursor[i + yoffset] & ~(mouse_and_mask[i] >> xoffset)) | (mouse_or_mask[i] >> xoffset); } for (i = 0; i < scp->font_size; ++i) { font_buf[i] = (cursor[i] & 0xff00) >> 8; font_buf[i + 32] = cursor[i] & 0xff; font_buf[i + 64] = (cursor[i + scp->font_size] & 0xff00) >> 8; font_buf[i + 96] = cursor[i + scp->font_size] & 0xff; } #if 1 /* wait for vertical retrace to avoid jitter on some videocards */ crtc_addr = scp->sc->adp->va_crtc_addr; while (!(inb(crtc_addr + 6) & 0x08)) /* idle */ ; #endif c = scp->sc->mouse_char; (*vidsw[scp->sc->adapter]->load_font)(scp->sc->adp, 0, 32, font_buf, c, 4); sc_vtb_putc(&scp->scr, pos, c, sc_vtb_geta(&scp->scr, pos)); /* FIXME: may be out of range! */ sc_vtb_putc(&scp->scr, pos + scp->xsize, c + 2, sc_vtb_geta(&scp->scr, pos + scp->xsize)); if (x < (scp->xsize - 1)*8) { sc_vtb_putc(&scp->scr, pos + 1, c + 1, sc_vtb_geta(&scp->scr, pos + 1)); sc_vtb_putc(&scp->scr, pos + scp->xsize + 1, c + 3, sc_vtb_geta(&scp->scr, pos + scp->xsize + 1)); } #else /* SC_ALT_MOUSE_IMAGE */ /* Red, magenta and brown are mapped to green to to keep it readable */ static const int col_conv[16] = { 6, 6, 6, 6, 2, 2, 2, 6, 14, 14, 14, 14, 10, 10, 10, 14 }; int pos; int color; int a; pos = (y/scp->font_size - scp->yoff)*scp->xsize + x/8 - scp->xoff; a = sc_vtb_geta(&scp->scr, pos); if (scp->sc->adp->va_flags & V_ADP_COLOR) color = (col_conv[(a & 0xf000) >> 12] << 12) | ((a & 0x0f00) | 0x0800); else color = ((a & 0xf000) >> 4) | ((a & 0x0f00) << 4); sc_vtb_putc(&scp->scr, pos, sc_vtb_getc(&scp->scr, pos), color); #endif /* SC_ALT_MOUSE_IMAGE */ } static void remove_txtmouse(scr_stat *scp, int x, int y) { } static void vga_txtmouse(scr_stat *scp, int x, int y, int on) { if (on) draw_txtmouse(scp, x, y); else remove_txtmouse(scp, x, y); } #endif /* SC_NO_CUTPASTE */ #ifdef SC_PIXEL_MODE /* pixel (raster text) mode renderer */ static void vga_pxlclear(scr_stat *scp, int c, int attr) { vm_offset_t p; int line_width; int lines; int i; /* XXX: we are just filling the screen with the background color... */ outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, ((attr & 0xf000) >> 4) | 0x00); /* set/reset */ line_width = scp->sc->adp->va_line_width; lines = scp->ysize*scp->font_size; p = scp->sc->adp->va_window + line_width*scp->yoff*scp->font_size + scp->xoff; for (i = 0; i < lines; ++i) { bzero_io((void *)p, scp->xsize); p += line_width; } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_pxlborder(scr_stat *scp, int color) { vm_offset_t p; int line_width; int i; (*vidsw[scp->sc->adapter]->set_border)(scp->sc->adp, color); outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, (color << 8) | 0x00); /* set/reset */ line_width = scp->sc->adp->va_line_width; p = scp->sc->adp->va_window; if (scp->yoff > 0) { bzero_io((void *)p, line_width*scp->yoff*scp->font_size); bzero_io((void *)(p + line_width*(scp->yoff + scp->ysize) *scp->font_size), line_width*(scp->ypixel - (scp->yoff + scp->ysize)*scp->font_size)); } if (scp->xoff > 0) { for (i = 0; i < scp->ysize*scp->font_size; ++i) { bzero_io((void *)(p + line_width *(scp->yoff*scp->font_size + i)), scp->xoff); bzero_io((void *)(p + line_width *(scp->yoff*scp->font_size + i) + scp->xoff + scp->xsize), scp->xpixel/8 - scp->xoff - scp->xsize); } } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_egadraw(scr_stat *scp, int from, int count, int flip) { vm_offset_t d; vm_offset_t e; u_char *f; u_short bg; u_short col1, col2; int line_width; int i, j; int a; u_char c; line_width = scp->sc->adp->va_line_width; d = scp->sc->adp->va_window + scp->xoff + scp->yoff*scp->font_size*line_width + (from%scp->xsize) + scp->font_size*line_width*(from/scp->xsize); outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ bg = -1; if (from + count > scp->xsize*scp->ysize) count = scp->xsize*scp->ysize - from; for (i = from; count-- > 0; ++i) { a = sc_vtb_geta(&scp->vtb, i); if (flip) { col1 = ((a & 0x7000) >> 4) | (a & 0x0800); col2 = ((a & 0x8000) >> 4) | (a & 0x0700); } else { col1 = (a & 0x0f00); col2 = (a & 0xf000) >> 4; } /* set background color in EGA/VGA latch */ if (bg != col2) { bg = col2; outw(GDCIDX, bg | 0x00); /* set/reset */ outw(GDCIDX, 0xff08); /* bit mask */ writeb(d, 0); c = readb(d); /* set bg color in the latch */ } /* foreground color */ outw(GDCIDX, col1 | 0x00); /* set/reset */ e = d; f = &(scp->font[sc_vtb_getc(&scp->vtb, i)*scp->font_size]); for (j = 0; j < scp->font_size; ++j, ++f) { outw(GDCIDX, (*f << 8) | 0x08); /* bit mask */ writeb(e, 0); e += line_width; } ++d; if ((i % scp->xsize) == scp->xsize - 1) d += scp->xoff*2 + (scp->font_size - 1)*line_width; } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ } static void vga_vgadraw(scr_stat *scp, int from, int count, int flip) { vm_offset_t d; vm_offset_t e; u_char *f; u_short bg; u_short col1, col2; int line_width; int i, j; int a; u_char c; line_width = scp->sc->adp->va_line_width; d = scp->sc->adp->va_window + scp->xoff + scp->yoff*scp->font_size*line_width + (from%scp->xsize) + scp->font_size*line_width*(from/scp->xsize); outw(GDCIDX, 0x0305); /* read mode 0, write mode 3 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ bg = -1; if (from + count > scp->xsize*scp->ysize) count = scp->xsize*scp->ysize - from; for (i = from; count-- > 0; ++i) { a = sc_vtb_geta(&scp->vtb, i); if (flip) { col1 = ((a & 0x7000) >> 4) | (a & 0x0800); col2 = ((a & 0x8000) >> 4) | (a & 0x0700); } else { col1 = (a & 0x0f00); col2 = (a & 0xf000) >> 4; } /* set background color in EGA/VGA latch */ if (bg != col2) { bg = col2; outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, bg | 0x00); /* set/reset */ writeb(d, 0); c = readb(d); /* set bg color in the latch */ outw(GDCIDX, 0x0305); /* read mode 0, write mode 3 */ } /* foreground color */ outw(GDCIDX, col1 | 0x00); /* set/reset */ e = d; f = &(scp->font[sc_vtb_getc(&scp->vtb, i)*scp->font_size]); for (j = 0; j < scp->font_size; ++j, ++f) { writeb(e, *f); e += line_width; } ++d; if ((i % scp->xsize) == scp->xsize - 1) d += scp->xoff*2 + (scp->font_size - 1)*line_width; } outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_pxlcursor_shape(scr_stat *scp, int base, int height, int blink) { if (base < 0 || base >= scp->font_size) return; /* the caller may set height <= 0 in order to disable the cursor */ #if 0 scp->cursor_base = base; scp->cursor_height = height; #endif } static void draw_pxlcursor(scr_stat *scp, int at, int on, int flip) { vm_offset_t d; u_char *f; int line_width; int height; int col; int a; int i; u_char c; line_width = scp->sc->adp->va_line_width; d = scp->sc->adp->va_window + scp->xoff + scp->yoff*scp->font_size*line_width + (at%scp->xsize) + scp->font_size*line_width*(at/scp->xsize) + (scp->font_size - scp->cursor_base - 1)*line_width; outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ /* set background color in EGA/VGA latch */ a = sc_vtb_geta(&scp->vtb, at); if (flip) col = (on) ? ((a & 0xf000) >> 4) : (a & 0x0f00); else col = (on) ? (a & 0x0f00) : ((a & 0xf000) >> 4); outw(GDCIDX, col | 0x00); /* set/reset */ outw(GDCIDX, 0xff08); /* bit mask */ writeb(d, 0); c = readb(d); /* set bg color in the latch */ /* foreground color */ if (flip) col = (on) ? (a & 0x0f00) : ((a & 0xf000) >> 4); else col = (on) ? ((a & 0xf000) >> 4) : (a & 0x0f00); outw(GDCIDX, col | 0x00); /* set/reset */ f = &(scp->font[sc_vtb_getc(&scp->vtb, at)*scp->font_size + scp->font_size - scp->cursor_base - 1]); height = imin(scp->cursor_height, scp->font_size); for (i = 0; i < height; ++i, --f) { outw(GDCIDX, (*f << 8) | 0x08); /* bit mask */ writeb(d, 0); d -= line_width; } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ } static void vga_pxlcursor(scr_stat *scp, int at, int blink, int on, int flip) { if (scp->cursor_height <= 0) /* the text cursor is disabled */ return; if (on) { scp->status |= VR_CURSOR_ON; draw_pxlcursor(scp, at, on, flip); } else { if (scp->status & VR_CURSOR_ON) draw_pxlcursor(scp, at, on, flip); scp->status &= ~VR_CURSOR_ON; } if (blink) scp->status |= VR_CURSOR_BLINK; else scp->status &= ~VR_CURSOR_BLINK; } static void vga_pxlblink(scr_stat *scp, int at, int flip) { static int blinkrate = 0; if (!(scp->status & VR_CURSOR_BLINK)) return; if (!(++blinkrate & 4)) return; blinkrate = 0; scp->status ^= VR_CURSOR_ON; draw_pxlcursor(scp, at, scp->status & VR_CURSOR_ON, flip); } #ifndef SC_NO_CUTPASTE static void draw_pxlmouse(scr_stat *scp, int x, int y) { vm_offset_t p; int line_width; int xoff, yoff; int ymax; u_short m; int i, j; line_width = scp->sc->adp->va_line_width; xoff = (x - scp->xoff*8)%8; yoff = y - (y/line_width)*line_width; ymax = imin(y + 16, scp->ypixel); outw(GDCIDX, 0x0805); /* read mode 1, write mode 0 */ outw(GDCIDX, 0x0001); /* set/reset enable */ outw(GDCIDX, 0x0002); /* color compare */ outw(GDCIDX, 0x0007); /* color don't care */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, 0x0803); /* data rotate/function select (and) */ p = scp->sc->adp->va_window + line_width*y + x/8; if (x < scp->xpixel - 16) { for (i = y, j = 0; i < ymax; ++i, ++j) { m = ~(mouse_and_mask[j] >> xoff); #ifdef __i386__ *(u_char *)p &= m >> 8; *(u_char *)(p + 1) &= m; #elif defined(__alpha__) writeb(p, readb(p) & (m >> 8)); writeb(p + 1, readb(p + 1) & (m >> 8)); #endif p += line_width; } } else { xoff += 8; for (i = y, j = 0; i < ymax; ++i, ++j) { m = ~(mouse_and_mask[j] >> xoff); #ifdef __i386__ *(u_char *)p &= m; #elif defined(__alpha__) writeb(p, readb(p) & (m >> 8)); #endif p += line_width; } } outw(GDCIDX, 0x1003); /* data rotate/function select (or) */ p = scp->sc->adp->va_window + line_width*y + x/8; if (x < scp->xpixel - 16) { for (i = y, j = 0; i < ymax; ++i, ++j) { m = mouse_or_mask[j] >> xoff; #ifdef __i386__ *(u_char *)p &= m >> 8; *(u_char *)(p + 1) &= m; #elif defined(__alpha__) writeb(p, readb(p) & (m >> 8)); writeb(p + 1, readb(p + 1) & (m >> 8)); #endif p += line_width; } } else { for (i = y, j = 0; i < ymax; ++i, ++j) { m = mouse_or_mask[j] >> xoff; #ifdef __i386__ *(u_char *)p &= m; #elif defined(__alpha__) writeb(p, readb(p) & (m >> 8)); #endif p += line_width; } } outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ } static void remove_pxlmouse(scr_stat *scp, int x, int y) { vm_offset_t p; int col, row; int pos; int line_width; int ymax; int i; /* erase the mouse cursor image */ col = x/8 - scp->xoff; row = y/scp->font_size - scp->yoff; pos = row*scp->xsize + col; i = (col < scp->xsize - 1) ? 2 : 1; (*scp->rndr->draw)(scp, pos, i, FALSE); if (row < scp->ysize - 1) (*scp->rndr->draw)(scp, pos + scp->xsize, i, FALSE); /* paint border if necessary */ line_width = scp->sc->adp->va_line_width; outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, (scp->border << 8) | 0x00); /* set/reset */ if (row == scp->ysize - 1) { i = (scp->ysize + scp->yoff)*scp->font_size; ymax = imin(i + scp->font_size, scp->ypixel); p = scp->sc->adp->va_window + i*line_width + scp->xoff + col; if (col < scp->xsize - 1) { for (; i < ymax; ++i) { writeb(p, 0); writeb(p + 1, 0); p += line_width; } } else { for (; i < ymax; ++i) { writeb(p, 0); p += line_width; } } } if ((col == scp->xsize - 1) && (scp->xoff > 0)) { i = (row + scp->yoff)*scp->font_size; ymax = imin(i + scp->font_size*2, scp->ypixel); p = scp->sc->adp->va_window + i*line_width + scp->xoff + scp->xsize; for (; i < ymax; ++i) { writeb(p, 0); p += line_width; } } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_pxlmouse(scr_stat *scp, int x, int y, int on) { if (on) draw_pxlmouse(scp, x, y); else remove_pxlmouse(scp, x, y); } #endif /* SC_NO_CUTPASTE */ #endif /* SC_PIXEL_MODE */ #ifndef SC_NO_MODE_CHANGE /* graphics mode renderer */ static void vga_grborder(scr_stat *scp, int color) { (*vidsw[scp->sc->adapter]->set_border)(scp->sc->adp, color); } #endif - -#endif /* NSC > 0 && NVGA > 0 */ Index: head/sys/dev/syscons/schistory.c =================================================================== --- head/sys/dev/syscons/schistory.c (revision 56835) +++ head/sys/dev/syscons/schistory.c (revision 56836) @@ -1,311 +1,307 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * Copyright (c) 1992-1998 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "sc.h" #include "opt_syscons.h" -#if NSC > 0 - #ifndef SC_NO_HISTORY #include #include #include #include #include #include #include #include #include #if !defined(SC_MAX_HISTORY_SIZE) #define SC_MAX_HISTORY_SIZE (1000 * MAXCONS * NSC) #endif #if !defined(SC_HISTORY_SIZE) #define SC_HISTORY_SIZE (ROW * 4) #endif #if (SC_HISTORY_SIZE * MAXCONS * NSC) > SC_MAX_HISTORY_SIZE #undef SC_MAX_HISTORY_SIZE #define SC_MAX_HISTORY_SIZE (SC_HISTORY_SIZE * MAXCONS * NSC) #endif /* local variables */ static int extra_history_size = SC_MAX_HISTORY_SIZE - SC_HISTORY_SIZE*MAXCONS; /* local functions */ static void copy_history(sc_vtb_t *from, sc_vtb_t *to); static void history_to_screen(scr_stat *scp); /* allocate a history buffer */ int sc_alloc_history_buffer(scr_stat *scp, int lines, int prev_ysize, int wait) { /* * syscons unconditionally allocates buffers upto * SC_HISTORY_SIZE lines or scp->ysize lines, whichever * is larger. A value greater than that is allowed, * subject to extra_history_size. */ sc_vtb_t *history; sc_vtb_t *prev_history; int cur_lines; /* current buffer size */ int min_lines; /* guaranteed buffer size */ int delta; /* lines to put back */ if (lines <= 0) lines = SC_HISTORY_SIZE; /* use the default value */ /* make it at least as large as the screen size */ lines = imax(lines, scp->ysize); /* remove the history buffer while we update it */ history = prev_history = scp->history; scp->history = NULL; /* calculate the amount of lines to put back to extra_history_size */ delta = 0; if (prev_history) { cur_lines = sc_vtb_rows(history); min_lines = imax(SC_HISTORY_SIZE, prev_ysize); if (cur_lines > min_lines) delta = cur_lines - min_lines; } /* lines upto min_lines are always allowed. */ min_lines = imax(SC_HISTORY_SIZE, scp->ysize); if (lines > min_lines) { if (lines - min_lines > extra_history_size + delta) { /* too many lines are requested */ scp->history = prev_history; return EINVAL; } } /* allocate a new buffer */ history = (sc_vtb_t *)malloc(sizeof(*history), M_DEVBUF, (wait) ? M_WAITOK : M_NOWAIT); if (history != NULL) { if (lines > min_lines) extra_history_size -= lines - min_lines; /* XXX error check? */ sc_vtb_init(history, VTB_RINGBUFFER, scp->xsize, lines, NULL, wait); /* FIXME: XXX no good? */ sc_vtb_clear(history, scp->sc->scr_map[0x20], SC_NORM_ATTR << 8); if (prev_history != NULL) copy_history(prev_history, history); scp->history_pos = sc_vtb_tail(history); } else { scp->history_pos = 0; } /* destroy the previous buffer */ if (prev_history != NULL) { extra_history_size += delta; sc_vtb_destroy(prev_history); free(prev_history, M_DEVBUF); } scp->history = history; return 0; } static void copy_history(sc_vtb_t *from, sc_vtb_t *to) { int lines; int cols; int cols1; int cols2; int pos; int i; lines = sc_vtb_rows(from); cols1 = sc_vtb_cols(from); cols2 = sc_vtb_cols(to); cols = imin(cols1, cols2); pos = sc_vtb_tail(from); for (i = 0; i < lines; ++i) { sc_vtb_append(from, pos, to, cols); if (cols < cols2) sc_vtb_seek(to, sc_vtb_pos(to, sc_vtb_tail(to), cols2 - cols)); pos = sc_vtb_pos(from, pos, cols1); } } void sc_free_history_buffer(scr_stat *scp, int prev_ysize) { sc_vtb_t *history; int cur_lines; /* current buffer size */ int min_lines; /* guaranteed buffer size */ history = scp->history; scp->history = NULL; if (history == NULL) return; cur_lines = sc_vtb_rows(history); min_lines = imax(SC_HISTORY_SIZE, prev_ysize); extra_history_size += (cur_lines > min_lines) ? cur_lines - min_lines : 0; sc_vtb_destroy(history); free(history, M_DEVBUF); } /* copy entire screen into the top of the history buffer */ void sc_hist_save(scr_stat *scp) { sc_vtb_append(&scp->vtb, 0, scp->history, scp->xsize*scp->ysize); scp->history_pos = sc_vtb_tail(scp->history); } /* restore the screen by copying from the history buffer */ int sc_hist_restore(scr_stat *scp) { int ret; if (scp->history_pos != sc_vtb_tail(scp->history)) { scp->history_pos = sc_vtb_tail(scp->history); history_to_screen(scp); ret = 0; } else { ret = 1; } sc_vtb_seek(scp->history, sc_vtb_pos(scp->history, sc_vtb_tail(scp->history), -scp->xsize*scp->ysize)); return ret; } /* copy screen-full of saved lines */ static void history_to_screen(scr_stat *scp) { int pos; int i; pos = scp->history_pos; for (i = 1; i <= scp->ysize; ++i) { pos = sc_vtb_pos(scp->history, pos, -scp->xsize); sc_vtb_copy(scp->history, pos, &scp->vtb, scp->xsize*(scp->ysize - i), scp->xsize); } mark_all(scp); } /* go to the tail of the history buffer */ void sc_hist_home(scr_stat *scp) { scp->history_pos = sc_vtb_tail(scp->history); history_to_screen(scp); } /* go to the top of the history buffer */ void sc_hist_end(scr_stat *scp) { scp->history_pos = sc_vtb_pos(scp->history, sc_vtb_tail(scp->history), scp->xsize*scp->ysize); history_to_screen(scp); } /* move one line up */ int sc_hist_up_line(scr_stat *scp) { if (sc_vtb_pos(scp->history, scp->history_pos, -(scp->xsize*scp->ysize)) == sc_vtb_tail(scp->history)) return -1; scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos, -scp->xsize); history_to_screen(scp); return 0; } /* move one line down */ int sc_hist_down_line(scr_stat *scp) { if (scp->history_pos == sc_vtb_tail(scp->history)) return -1; scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos, scp->xsize); history_to_screen(scp); return 0; } int sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p) { scr_stat *scp; int error; switch (cmd) { case CONS_HISTORY: /* set history size */ scp = SC_STAT(tp->t_dev); if (*(int *)data <= 0) return EINVAL; if (scp->status & BUFFER_SAVED) return EBUSY; DPRINTF(5, ("lines:%d, ysize:%d, pool:%d\n", *(int *)data, scp->ysize, extra_history_size)); error = sc_alloc_history_buffer(scp, imax(*(int *)data, scp->ysize), scp->ysize, TRUE); DPRINTF(5, ("error:%d, rows:%d, pool:%d\n", error, sc_vtb_rows(scp->history), extra_history_size)); return error; } return ENOIOCTL; } #endif /* SC_NO_HISTORY */ - -#endif /* NSC */ Index: head/sys/dev/syscons/scmouse.c =================================================================== --- head/sys/dev/syscons/scmouse.c (revision 56835) +++ head/sys/dev/syscons/scmouse.c (revision 56836) @@ -1,906 +1,901 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" #include "opt_syscons.h" -#if NSC > 0 - #include #include #include #include #include #include #include #include #include #include #include #ifdef SC_TWOBUTTON_MOUSE #define SC_MOUSE_PASTEBUTTON MOUSE_BUTTON3DOWN /* right button */ #define SC_MOUSE_EXTENDBUTTON MOUSE_BUTTON2DOWN /* not really used */ #else #define SC_MOUSE_PASTEBUTTON MOUSE_BUTTON2DOWN /* middle button */ #define SC_MOUSE_EXTENDBUTTON MOUSE_BUTTON3DOWN /* right button */ #endif /* SC_TWOBUTTON_MOUSE */ #define SC_WAKEUP_DELTA 20 /* for backward compatibility */ #define OLD_CONS_MOUSECTL _IOWR('c', 10, old_mouse_info_t) typedef struct old_mouse_data { int x; int y; int buttons; } old_mouse_data_t; typedef struct old_mouse_info { int operation; union { struct old_mouse_data data; struct mouse_mode mode; } u; } old_mouse_info_t; #ifndef SC_NO_SYSMOUSE /* local variables */ static int cut_buffer_size; static u_char *cut_buffer; /* local functions */ static void set_mouse_pos(scr_stat *scp); #ifndef SC_NO_CUTPASTE static int skip_spc_right(scr_stat *scp, int p); static int skip_spc_left(scr_stat *scp, int p); static void mouse_cut(scr_stat *scp); static void mouse_cut_start(scr_stat *scp); static void mouse_cut_end(scr_stat *scp); static void mouse_cut_word(scr_stat *scp); static void mouse_cut_line(scr_stat *scp); static void mouse_cut_extend(scr_stat *scp); static void mouse_paste(scr_stat *scp); #endif /* SC_NO_CUTPASTE */ #ifndef SC_NO_CUTPASTE /* allocate a cut buffer */ void sc_alloc_cut_buffer(scr_stat *scp, int wait) { u_char *p; if ((cut_buffer == NULL) || (cut_buffer_size < scp->xsize * scp->ysize + 1)) { p = cut_buffer; cut_buffer = NULL; if (p != NULL) free(p, M_DEVBUF); cut_buffer_size = scp->xsize * scp->ysize + 1; p = (u_char *)malloc(cut_buffer_size, M_DEVBUF, (wait) ? M_WAITOK : M_NOWAIT); if (p != NULL) p[0] = '\0'; cut_buffer = p; } } #endif /* SC_NO_CUTPASTE */ /* move mouse */ void sc_mouse_move(scr_stat *scp, int x, int y) { int s; s = spltty(); scp->mouse_xpos = x; scp->mouse_ypos = y; if (ISGRAPHSC(scp)) scp->mouse_pos = scp->mouse_oldpos = 0; else scp->mouse_pos = scp->mouse_oldpos = (y/scp->font_size - scp->yoff)*scp->xsize + x/8 - scp->xoff; splx(s); } /* adjust mouse position */ static void set_mouse_pos(scr_stat *scp) { static int last_xpos = -1, last_ypos = -1; if (scp->mouse_xpos < scp->xoff*8) scp->mouse_xpos = scp->xoff*8; if (scp->mouse_ypos < scp->yoff*scp->font_size) scp->mouse_ypos = scp->yoff*scp->font_size; if (ISGRAPHSC(scp)) { if (scp->mouse_xpos > scp->xpixel-1) scp->mouse_xpos = scp->xpixel-1; if (scp->mouse_ypos > scp->ypixel-1) scp->mouse_ypos = scp->ypixel-1; return; } else { if (scp->mouse_xpos > (scp->xsize + scp->xoff)*8 - 1) scp->mouse_xpos = (scp->xsize + scp->xoff)*8 - 1; if (scp->mouse_ypos > (scp->ysize + scp->yoff)*scp->font_size - 1) scp->mouse_ypos = (scp->ysize + scp->yoff)*scp->font_size - 1; } if (scp->mouse_xpos != last_xpos || scp->mouse_ypos != last_ypos) { scp->status |= MOUSE_MOVED; scp->mouse_pos = (scp->mouse_ypos/scp->font_size - scp->yoff)*scp->xsize + scp->mouse_xpos/8 - scp->xoff; #ifndef SC_NO_CUTPASTE if ((scp->status & MOUSE_VISIBLE) && (scp->status & MOUSE_CUTTING)) mouse_cut(scp); #endif } } #ifndef SC_NO_CUTPASTE void sc_draw_mouse_image(scr_stat *scp) { if (ISGRAPHSC(scp)) return; ++scp->sc->videoio_in_progress; (*scp->rndr->draw_mouse)(scp, scp->mouse_xpos, scp->mouse_ypos, TRUE); scp->mouse_oldpos = scp->mouse_pos; --scp->sc->videoio_in_progress; } void sc_remove_mouse_image(scr_stat *scp) { int size; int i; if (ISGRAPHSC(scp)) return; ++scp->sc->videoio_in_progress; (*scp->rndr->draw_mouse)(scp, (scp->mouse_oldpos%scp->xsize + scp->xoff)*8, (scp->mouse_oldpos/scp->xsize + scp->yoff) * scp->font_size, FALSE); size = scp->xsize*scp->ysize; i = scp->mouse_oldpos; mark_for_update(scp, i); mark_for_update(scp, i); #ifndef PC98 if (i + scp->xsize + 1 < size) { mark_for_update(scp, i + scp->xsize + 1); } else if (i + scp->xsize < size) { mark_for_update(scp, i + scp->xsize); } else if (i + 1 < size) { mark_for_update(scp, i + 1); } #endif /* PC98 */ --scp->sc->videoio_in_progress; } int sc_inside_cutmark(scr_stat *scp, int pos) { int start; int end; if (scp->mouse_cut_end < 0) return FALSE; if (scp->mouse_cut_start <= scp->mouse_cut_end) { start = scp->mouse_cut_start; end = scp->mouse_cut_end; } else { start = scp->mouse_cut_end; end = scp->mouse_cut_start - 1; } return ((start <= pos) && (pos <= end)); } void sc_remove_cutmarking(scr_stat *scp) { int s; s = spltty(); if (scp->mouse_cut_end >= 0) { mark_for_update(scp, scp->mouse_cut_start); mark_for_update(scp, scp->mouse_cut_end); } scp->mouse_cut_start = scp->xsize*scp->ysize; scp->mouse_cut_end = -1; splx(s); scp->status &= ~MOUSE_CUTTING; } void sc_remove_all_cutmarkings(sc_softc_t *sc) { scr_stat *scp; int i; /* delete cut markings in all vtys */ for (i = 0; i < sc->vtys; ++i) { scp = SC_STAT(sc->dev[i]); if (scp == NULL) continue; sc_remove_cutmarking(scp); } } void sc_remove_all_mouse(sc_softc_t *sc) { scr_stat *scp; int i; for (i = 0; i < sc->vtys; ++i) { scp = SC_STAT(sc->dev[i]); if (scp == NULL) continue; if (scp->status & MOUSE_VISIBLE) { scp->status &= ~MOUSE_VISIBLE; mark_all(scp); } } } #define IS_SPACE_CHAR(c) (((c) & 0xff) == ' ') /* skip spaces to right */ static int skip_spc_right(scr_stat *scp, int p) { int c; int i; for (i = p % scp->xsize; i < scp->xsize; ++i) { c = sc_vtb_getc(&scp->vtb, p); if (!IS_SPACE_CHAR(c)) break; ++p; } return i; } /* skip spaces to left */ static int skip_spc_left(scr_stat *scp, int p) { int c; int i; for (i = p-- % scp->xsize - 1; i >= 0; --i) { c = sc_vtb_getc(&scp->vtb, p); if (!IS_SPACE_CHAR(c)) break; --p; } return i; } /* copy marked region to the cut buffer */ static void mouse_cut(scr_stat *scp) { int start; int end; int from; int to; int blank; int c; int p; int s; int i; start = scp->mouse_cut_start; end = scp->mouse_cut_end; if (scp->mouse_pos >= start) { from = start; to = end = scp->mouse_pos; } else { from = end = scp->mouse_pos; to = start - 1; } for (p = from, i = blank = 0; p <= to; ++p) { cut_buffer[i] = sc_vtb_getc(&scp->vtb, p); /* remember the position of the last non-space char */ if (!IS_SPACE_CHAR(cut_buffer[i++])) blank = i; /* the first space after the last non-space */ /* trim trailing blank when crossing lines */ if ((p % scp->xsize) == (scp->xsize - 1)) { cut_buffer[blank] = '\r'; i = blank + 1; } } cut_buffer[i] = '\0'; /* scan towards the end of the last line */ --p; for (i = p % scp->xsize; i < scp->xsize; ++i) { c = sc_vtb_getc(&scp->vtb, p); if (!IS_SPACE_CHAR(c)) break; ++p; } /* if there is nothing but blank chars, trim them, but mark towards eol */ if (i >= scp->xsize) { if (end >= start) to = end = p - 1; else to = start = p; cut_buffer[blank++] = '\r'; cut_buffer[blank] = '\0'; } /* remove the current marking */ s = spltty(); if (scp->mouse_cut_start <= scp->mouse_cut_end) { mark_for_update(scp, scp->mouse_cut_start); mark_for_update(scp, scp->mouse_cut_end); } else if (scp->mouse_cut_end >= 0) { mark_for_update(scp, scp->mouse_cut_end); mark_for_update(scp, scp->mouse_cut_start); } /* mark the new region */ scp->mouse_cut_start = start; scp->mouse_cut_end = end; mark_for_update(scp, from); mark_for_update(scp, to); splx(s); } /* a mouse button is pressed, start cut operation */ static void mouse_cut_start(scr_stat *scp) { int i; int j; int s; if (scp->status & MOUSE_VISIBLE) { i = scp->mouse_cut_start; j = scp->mouse_cut_end; sc_remove_all_cutmarkings(scp->sc); if (scp->mouse_pos == i && i == j) { cut_buffer[0] = '\0'; } else if (skip_spc_right(scp, scp->mouse_pos) >= scp->xsize) { /* if the pointer is on trailing blank chars, mark towards eol */ i = skip_spc_left(scp, scp->mouse_pos) + 1; s = spltty(); scp->mouse_cut_start = (scp->mouse_pos / scp->xsize) * scp->xsize + i; scp->mouse_cut_end = (scp->mouse_pos / scp->xsize + 1) * scp->xsize - 1; splx(s); cut_buffer[0] = '\r'; cut_buffer[1] = '\0'; scp->status |= MOUSE_CUTTING; } else { s = spltty(); scp->mouse_cut_start = scp->mouse_pos; scp->mouse_cut_end = scp->mouse_cut_start; splx(s); cut_buffer[0] = sc_vtb_getc(&scp->vtb, scp->mouse_cut_start); cut_buffer[1] = '\0'; scp->status |= MOUSE_CUTTING; } mark_all(scp); /* this is probably overkill XXX */ } } /* end of cut operation */ static void mouse_cut_end(scr_stat *scp) { if (scp->status & MOUSE_VISIBLE) scp->status &= ~MOUSE_CUTTING; } /* copy a word under the mouse pointer */ static void mouse_cut_word(scr_stat *scp) { int start; int end; int sol; int eol; int c; int s; int i; int j; /* * Because we don't have locale information in the kernel, * we only distinguish space char and non-space chars. Punctuation * chars, symbols and other regular chars are all treated alike. */ if (scp->status & MOUSE_VISIBLE) { /* remove the current cut mark */ s = spltty(); if (scp->mouse_cut_start <= scp->mouse_cut_end) { mark_for_update(scp, scp->mouse_cut_start); mark_for_update(scp, scp->mouse_cut_end); } else if (scp->mouse_cut_end >= 0) { mark_for_update(scp, scp->mouse_cut_end); mark_for_update(scp, scp->mouse_cut_start); } scp->mouse_cut_start = scp->xsize*scp->ysize; scp->mouse_cut_end = -1; splx(s); sol = (scp->mouse_pos / scp->xsize) * scp->xsize; eol = sol + scp->xsize; c = sc_vtb_getc(&scp->vtb, scp->mouse_pos); if (IS_SPACE_CHAR(c)) { /* blank space */ for (j = scp->mouse_pos; j >= sol; --j) { c = sc_vtb_getc(&scp->vtb, j); if (!IS_SPACE_CHAR(c)) break; } start = ++j; for (j = scp->mouse_pos; j < eol; ++j) { c = sc_vtb_getc(&scp->vtb, j); if (!IS_SPACE_CHAR(c)) break; } end = j - 1; } else { /* non-space word */ for (j = scp->mouse_pos; j >= sol; --j) { c = sc_vtb_getc(&scp->vtb, j); if (IS_SPACE_CHAR(c)) break; } start = ++j; for (j = scp->mouse_pos; j < eol; ++j) { c = sc_vtb_getc(&scp->vtb, j); if (IS_SPACE_CHAR(c)) break; } end = j - 1; } /* copy the found word */ for (i = 0, j = start; j <= end; ++j) cut_buffer[i++] = sc_vtb_getc(&scp->vtb, j); cut_buffer[i] = '\0'; scp->status |= MOUSE_CUTTING; /* mark the region */ s = spltty(); scp->mouse_cut_start = start; scp->mouse_cut_end = end; mark_for_update(scp, start); mark_for_update(scp, end); splx(s); } } /* copy a line under the mouse pointer */ static void mouse_cut_line(scr_stat *scp) { int s; int i; int j; if (scp->status & MOUSE_VISIBLE) { /* remove the current cut mark */ s = spltty(); if (scp->mouse_cut_start <= scp->mouse_cut_end) { mark_for_update(scp, scp->mouse_cut_start); mark_for_update(scp, scp->mouse_cut_end); } else if (scp->mouse_cut_end >= 0) { mark_for_update(scp, scp->mouse_cut_end); mark_for_update(scp, scp->mouse_cut_start); } /* mark the entire line */ scp->mouse_cut_start = (scp->mouse_pos / scp->xsize) * scp->xsize; scp->mouse_cut_end = scp->mouse_cut_start + scp->xsize - 1; mark_for_update(scp, scp->mouse_cut_start); mark_for_update(scp, scp->mouse_cut_end); splx(s); /* copy the line into the cut buffer */ for (i = 0, j = scp->mouse_cut_start; j <= scp->mouse_cut_end; ++j) cut_buffer[i++] = sc_vtb_getc(&scp->vtb, j); cut_buffer[i++] = '\r'; cut_buffer[i] = '\0'; scp->status |= MOUSE_CUTTING; } } /* extend the marked region to the mouse pointer position */ static void mouse_cut_extend(scr_stat *scp) { int start; int end; int s; if ((scp->status & MOUSE_VISIBLE) && !(scp->status & MOUSE_CUTTING) && (scp->mouse_cut_end >= 0)) { if (scp->mouse_cut_start <= scp->mouse_cut_end) { start = scp->mouse_cut_start; end = scp->mouse_cut_end; } else { start = scp->mouse_cut_end; end = scp->mouse_cut_start - 1; } s = spltty(); if (scp->mouse_pos > end) { scp->mouse_cut_start = start; scp->mouse_cut_end = end; } else if (scp->mouse_pos < start) { scp->mouse_cut_start = end + 1; scp->mouse_cut_end = start; } else { if (scp->mouse_pos - start > end + 1 - scp->mouse_pos) { scp->mouse_cut_start = start; scp->mouse_cut_end = end; } else { scp->mouse_cut_start = end + 1; scp->mouse_cut_end = start; } } splx(s); mouse_cut(scp); scp->status |= MOUSE_CUTTING; } } /* paste cut buffer contents into the current vty */ static void mouse_paste(scr_stat *scp) { if (scp->status & MOUSE_VISIBLE) sc_paste(scp, cut_buffer, strlen(cut_buffer)); } #endif /* SC_NO_CUTPASTE */ int sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p) { mouse_info_t *mouse; mouse_info_t buf; scr_stat *cur_scp; scr_stat *scp; int s; int f; scp = SC_STAT(tp->t_dev); switch (cmd) { case CONS_MOUSECTL: /* control mouse arrow */ case OLD_CONS_MOUSECTL: mouse = (mouse_info_t*)data; if (cmd == OLD_CONS_MOUSECTL) { static u_char swapb[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; old_mouse_info_t *old_mouse = (old_mouse_info_t *)data; mouse = &buf; mouse->operation = old_mouse->operation; switch (mouse->operation) { case MOUSE_MODE: mouse->u.mode = old_mouse->u.mode; break; case MOUSE_SHOW: case MOUSE_HIDE: break; case MOUSE_MOVEABS: case MOUSE_MOVEREL: case MOUSE_ACTION: mouse->u.data.x = old_mouse->u.data.x; mouse->u.data.y = old_mouse->u.data.y; mouse->u.data.z = 0; mouse->u.data.buttons = swapb[old_mouse->u.data.buttons & 0x7]; break; case MOUSE_GETINFO: old_mouse->u.data.x = scp->mouse_xpos; old_mouse->u.data.y = scp->mouse_ypos; old_mouse->u.data.buttons = swapb[scp->mouse_buttons & 0x7]; break; default: return EINVAL; } } cur_scp = scp->sc->cur_scp; switch (mouse->operation) { case MOUSE_MODE: if (ISSIGVALID(mouse->u.mode.signal)) { scp->mouse_signal = mouse->u.mode.signal; scp->mouse_proc = p; scp->mouse_pid = p->p_pid; } else { scp->mouse_signal = 0; scp->mouse_proc = NULL; scp->mouse_pid = 0; } return 0; case MOUSE_SHOW: if (!ISMOUSEAVAIL(scp->sc->adp->va_flags)) return EINVAL; s = spltty(); if (!(scp->sc->flags & SC_MOUSE_ENABLED)) { scp->sc->flags |= SC_MOUSE_ENABLED; if (!ISGRAPHSC(cur_scp)) { cur_scp->status |= MOUSE_VISIBLE; mark_all(cur_scp); } splx(s); return 0; } else { splx(s); return EINVAL; } break; case MOUSE_HIDE: s = spltty(); if (scp->sc->flags & SC_MOUSE_ENABLED) { scp->sc->flags &= ~SC_MOUSE_ENABLED; sc_remove_all_mouse(scp->sc); splx(s); return 0; } else { splx(s); return EINVAL; } break; case MOUSE_MOVEABS: s = spltty(); scp->mouse_xpos = mouse->u.data.x; scp->mouse_ypos = mouse->u.data.y; set_mouse_pos(scp); splx(s); break; case MOUSE_MOVEREL: s = spltty(); scp->mouse_xpos += mouse->u.data.x; scp->mouse_ypos += mouse->u.data.y; set_mouse_pos(scp); splx(s); break; case MOUSE_GETINFO: mouse->u.data.x = scp->mouse_xpos; mouse->u.data.y = scp->mouse_ypos; mouse->u.data.z = 0; mouse->u.data.buttons = scp->mouse_buttons; return 0; case MOUSE_ACTION: case MOUSE_MOTION_EVENT: /* send out mouse event on /dev/sysmouse */ #if 0 /* this should maybe only be settable from /dev/consolectl SOS */ if (SC_VTY(tp->t_dev) != SC_CONSOLECTL) return ENOTTY; #endif s = spltty(); if (mouse->u.data.x != 0 || mouse->u.data.y != 0) { cur_scp->mouse_xpos += mouse->u.data.x; cur_scp->mouse_ypos += mouse->u.data.y; set_mouse_pos(cur_scp); } f = 0; if (mouse->operation == MOUSE_ACTION) { f = cur_scp->mouse_buttons ^ mouse->u.data.buttons; cur_scp->mouse_buttons = mouse->u.data.buttons; } splx(s); if (sysmouse_event(mouse) == 0) return 0; /* * If any buttons are down or the mouse has moved a lot, * stop the screen saver. */ if (((mouse->operation == MOUSE_ACTION) && mouse->u.data.buttons) || (mouse->u.data.x*mouse->u.data.x + mouse->u.data.y*mouse->u.data.y >= SC_WAKEUP_DELTA*SC_WAKEUP_DELTA)) { sc_touch_scrn_saver(); } #ifndef SC_NO_CUTPASTE if (!ISGRAPHSC(cur_scp) && (cur_scp->sc->flags & SC_MOUSE_ENABLED)) cur_scp->status |= MOUSE_VISIBLE; #endif /* SC_NO_CUTPASTE */ if (cur_scp->mouse_signal) { /* has controlling process died? */ if (cur_scp->mouse_proc && (cur_scp->mouse_proc != pfind(cur_scp->mouse_pid))){ cur_scp->mouse_signal = 0; cur_scp->mouse_proc = NULL; cur_scp->mouse_pid = 0; } else { psignal(cur_scp->mouse_proc, cur_scp->mouse_signal); break; } } if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL)) break; #ifndef SC_NO_CUTPASTE if ((mouse->operation == MOUSE_ACTION) && f) { /* process button presses */ if (cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN) mouse_cut_start(cur_scp); else mouse_cut_end(cur_scp); if (cur_scp->mouse_buttons & MOUSE_BUTTON2DOWN || cur_scp->mouse_buttons & MOUSE_BUTTON3DOWN) mouse_paste(cur_scp); } #endif /* SC_NO_CUTPASTE */ break; case MOUSE_BUTTON_EVENT: if ((mouse->u.event.id & MOUSE_BUTTONS) == 0) return EINVAL; if (mouse->u.event.value < 0) return EINVAL; #if 0 /* this should maybe only be settable from /dev/consolectl SOS */ if (SC_VTY(tp->t_dev) != SC_CONSOLECTL) return ENOTTY; #endif if (mouse->u.event.value > 0) cur_scp->mouse_buttons |= mouse->u.event.id; else cur_scp->mouse_buttons &= ~mouse->u.event.id; if (sysmouse_event(mouse) == 0) return 0; /* if a button is held down, stop the screen saver */ if (mouse->u.event.value > 0) sc_touch_scrn_saver(); #ifndef SC_NO_CUTPASTE if (!ISGRAPHSC(cur_scp) && (cur_scp->sc->flags & SC_MOUSE_ENABLED)) cur_scp->status |= MOUSE_VISIBLE; #endif /* SC_NO_CUTPASTE */ if (cur_scp->mouse_signal) { if (cur_scp->mouse_proc && (cur_scp->mouse_proc != pfind(cur_scp->mouse_pid))){ cur_scp->mouse_signal = 0; cur_scp->mouse_proc = NULL; cur_scp->mouse_pid = 0; } else { psignal(cur_scp->mouse_proc, cur_scp->mouse_signal); break; } } if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL)) break; #ifndef SC_NO_CUTPASTE switch (mouse->u.event.id) { case MOUSE_BUTTON1DOWN: switch (mouse->u.event.value % 4) { case 0: /* up */ mouse_cut_end(cur_scp); break; case 1: /* single click: start cut operation */ mouse_cut_start(cur_scp); break; case 2: /* double click: cut a word */ mouse_cut_word(cur_scp); mouse_cut_end(cur_scp); break; case 3: /* triple click: cut a line */ mouse_cut_line(cur_scp); mouse_cut_end(cur_scp); break; } break; case SC_MOUSE_PASTEBUTTON: switch (mouse->u.event.value) { case 0: /* up */ break; default: mouse_paste(cur_scp); break; } break; case SC_MOUSE_EXTENDBUTTON: switch (mouse->u.event.value) { case 0: /* up */ if (!(cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN)) mouse_cut_end(cur_scp); break; default: mouse_cut_extend(cur_scp); break; } break; } #endif /* SC_NO_CUTPASTE */ break; case MOUSE_MOUSECHAR: if (mouse->u.mouse_char < 0) { mouse->u.mouse_char = scp->sc->mouse_char; } else { if (mouse->u.mouse_char >= UCHAR_MAX - 4) return EINVAL; s = spltty(); sc_remove_all_mouse(scp->sc); #ifndef SC_NO_FONT_LOADING if (ISTEXTSC(cur_scp) && (cur_scp->font != NULL)) sc_load_font(cur_scp, 0, cur_scp->font_size, cur_scp->font, cur_scp->sc->mouse_char, 4); #endif scp->sc->mouse_char = mouse->u.mouse_char; splx(s); } break; default: return EINVAL; } return 0; } return ENOIOCTL; } #endif /* SC_NO_SYSMOUSE */ - -#endif /* NSC */ Index: head/sys/dev/syscons/scterm-dumb.c =================================================================== --- head/sys/dev/syscons/scterm-dumb.c (revision 56835) +++ head/sys/dev/syscons/scterm-dumb.c (revision 56836) @@ -1,156 +1,151 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" #include "opt_syscons.h" -#if NSC > 0 - #include #include #include #include #include #include #include #ifdef SC_DUMB_TERMINAL /* dumb terminal emulator */ static sc_term_init_t dumb_init; static sc_term_term_t dumb_term; static sc_term_puts_t dumb_puts; static sc_term_ioctl_t dumb_ioctl; static sc_term_clear_t dumb_clear; static sc_term_input_t dumb_input; static void dumb_nop(void); static sc_term_sw_t sc_term_dumb = { { NULL, NULL }, "dumb", /* emulator name */ "dumb terminal", /* description */ "*", /* matching renderer */ 0, /* softc size */ 0, dumb_init, dumb_term, dumb_puts, dumb_ioctl, (sc_term_reset_t *)dumb_nop, (sc_term_default_attr_t *)dumb_nop, dumb_clear, (sc_term_notify_t *)dumb_nop, dumb_input, }; SCTERM_MODULE(dumb, sc_term_dumb); static int dumb_init(scr_stat *scp, void **softc, int code) { switch (code) { case SC_TE_COLD_INIT: ++sc_term_dumb.te_refcount; break; case SC_TE_WARM_INIT: break; } return 0; } static int dumb_term(scr_stat *scp, void **softc) { --sc_term_dumb.te_refcount; return 0; } static void dumb_puts(scr_stat *scp, u_char *buf, int len) { while (len > 0) { ++scp->sc->write_in_progress; sc_term_gen_print(scp, &buf, &len, SC_NORM_ATTR << 8); sc_term_gen_scroll(scp, scp->sc->scr_map[0x20], SC_NORM_ATTR << 8); --scp->sc->write_in_progress; } } static int dumb_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p) { vid_info_t *vi; switch (cmd) { case GIO_ATTR: /* get current attributes */ *(int*)data = SC_NORM_ATTR; return 0; case CONS_GETINFO: /* get current (virtual) console info */ vi = (vid_info_t *)data; if (vi->size != sizeof(struct vid_info)) return EINVAL; vi->mv_norm.fore = SC_NORM_ATTR & 0x0f; vi->mv_norm.back = (SC_NORM_ATTR >> 4) & 0x0f; vi->mv_rev.fore = SC_NORM_ATTR & 0x0f; vi->mv_rev.back = (SC_NORM_ATTR >> 4) & 0x0f; /* * The other fields are filled by the upper routine. XXX */ return ENOIOCTL; } return ENOIOCTL; } static void dumb_clear(scr_stat *scp) { sc_move_cursor(scp, 0, 0); sc_vtb_clear(&scp->vtb, scp->sc->scr_map[0x20], SC_NORM_ATTR << 8); mark_all(scp); } static int dumb_input(scr_stat *scp, int c, struct tty *tp) { return FALSE; } static void dumb_nop(void) { /* nothing */ } #endif /* SC_DUMB_TERMINAL */ - -#endif /* NSC > 1 */ Index: head/sys/dev/syscons/scterm-sc.c =================================================================== --- head/sys/dev/syscons/scterm-sc.c (revision 56835) +++ head/sys/dev/syscons/scterm-sc.c (revision 56836) @@ -1,729 +1,724 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * Copyright (c) 1992-1998 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" #include "opt_syscons.h" -#if NSC > 0 - #include #include #include #include #include #include #include #ifndef SC_DUMB_TERMINAL #define MAX_ESC_PAR 5 /* attribute flags */ typedef struct { u_short fg; /* foreground color */ u_short bg; /* background color */ } color_t; typedef struct { int flags; #define SCTERM_BUSY (1 << 0) int esc; int num_param; int last_param; int param[MAX_ESC_PAR]; int saved_xpos; int saved_ypos; int attr_mask; /* current logical attr mask */ #define NORMAL_ATTR 0x00 #define BLINK_ATTR 0x01 #define BOLD_ATTR 0x02 #define UNDERLINE_ATTR 0x04 #define REVERSE_ATTR 0x08 #define FG_CHANGED 0x10 #define BG_CHANGED 0x20 int cur_attr; /* current hardware attr word */ color_t cur_color; /* current hardware color */ color_t std_color; /* normal hardware color */ color_t rev_color; /* reverse hardware color */ color_t dflt_std_color; /* default normal color */ color_t dflt_rev_color; /* default reverse color */ } term_stat; static sc_term_init_t scterm_init; static sc_term_term_t scterm_term; static sc_term_puts_t scterm_puts; static sc_term_ioctl_t scterm_ioctl; static sc_term_reset_t scterm_reset; static sc_term_default_attr_t scterm_default_attr; static sc_term_clear_t scterm_clear; static sc_term_notify_t scterm_notify; static sc_term_input_t scterm_input; static sc_term_sw_t sc_term_sc = { { NULL, NULL }, "sc", /* emulator name */ "syscons terminal", /* description */ "*", /* matching renderer, any :-) */ sizeof(term_stat), /* softc size */ 0, scterm_init, scterm_term, scterm_puts, scterm_ioctl, scterm_reset, scterm_default_attr, scterm_clear, scterm_notify, scterm_input, }; SCTERM_MODULE(sc, sc_term_sc); static term_stat reserved_term_stat; static void scterm_scan_esc(scr_stat *scp, term_stat *tcp, u_char c); static int mask2attr(term_stat *tcp); static int scterm_init(scr_stat *scp, void **softc, int code) { term_stat *tcp; if (*softc == NULL) { if (reserved_term_stat.flags & SCTERM_BUSY) return EINVAL; *softc = &reserved_term_stat; } tcp = *softc; switch (code) { case SC_TE_COLD_INIT: bzero(tcp, sizeof(*tcp)); tcp->flags = SCTERM_BUSY; tcp->esc = 0; tcp->saved_xpos = -1; tcp->saved_ypos = -1; tcp->attr_mask = NORMAL_ATTR; /* XXX */ tcp->dflt_std_color.fg = SC_NORM_ATTR & 0x0f; tcp->dflt_std_color.bg = (SC_NORM_ATTR >> 4) & 0x0f; tcp->dflt_rev_color.fg = SC_NORM_REV_ATTR & 0x0f; tcp->dflt_rev_color.bg = (SC_NORM_REV_ATTR >> 4) & 0x0f; tcp->std_color = tcp->dflt_std_color; tcp->rev_color = tcp->dflt_rev_color; tcp->cur_color = tcp->std_color; tcp->cur_attr = mask2attr(tcp); ++sc_term_sc.te_refcount; break; case SC_TE_WARM_INIT: tcp->esc = 0; tcp->saved_xpos = -1; tcp->saved_ypos = -1; tcp->std_color = tcp->dflt_std_color; tcp->rev_color = tcp->dflt_rev_color; tcp->cur_color = tcp->std_color; tcp->cur_attr = mask2attr(tcp); break; } return 0; } static int scterm_term(scr_stat *scp, void **softc) { if (*softc == &reserved_term_stat) { *softc = NULL; bzero(&reserved_term_stat, sizeof(reserved_term_stat)); } --sc_term_sc.te_refcount; return 0; } static void scterm_scan_esc(scr_stat *scp, term_stat *tcp, u_char c) { static u_char ansi_col[16] = { FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE, FG_MAGENTA, FG_CYAN, FG_LIGHTGREY, FG_DARKGREY, FG_LIGHTRED, FG_LIGHTGREEN, FG_YELLOW, FG_LIGHTBLUE, FG_LIGHTMAGENTA, FG_LIGHTCYAN, FG_WHITE }; sc_softc_t *sc; int i, n; i = n = 0; sc = scp->sc; if (tcp->esc == 1) { /* seen ESC */ switch (c) { case '7': /* Save cursor position */ tcp->saved_xpos = scp->xpos; tcp->saved_ypos = scp->ypos; break; case '8': /* Restore saved cursor position */ if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0) sc_move_cursor(scp, tcp->saved_xpos, tcp->saved_ypos); break; case '[': /* Start ESC [ sequence */ tcp->esc = 2; tcp->last_param = -1; for (i = tcp->num_param; i < MAX_ESC_PAR; i++) tcp->param[i] = 1; tcp->num_param = 0; return; case 'M': /* Move cursor up 1 line, scroll if at top */ sc_term_up_scroll(scp, 1, sc->scr_map[0x20], tcp->cur_attr, 0, 0); break; #if notyet case 'Q': tcp->esc = 4; return; #endif case 'c': /* Clear screen & home */ sc_clear_screen(scp); break; case '(': /* iso-2022: designate 94 character set to G0 */ tcp->esc = 5; return; } } else if (tcp->esc == 2) { /* seen ESC [ */ if (c >= '0' && c <= '9') { if (tcp->num_param < MAX_ESC_PAR) { if (tcp->last_param != tcp->num_param) { tcp->last_param = tcp->num_param; tcp->param[tcp->num_param] = 0; } else { tcp->param[tcp->num_param] *= 10; } tcp->param[tcp->num_param] += c - '0'; return; } } tcp->num_param = tcp->last_param + 1; switch (c) { case ';': if (tcp->num_param < MAX_ESC_PAR) return; break; case '=': tcp->esc = 3; tcp->last_param = -1; for (i = tcp->num_param; i < MAX_ESC_PAR; i++) tcp->param[i] = 1; tcp->num_param = 0; return; case 'A': /* up n rows */ sc_term_up(scp, tcp->param[0], 0); break; case 'B': /* down n rows */ sc_term_down(scp, tcp->param[0], 0); break; case 'C': /* right n columns */ sc_term_right(scp, tcp->param[0]); break; case 'D': /* left n columns */ sc_term_left(scp, tcp->param[0]); break; case 'E': /* cursor to start of line n lines down */ n = tcp->param[0]; if (n < 1) n = 1; sc_move_cursor(scp, 0, scp->ypos + n); break; case 'F': /* cursor to start of line n lines up */ n = tcp->param[0]; if (n < 1) n = 1; sc_move_cursor(scp, 0, scp->ypos - n); break; case 'f': /* Cursor move */ case 'H': if (tcp->num_param == 0) sc_move_cursor(scp, 0, 0); else if (tcp->num_param == 2) sc_move_cursor(scp, tcp->param[1] - 1, tcp->param[0] - 1); break; case 'J': /* Clear all or part of display */ if (tcp->num_param == 0) n = 0; else n = tcp->param[0]; sc_term_clr_eos(scp, n, sc->scr_map[0x20], tcp->cur_attr); break; case 'K': /* Clear all or part of line */ if (tcp->num_param == 0) n = 0; else n = tcp->param[0]; sc_term_clr_eol(scp, n, sc->scr_map[0x20], tcp->cur_attr); break; case 'L': /* Insert n lines */ sc_term_ins_line(scp, scp->ypos, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr, 0); break; case 'M': /* Delete n lines */ sc_term_del_line(scp, scp->ypos, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr, 0); break; case 'P': /* Delete n chars */ sc_term_del_char(scp, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr); break; case '@': /* Insert n chars */ sc_term_ins_char(scp, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr); break; case 'S': /* scroll up n lines */ sc_term_del_line(scp, 0, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr, 0); break; case 'T': /* scroll down n lines */ sc_term_ins_line(scp, 0, tcp->param[0], sc->scr_map[0x20], tcp->cur_attr, 0); break; case 'X': /* erase n characters in line */ n = tcp->param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; sc_vtb_erase(&scp->vtb, scp->cursor_pos, n, sc->scr_map[0x20], tcp->cur_attr); mark_for_update(scp, scp->cursor_pos); mark_for_update(scp, scp->cursor_pos + n - 1); break; case 'Z': /* move n tabs backwards */ sc_term_backtab(scp, tcp->param[0]); break; case '`': /* move cursor to column n */ sc_term_col(scp, tcp->param[0]); break; case 'a': /* move cursor n columns to the right */ sc_term_right(scp, tcp->param[0]); break; case 'd': /* move cursor to row n */ sc_term_row(scp, tcp->param[0]); break; case 'e': /* move cursor n rows down */ sc_term_down(scp, tcp->param[0], 0); break; case 'm': /* change attribute */ if (tcp->num_param == 0) { tcp->attr_mask = NORMAL_ATTR; tcp->cur_color = tcp->std_color; tcp->cur_attr = mask2attr(tcp); break; } for (i = 0; i < tcp->num_param; i++) { switch (n = tcp->param[i]) { case 0: /* back to normal */ tcp->attr_mask = NORMAL_ATTR; tcp->cur_color = tcp->std_color; tcp->cur_attr = mask2attr(tcp); break; case 1: /* bold */ tcp->attr_mask |= BOLD_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 4: /* underline */ tcp->attr_mask |= UNDERLINE_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 5: /* blink */ tcp->attr_mask |= BLINK_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 7: /* reverse video */ tcp->attr_mask |= REVERSE_ATTR; tcp->cur_attr = mask2attr(tcp); break; case 30: case 31: /* set fg color */ case 32: case 33: case 34: case 35: case 36: case 37: tcp->attr_mask |= FG_CHANGED; tcp->cur_color.fg = ansi_col[n - 30]; tcp->cur_attr = mask2attr(tcp); break; case 40: case 41: /* set bg color */ case 42: case 43: case 44: case 45: case 46: case 47: tcp->attr_mask |= BG_CHANGED; tcp->cur_color.bg = ansi_col[n - 40]; tcp->cur_attr = mask2attr(tcp); break; } } break; case 's': /* Save cursor position */ tcp->saved_xpos = scp->xpos; tcp->saved_ypos = scp->ypos; break; case 'u': /* Restore saved cursor position */ if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0) sc_move_cursor(scp, tcp->saved_xpos, tcp->saved_ypos); break; case 'x': if (tcp->num_param == 0) n = 0; else n = tcp->param[0]; switch (n) { case 0: /* reset attributes */ tcp->attr_mask = NORMAL_ATTR; tcp->cur_color = tcp->std_color = tcp->dflt_std_color; tcp->rev_color = tcp->dflt_rev_color; tcp->cur_attr = mask2attr(tcp); break; case 1: /* set ansi background */ tcp->attr_mask &= ~BG_CHANGED; tcp->cur_color.bg = tcp->std_color.bg = ansi_col[tcp->param[1] & 0x0f]; tcp->cur_attr = mask2attr(tcp); break; case 2: /* set ansi foreground */ tcp->attr_mask &= ~FG_CHANGED; tcp->cur_color.fg = tcp->std_color.fg = ansi_col[tcp->param[1] & 0x0f]; tcp->cur_attr = mask2attr(tcp); break; case 3: /* set ansi attribute directly */ tcp->attr_mask &= ~(FG_CHANGED | BG_CHANGED); tcp->cur_color.fg = tcp->std_color.fg = tcp->param[1] & 0x0f; tcp->cur_color.bg = tcp->std_color.bg = (tcp->param[1] >> 4) & 0x0f; tcp->cur_attr = mask2attr(tcp); break; case 5: /* set ansi reverse video background */ tcp->rev_color.bg = ansi_col[tcp->param[1] & 0x0f]; tcp->cur_attr = mask2attr(tcp); break; case 6: /* set ansi reverse video foreground */ tcp->rev_color.fg = ansi_col[tcp->param[1] & 0x0f]; tcp->cur_attr = mask2attr(tcp); break; case 7: /* set ansi reverse video directly */ tcp->rev_color.fg = tcp->param[1] & 0x0f; tcp->rev_color.bg = (tcp->param[1] >> 4) & 0x0f; tcp->cur_attr = mask2attr(tcp); break; } break; case 'z': /* switch to (virtual) console n */ if (tcp->num_param == 1) sc_switch_scr(sc, tcp->param[0]); break; } } else if (tcp->esc == 3) { /* seen ESC [0-9]+ = */ if (c >= '0' && c <= '9') { if (tcp->num_param < MAX_ESC_PAR) { if (tcp->last_param != tcp->num_param) { tcp->last_param = tcp->num_param; tcp->param[tcp->num_param] = 0; } else { tcp->param[tcp->num_param] *= 10; } tcp->param[tcp->num_param] += c - '0'; return; } } tcp->num_param = tcp->last_param + 1; switch (c) { case ';': if (tcp->num_param < MAX_ESC_PAR) return; break; case 'A': /* set display border color */ if (tcp->num_param == 1) { scp->border=tcp->param[0] & 0xff; if (scp == sc->cur_scp) sc_set_border(scp, scp->border); } break; case 'B': /* set bell pitch and duration */ if (tcp->num_param == 2) { scp->bell_pitch = tcp->param[0]; scp->bell_duration = tcp->param[1]; } break; case 'C': /* set cursor type & shape */ if (!ISGRAPHSC(sc->cur_scp)) sc_remove_cursor_image(sc->cur_scp); if (tcp->num_param == 1) { if (tcp->param[0] & 0x01) sc->flags |= SC_BLINK_CURSOR; else sc->flags &= ~SC_BLINK_CURSOR; if (tcp->param[0] & 0x02) sc->flags |= SC_CHAR_CURSOR; else sc->flags &= ~SC_CHAR_CURSOR; } else if (tcp->num_param == 2) { sc->cursor_base = scp->font_size - (tcp->param[1] & 0x1F) - 1; sc->cursor_height = (tcp->param[1] & 0x1F) - (tcp->param[0] & 0x1F) + 1; } /* * The cursor shape is global property; all virtual consoles * are affected. Update the cursor in the current console... */ if (!ISGRAPHSC(sc->cur_scp)) { i = spltty(); sc_set_cursor_image(sc->cur_scp); sc_draw_cursor_image(sc->cur_scp); splx(i); } break; case 'F': /* set ansi foreground */ if (tcp->num_param == 1) { tcp->attr_mask &= ~FG_CHANGED; tcp->cur_color.fg = tcp->std_color.fg = tcp->param[0] & 0x0f; tcp->cur_attr = mask2attr(tcp); } break; case 'G': /* set ansi background */ if (tcp->num_param == 1) { tcp->attr_mask &= ~BG_CHANGED; tcp->cur_color.bg = tcp->std_color.bg = tcp->param[0] & 0x0f; tcp->cur_attr = mask2attr(tcp); } break; case 'H': /* set ansi reverse video foreground */ if (tcp->num_param == 1) { tcp->rev_color.fg = tcp->param[0] & 0x0f; tcp->cur_attr = mask2attr(tcp); } break; case 'I': /* set ansi reverse video background */ if (tcp->num_param == 1) { tcp->rev_color.bg = tcp->param[0] & 0x0f; tcp->cur_attr = mask2attr(tcp); } break; } #if notyet } else if (tcp->esc == 4) { /* seen ESC Q */ /* to be filled */ #endif } else if (tcp->esc == 5) { /* seen ESC ( */ switch (c) { case 'B': /* iso-2022: desginate ASCII into G0 */ break; /* other items to be filled */ default: break; } } tcp->esc = 0; } static void scterm_puts(scr_stat *scp, u_char *buf, int len) { term_stat *tcp; tcp = scp->ts; outloop: scp->sc->write_in_progress++; if (tcp->esc) { scterm_scan_esc(scp, tcp, *buf); buf++; len--; } else { switch (*buf) { case 0x1b: tcp->esc = 1; tcp->num_param = 0; buf++; len--; break; default: sc_term_gen_print(scp, &buf, &len, tcp->cur_attr); break; } } sc_term_gen_scroll(scp, scp->sc->scr_map[0x20], tcp->cur_attr); scp->sc->write_in_progress--; if (len) goto outloop; } static int scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p) { term_stat *tcp = scp->ts; vid_info_t *vi; switch (cmd) { case GIO_ATTR: /* get current attributes */ /* FIXME: */ *(int*)data = (tcp->cur_attr >> 8) & 0xff; return 0; case CONS_GETINFO: /* get current (virtual) console info */ vi = (vid_info_t *)data; if (vi->size != sizeof(struct vid_info)) return EINVAL; vi->mv_norm.fore = tcp->std_color.fg; vi->mv_norm.back = tcp->std_color.bg; vi->mv_rev.fore = tcp->rev_color.fg; vi->mv_rev.back = tcp->rev_color.bg; /* * The other fields are filled by the upper routine. XXX */ return ENOIOCTL; } return ENOIOCTL; } static int scterm_reset(scr_stat *scp, int code) { /* FIXME */ return 0; } static void scterm_default_attr(scr_stat *scp, int color, int rev_color) { term_stat *tcp = scp->ts; tcp->dflt_std_color.fg = color & 0x0f; tcp->dflt_std_color.bg = (color >> 4) & 0x0f; tcp->dflt_rev_color.fg = rev_color & 0x0f; tcp->dflt_rev_color.bg = (rev_color >> 4) & 0x0f; tcp->std_color = tcp->dflt_std_color; tcp->rev_color = tcp->dflt_rev_color; tcp->cur_color = tcp->std_color; tcp->cur_attr = mask2attr(tcp); } static void scterm_clear(scr_stat *scp) { term_stat *tcp = scp->ts; sc_move_cursor(scp, 0, 0); sc_vtb_clear(&scp->vtb, scp->sc->scr_map[0x20], tcp->cur_attr); mark_all(scp); } static void scterm_notify(scr_stat *scp, int event) { switch (event) { case SC_TE_NOTIFY_VTSWITCH_IN: break; case SC_TE_NOTIFY_VTSWITCH_OUT: break; } } static int scterm_input(scr_stat *scp, int c, struct tty *tp) { return FALSE; } /* * Calculate hardware attributes word using logical attributes mask and * hardware colors */ /* FIXME */ static int mask2attr(term_stat *tcp) { int attr, mask = tcp->attr_mask; if (mask & REVERSE_ATTR) { attr = ((mask & FG_CHANGED) ? tcp->cur_color.bg : tcp->rev_color.fg) | (((mask & BG_CHANGED) ? tcp->cur_color.fg : tcp->rev_color.bg) << 4); } else attr = tcp->cur_color.fg | (tcp->cur_color.bg << 4); /* XXX: underline mapping for Hercules adapter can be better */ if (mask & (BOLD_ATTR | UNDERLINE_ATTR)) attr ^= 0x08; if (mask & BLINK_ATTR) attr ^= 0x80; return (attr << 8); } #endif /* SC_DUMB_TERMINAL */ - -#endif /* NSC > 0 */ Index: head/sys/dev/syscons/scterm.c =================================================================== --- head/sys/dev/syscons/scterm.c (revision 56835) +++ head/sys/dev/syscons/scterm.c (revision 56836) @@ -1,128 +1,123 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" #include "opt_syscons.h" -#if NSC > 0 - #include #include #include #include #include #include /* exported subroutines */ void sc_move_cursor(scr_stat *scp, int x, int y) { if (x < 0) x = 0; if (y < 0) y = 0; if (x >= scp->xsize) x = scp->xsize - 1; if (y >= scp->ysize) y = scp->ysize - 1; scp->xpos = x; scp->ypos = y; scp->cursor_pos = scp->ypos*scp->xsize + scp->xpos; } void sc_clear_screen(scr_stat *scp) { (*scp->tsw->te_clear)(scp); scp->cursor_oldpos = scp->cursor_pos; sc_remove_cutmarking(scp); } /* terminal emulator manager routines */ static LIST_HEAD(, sc_term_sw) sc_term_list = LIST_HEAD_INITIALIZER(sc_term_list); int sc_term_add(sc_term_sw_t *sw) { LIST_INSERT_HEAD(&sc_term_list, sw, link); return 0; } int sc_term_remove(sc_term_sw_t *sw) { LIST_REMOVE(sw, link); return 0; } sc_term_sw_t *sc_term_match(char *name) { sc_term_sw_t **list; sc_term_sw_t *p; if (!LIST_EMPTY(&sc_term_list)) { LIST_FOREACH(p, &sc_term_list, link) { if ((strcmp(name, p->te_name) == 0) || (strcmp(name, "*") == 0)) { return p; } } } else { list = (sc_term_sw_t **)scterm_set.ls_items; while ((p = *list++) != NULL) { if ((strcmp(name, p->te_name) == 0) || (strcmp(name, "*") == 0)) { return p; } } } return NULL; } sc_term_sw_t *sc_term_match_by_number(int index) { sc_term_sw_t *p; if (index <= 0) return NULL; LIST_FOREACH(p, &sc_term_list, link) { if (--index <= 0) return p; } return NULL; } - -#endif /* NSC > 0 */ Index: head/sys/dev/syscons/scvesactl.c =================================================================== --- head/sys/dev/syscons/scvesactl.c (revision 56835) +++ head/sys/dev/syscons/scvesactl.c (revision 56836) @@ -1,141 +1,133 @@ /*- * Copyright (c) 1998 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" -#include "vga.h" -#include "opt_syscons.h" #include "opt_vga.h" -#include "opt_vesa.h" -#ifdef VGA_NO_MODE_CHANGE -#undef VESA -#endif +#ifndef VGA_NO_MODE_CHANGE -#if (NSC > 0 && NVGA > 0 && defined(VESA)) || defined(KLD_MODULE) - #include #include #include #include #include #include #include #include #include static d_ioctl_t *prev_user_ioctl; static int vesa_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { scr_stat *scp; struct tty *tp; int mode; tp = dev->si_tty; if (!tp) return ENXIO; scp = SC_STAT(tp->t_dev); switch (cmd) { /* generic text modes */ case SW_TEXT_132x25: case SW_TEXT_132x30: case SW_TEXT_132x43: case SW_TEXT_132x50: case SW_TEXT_132x60: if (!(scp->sc->adp->va_flags & V_ADP_MODECHANGE)) return ENODEV; return sc_set_text_mode(scp, tp, cmd & 0xff, 0, 0, 0); /* text modes */ case SW_VESA_C80x60: case SW_VESA_C132x25: case SW_VESA_C132x43: case SW_VESA_C132x50: case SW_VESA_C132x60: if (!(scp->sc->adp->va_flags & V_ADP_MODECHANGE)) return ENODEV; mode = (cmd & 0xff) + M_VESA_BASE; return sc_set_text_mode(scp, tp, mode, 0, 0, 0); /* graphics modes */ case SW_VESA_32K_320: case SW_VESA_64K_320: case SW_VESA_FULL_320: case SW_VESA_CG640x400: case SW_VESA_CG640x480: case SW_VESA_32K_640: case SW_VESA_64K_640: case SW_VESA_FULL_640: case SW_VESA_800x600: case SW_VESA_CG800x600: case SW_VESA_32K_800: case SW_VESA_64K_800: case SW_VESA_FULL_800: case SW_VESA_1024x768: case SW_VESA_CG1024x768: case SW_VESA_32K_1024: case SW_VESA_64K_1024: case SW_VESA_FULL_1024: case SW_VESA_1280x1024: case SW_VESA_CG1280x1024: case SW_VESA_32K_1280: case SW_VESA_64K_1280: case SW_VESA_FULL_1280: if (!(scp->sc->adp->va_flags & V_ADP_MODECHANGE)) return ENODEV; mode = (cmd & 0xff) + M_VESA_BASE; return sc_set_graphics_mode(scp, tp, mode); } if (prev_user_ioctl) return (*prev_user_ioctl)(dev, cmd, data, flag, p); else return ENOIOCTL; } int vesa_load_ioctl(void) { if (prev_user_ioctl) return EBUSY; prev_user_ioctl = sc_user_ioctl; sc_user_ioctl = vesa_ioctl; return 0; } int vesa_unload_ioctl(void) { if (sc_user_ioctl != vesa_ioctl) return EBUSY; sc_user_ioctl = prev_user_ioctl; prev_user_ioctl = NULL; return 0; } -#endif /* (NSC > 0 && NVGA > 0 && VESA) || KLD_MODULE */ +#endif /* SC_NO_MODE_CHANGE */ Index: head/sys/dev/syscons/scvgarndr.c =================================================================== --- head/sys/dev/syscons/scvgarndr.c (revision 56835) +++ head/sys/dev/syscons/scvgarndr.c (revision 56836) @@ -1,830 +1,824 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" -#include "vga.h" #include "opt_syscons.h" #include "opt_vga.h" -#if NSC > 0 && NVGA > 0 - #include #include #include #include #include #include #include #include #ifndef SC_RENDER_DEBUG #define SC_RENDER_DEBUG 0 #endif static vr_clear_t vga_txtclear; static vr_draw_border_t vga_txtborder; static vr_draw_t vga_txtdraw; static vr_set_cursor_t vga_txtcursor_shape; static vr_draw_cursor_t vga_txtcursor; static vr_blink_cursor_t vga_txtblink; #ifndef SC_NO_CUTPASTE static vr_draw_mouse_t vga_txtmouse; #else #define vga_txtmouse (vr_draw_mouse_t *)vga_nop #endif #ifdef SC_PIXEL_MODE static vr_clear_t vga_pxlclear; static vr_draw_border_t vga_pxlborder; static vr_draw_t vga_egadraw; static vr_draw_t vga_vgadraw; static vr_set_cursor_t vga_pxlcursor_shape; static vr_draw_cursor_t vga_pxlcursor; static vr_blink_cursor_t vga_pxlblink; #ifndef SC_NO_CUTPASTE static vr_draw_mouse_t vga_pxlmouse; #else #define vga_pxlmouse (vr_draw_mouse_t *)vga_nop #endif #endif /* SC_PIXEL_MODE */ #ifndef SC_NO_MODE_CHANGE static vr_draw_border_t vga_grborder; #endif static void vga_nop(scr_stat *scp, ...); static struct linker_set vga_set; static sc_rndr_sw_t txtrndrsw = { vga_txtclear, vga_txtborder, vga_txtdraw, vga_txtcursor_shape, vga_txtcursor, vga_txtblink, (vr_set_mouse_t *)vga_nop, vga_txtmouse, }; RENDERER(mda, 0, txtrndrsw, vga_set); RENDERER(cga, 0, txtrndrsw, vga_set); RENDERER(ega, 0, txtrndrsw, vga_set); RENDERER(vga, 0, txtrndrsw, vga_set); #ifdef SC_PIXEL_MODE static sc_rndr_sw_t egarndrsw = { vga_pxlclear, vga_pxlborder, vga_egadraw, vga_pxlcursor_shape, vga_pxlcursor, vga_pxlblink, (vr_set_mouse_t *)vga_nop, vga_pxlmouse, }; RENDERER(ega, PIXEL_MODE, egarndrsw, vga_set); static sc_rndr_sw_t vgarndrsw = { vga_pxlclear, vga_pxlborder, vga_vgadraw, vga_pxlcursor_shape, vga_pxlcursor, vga_pxlblink, (vr_set_mouse_t *)vga_nop, vga_pxlmouse, }; RENDERER(vga, PIXEL_MODE, vgarndrsw, vga_set); #endif /* SC_PIXEL_MODE */ #ifndef SC_NO_MODE_CHANGE static sc_rndr_sw_t grrndrsw = { (vr_clear_t *)vga_nop, vga_grborder, (vr_draw_t *)vga_nop, (vr_set_cursor_t *)vga_nop, (vr_draw_cursor_t *)vga_nop, (vr_blink_cursor_t *)vga_nop, (vr_set_mouse_t *)vga_nop, (vr_draw_mouse_t *)vga_nop, }; RENDERER(cga, GRAPHICS_MODE, grrndrsw, vga_set); RENDERER(ega, GRAPHICS_MODE, grrndrsw, vga_set); RENDERER(vga, GRAPHICS_MODE, grrndrsw, vga_set); #endif /* SC_NO_MODE_CHANGE */ RENDERER_MODULE(vga, vga_set); #ifndef SC_NO_CUTPASTE static u_short mouse_and_mask[16] = { 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80, 0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000 }; static u_short mouse_or_mask[16] = { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800, 0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000 }; #endif static void vga_nop(scr_stat *scp, ...) { } /* text mode renderer */ static void vga_txtclear(scr_stat *scp, int c, int attr) { sc_vtb_clear(&scp->scr, c, attr); } static void vga_txtborder(scr_stat *scp, int color) { (*vidsw[scp->sc->adapter]->set_border)(scp->sc->adp, color); } static void vga_txtdraw(scr_stat *scp, int from, int count, int flip) { vm_offset_t p; int c; int a; if (from + count > scp->xsize*scp->ysize) count = scp->xsize*scp->ysize - from; if (flip) { for (p = sc_vtb_pointer(&scp->scr, from); count-- > 0; ++from) { c = sc_vtb_getc(&scp->vtb, from); a = sc_vtb_geta(&scp->vtb, from); a = (a & 0x8800) | ((a & 0x7000) >> 4) | ((a & 0x0700) << 4); p = sc_vtb_putchar(&scp->scr, p, c, a); } } else { sc_vtb_copy(&scp->vtb, from, &scp->scr, from, count); } } static void vga_txtcursor_shape(scr_stat *scp, int base, int height, int blink) { if (base < 0 || base >= scp->font_size) return; /* the caller may set height <= 0 in order to disable the cursor */ #if 0 scp->cursor_base = base; scp->cursor_height = height; #endif (*vidsw[scp->sc->adapter]->set_hw_cursor_shape)(scp->sc->adp, base, height, scp->font_size, blink); } static void vga_txtcursor(scr_stat *scp, int at, int blink, int on, int flip) { video_adapter_t *adp; int cursor_attr; if (scp->cursor_height <= 0) /* the text cursor is disabled */ return; adp = scp->sc->adp; if (blink) { scp->status |= VR_CURSOR_BLINK; if (on) { scp->status |= VR_CURSOR_ON; (*vidsw[adp->va_index]->set_hw_cursor)(adp, at%scp->xsize, at/scp->xsize); } else { if (scp->status & VR_CURSOR_ON) (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1); scp->status &= ~VR_CURSOR_ON; } } else { scp->status &= ~VR_CURSOR_BLINK; if (on) { scp->status |= VR_CURSOR_ON; cursor_attr = sc_vtb_geta(&scp->vtb, at); scp->cursor_saveunder_char = sc_vtb_getc(&scp->scr, at); scp->cursor_saveunder_attr = cursor_attr; if ((cursor_attr & 0x7000) == 0x7000) { cursor_attr &= 0x8f00; if ((cursor_attr & 0x0700) == 0) cursor_attr |= 0x0700; } else { cursor_attr |= 0x7000; if ((cursor_attr & 0x0700) == 0x0700) cursor_attr &= 0xf000; } if (flip) cursor_attr = (cursor_attr & 0x8800) | ((cursor_attr & 0x7000) >> 4) | ((cursor_attr & 0x0700) << 4); sc_vtb_putc(&scp->scr, at, sc_vtb_getc(&scp->scr, at), cursor_attr); } else { cursor_attr = scp->cursor_saveunder_attr; if (flip) cursor_attr = (cursor_attr & 0x8800) | ((cursor_attr & 0x7000) >> 4) | ((cursor_attr & 0x0700) << 4); if (scp->status & VR_CURSOR_ON) sc_vtb_putc(&scp->scr, at, scp->cursor_saveunder_char, cursor_attr); scp->status &= ~VR_CURSOR_ON; } } } static void vga_txtblink(scr_stat *scp, int at, int flip) { } #ifndef SC_NO_CUTPASTE static void draw_txtmouse(scr_stat *scp, int x, int y) { #ifndef SC_ALT_MOUSE_IMAGE u_char font_buf[128]; u_short cursor[32]; u_char c; int pos; int xoffset, yoffset; int crtc_addr; int i; /* prepare mousepointer char's bitmaps */ pos = (y/scp->font_size - scp->yoff)*scp->xsize + x/8 - scp->xoff; bcopy(scp->font + sc_vtb_getc(&scp->vtb, pos)*scp->font_size, &font_buf[0], scp->font_size); bcopy(scp->font + sc_vtb_getc(&scp->vtb, pos + 1)*scp->font_size, &font_buf[32], scp->font_size); bcopy(scp->font + sc_vtb_getc(&scp->vtb, pos + scp->xsize)*scp->font_size, &font_buf[64], scp->font_size); bcopy(scp->font + sc_vtb_getc(&scp->vtb, pos + scp->xsize + 1)*scp->font_size, &font_buf[96], scp->font_size); for (i = 0; i < scp->font_size; ++i) { cursor[i] = font_buf[i]<<8 | font_buf[i+32]; cursor[i + scp->font_size] = font_buf[i+64]<<8 | font_buf[i+96]; } /* now and-or in the mousepointer image */ xoffset = x%8; yoffset = y%scp->font_size; for (i = 0; i < 16; ++i) { cursor[i + yoffset] = (cursor[i + yoffset] & ~(mouse_and_mask[i] >> xoffset)) | (mouse_or_mask[i] >> xoffset); } for (i = 0; i < scp->font_size; ++i) { font_buf[i] = (cursor[i] & 0xff00) >> 8; font_buf[i + 32] = cursor[i] & 0xff; font_buf[i + 64] = (cursor[i + scp->font_size] & 0xff00) >> 8; font_buf[i + 96] = cursor[i + scp->font_size] & 0xff; } #if 1 /* wait for vertical retrace to avoid jitter on some videocards */ crtc_addr = scp->sc->adp->va_crtc_addr; while (!(inb(crtc_addr + 6) & 0x08)) /* idle */ ; #endif c = scp->sc->mouse_char; (*vidsw[scp->sc->adapter]->load_font)(scp->sc->adp, 0, 32, font_buf, c, 4); sc_vtb_putc(&scp->scr, pos, c, sc_vtb_geta(&scp->scr, pos)); /* FIXME: may be out of range! */ sc_vtb_putc(&scp->scr, pos + scp->xsize, c + 2, sc_vtb_geta(&scp->scr, pos + scp->xsize)); if (x < (scp->xsize - 1)*8) { sc_vtb_putc(&scp->scr, pos + 1, c + 1, sc_vtb_geta(&scp->scr, pos + 1)); sc_vtb_putc(&scp->scr, pos + scp->xsize + 1, c + 3, sc_vtb_geta(&scp->scr, pos + scp->xsize + 1)); } #else /* SC_ALT_MOUSE_IMAGE */ /* Red, magenta and brown are mapped to green to to keep it readable */ static const int col_conv[16] = { 6, 6, 6, 6, 2, 2, 2, 6, 14, 14, 14, 14, 10, 10, 10, 14 }; int pos; int color; int a; pos = (y/scp->font_size - scp->yoff)*scp->xsize + x/8 - scp->xoff; a = sc_vtb_geta(&scp->scr, pos); if (scp->sc->adp->va_flags & V_ADP_COLOR) color = (col_conv[(a & 0xf000) >> 12] << 12) | ((a & 0x0f00) | 0x0800); else color = ((a & 0xf000) >> 4) | ((a & 0x0f00) << 4); sc_vtb_putc(&scp->scr, pos, sc_vtb_getc(&scp->scr, pos), color); #endif /* SC_ALT_MOUSE_IMAGE */ } static void remove_txtmouse(scr_stat *scp, int x, int y) { } static void vga_txtmouse(scr_stat *scp, int x, int y, int on) { if (on) draw_txtmouse(scp, x, y); else remove_txtmouse(scp, x, y); } #endif /* SC_NO_CUTPASTE */ #ifdef SC_PIXEL_MODE /* pixel (raster text) mode renderer */ static void vga_pxlclear(scr_stat *scp, int c, int attr) { vm_offset_t p; int line_width; int lines; int i; /* XXX: we are just filling the screen with the background color... */ outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, ((attr & 0xf000) >> 4) | 0x00); /* set/reset */ line_width = scp->sc->adp->va_line_width; lines = scp->ysize*scp->font_size; p = scp->sc->adp->va_window + line_width*scp->yoff*scp->font_size + scp->xoff; for (i = 0; i < lines; ++i) { bzero_io((void *)p, scp->xsize); p += line_width; } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_pxlborder(scr_stat *scp, int color) { vm_offset_t p; int line_width; int i; (*vidsw[scp->sc->adapter]->set_border)(scp->sc->adp, color); outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, (color << 8) | 0x00); /* set/reset */ line_width = scp->sc->adp->va_line_width; p = scp->sc->adp->va_window; if (scp->yoff > 0) { bzero_io((void *)p, line_width*scp->yoff*scp->font_size); bzero_io((void *)(p + line_width*(scp->yoff + scp->ysize) *scp->font_size), line_width*(scp->ypixel - (scp->yoff + scp->ysize)*scp->font_size)); } if (scp->xoff > 0) { for (i = 0; i < scp->ysize*scp->font_size; ++i) { bzero_io((void *)(p + line_width *(scp->yoff*scp->font_size + i)), scp->xoff); bzero_io((void *)(p + line_width *(scp->yoff*scp->font_size + i) + scp->xoff + scp->xsize), scp->xpixel/8 - scp->xoff - scp->xsize); } } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_egadraw(scr_stat *scp, int from, int count, int flip) { vm_offset_t d; vm_offset_t e; u_char *f; u_short bg; u_short col1, col2; int line_width; int i, j; int a; u_char c; line_width = scp->sc->adp->va_line_width; d = scp->sc->adp->va_window + scp->xoff + scp->yoff*scp->font_size*line_width + (from%scp->xsize) + scp->font_size*line_width*(from/scp->xsize); outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ bg = -1; if (from + count > scp->xsize*scp->ysize) count = scp->xsize*scp->ysize - from; for (i = from; count-- > 0; ++i) { a = sc_vtb_geta(&scp->vtb, i); if (flip) { col1 = ((a & 0x7000) >> 4) | (a & 0x0800); col2 = ((a & 0x8000) >> 4) | (a & 0x0700); } else { col1 = (a & 0x0f00); col2 = (a & 0xf000) >> 4; } /* set background color in EGA/VGA latch */ if (bg != col2) { bg = col2; outw(GDCIDX, bg | 0x00); /* set/reset */ outw(GDCIDX, 0xff08); /* bit mask */ writeb(d, 0); c = readb(d); /* set bg color in the latch */ } /* foreground color */ outw(GDCIDX, col1 | 0x00); /* set/reset */ e = d; f = &(scp->font[sc_vtb_getc(&scp->vtb, i)*scp->font_size]); for (j = 0; j < scp->font_size; ++j, ++f) { outw(GDCIDX, (*f << 8) | 0x08); /* bit mask */ writeb(e, 0); e += line_width; } ++d; if ((i % scp->xsize) == scp->xsize - 1) d += scp->xoff*2 + (scp->font_size - 1)*line_width; } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ } static void vga_vgadraw(scr_stat *scp, int from, int count, int flip) { vm_offset_t d; vm_offset_t e; u_char *f; u_short bg; u_short col1, col2; int line_width; int i, j; int a; u_char c; line_width = scp->sc->adp->va_line_width; d = scp->sc->adp->va_window + scp->xoff + scp->yoff*scp->font_size*line_width + (from%scp->xsize) + scp->font_size*line_width*(from/scp->xsize); outw(GDCIDX, 0x0305); /* read mode 0, write mode 3 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ bg = -1; if (from + count > scp->xsize*scp->ysize) count = scp->xsize*scp->ysize - from; for (i = from; count-- > 0; ++i) { a = sc_vtb_geta(&scp->vtb, i); if (flip) { col1 = ((a & 0x7000) >> 4) | (a & 0x0800); col2 = ((a & 0x8000) >> 4) | (a & 0x0700); } else { col1 = (a & 0x0f00); col2 = (a & 0xf000) >> 4; } /* set background color in EGA/VGA latch */ if (bg != col2) { bg = col2; outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, bg | 0x00); /* set/reset */ writeb(d, 0); c = readb(d); /* set bg color in the latch */ outw(GDCIDX, 0x0305); /* read mode 0, write mode 3 */ } /* foreground color */ outw(GDCIDX, col1 | 0x00); /* set/reset */ e = d; f = &(scp->font[sc_vtb_getc(&scp->vtb, i)*scp->font_size]); for (j = 0; j < scp->font_size; ++j, ++f) { writeb(e, *f); e += line_width; } ++d; if ((i % scp->xsize) == scp->xsize - 1) d += scp->xoff*2 + (scp->font_size - 1)*line_width; } outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_pxlcursor_shape(scr_stat *scp, int base, int height, int blink) { if (base < 0 || base >= scp->font_size) return; /* the caller may set height <= 0 in order to disable the cursor */ #if 0 scp->cursor_base = base; scp->cursor_height = height; #endif } static void draw_pxlcursor(scr_stat *scp, int at, int on, int flip) { vm_offset_t d; u_char *f; int line_width; int height; int col; int a; int i; u_char c; line_width = scp->sc->adp->va_line_width; d = scp->sc->adp->va_window + scp->xoff + scp->yoff*scp->font_size*line_width + (at%scp->xsize) + scp->font_size*line_width*(at/scp->xsize) + (scp->font_size - scp->cursor_base - 1)*line_width; outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ /* set background color in EGA/VGA latch */ a = sc_vtb_geta(&scp->vtb, at); if (flip) col = (on) ? ((a & 0xf000) >> 4) : (a & 0x0f00); else col = (on) ? (a & 0x0f00) : ((a & 0xf000) >> 4); outw(GDCIDX, col | 0x00); /* set/reset */ outw(GDCIDX, 0xff08); /* bit mask */ writeb(d, 0); c = readb(d); /* set bg color in the latch */ /* foreground color */ if (flip) col = (on) ? (a & 0x0f00) : ((a & 0xf000) >> 4); else col = (on) ? ((a & 0xf000) >> 4) : (a & 0x0f00); outw(GDCIDX, col | 0x00); /* set/reset */ f = &(scp->font[sc_vtb_getc(&scp->vtb, at)*scp->font_size + scp->font_size - scp->cursor_base - 1]); height = imin(scp->cursor_height, scp->font_size); for (i = 0; i < height; ++i, --f) { outw(GDCIDX, (*f << 8) | 0x08); /* bit mask */ writeb(d, 0); d -= line_width; } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ } static void vga_pxlcursor(scr_stat *scp, int at, int blink, int on, int flip) { if (scp->cursor_height <= 0) /* the text cursor is disabled */ return; if (on) { scp->status |= VR_CURSOR_ON; draw_pxlcursor(scp, at, on, flip); } else { if (scp->status & VR_CURSOR_ON) draw_pxlcursor(scp, at, on, flip); scp->status &= ~VR_CURSOR_ON; } if (blink) scp->status |= VR_CURSOR_BLINK; else scp->status &= ~VR_CURSOR_BLINK; } static void vga_pxlblink(scr_stat *scp, int at, int flip) { static int blinkrate = 0; if (!(scp->status & VR_CURSOR_BLINK)) return; if (!(++blinkrate & 4)) return; blinkrate = 0; scp->status ^= VR_CURSOR_ON; draw_pxlcursor(scp, at, scp->status & VR_CURSOR_ON, flip); } #ifndef SC_NO_CUTPASTE static void draw_pxlmouse(scr_stat *scp, int x, int y) { vm_offset_t p; int line_width; int xoff, yoff; int ymax; u_short m; int i, j; line_width = scp->sc->adp->va_line_width; xoff = (x - scp->xoff*8)%8; yoff = y - (y/line_width)*line_width; ymax = imin(y + 16, scp->ypixel); outw(GDCIDX, 0x0805); /* read mode 1, write mode 0 */ outw(GDCIDX, 0x0001); /* set/reset enable */ outw(GDCIDX, 0x0002); /* color compare */ outw(GDCIDX, 0x0007); /* color don't care */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, 0x0803); /* data rotate/function select (and) */ p = scp->sc->adp->va_window + line_width*y + x/8; if (x < scp->xpixel - 16) { for (i = y, j = 0; i < ymax; ++i, ++j) { m = ~(mouse_and_mask[j] >> xoff); #ifdef __i386__ *(u_char *)p &= m >> 8; *(u_char *)(p + 1) &= m; #elif defined(__alpha__) writeb(p, readb(p) & (m >> 8)); writeb(p + 1, readb(p + 1) & (m >> 8)); #endif p += line_width; } } else { xoff += 8; for (i = y, j = 0; i < ymax; ++i, ++j) { m = ~(mouse_and_mask[j] >> xoff); #ifdef __i386__ *(u_char *)p &= m; #elif defined(__alpha__) writeb(p, readb(p) & (m >> 8)); #endif p += line_width; } } outw(GDCIDX, 0x1003); /* data rotate/function select (or) */ p = scp->sc->adp->va_window + line_width*y + x/8; if (x < scp->xpixel - 16) { for (i = y, j = 0; i < ymax; ++i, ++j) { m = mouse_or_mask[j] >> xoff; #ifdef __i386__ *(u_char *)p &= m >> 8; *(u_char *)(p + 1) &= m; #elif defined(__alpha__) writeb(p, readb(p) & (m >> 8)); writeb(p + 1, readb(p + 1) & (m >> 8)); #endif p += line_width; } } else { for (i = y, j = 0; i < ymax; ++i, ++j) { m = mouse_or_mask[j] >> xoff; #ifdef __i386__ *(u_char *)p &= m; #elif defined(__alpha__) writeb(p, readb(p) & (m >> 8)); #endif p += line_width; } } outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ } static void remove_pxlmouse(scr_stat *scp, int x, int y) { vm_offset_t p; int col, row; int pos; int line_width; int ymax; int i; /* erase the mouse cursor image */ col = x/8 - scp->xoff; row = y/scp->font_size - scp->yoff; pos = row*scp->xsize + col; i = (col < scp->xsize - 1) ? 2 : 1; (*scp->rndr->draw)(scp, pos, i, FALSE); if (row < scp->ysize - 1) (*scp->rndr->draw)(scp, pos + scp->xsize, i, FALSE); /* paint border if necessary */ line_width = scp->sc->adp->va_line_width; outw(GDCIDX, 0x0005); /* read mode 0, write mode 0 */ outw(GDCIDX, 0x0003); /* data rotate/function select */ outw(GDCIDX, 0x0f01); /* set/reset enable */ outw(GDCIDX, 0xff08); /* bit mask */ outw(GDCIDX, (scp->border << 8) | 0x00); /* set/reset */ if (row == scp->ysize - 1) { i = (scp->ysize + scp->yoff)*scp->font_size; ymax = imin(i + scp->font_size, scp->ypixel); p = scp->sc->adp->va_window + i*line_width + scp->xoff + col; if (col < scp->xsize - 1) { for (; i < ymax; ++i) { writeb(p, 0); writeb(p + 1, 0); p += line_width; } } else { for (; i < ymax; ++i) { writeb(p, 0); p += line_width; } } } if ((col == scp->xsize - 1) && (scp->xoff > 0)) { i = (row + scp->yoff)*scp->font_size; ymax = imin(i + scp->font_size*2, scp->ypixel); p = scp->sc->adp->va_window + i*line_width + scp->xoff + scp->xsize; for (; i < ymax; ++i) { writeb(p, 0); p += line_width; } } outw(GDCIDX, 0x0000); /* set/reset */ outw(GDCIDX, 0x0001); /* set/reset enable */ } static void vga_pxlmouse(scr_stat *scp, int x, int y, int on) { if (on) draw_pxlmouse(scp, x, y); else remove_pxlmouse(scp, x, y); } #endif /* SC_NO_CUTPASTE */ #endif /* SC_PIXEL_MODE */ #ifndef SC_NO_MODE_CHANGE /* graphics mode renderer */ static void vga_grborder(scr_stat *scp, int color) { (*vidsw[scp->sc->adapter]->set_border)(scp->sc->adp, color); } #endif - -#endif /* NSC > 0 && NVGA > 0 */ Index: head/sys/dev/syscons/scvidctl.c =================================================================== --- head/sys/dev/syscons/scvidctl.c (revision 56835) +++ head/sys/dev/syscons/scvidctl.c (revision 56836) @@ -1,818 +1,813 @@ /*- * Copyright (c) 1998 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" #include "opt_syscons.h" -#if NSC > 0 - #include #include #include #include #include #include #include #include #include /* for compatibility with previous versions */ /* 3.0-RELEASE used the following structure */ typedef struct old_video_adapter { int va_index; int va_type; int va_flags; /* flag bits are the same as the -CURRENT #define V_ADP_COLOR (1<<0) #define V_ADP_MODECHANGE (1<<1) #define V_ADP_STATESAVE (1<<2) #define V_ADP_STATELOAD (1<<3) #define V_ADP_FONT (1<<4) #define V_ADP_PALETTE (1<<5) #define V_ADP_BORDER (1<<6) #define V_ADP_VESA (1<<7) */ int va_crtc_addr; u_int va_window; /* virtual address */ size_t va_window_size; size_t va_window_gran; u_int va_buffer; /* virtual address */ size_t va_buffer_size; int va_initial_mode; int va_initial_bios_mode; int va_mode; } old_video_adapter_t; #define OLD_CONS_ADPINFO _IOWR('c', 101, old_video_adapter_t) /* 3.1-RELEASE used the following structure */ typedef struct old_video_adapter_info { int va_index; int va_type; char va_name[16]; int va_unit; int va_flags; int va_io_base; int va_io_size; int va_crtc_addr; int va_mem_base; int va_mem_size; u_int va_window; /* virtual address */ size_t va_window_size; size_t va_window_gran; u_int va_buffer;; size_t va_buffer_size; int va_initial_mode; int va_initial_bios_mode; int va_mode; int va_line_width; } old_video_adapter_info_t; #define OLD_CONS_ADPINFO2 _IOWR('c', 101, old_video_adapter_info_t) /* 3.0-RELEASE and 3.1-RELEASE used the following structure */ typedef struct old_video_info { int vi_mode; int vi_flags; /* flag bits are the same as the -CURRENT #define V_INFO_COLOR (1<<0) #define V_INFO_GRAPHICS (1<<1) #define V_INFO_LINEAR (1<<2) #define V_INFO_VESA (1<<3) */ int vi_width; int vi_height; int vi_cwidth; int vi_cheight; int vi_depth; int vi_planes; u_int vi_window; /* physical address */ size_t vi_window_size; size_t vi_window_gran; u_int vi_buffer; /* physical address */ size_t vi_buffer_size; } old_video_info_t; #define OLD_CONS_MODEINFO _IOWR('c', 102, old_video_info_t) #define OLD_CONS_FINDMODE _IOWR('c', 103, old_video_info_t) int sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize, int fontsize) { video_info_t info; u_char *font; int prev_ysize; int error; int s; if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info)) return ENODEV; /* adjust argument values */ if (fontsize <= 0) fontsize = info.vi_cheight; if (fontsize < 14) { fontsize = 8; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_8)) return EINVAL; font = scp->sc->font_8; #else font = NULL; #endif } else if (fontsize >= 16) { fontsize = 16; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_16)) return EINVAL; font = scp->sc->font_16; #else font = NULL; #endif } else { fontsize = 14; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_14)) return EINVAL; font = scp->sc->font_14; #else font = NULL; #endif } if ((xsize <= 0) || (xsize > info.vi_width)) xsize = info.vi_width; if ((ysize <= 0) || (ysize > info.vi_height)) ysize = info.vi_height; /* stop screen saver, etc */ s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } if (sc_render_match(scp, scp->sc->adp->va_name, 0) == NULL) { splx(s); return ENODEV; } /* set up scp */ #ifndef SC_NO_HISTORY if (scp->history != NULL) sc_hist_save(scp); #endif prev_ysize = scp->ysize; /* * This is a kludge to fend off scrn_update() while we * muck around with scp. XXX */ scp->status |= UNKNOWN_MODE; scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE); scp->mode = mode; scp->xsize = xsize; scp->ysize = ysize; scp->xoff = 0; scp->yoff = 0; scp->xpixel = scp->xsize*8; scp->ypixel = scp->ysize*fontsize; scp->font = font; scp->font_size = fontsize; /* allocate buffers */ sc_alloc_scr_buffer(scp, TRUE, TRUE); sc_init_emulator(scp, NULL); #ifndef SC_NO_CUTPASTE sc_alloc_cut_buffer(scp, FALSE); #endif #ifndef SC_NO_HISTORY sc_alloc_history_buffer(scp, 0, prev_ysize, FALSE); #endif splx(s); if (scp == scp->sc->cur_scp) set_mode(scp); scp->status &= ~UNKNOWN_MODE; if (tp == NULL) return 0; DPRINTF(5, ("ws_*size (%d,%d), size (%d,%d)\n", tp->t_winsize.ws_col, tp->t_winsize.ws_row, scp->xsize, scp->ysize)); if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; } int sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode) { #ifdef SC_NO_MODE_CHANGE return ENODEV; #else video_info_t info; int error; int s; if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info)) return ENODEV; /* stop screen saver, etc */ s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } if (sc_render_match(scp, scp->sc->adp->va_name, GRAPHICS_MODE) == NULL) { splx(s); return ENODEV; } /* set up scp */ scp->status |= (UNKNOWN_MODE | GRAPHICS_MODE); scp->status &= ~PIXEL_MODE; scp->mode = mode; /* * Don't change xsize and ysize; preserve the previous vty * and history buffers. */ scp->xoff = 0; scp->yoff = 0; scp->xpixel = info.vi_width; scp->ypixel = info.vi_height; scp->font = NULL; scp->font_size = 0; #ifndef SC_NO_SYSMOUSE /* move the mouse cursor at the center of the screen */ sc_mouse_move(scp, scp->xpixel / 2, scp->ypixel / 2); #endif sc_init_emulator(scp, NULL); splx(s); if (scp == scp->sc->cur_scp) set_mode(scp); /* clear_graphics();*/ scp->status &= ~UNKNOWN_MODE; if (tp == NULL) return 0; if (tp->t_winsize.ws_xpixel != scp->xpixel || tp->t_winsize.ws_ypixel != scp->ypixel) { tp->t_winsize.ws_xpixel = scp->xpixel; tp->t_winsize.ws_ypixel = scp->ypixel; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; #endif /* SC_NO_MODE_CHANGE */ } int sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, int fontsize) { #ifndef SC_PIXEL_MODE return ENODEV; #else video_info_t info; u_char *font; int prev_ysize; int error; int s; if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, scp->mode, &info)) return ENODEV; /* this shouldn't happen */ /* adjust argument values */ if (fontsize <= 0) fontsize = info.vi_cheight; if (fontsize < 14) { fontsize = 8; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_8)) return EINVAL; font = scp->sc->font_8; #else font = NULL; #endif } else if (fontsize >= 16) { fontsize = 16; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_16)) return EINVAL; font = scp->sc->font_16; #else font = NULL; #endif } else { fontsize = 14; #ifndef SC_NO_FONT_LOADING if (!(scp->sc->fonts_loaded & FONT_14)) return EINVAL; font = scp->sc->font_14; #else font = NULL; #endif } if (xsize <= 0) xsize = info.vi_width/8; if (ysize <= 0) ysize = info.vi_height/fontsize; if ((info.vi_width < xsize*8) || (info.vi_height < ysize*fontsize)) return EINVAL; /* only 16 color, 4 plane modes are supported XXX */ if ((info.vi_depth != 4) || (info.vi_planes != 4)) return ENODEV; /* * set_pixel_mode() currently does not support video modes whose * memory size is larger than 64K. Because such modes require * bank switching to access the entire screen. XXX */ if (info.vi_width*info.vi_height/8 > info.vi_window_size) return ENODEV; /* stop screen saver, etc */ s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } if (sc_render_match(scp, scp->sc->adp->va_name, PIXEL_MODE) == NULL) { splx(s); return ENODEV; } #if 0 if (scp->tsw) (*scp->tsw->te_term)(scp, scp->ts); scp->tsw = NULL; scp->ts = NULL; #endif /* set up scp */ #ifndef SC_NO_HISTORY if (scp->history != NULL) sc_hist_save(scp); #endif prev_ysize = scp->ysize; scp->status |= (UNKNOWN_MODE | PIXEL_MODE); scp->status &= ~GRAPHICS_MODE; scp->xsize = xsize; scp->ysize = ysize; scp->xoff = (scp->xpixel/8 - xsize)/2; scp->yoff = (scp->ypixel/fontsize - ysize)/2; scp->font = font; scp->font_size = fontsize; /* allocate buffers */ sc_alloc_scr_buffer(scp, TRUE, TRUE); sc_init_emulator(scp, NULL); #ifndef SC_NO_CUTPASTE sc_alloc_cut_buffer(scp, FALSE); #endif #ifndef SC_NO_HISTORY sc_alloc_history_buffer(scp, 0, prev_ysize, FALSE); #endif splx(s); if (scp == scp->sc->cur_scp) { sc_set_border(scp, scp->border); sc_set_cursor_image(scp); } scp->status &= ~UNKNOWN_MODE; if (tp == NULL) return 0; if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; #endif /* SC_PIXEL_MODE */ } #define fb_ioctl(a, c, d) \ (((a) == NULL) ? ENODEV : \ (*vidsw[(a)->va_index]->ioctl)((a), (c), (caddr_t)(d))) int sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p) { scr_stat *scp; video_adapter_t *adp; video_info_t info; video_adapter_info_t adp_info; int error; int s; scp = SC_STAT(tp->t_dev); if (scp == NULL) /* tp == SC_MOUSE */ return ENOIOCTL; adp = scp->sc->adp; if (adp == NULL) /* shouldn't happen??? */ return ENODEV; switch (cmd) { case CONS_CURRENTADP: /* get current adapter index */ case FBIO_ADAPTER: return fb_ioctl(adp, FBIO_ADAPTER, data); case CONS_CURRENT: /* get current adapter type */ case FBIO_ADPTYPE: return fb_ioctl(adp, FBIO_ADPTYPE, data); case OLD_CONS_ADPINFO: /* adapter information (old interface) */ if (((old_video_adapter_t *)data)->va_index >= 0) { adp = vid_get_adapter(((old_video_adapter_t *)data)->va_index); if (adp == NULL) return ENODEV; } ((old_video_adapter_t *)data)->va_index = adp->va_index; ((old_video_adapter_t *)data)->va_type = adp->va_type; ((old_video_adapter_t *)data)->va_flags = adp->va_flags; ((old_video_adapter_t *)data)->va_crtc_addr = adp->va_crtc_addr; ((old_video_adapter_t *)data)->va_window = adp->va_window; ((old_video_adapter_t *)data)->va_window_size = adp->va_window_size; ((old_video_adapter_t *)data)->va_window_gran = adp->va_window_gran; ((old_video_adapter_t *)data)->va_buffer = adp->va_buffer; ((old_video_adapter_t *)data)->va_buffer_size = adp->va_buffer_size; ((old_video_adapter_t *)data)->va_mode = adp->va_mode; ((old_video_adapter_t *)data)->va_initial_mode = adp->va_initial_mode; ((old_video_adapter_t *)data)->va_initial_bios_mode = adp->va_initial_bios_mode; return 0; case OLD_CONS_ADPINFO2: /* adapter information (yet another old I/F) */ adp_info.va_index = ((old_video_adapter_info_t *)data)->va_index; if (adp_info.va_index >= 0) { adp = vid_get_adapter(adp_info.va_index); if (adp == NULL) return ENODEV; } error = fb_ioctl(adp, FBIO_ADPINFO, &adp_info); if (error == 0) bcopy(&adp_info, data, sizeof(old_video_adapter_info_t)); return error; case CONS_ADPINFO: /* adapter information */ case FBIO_ADPINFO: if (((video_adapter_info_t *)data)->va_index >= 0) { adp = vid_get_adapter(((video_adapter_info_t *)data)->va_index); if (adp == NULL) return ENODEV; } return fb_ioctl(adp, FBIO_ADPINFO, data); case CONS_GET: /* get current video mode */ case FBIO_GETMODE: *(int *)data = scp->mode; return 0; #ifndef SC_NO_MODE_CHANGE case FBIO_SETMODE: /* set video mode */ if (!(adp->va_flags & V_ADP_MODECHANGE)) return ENODEV; info.vi_mode = *(int *)data; error = fb_ioctl(adp, FBIO_MODEINFO, &info); if (error) return error; if (info.vi_flags & V_INFO_GRAPHICS) return sc_set_graphics_mode(scp, tp, *(int *)data); else return sc_set_text_mode(scp, tp, *(int *)data, 0, 0, 0); #endif /* SC_NO_MODE_CHANGE */ case OLD_CONS_MODEINFO: /* get mode information (old infterface) */ info.vi_mode = ((old_video_info_t *)data)->vi_mode; error = fb_ioctl(adp, FBIO_MODEINFO, &info); if (error == 0) bcopy(&info, (old_video_info_t *)data, sizeof(old_video_info_t)); return error; case CONS_MODEINFO: /* get mode information */ case FBIO_MODEINFO: return fb_ioctl(adp, FBIO_MODEINFO, data); case OLD_CONS_FINDMODE: /* find a matching video mode (old interface) */ bzero(&info, sizeof(info)); bcopy((old_video_info_t *)data, &info, sizeof(old_video_info_t)); error = fb_ioctl(adp, FBIO_FINDMODE, &info); if (error == 0) bcopy(&info, (old_video_info_t *)data, sizeof(old_video_info_t)); return error; case CONS_FINDMODE: /* find a matching video mode */ case FBIO_FINDMODE: return fb_ioctl(adp, FBIO_FINDMODE, data); case CONS_SETWINORG: /* set frame buffer window origin */ case FBIO_SETWINORG: if (scp != scp->sc->cur_scp) return ENODEV; /* XXX */ return fb_ioctl(adp, FBIO_SETWINORG, data); case FBIO_GETWINORG: /* get frame buffer window origin */ if (scp != scp->sc->cur_scp) return ENODEV; /* XXX */ return fb_ioctl(adp, FBIO_GETWINORG, data); case FBIO_GETDISPSTART: case FBIO_SETDISPSTART: case FBIO_GETLINEWIDTH: case FBIO_SETLINEWIDTH: if (scp != scp->sc->cur_scp) return ENODEV; /* XXX */ return fb_ioctl(adp, cmd, data); case FBIO_GETPALETTE: case FBIO_SETPALETTE: case FBIOPUTCMAP: case FBIOGETCMAP: case FBIOGTYPE: case FBIOGATTR: case FBIOSVIDEO: case FBIOGVIDEO: case FBIOSCURSOR: case FBIOGCURSOR: case FBIOSCURPOS: case FBIOGCURPOS: case FBIOGCURMAX: if (scp != scp->sc->cur_scp) return ENODEV; /* XXX */ return fb_ioctl(adp, cmd, data); #ifndef SC_NO_MODE_CHANGE /* generic text modes */ case SW_TEXT_80x25: case SW_TEXT_80x30: case SW_TEXT_80x43: case SW_TEXT_80x50: case SW_TEXT_80x60: /* FALL THROUGH */ /* VGA TEXT MODES */ case SW_VGA_C40x25: case SW_VGA_C80x25: case SW_VGA_M80x25: case SW_VGA_C80x30: case SW_VGA_M80x30: case SW_VGA_C80x50: case SW_VGA_M80x50: case SW_VGA_C80x60: case SW_VGA_M80x60: case SW_VGA_C90x25: case SW_VGA_M90x25: case SW_VGA_C90x30: case SW_VGA_M90x30: case SW_VGA_C90x43: case SW_VGA_M90x43: case SW_VGA_C90x50: case SW_VGA_M90x50: case SW_VGA_C90x60: case SW_VGA_M90x60: case SW_B40x25: case SW_C40x25: case SW_B80x25: case SW_C80x25: case SW_ENH_B40x25: case SW_ENH_C40x25: case SW_ENH_B80x25: case SW_ENH_C80x25: case SW_ENH_B80x43: case SW_ENH_C80x43: case SW_EGAMONO80x25: #ifdef PC98 /* PC98 TEXT MODES */ case SW_PC98_80x25: case SW_PC98_80x30: #endif if (!(adp->va_flags & V_ADP_MODECHANGE)) return ENODEV; return sc_set_text_mode(scp, tp, cmd & 0xff, 0, 0, 0); /* GRAPHICS MODES */ case SW_BG320: case SW_BG640: case SW_CG320: case SW_CG320_D: case SW_CG640_E: case SW_CG640x350: case SW_ENH_CG640: case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320: case SW_VGA_MODEX: if (!(adp->va_flags & V_ADP_MODECHANGE)) return ENODEV; return sc_set_graphics_mode(scp, tp, cmd & 0xff); #endif /* SC_NO_MODE_CHANGE */ case KDSETMODE: /* set current mode of this (virtual) console */ switch (*(int *)data) { case KD_TEXT: /* switch to TEXT (known) mode */ /* * If scp->mode is of graphics modes, we don't know which * text mode to switch back to... */ if (scp->status & GRAPHICS_MODE) return EINVAL; /* restore fonts & palette ! */ #if 0 #ifndef SC_NO_FONT_LOADING if (ISFONTAVAIL(adp->va_flags) && !(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) /* * FONT KLUDGE * Don't load fonts for now... XXX */ if (scp->sc->fonts_loaded & FONT_8) sc_load_font(scp, 0, 8, scp->sc->font_8, 0, 256); if (scp->sc->fonts_loaded & FONT_14) sc_load_font(scp, 0, 14, scp->sc->font_14, 0, 256); if (scp->sc->fonts_loaded & FONT_16) sc_load_font(scp, 0, 16, scp->sc->font_16, 0, 256); } #endif /* SC_NO_FONT_LOADING */ #endif #ifndef SC_NO_PALETTE_LOADING load_palette(adp, scp->sc->palette); #endif #ifndef PC98 /* move hardware cursor out of the way */ (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1); #endif /* FALL THROUGH */ case KD_TEXT1: /* switch to TEXT (known) mode */ /* * If scp->mode is of graphics modes, we don't know which * text/pixel mode to switch back to... */ if (scp->status & GRAPHICS_MODE) return EINVAL; s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } #ifndef PC98 scp->status |= UNKNOWN_MODE; splx(s); /* no restore fonts & palette */ if (scp == scp->sc->cur_scp) set_mode(scp); sc_clear_screen(scp); scp->status &= ~UNKNOWN_MODE; #else /* PC98 */ scp->status &= ~UNKNOWN_MODE; /* no restore fonts & palette */ if (scp == scp->sc->cur_scp) set_mode(scp); sc_clear_screen(scp); splx(s); #endif /* PC98 */ return 0; #ifdef SC_PIXEL_MODE case KD_PIXEL: /* pixel (raster) display */ if (!(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) return EINVAL; if (scp->status & GRAPHICS_MODE) return sc_set_pixel_mode(scp, tp, scp->xsize, scp->ysize, scp->font_size); s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } scp->status |= (UNKNOWN_MODE | PIXEL_MODE); splx(s); if (scp == scp->sc->cur_scp) { set_mode(scp); #ifndef SC_NO_PALETTE_LOADING load_palette(adp, scp->sc->palette); #endif } sc_clear_screen(scp); scp->status &= ~UNKNOWN_MODE; return 0; #endif /* SC_PIXEL_MODE */ case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */ s = spltty(); if ((error = sc_clean_up(scp))) { splx(s); return error; } scp->status |= UNKNOWN_MODE; splx(s); #ifdef PC98 if (scp == scp->sc->cur_scp) set_mode(scp); #endif return 0; default: return EINVAL; } /* NOT REACHED */ #ifdef SC_PIXEL_MODE case KDRASTER: /* set pixel (raster) display mode */ if (ISUNKNOWNSC(scp) || ISTEXTSC(scp)) return ENODEV; return sc_set_pixel_mode(scp, tp, ((int *)data)[0], ((int *)data)[1], ((int *)data)[2]); #endif /* SC_PIXEL_MODE */ case KDGETMODE: /* get current mode of this (virtual) console */ /* * From the user program's point of view, KD_PIXEL is the same * as KD_TEXT... */ *data = ISGRAPHSC(scp) ? KD_GRAPHICS : KD_TEXT; return 0; case KDSBORDER: /* set border color of this (virtual) console */ scp->border = *data; if (scp == scp->sc->cur_scp) sc_set_border(scp, scp->border); return 0; } return ENOIOCTL; } static LIST_HEAD(, sc_renderer) sc_rndr_list = LIST_HEAD_INITIALIZER(sc_rndr_list); int sc_render_add(sc_renderer_t *rndr) { LIST_INSERT_HEAD(&sc_rndr_list, rndr, link); return 0; } int sc_render_remove(sc_renderer_t *rndr) { /* LIST_REMOVE(rndr, link); */ return EBUSY; /* XXX */ } sc_rndr_sw_t *sc_render_match(scr_stat *scp, char *name, int mode) { const sc_renderer_t **list; const sc_renderer_t *p; if (!LIST_EMPTY(&sc_rndr_list)) { LIST_FOREACH(p, &sc_rndr_list, link) { if ((strcmp(p->name, name) == 0) && (mode == p->mode)) { scp->status &= ~(VR_CURSOR_ON | VR_CURSOR_BLINK); return p->rndrsw; } } } else { list = (const sc_renderer_t **)scrndr_set.ls_items; while ((p = *list++) != NULL) { if ((strcmp(p->name, name) == 0) && (mode == p->mode)) { scp->status &= ~(VR_CURSOR_ON | VR_CURSOR_BLINK); return p->rndrsw; } } } return NULL; } - -#endif /* NSC > 0 */ Index: head/sys/dev/syscons/scvtb.c =================================================================== --- head/sys/dev/syscons/scvtb.c (revision 56835) +++ head/sys/dev/syscons/scvtb.c (revision 56836) @@ -1,300 +1,295 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" #include "opt_syscons.h" -#if NSC > 0 - #include #include #include #include #include #include #include #define vtb_wrap(vtb, at, offset) \ (((at) + (offset) + (vtb)->vtb_size)%(vtb)->vtb_size) void sc_vtb_init(sc_vtb_t *vtb, int type, int cols, int rows, void *buf, int wait) { vtb->vtb_flags = 0; vtb->vtb_type = type; vtb->vtb_cols = cols; vtb->vtb_rows = rows; vtb->vtb_size = cols*rows; vtb->vtb_buffer = NULL; vtb->vtb_tail = 0; switch (type) { case VTB_MEMORY: case VTB_RINGBUFFER: if ((buf == NULL) && (cols*rows != 0)) { vtb->vtb_buffer = (vm_offset_t)malloc(cols*rows*sizeof(u_int16_t), M_DEVBUF, (wait) ? M_WAITOK : M_NOWAIT); if (vtb->vtb_buffer != NULL) bzero((void *)sc_vtb_pointer(vtb, 0), cols*rows*sizeof(u_int16_t)); } else { vtb->vtb_buffer = (vm_offset_t)buf; } vtb->vtb_flags |= VTB_VALID; break; case VTB_FRAMEBUFFER: vtb->vtb_buffer = (vm_offset_t)buf; vtb->vtb_flags |= VTB_VALID; break; default: break; } } void sc_vtb_destroy(sc_vtb_t *vtb) { vm_offset_t p; vtb->vtb_flags = 0; vtb->vtb_cols = 0; vtb->vtb_rows = 0; vtb->vtb_size = 0; vtb->vtb_tail = 0; p = vtb->vtb_buffer; vtb->vtb_buffer = NULL; switch (vtb->vtb_type) { case VTB_MEMORY: case VTB_RINGBUFFER: if (p != NULL) free((void *)p, M_DEVBUF); break; default: break; } vtb->vtb_type = VTB_INVALID; } size_t sc_vtb_size(int cols, int rows) { return (size_t)(cols*rows*sizeof(u_int16_t)); } int sc_vtb_getc(sc_vtb_t *vtb, int at) { if (vtb->vtb_type == VTB_FRAMEBUFFER) return (readw(sc_vtb_pointer(vtb, at)) & 0x00ff); else return (*(u_int16_t *)sc_vtb_pointer(vtb, at) & 0x00ff); } int sc_vtb_geta(sc_vtb_t *vtb, int at) { if (vtb->vtb_type == VTB_FRAMEBUFFER) return (readw(sc_vtb_pointer(vtb, at)) & 0xff00); else return (*(u_int16_t *)sc_vtb_pointer(vtb, at) & 0xff00); } void sc_vtb_putc(sc_vtb_t *vtb, int at, int c, int a) { if (vtb->vtb_type == VTB_FRAMEBUFFER) writew(sc_vtb_pointer(vtb, at), a | c); else *(u_int16_t *)sc_vtb_pointer(vtb, at) = a | c; } vm_offset_t sc_vtb_putchar(sc_vtb_t *vtb, vm_offset_t p, int c, int a) { if (vtb->vtb_type == VTB_FRAMEBUFFER) writew(p, a | c); else *(u_int16_t *)p = a | c; return (p + sizeof(u_int16_t)); } vm_offset_t sc_vtb_pointer(sc_vtb_t *vtb, int at) { return (vtb->vtb_buffer + sizeof(u_int16_t)*(at)); } int sc_vtb_pos(sc_vtb_t *vtb, int pos, int offset) { return ((pos + offset + vtb->vtb_size)%vtb->vtb_size); } void sc_vtb_clear(sc_vtb_t *vtb, int c, int attr) { if (vtb->vtb_type == VTB_FRAMEBUFFER) fillw_io(attr | c, sc_vtb_pointer(vtb, 0), vtb->vtb_size); else fillw(attr | c, (void *)sc_vtb_pointer(vtb, 0), vtb->vtb_size); } void sc_vtb_copy(sc_vtb_t *vtb1, int from, sc_vtb_t *vtb2, int to, int count) { /* XXX if both are VTB_VRAMEBUFFER... */ if (vtb2->vtb_type == VTB_FRAMEBUFFER) { bcopy_toio(sc_vtb_pointer(vtb1, from), sc_vtb_pointer(vtb2, to), count*sizeof(u_int16_t)); } else if (vtb1->vtb_type == VTB_FRAMEBUFFER) { bcopy_fromio(sc_vtb_pointer(vtb1, from), sc_vtb_pointer(vtb2, to), count*sizeof(u_int16_t)); } else { bcopy((void *)sc_vtb_pointer(vtb1, from), (void *)sc_vtb_pointer(vtb2, to), count*sizeof(u_int16_t)); } } void sc_vtb_append(sc_vtb_t *vtb1, int from, sc_vtb_t *vtb2, int count) { int len; if (vtb2->vtb_type != VTB_RINGBUFFER) return; while (count > 0) { len = imin(count, vtb2->vtb_size - vtb2->vtb_tail); if (vtb1->vtb_type == VTB_FRAMEBUFFER) { bcopy_fromio(sc_vtb_pointer(vtb1, from), sc_vtb_pointer(vtb2, vtb2->vtb_tail), len*sizeof(u_int16_t)); } else { bcopy((void *)sc_vtb_pointer(vtb1, from), (void *)sc_vtb_pointer(vtb2, vtb2->vtb_tail), len*sizeof(u_int16_t)); } from += len; count -= len; vtb2->vtb_tail = vtb_wrap(vtb2, vtb2->vtb_tail, len); } } void sc_vtb_seek(sc_vtb_t *vtb, int pos) { vtb->vtb_tail = pos%vtb->vtb_size; } void sc_vtb_erase(sc_vtb_t *vtb, int at, int count, int c, int attr) { if (at + count > vtb->vtb_size) count = vtb->vtb_size - at; if (vtb->vtb_type == VTB_FRAMEBUFFER) fillw_io(attr | c, sc_vtb_pointer(vtb, at), count); else fillw(attr | c, (void *)sc_vtb_pointer(vtb, at), count); } void sc_vtb_move(sc_vtb_t *vtb, int from, int to, int count) { if (from + count > vtb->vtb_size) count = vtb->vtb_size - from; if (to + count > vtb->vtb_size) count = vtb->vtb_size - to; if (count <= 0) return; if (vtb->vtb_type == VTB_FRAMEBUFFER) { bcopy_io(sc_vtb_pointer(vtb, from), sc_vtb_pointer(vtb, to), count*sizeof(u_int16_t)); } else { bcopy((void *)sc_vtb_pointer(vtb, from), (void *)sc_vtb_pointer(vtb, to), count*sizeof(u_int16_t)); } } void sc_vtb_delete(sc_vtb_t *vtb, int at, int count, int c, int attr) { int len; if (at + count > vtb->vtb_size) count = vtb->vtb_size - at; len = vtb->vtb_size - at - count; if (len > 0) { if (vtb->vtb_type == VTB_FRAMEBUFFER) { bcopy_io(sc_vtb_pointer(vtb, at + count), sc_vtb_pointer(vtb, at), len*sizeof(u_int16_t)); } else { bcopy((void *)sc_vtb_pointer(vtb, at + count), (void *)sc_vtb_pointer(vtb, at), len*sizeof(u_int16_t)); } } if (vtb->vtb_type == VTB_FRAMEBUFFER) fillw_io(attr | c, sc_vtb_pointer(vtb, at + len), vtb->vtb_size - at - len); else fillw(attr | c, (void *)sc_vtb_pointer(vtb, at + len), vtb->vtb_size - at - len); } void sc_vtb_ins(sc_vtb_t *vtb, int at, int count, int c, int attr) { if (at + count > vtb->vtb_size) { count = vtb->vtb_size - at; } else { if (vtb->vtb_type == VTB_FRAMEBUFFER) { bcopy_io(sc_vtb_pointer(vtb, at), sc_vtb_pointer(vtb, at + count), (vtb->vtb_size - at - count)*sizeof(u_int16_t)); } else { bcopy((void *)sc_vtb_pointer(vtb, at), (void *)sc_vtb_pointer(vtb, at + count), (vtb->vtb_size - at - count)*sizeof(u_int16_t)); } } if (vtb->vtb_type == VTB_FRAMEBUFFER) fillw_io(attr | c, sc_vtb_pointer(vtb, at), count); else fillw(attr | c, (void *)sc_vtb_pointer(vtb, at), count); } - -#endif /* NSC */ Index: head/sys/dev/syscons/syscons.c =================================================================== --- head/sys/dev/syscons/syscons.c (revision 56835) +++ head/sys/dev/syscons/syscons.c (revision 56836) @@ -1,3410 +1,3406 @@ /*- * Copyright (c) 1992-1998 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" #include "splash.h" #include "opt_syscons.h" #include "opt_ddb.h" #ifdef __i386__ #include "apm.h" #endif -#if NSC > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __i386__ #include #include #include #endif #include #include #include #include #define COLD 0 #define WARM 1 #define DEFAULT_BLANKTIME (5*60) /* 5 minutes */ #define MAX_BLANKTIME (7*24*60*60) /* 7 days!? */ #define KEYCODE_BS 0x0e /* "<-- Backspace" key, XXX */ typedef struct default_attr { int std_color; /* normal hardware color */ int rev_color; /* reverse hardware color */ } default_attr; static default_attr user_default = { SC_NORM_ATTR, SC_NORM_REV_ATTR, }; static default_attr kernel_default = { SC_KERNEL_CONS_ATTR, SC_KERNEL_CONS_REV_ATTR, }; static int sc_console_unit = -1; static scr_stat *sc_console; static struct tty *sc_console_tty; static void *kernel_console_ts; static char init_done = COLD; static char shutdown_in_progress = FALSE; static char sc_malloc = FALSE; static int saver_mode = CONS_LKM_SAVER; /* LKM/user saver */ static int run_scrn_saver = FALSE; /* should run the saver? */ static long scrn_blank_time = 0; /* screen saver timeout value */ #if NSPLASH > 0 static int scrn_blanked; /* # of blanked screen */ static int sticky_splash = FALSE; static void none_saver(sc_softc_t *sc, int blank) { } static void (*current_saver)(sc_softc_t *, int) = none_saver; #endif #if !defined(SC_NO_FONT_LOADING) && defined(SC_DFLT_FONT) #include "font.h" #endif d_ioctl_t *sc_user_ioctl; static bios_values_t bios_value; static int enable_panic_key; SYSCTL_INT(_machdep, OID_AUTO, enable_panic_key, CTLFLAG_RW, &enable_panic_key, 0, ""); #define SC_CONSOLECTL 255 #define VIRTUAL_TTY(sc, x) (SC_DEV((sc), (x))->si_tty) static int debugger; /* prototypes */ static int scvidprobe(int unit, int flags, int cons); static int sckbdprobe(int unit, int flags, int cons); static void scmeminit(void *arg); static int scdevtounit(dev_t dev); static kbd_callback_func_t sckbdevent; static int scparam(struct tty *tp, struct termios *t); static void scstart(struct tty *tp); static void scinit(int unit, int flags); #if __i386__ static void scterm(int unit, int flags); #endif static void scshutdown(void *arg, int howto); static u_int scgetc(sc_softc_t *sc, u_int flags); #define SCGETC_CN 1 #define SCGETC_NONBLOCK 2 static int sccngetch(int flags); static void sccnupdate(scr_stat *scp); static scr_stat *alloc_scp(sc_softc_t *sc, int vty); static void init_scp(sc_softc_t *sc, int vty, scr_stat *scp); static timeout_t scrn_timer; static int and_region(int *s1, int *e1, int s2, int e2); static void scrn_update(scr_stat *scp, int show_cursor); #if NSPLASH > 0 static int scsplash_callback(int event, void *arg); static void scsplash_saver(sc_softc_t *sc, int show); static int add_scrn_saver(void (*this_saver)(sc_softc_t *, int)); static int remove_scrn_saver(void (*this_saver)(sc_softc_t *, int)); static int set_scrn_saver_mode(scr_stat *scp, int mode, u_char *pal, int border); static int restore_scrn_saver_mode(scr_stat *scp, int changemode); static void stop_scrn_saver(sc_softc_t *sc, void (*saver)(sc_softc_t *, int)); static int wait_scrn_saver_stop(sc_softc_t *sc); #define scsplash_stick(stick) (sticky_splash = (stick)) #else /* !NSPLASH */ #define scsplash_stick(stick) #endif /* NSPLASH */ static int do_switch_scr(sc_softc_t *sc, int s); static int vt_proc_alive(scr_stat *scp); static int signal_vt_rel(scr_stat *scp); static int signal_vt_acq(scr_stat *scp); static void exchange_scr(sc_softc_t *sc); static void update_cursor_image(scr_stat *scp); static int save_kbd_state(scr_stat *scp); static int update_kbd_state(scr_stat *scp, int state, int mask); static int update_kbd_leds(scr_stat *scp, int which); static timeout_t blink_screen; #define CDEV_MAJOR 12 static cn_probe_t sccnprobe; static cn_init_t sccninit; static cn_getc_t sccngetc; static cn_checkc_t sccncheckc; static cn_putc_t sccnputc; static cn_dbctl_t sccndbctl; static cn_term_t sccnterm; #if __alpha__ void sccnattach(void); #endif CONS_DRIVER(sc, sccnprobe, sccninit, sccnterm, sccngetc, sccncheckc, sccnputc, sccndbctl); static d_open_t scopen; static d_close_t scclose; static d_read_t scread; static d_ioctl_t scioctl; static d_mmap_t scmmap; static struct cdevsw sc_cdevsw = { /* open */ scopen, /* close */ scclose, /* read */ scread, /* write */ ttywrite, /* ioctl */ scioctl, /* poll */ ttypoll, /* mmap */ scmmap, /* strategy */ nostrategy, /* name */ "sc", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_TTY, /* bmaj */ -1 }; int sc_probe_unit(int unit, int flags) { if (!scvidprobe(unit, flags, FALSE)) { if (bootverbose) printf("sc%d: no video adapter is found.\n", unit); return ENXIO; } /* syscons will be attached even when there is no keyboard */ sckbdprobe(unit, flags, FALSE); return 0; } /* probe video adapters, return TRUE if found */ static int scvidprobe(int unit, int flags, int cons) { /* * Access the video adapter driver through the back door! * Video adapter drivers need to be configured before syscons. * However, when syscons is being probed as the low-level console, * they have not been initialized yet. We force them to initialize * themselves here. XXX */ vid_configure(cons ? VIO_PROBE_ONLY : 0); return (vid_find_adapter("*", unit) >= 0); } /* probe the keyboard, return TRUE if found */ static int sckbdprobe(int unit, int flags, int cons) { /* access the keyboard driver through the backdoor! */ kbd_configure(cons ? KB_CONF_PROBE_ONLY : 0); return (kbd_find_keyboard("*", unit) >= 0); } static char *adapter_name(video_adapter_t *adp) { static struct { int type; char *name[2]; } names[] = { { KD_MONO, { "MDA", "MDA" } }, { KD_HERCULES, { "Hercules", "Hercules" } }, { KD_CGA, { "CGA", "CGA" } }, { KD_EGA, { "EGA", "EGA (mono)" } }, { KD_VGA, { "VGA", "VGA (mono)" } }, { KD_PC98, { "PC-98x1", "PC-98x1" } }, { KD_TGA, { "TGA", "TGA" } }, { -1, { "Unknown", "Unknown" } }, }; int i; for (i = 0; names[i].type != -1; ++i) if (names[i].type == adp->va_type) break; return names[i].name[(adp->va_flags & V_ADP_COLOR) ? 0 : 1]; } int sc_attach_unit(int unit, int flags) { sc_softc_t *sc; scr_stat *scp; #ifdef SC_PIXEL_MODE video_info_t info; #endif int vc; dev_t dev; flags &= ~SC_KERNEL_CONSOLE; if (sc_console_unit == unit) { /* * If this unit is being used as the system console, we need to * adjust some variables and buffers before and after scinit(). */ /* assert(sc_console != NULL) */ flags |= SC_KERNEL_CONSOLE; scmeminit(NULL); scinit(unit, flags); if (sc_console->tsw->te_size > 0) { /* assert(sc_console->ts != NULL); */ kernel_console_ts = sc_console->ts; sc_console->ts = malloc(sc_console->tsw->te_size, M_DEVBUF, M_WAITOK); bcopy(kernel_console_ts, sc_console->ts, sc_console->tsw->te_size); (*sc_console->tsw->te_default_attr)(sc_console, user_default.std_color, user_default.rev_color); } } else { scinit(unit, flags); } sc = sc_get_softc(unit, flags & SC_KERNEL_CONSOLE); sc->config = flags; scp = SC_STAT(sc->dev[0]); if (sc_console == NULL) /* sc_console_unit < 0 */ sc_console = scp; #ifdef SC_PIXEL_MODE if ((sc->config & SC_VESA800X600) && ((*vidsw[sc->adapter]->get_info)(sc->adp, M_VESA_800x600, &info) == 0)) { #if NSPLASH > 0 if (sc->flags & SC_SPLASH_SCRN) splash_term(sc->adp); #endif sc_set_graphics_mode(scp, NULL, M_VESA_800x600); sc_set_pixel_mode(scp, NULL, COL, ROW, 16); sc->initial_mode = M_VESA_800x600; #if NSPLASH > 0 /* put up the splash again! */ if (sc->flags & SC_SPLASH_SCRN) splash_init(sc->adp, scsplash_callback, sc); #endif } #endif /* SC_PIXEL_MODE */ /* initialize cursor */ if (!ISGRAPHSC(scp)) update_cursor_image(scp); /* get screen update going */ scrn_timer(sc); /* set up the keyboard */ kbd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); update_kbd_state(scp, scp->status, LOCK_MASK); printf("sc%d: %s <%d virtual consoles, flags=0x%x>\n", unit, adapter_name(sc->adp), sc->vtys, sc->config); if (bootverbose) { printf("sc%d:", unit); if (sc->adapter >= 0) printf(" fb%d", sc->adapter); if (sc->keyboard >= 0) printf(", kbd%d", sc->keyboard); if (scp->tsw) printf(", terminal emulator: %s (%s)", scp->tsw->te_name, scp->tsw->te_desc); printf("\n"); } /* register a shutdown callback for the kernel console */ if (sc_console_unit == unit) EVENTHANDLER_REGISTER(shutdown_pre_sync, scshutdown, (void *)(uintptr_t)unit, SHUTDOWN_PRI_DEFAULT); for (vc = 0; vc < sc->vtys; vc++) { dev = make_dev(&sc_cdevsw, vc + unit * MAXCONS, UID_ROOT, GID_WHEEL, 0600, "ttyv%r", vc + unit * MAXCONS); sc->dev[vc] = dev; /* * The first vty already has struct tty and scr_stat initialized * in scinit(). The other vtys will have these structs when * first opened. */ } dev = make_dev(&sc_cdevsw, SC_CONSOLECTL, UID_ROOT, GID_WHEEL, 0600, "consolectl"); dev->si_tty = sc_console_tty = ttymalloc(sc_console_tty); SC_STAT(dev) = sc_console; return 0; } static void scmeminit(void *arg) { if (sc_malloc) return; sc_malloc = TRUE; /* * As soon as malloc() becomes functional, we had better allocate * various buffers for the kernel console. */ if (sc_console_unit < 0) /* sc_console == NULL */ return; /* copy the temporary buffer to the final buffer */ sc_alloc_scr_buffer(sc_console, FALSE, FALSE); #ifndef SC_NO_CUTPASTE /* cut buffer is available only when the mouse pointer is used */ if (ISMOUSEAVAIL(sc_console->sc->adp->va_flags)) sc_alloc_cut_buffer(sc_console, FALSE); #endif #ifndef SC_NO_HISTORY /* initialize history buffer & pointers */ sc_alloc_history_buffer(sc_console, 0, 0, FALSE); #endif } /* XXX */ SYSINIT(sc_mem, SI_SUB_KMEM, SI_ORDER_ANY, scmeminit, NULL); int sc_resume_unit(int unit) { /* XXX should be moved to the keyboard driver? */ sc_softc_t *sc; sc = sc_get_softc(unit, (sc_console_unit == unit) ? SC_KERNEL_CONSOLE : 0); if (sc->kbd != NULL) kbd_clear_state(sc->kbd); return 0; } static int scdevtounit(dev_t dev) { int vty = SC_VTY(dev); if (vty == SC_CONSOLECTL) return ((sc_console != NULL) ? sc_console->sc->unit : -1); else if ((vty < 0) || (vty >= MAXCONS*sc_max_unit())) return -1; else return vty/MAXCONS; } int scopen(dev_t dev, int flag, int mode, struct proc *p) { int unit = scdevtounit(dev); sc_softc_t *sc; struct tty *tp; scr_stat *scp; keyarg_t key; int error; DPRINTF(5, ("scopen: dev:%d,%d, unit:%d, vty:%d\n", major(dev), minor(dev), unit, SC_VTY(dev))); sc = sc_get_softc(unit, (sc_console_unit == unit) ? SC_KERNEL_CONSOLE : 0); if (sc == NULL) return ENXIO; tp = dev->si_tty = ttymalloc(dev->si_tty); tp->t_oproc = scstart; tp->t_param = scparam; tp->t_stop = nottystop; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { ttychars(tp); /* Use the current setting of the <-- key as default VERASE. */ /* If the Delete key is preferable, an stty is necessary */ if (sc != NULL) { key.keynum = KEYCODE_BS; kbd_ioctl(sc->kbd, GIO_KEYMAPENT, (caddr_t)&key); tp->t_cc[VERASE] = key.key.map[0]; } tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; scparam(tp, &tp->t_termios); (*linesw[tp->t_line].l_modem)(tp, 1); } else if (tp->t_state & TS_XCLUDE && suser(p)) return(EBUSY); error = (*linesw[tp->t_line].l_open)(dev, tp); scp = SC_STAT(dev); if (scp == NULL) { scp = SC_STAT(dev) = alloc_scp(sc, SC_VTY(dev)); if (ISGRAPHSC(scp)) sc_set_pixel_mode(scp, NULL, COL, ROW, 16); } if (!tp->t_winsize.ws_col && !tp->t_winsize.ws_row) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; } return error; } int scclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = dev->si_tty; scr_stat *scp; int s; if (SC_VTY(dev) != SC_CONSOLECTL) { scp = SC_STAT(tp->t_dev); /* were we in the middle of the VT switching process? */ DPRINTF(5, ("sc%d: scclose(), ", scp->sc->unit)); s = spltty(); if ((scp == scp->sc->cur_scp) && (scp->sc->unit == sc_console_unit)) cons_unavail = FALSE; if (scp->status & SWITCH_WAIT_REL) { /* assert(scp == scp->sc->cur_scp) */ DPRINTF(5, ("reset WAIT_REL, ")); scp->status &= ~SWITCH_WAIT_REL; do_switch_scr(scp->sc, s); } if (scp->status & SWITCH_WAIT_ACQ) { /* assert(scp == scp->sc->cur_scp) */ DPRINTF(5, ("reset WAIT_ACQ, ")); scp->status &= ~SWITCH_WAIT_ACQ; scp->sc->switch_in_progress = 0; } #if not_yet_done if (scp == &main_console) { scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; } else { sc_vtb_destroy(&scp->vtb); sc_vtb_destroy(&scp->scr); sc_free_history_buffer(scp, scp->ysize); SC_STAT(dev) = NULL; free(scp, M_DEVBUF); } #else scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; #endif scp->kbd_mode = K_XLATE; if (scp == scp->sc->cur_scp) kbd_ioctl(scp->sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); DPRINTF(5, ("done.\n")); } spltty(); (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); spl0(); return(0); } int scread(dev_t dev, struct uio *uio, int flag) { sc_touch_scrn_saver(); return ttyread(dev, uio, flag); } static int sckbdevent(keyboard_t *thiskbd, int event, void *arg) { sc_softc_t *sc; struct tty *cur_tty; int c; size_t len; u_char *cp; sc = (sc_softc_t *)arg; /* assert(thiskbd == sc->kbd) */ switch (event) { case KBDIO_KEYINPUT: break; case KBDIO_UNLOADING: sc->kbd = NULL; sc->keyboard = -1; kbd_release(thiskbd, (void *)&sc->keyboard); return 0; default: return EINVAL; } /* * Loop while there is still input to get from the keyboard. * I don't think this is nessesary, and it doesn't fix * the Xaccel-2.1 keyboard hang, but it can't hurt. XXX */ while ((c = scgetc(sc, SCGETC_NONBLOCK)) != NOKEY) { cur_tty = VIRTUAL_TTY(sc, sc->cur_scp->index); /* XXX */ if (!(cur_tty->t_state & TS_ISOPEN)) if (((cur_tty = sc_console_tty) == NULL) || !(cur_tty->t_state & TS_ISOPEN)) continue; if ((*sc->cur_scp->tsw->te_input)(sc->cur_scp, c, cur_tty)) continue; switch (KEYFLAGS(c)) { case 0x0000: /* normal key */ (*linesw[cur_tty->t_line].l_rint)(KEYCHAR(c), cur_tty); break; case FKEY: /* function key, return string */ cp = kbd_get_fkeystr(thiskbd, KEYCHAR(c), &len); if (cp != NULL) { while (len-- > 0) (*linesw[cur_tty->t_line].l_rint)(*cp++, cur_tty); } break; case MKEY: /* meta is active, prepend ESC */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)(KEYCHAR(c), cur_tty); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)('[', cur_tty); (*linesw[cur_tty->t_line].l_rint)('Z', cur_tty); break; } } #ifndef SC_NO_CUTPASTE if (sc->cur_scp->status & MOUSE_VISIBLE) { sc_remove_mouse_image(sc->cur_scp); sc->cur_scp->status &= ~MOUSE_VISIBLE; } #endif /* SC_NO_CUTPASTE */ return 0; } static int scparam(struct tty *tp, struct termios *t) { tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; return 0; } int scioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { int error; int i; struct tty *tp; sc_softc_t *sc; scr_stat *scp; int s; tp = dev->si_tty; /* If there is a user_ioctl function call that first */ if (sc_user_ioctl) { error = (*sc_user_ioctl)(dev, cmd, data, flag, p); if (error != ENOIOCTL) return error; } error = sc_vid_ioctl(tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; #ifndef SC_NO_HISTORY error = sc_hist_ioctl(tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; #endif #ifndef SC_NO_SYSMOUSE error = sc_mouse_ioctl(tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; #endif scp = SC_STAT(tp->t_dev); /* assert(scp != NULL) */ /* scp is sc_console, if SC_VTY(dev) == SC_CONSOLECTL. */ sc = scp->sc; if (scp->tsw) { error = (*scp->tsw->te_ioctl)(scp, tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; } switch (cmd) { /* process console hardware related ioctl's */ case GIO_ATTR: /* get current attributes */ /* this ioctl is not processed here, but in the terminal emulator */ return ENOTTY; case GIO_COLOR: /* is this a color console ? */ *(int *)data = (sc->adp->va_flags & V_ADP_COLOR) ? 1 : 0; return 0; case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ if (*(int *)data < 0 || *(int *)data > MAX_BLANKTIME) return EINVAL; s = spltty(); scrn_blank_time = *(int *)data; run_scrn_saver = (scrn_blank_time != 0); splx(s); return 0; case CONS_CURSORTYPE: /* set cursor type blink/noblink */ if (!ISGRAPHSC(sc->cur_scp)) sc_remove_cursor_image(sc->cur_scp); if ((*(int*)data) & 0x01) sc->flags |= SC_BLINK_CURSOR; else sc->flags &= ~SC_BLINK_CURSOR; if ((*(int*)data) & 0x02) { sc->flags |= SC_CHAR_CURSOR; } else sc->flags &= ~SC_CHAR_CURSOR; /* * The cursor shape is global property; all virtual consoles * are affected. Update the cursor in the current console... */ if (!ISGRAPHSC(sc->cur_scp)) { s = spltty(); sc_set_cursor_image(sc->cur_scp); sc_draw_cursor_image(sc->cur_scp); splx(s); } return 0; case CONS_BELLTYPE: /* set bell type sound/visual */ if ((*(int *)data) & 0x01) sc->flags |= SC_VISUAL_BELL; else sc->flags &= ~SC_VISUAL_BELL; if ((*(int *)data) & 0x02) sc->flags |= SC_QUIET_BELL; else sc->flags &= ~SC_QUIET_BELL; return 0; case CONS_GETINFO: /* get current (virtual) console info */ { vid_info_t *ptr = (vid_info_t*)data; if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = sc->cur_scp->index; ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; ptr->mv_csz = scp->xsize; ptr->mv_rsz = scp->ysize; /* * The following fields are filled by the terminal emulator. XXX * * ptr->mv_norm.fore * ptr->mv_norm.back * ptr->mv_rev.fore * ptr->mv_rev.back */ ptr->mv_grfc.fore = 0; /* not supported */ ptr->mv_grfc.back = 0; /* not supported */ ptr->mv_ovscan = scp->border; if (scp == sc->cur_scp) save_kbd_state(scp); ptr->mk_keylock = scp->status & LOCK_MASK; return 0; } return EINVAL; } case CONS_GETVERS: /* get version number */ *(int*)data = 0x200; /* version 2.0 */ return 0; case CONS_IDLE: /* see if the screen has been idle */ /* * When the screen is in the GRAPHICS_MODE or UNKNOWN_MODE, * the user process may have been writing something on the * screen and syscons is not aware of it. Declare the screen * is NOT idle if it is in one of these modes. But there is * an exception to it; if a screen saver is running in the * graphics mode in the current screen, we should say that the * screen has been idle. */ *(int *)data = (sc->flags & SC_SCRN_IDLE) && (!ISGRAPHSC(sc->cur_scp) || (sc->cur_scp->status & SAVER_RUNNING)); return 0; case CONS_SAVERMODE: /* set saver mode */ switch(*(int *)data) { case CONS_USR_SAVER: /* if a LKM screen saver is running, stop it first. */ scsplash_stick(FALSE); saver_mode = *(int *)data; s = spltty(); #if NSPLASH > 0 if ((error = wait_scrn_saver_stop(NULL))) { splx(s); return error; } #endif /* NSPLASH */ run_scrn_saver = TRUE; scp->status |= SAVER_RUNNING; scsplash_stick(TRUE); splx(s); break; case CONS_LKM_SAVER: s = spltty(); if ((saver_mode == CONS_USR_SAVER) && (scp->status & SAVER_RUNNING)) scp->status &= ~SAVER_RUNNING; saver_mode = *(int *)data; splx(s); break; default: return EINVAL; } return 0; case CONS_SAVERSTART: /* immediately start/stop the screen saver */ /* * Note that this ioctl does not guarantee the screen saver * actually starts or stops. It merely attempts to do so... */ s = spltty(); run_scrn_saver = (*(int *)data != 0); if (run_scrn_saver) sc->scrn_time_stamp -= scrn_blank_time; splx(s); return 0; case VT_SETMODE: /* set screen switcher mode */ { struct vt_mode *mode; mode = (struct vt_mode *)data; DPRINTF(5, ("sc%d: VT_SETMODE ", sc->unit)); if (scp->smode.mode == VT_PROCESS) { if (scp->proc == pfind(scp->pid) && scp->proc != p) { DPRINTF(5, ("error EPERM\n")); return EPERM; } } s = spltty(); if (mode->mode == VT_AUTO) { scp->smode.mode = VT_AUTO; scp->proc = NULL; scp->pid = 0; DPRINTF(5, ("VT_AUTO, ")); if ((scp == sc->cur_scp) && (sc->unit == sc_console_unit)) cons_unavail = FALSE; /* were we in the middle of the vty switching process? */ if (scp->status & SWITCH_WAIT_REL) { /* assert(scp == scp->sc->cur_scp) */ DPRINTF(5, ("reset WAIT_REL, ")); scp->status &= ~SWITCH_WAIT_REL; s = do_switch_scr(sc, s); } if (scp->status & SWITCH_WAIT_ACQ) { /* assert(scp == scp->sc->cur_scp) */ DPRINTF(5, ("reset WAIT_ACQ, ")); scp->status &= ~SWITCH_WAIT_ACQ; sc->switch_in_progress = 0; } } else { if (!ISSIGVALID(mode->relsig) || !ISSIGVALID(mode->acqsig) || !ISSIGVALID(mode->frsig)) { splx(s); DPRINTF(5, ("error EINVAL\n")); return EINVAL; } DPRINTF(5, ("VT_PROCESS %d, ", p->p_pid)); bcopy(data, &scp->smode, sizeof(struct vt_mode)); scp->proc = p; scp->pid = scp->proc->p_pid; if ((scp == sc->cur_scp) && (sc->unit == sc_console_unit)) cons_unavail = TRUE; } splx(s); DPRINTF(5, ("\n")); return 0; } case VT_GETMODE: /* get screen switcher mode */ bcopy(&scp->smode, data, sizeof(struct vt_mode)); return 0; case VT_RELDISP: /* screen switcher ioctl */ s = spltty(); /* * This must be the current vty which is in the VT_PROCESS * switching mode... */ if ((scp != sc->cur_scp) || (scp->smode.mode != VT_PROCESS)) { splx(s); return EINVAL; } /* ...and this process is controlling it. */ if (scp->proc != p) { splx(s); return EPERM; } error = EINVAL; switch(*(int *)data) { case VT_FALSE: /* user refuses to release screen, abort */ if ((scp == sc->old_scp) && (scp->status & SWITCH_WAIT_REL)) { sc->old_scp->status &= ~SWITCH_WAIT_REL; sc->switch_in_progress = 0; DPRINTF(5, ("sc%d: VT_FALSE\n", sc->unit)); error = 0; } break; case VT_TRUE: /* user has released screen, go on */ if ((scp == sc->old_scp) && (scp->status & SWITCH_WAIT_REL)) { scp->status &= ~SWITCH_WAIT_REL; s = do_switch_scr(sc, s); DPRINTF(5, ("sc%d: VT_TRUE\n", sc->unit)); error = 0; } break; case VT_ACKACQ: /* acquire acknowledged, switch completed */ if ((scp == sc->new_scp) && (scp->status & SWITCH_WAIT_ACQ)) { scp->status &= ~SWITCH_WAIT_ACQ; sc->switch_in_progress = 0; DPRINTF(5, ("sc%d: VT_ACKACQ\n", sc->unit)); error = 0; } break; default: break; } splx(s); return error; case VT_OPENQRY: /* return free virtual console */ for (i = sc->first_vty; i < sc->first_vty + sc->vtys; i++) { tp = VIRTUAL_TTY(sc, i); if ((tp == NULL) || !(tp->t_state & TS_ISOPEN)) { *(int *)data = i + 1; return 0; } } return EINVAL; case VT_ACTIVATE: /* switch to screen *data */ s = spltty(); sc_clean_up(sc->cur_scp); splx(s); return sc_switch_scr(sc, *(int *)data - 1); case VT_WAITACTIVE: /* wait for switch to occur */ if ((*(int *)data >= sc->first_vty + sc->vtys) || (*(int *)data < sc->first_vty)) return EINVAL; s = spltty(); error = sc_clean_up(sc->cur_scp); splx(s); if (error) return error; if (*(int *)data != 0) scp = SC_STAT(SC_DEV(sc, *(int *)data - 1)); if (scp == scp->sc->cur_scp) return 0; while ((error=tsleep((caddr_t)&scp->smode, PZERO|PCATCH, "waitvt", 0)) == ERESTART) ; return error; case VT_GETACTIVE: /* get active vty # */ *(int *)data = sc->cur_scp->index + 1; return 0; case VT_GETINDEX: /* get this vty # */ *(int *)data = scp->index + 1; return 0; case KDENABIO: /* allow io operations */ error = suser(p); if (error != 0) return error; if (securelevel > 0) return EPERM; #ifdef __i386__ p->p_md.md_regs->tf_eflags |= PSL_IOPL; #endif return 0; case KDDISABIO: /* disallow io operations (default) */ #ifdef __i386__ p->p_md.md_regs->tf_eflags &= ~PSL_IOPL; #endif return 0; case KDSKBSTATE: /* set keyboard state (locks) */ if (*(int *)data & ~LOCK_MASK) return EINVAL; scp->status &= ~LOCK_MASK; scp->status |= *(int *)data; if (scp == sc->cur_scp) update_kbd_state(scp, scp->status, LOCK_MASK); return 0; case KDGKBSTATE: /* get keyboard state (locks) */ if (scp == sc->cur_scp) save_kbd_state(scp); *(int *)data = scp->status & LOCK_MASK; return 0; case KDGETREPEAT: /* get keyboard repeat & delay rates */ case KDSETREPEAT: /* set keyboard repeat & delay rates (new) */ error = kbd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; case KDSETRAD: /* set keyboard repeat & delay rates (old) */ if (*(int *)data & ~0x7f) return EINVAL; error = kbd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; case KDSKBMODE: /* set keyboard mode */ switch (*(int *)data) { case K_XLATE: /* switch to XLT ascii mode */ case K_RAW: /* switch to RAW scancode mode */ case K_CODE: /* switch to CODE mode */ scp->kbd_mode = *(int *)data; if (scp == sc->cur_scp) kbd_ioctl(sc->kbd, cmd, data); return 0; default: return EINVAL; } /* NOT REACHED */ case KDGKBMODE: /* get keyboard mode */ *(int *)data = scp->kbd_mode; return 0; case KDGKBINFO: error = kbd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; case KDMKTONE: /* sound the bell */ if (*(int*)data) sc_bell(scp, (*(int*)data)&0xffff, (((*(int*)data)>>16)&0xffff)*hz/1000); else sc_bell(scp, scp->bell_pitch, scp->bell_duration); return 0; case KIOCSOUND: /* make tone (*data) hz */ if (scp == sc->cur_scp) { if (*(int *)data) return sc_tone(*(int *)data); else return sc_tone(0); } return 0; case KDGKBTYPE: /* get keyboard type */ error = kbd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) { /* always return something? XXX */ *(int *)data = 0; } return 0; case KDSETLED: /* set keyboard LED status */ if (*(int *)data & ~LED_MASK) /* FIXME: LOCK_MASK? */ return EINVAL; scp->status &= ~LED_MASK; scp->status |= *(int *)data; if (scp == sc->cur_scp) update_kbd_leds(scp, scp->status); return 0; case KDGETLED: /* get keyboard LED status */ if (scp == sc->cur_scp) save_kbd_state(scp); *(int *)data = scp->status & LED_MASK; return 0; case CONS_SETKBD: /* set the new keyboard */ { keyboard_t *newkbd; s = spltty(); newkbd = kbd_get_keyboard(*(int *)data); if (newkbd == NULL) { splx(s); return EINVAL; } error = 0; if (sc->kbd != newkbd) { i = kbd_allocate(newkbd->kb_name, newkbd->kb_unit, (void *)&sc->keyboard, sckbdevent, sc); /* i == newkbd->kb_index */ if (i >= 0) { if (sc->kbd != NULL) { save_kbd_state(sc->cur_scp); kbd_release(sc->kbd, (void *)&sc->keyboard); } sc->kbd = kbd_get_keyboard(i); /* sc->kbd == newkbd */ sc->keyboard = i; kbd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&sc->cur_scp->kbd_mode); update_kbd_state(sc->cur_scp, sc->cur_scp->status, LOCK_MASK); } else { error = EPERM; /* XXX */ } } splx(s); return error; } case CONS_RELKBD: /* release the current keyboard */ s = spltty(); error = 0; if (sc->kbd != NULL) { save_kbd_state(sc->cur_scp); error = kbd_release(sc->kbd, (void *)&sc->keyboard); if (error == 0) { sc->kbd = NULL; sc->keyboard = -1; } } splx(s); return error; case CONS_GETTERM: /* get the current terminal emulator info */ { sc_term_sw_t *sw; if (((term_info_t *)data)->ti_index == 0) { sw = scp->tsw; } else { sw = sc_term_match_by_number(((term_info_t *)data)->ti_index); } if (sw != NULL) { strncpy(((term_info_t *)data)->ti_name, sw->te_name, sizeof(((term_info_t *)data)->ti_name)); strncpy(((term_info_t *)data)->ti_desc, sw->te_desc, sizeof(((term_info_t *)data)->ti_desc)); ((term_info_t *)data)->ti_flags = 0; return 0; } else { ((term_info_t *)data)->ti_name[0] = '\0'; ((term_info_t *)data)->ti_desc[0] = '\0'; ((term_info_t *)data)->ti_flags = 0; return EINVAL; } } case CONS_SETTERM: /* set the current terminal emulator */ s = spltty(); error = sc_init_emulator(scp, ((term_info_t *)data)->ti_name); /* FIXME: what if scp == sc_console! XXX */ splx(s); return error; case GIO_SCRNMAP: /* get output translation table */ bcopy(&sc->scr_map, data, sizeof(sc->scr_map)); return 0; case PIO_SCRNMAP: /* set output translation table */ bcopy(data, &sc->scr_map, sizeof(sc->scr_map)); for (i=0; iscr_map); i++) { sc->scr_rmap[sc->scr_map[i]] = i; } return 0; case GIO_KEYMAP: /* get keyboard translation table */ case PIO_KEYMAP: /* set keyboard translation table */ case GIO_DEADKEYMAP: /* get accent key translation table */ case PIO_DEADKEYMAP: /* set accent key translation table */ case GETFKEY: /* get function key string */ case SETFKEY: /* set function key string */ error = kbd_ioctl(sc->kbd, cmd, data); if (error == ENOIOCTL) error = ENODEV; return error; #ifndef SC_NO_FONT_LOADING case PIO_FONT8x8: /* set 8x8 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; bcopy(data, sc->font_8, 8*256); sc->fonts_loaded |= FONT_8; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x8. */ if (ISTEXTSC(sc->cur_scp) && (sc->cur_scp->font_size < 14)) sc_load_font(sc->cur_scp, 0, 8, sc->font_8, 0, 256); return 0; case GIO_FONT8x8: /* get 8x8 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; if (sc->fonts_loaded & FONT_8) { bcopy(sc->font_8, data, 8*256); return 0; } else return ENXIO; case PIO_FONT8x14: /* set 8x14 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; bcopy(data, sc->font_14, 14*256); sc->fonts_loaded |= FONT_14; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x14. */ if (ISTEXTSC(sc->cur_scp) && (sc->cur_scp->font_size >= 14) && (sc->cur_scp->font_size < 16)) sc_load_font(sc->cur_scp, 0, 14, sc->font_14, 0, 256); return 0; case GIO_FONT8x14: /* get 8x14 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; if (sc->fonts_loaded & FONT_14) { bcopy(sc->font_14, data, 14*256); return 0; } else return ENXIO; case PIO_FONT8x16: /* set 8x16 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; bcopy(data, sc->font_16, 16*256); sc->fonts_loaded |= FONT_16; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x16. */ if (ISTEXTSC(sc->cur_scp) && (sc->cur_scp->font_size >= 16)) sc_load_font(sc->cur_scp, 0, 16, sc->font_16, 0, 256); return 0; case GIO_FONT8x16: /* get 8x16 dot font */ if (!ISFONTAVAIL(sc->adp->va_flags)) return ENXIO; if (sc->fonts_loaded & FONT_16) { bcopy(sc->font_16, data, 16*256); return 0; } else return ENXIO; #endif /* SC_NO_FONT_LOADING */ default: break; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return(error); error = ttioctl(tp, cmd, data, flag); if (error != ENOIOCTL) return(error); return(ENOTTY); } static void scstart(struct tty *tp) { struct clist *rbp; int s, len; u_char buf[PCBURST]; scr_stat *scp = SC_STAT(tp->t_dev); if (scp->status & SLKED || scp->sc->blink_in_progress) return; s = spltty(); if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { tp->t_state |= TS_BUSY; rbp = &tp->t_outq; while (rbp->c_cc) { len = q_to_b(rbp, buf, PCBURST); splx(s); sc_puts(scp, buf, len); s = spltty(); } tp->t_state &= ~TS_BUSY; ttwwakeup(tp); } splx(s); } static void sccnprobe(struct consdev *cp) { #if __i386__ int unit; int flags; cp->cn_pri = sc_get_cons_priority(&unit, &flags); /* a video card is always required */ if (!scvidprobe(unit, flags, TRUE)) cp->cn_pri = CN_DEAD; /* syscons will become console even when there is no keyboard */ sckbdprobe(unit, flags, TRUE); if (cp->cn_pri == CN_DEAD) return; /* initialize required fields */ cp->cn_dev = makedev(CDEV_MAJOR, SC_CONSOLECTL); #endif /* __i386__ */ #if __alpha__ /* * alpha use sccnattach() rather than cnprobe()/cninit()/cnterm() * interface to install the console. Always return CN_DEAD from * here. */ cp->cn_pri = CN_DEAD; #endif /* __alpha__ */ } static void sccninit(struct consdev *cp) { #if __i386__ int unit; int flags; sc_get_cons_priority(&unit, &flags); scinit(unit, flags | SC_KERNEL_CONSOLE); sc_console_unit = unit; sc_console = SC_STAT(sc_get_softc(unit, SC_KERNEL_CONSOLE)->dev[0]); #endif /* __i386__ */ #if __alpha__ /* SHOULDN'T REACH HERE */ #endif /* __alpha__ */ } static void sccnterm(struct consdev *cp) { /* we are not the kernel console any more, release everything */ if (sc_console_unit < 0) return; /* shouldn't happen */ #if __i386__ #if 0 /* XXX */ sc_clear_screen(sc_console); sccnupdate(sc_console); #endif scterm(sc_console_unit, SC_KERNEL_CONSOLE); sc_console_unit = -1; sc_console = NULL; #endif /* __i386__ */ #if __alpha__ /* do nothing XXX */ #endif /* __alpha__ */ } #ifdef __alpha__ void sccnattach(void) { static struct consdev consdev; int unit; int flags; bcopy(&sc_consdev, &consdev, sizeof(sc_consdev)); consdev.cn_pri = sc_get_cons_priority(&unit, &flags); /* a video card is always required */ if (!scvidprobe(unit, flags, TRUE)) consdev.cn_pri = CN_DEAD; /* alpha doesn't allow the console being without a keyboard... Why? */ if (!sckbdprobe(unit, flags, TRUE)) consdev.cn_pri = CN_DEAD; if (consdev.cn_pri == CN_DEAD) return; scinit(unit, flags | SC_KERNEL_CONSOLE); sc_console_unit = unit; sc_console = SC_STAT(sc_get_softc(unit, SC_KERNEL_CONSOLE)->dev[0]); consdev.cn_dev = makedev(CDEV_MAJOR, 0); cn_tab = &consdev; } #endif /* __alpha__ */ static void sccnputc(dev_t dev, int c) { u_char buf[1]; scr_stat *scp = sc_console; void *save; #ifndef SC_NO_HISTORY struct tty *tp; #endif /* !SC_NO_HISTORY */ int s; /* assert(sc_console != NULL) */ #ifndef SC_NO_HISTORY if (scp == scp->sc->cur_scp && scp->status & SLKED) { scp->status &= ~SLKED; update_kbd_state(scp, scp->status, SLKED); if (scp->status & BUFFER_SAVED) { if (!sc_hist_restore(scp)) sc_remove_cutmarking(scp); scp->status &= ~BUFFER_SAVED; scp->status |= CURSOR_ENABLED; sc_draw_cursor_image(scp); } tp = VIRTUAL_TTY(scp->sc, scp->index); if (tp->t_state & TS_ISOPEN) scstart(tp); } #endif /* !SC_NO_HISTORY */ save = scp->ts; if (kernel_console_ts != NULL) scp->ts = kernel_console_ts; buf[0] = c; sc_puts(scp, buf, 1); scp->ts = save; s = spltty(); /* block sckbdevent and scrn_timer */ sccnupdate(scp); splx(s); } static int sccngetc(dev_t dev) { return sccngetch(0); } static int sccncheckc(dev_t dev) { return sccngetch(SCGETC_NONBLOCK); } static void sccndbctl(dev_t dev, int on) { /* try to switch to the kernel console screen */ if (on && debugger == 0) { /* * TRY to make sure the screen saver is stopped, * and the screen is updated before switching to * the vty0. */ scrn_timer(NULL); if (!cold && sc_console->sc->cur_scp->smode.mode == VT_AUTO && sc_console->smode.mode == VT_AUTO) sc_switch_scr(sc_console->sc, sc_console->index); } if (on) ++debugger; else --debugger; } static int sccngetch(int flags) { static struct fkeytab fkey; static int fkeycp; scr_stat *scp; u_char *p; int cur_mode; int s = spltty(); /* block sckbdevent and scrn_timer while we poll */ int c; /* assert(sc_console != NULL) */ /* * Stop the screen saver and update the screen if necessary. * What if we have been running in the screen saver code... XXX */ sc_touch_scrn_saver(); scp = sc_console->sc->cur_scp; /* XXX */ sccnupdate(scp); if (fkeycp < fkey.len) { splx(s); return fkey.str[fkeycp++]; } if (scp->sc->kbd == NULL) { splx(s); return -1; } /* * Make sure the keyboard is accessible even when the kbd device * driver is disabled. */ kbd_enable(scp->sc->kbd); /* we shall always use the keyboard in the XLATE mode here */ cur_mode = scp->kbd_mode; scp->kbd_mode = K_XLATE; kbd_ioctl(scp->sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); kbd_poll(scp->sc->kbd, TRUE); c = scgetc(scp->sc, SCGETC_CN | flags); kbd_poll(scp->sc->kbd, FALSE); scp->kbd_mode = cur_mode; kbd_ioctl(scp->sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); kbd_disable(scp->sc->kbd); splx(s); switch (KEYFLAGS(c)) { case 0: /* normal char */ return KEYCHAR(c); case FKEY: /* function key */ p = kbd_get_fkeystr(scp->sc->kbd, KEYCHAR(c), (size_t *)&fkeycp); fkey.len = fkeycp; if ((p != NULL) && (fkey.len > 0)) { bcopy(p, fkey.str, fkey.len); fkeycp = 1; return fkey.str[0]; } return c; /* XXX */ case NOKEY: case ERRKEY: default: return -1; } /* NOT REACHED */ } static void sccnupdate(scr_stat *scp) { /* this is a cut-down version of scrn_timer()... */ if (scp->sc->font_loading_in_progress || scp->sc->videoio_in_progress) return; if (debugger > 0 || panicstr || shutdown_in_progress) { sc_touch_scrn_saver(); } else if (scp != scp->sc->cur_scp) { return; } if (!run_scrn_saver) scp->sc->flags &= ~SC_SCRN_IDLE; #if NSPLASH > 0 if ((saver_mode != CONS_LKM_SAVER) || !(scp->sc->flags & SC_SCRN_IDLE)) if (scp->sc->flags & SC_SCRN_BLANKED) stop_scrn_saver(scp->sc, current_saver); #endif /* NSPLASH */ if (scp != scp->sc->cur_scp || scp->sc->blink_in_progress || scp->sc->switch_in_progress) return; /* * FIXME: unlike scrn_timer(), we call scrn_update() from here even * when write_in_progress is non-zero. XXX */ if (!ISGRAPHSC(scp) && !(scp->status & SAVER_RUNNING)) scrn_update(scp, TRUE); } static void scrn_timer(void *arg) { static int kbd_interval = 0; struct timeval tv; sc_softc_t *sc; scr_stat *scp; int again; int s; again = (arg != NULL); if (arg != NULL) sc = (sc_softc_t *)arg; else if (sc_console != NULL) sc = sc_console->sc; else return; /* don't do anything when we are performing some I/O operations */ if (sc->font_loading_in_progress || sc->videoio_in_progress) { if (again) timeout(scrn_timer, sc, hz / 10); return; } s = spltty(); if ((sc->kbd == NULL) && (sc->config & SC_AUTODETECT_KBD)) { /* try to allocate a keyboard automatically */ if (++kbd_interval >= 25) { sc->keyboard = kbd_allocate("*", -1, (void *)&sc->keyboard, sckbdevent, sc); if (sc->keyboard >= 0) { sc->kbd = kbd_get_keyboard(sc->keyboard); kbd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&sc->cur_scp->kbd_mode); update_kbd_state(sc->cur_scp, sc->cur_scp->status, LOCK_MASK); } kbd_interval = 0; } } /* find the vty to update */ scp = sc->cur_scp; /* should we stop the screen saver? */ getmicrouptime(&tv); if (debugger > 0 || panicstr || shutdown_in_progress) sc_touch_scrn_saver(); if (run_scrn_saver) { if (tv.tv_sec > sc->scrn_time_stamp + scrn_blank_time) sc->flags |= SC_SCRN_IDLE; else sc->flags &= ~SC_SCRN_IDLE; } else { sc->scrn_time_stamp = tv.tv_sec; sc->flags &= ~SC_SCRN_IDLE; if (scrn_blank_time > 0) run_scrn_saver = TRUE; } #if NSPLASH > 0 if ((saver_mode != CONS_LKM_SAVER) || !(sc->flags & SC_SCRN_IDLE)) if (sc->flags & SC_SCRN_BLANKED) stop_scrn_saver(sc, current_saver); #endif /* NSPLASH */ /* should we just return ? */ if (sc->blink_in_progress || sc->switch_in_progress || sc->write_in_progress) { if (again) timeout(scrn_timer, sc, hz / 10); splx(s); return; } /* Update the screen */ scp = sc->cur_scp; /* cur_scp may have changed... */ if (!ISGRAPHSC(scp) && !(scp->status & SAVER_RUNNING)) scrn_update(scp, TRUE); #if NSPLASH > 0 /* should we activate the screen saver? */ if ((saver_mode == CONS_LKM_SAVER) && (sc->flags & SC_SCRN_IDLE)) if (!ISGRAPHSC(scp) || (sc->flags & SC_SCRN_BLANKED)) (*current_saver)(sc, TRUE); #endif /* NSPLASH */ if (again) timeout(scrn_timer, sc, hz / 25); splx(s); } static int and_region(int *s1, int *e1, int s2, int e2) { if (*e1 < s2 || e2 < *s1) return FALSE; *s1 = imax(*s1, s2); *e1 = imin(*e1, e2); return TRUE; } static void scrn_update(scr_stat *scp, int show_cursor) { int start; int end; int s; int e; /* assert(scp == scp->sc->cur_scp) */ ++scp->sc->videoio_in_progress; #ifndef SC_NO_CUTPASTE /* remove the previous mouse pointer image if necessary */ if ((scp->status & (MOUSE_VISIBLE | MOUSE_MOVED)) == (MOUSE_VISIBLE | MOUSE_MOVED)) { /* FIXME: I don't like this... XXX */ sc_remove_mouse_image(scp); if (scp->end >= scp->xsize*scp->ysize) scp->end = scp->xsize*scp->ysize - 1; } #endif /* !SC_NO_CUTPASTE */ #if 1 /* debug: XXX */ if (scp->end >= scp->xsize*scp->ysize) { printf("scrn_update(): scp->end %d > size_of_screen!!\n", scp->end); scp->end = scp->xsize*scp->ysize - 1; } if (scp->start < 0) { printf("scrn_update(): scp->start %d < 0\n", scp->start); scp->start = 0; } #endif /* update screen image */ if (scp->start <= scp->end) { if (scp->mouse_cut_end >= 0) { /* there is a marked region for cut & paste */ if (scp->mouse_cut_start <= scp->mouse_cut_end) { start = scp->mouse_cut_start; end = scp->mouse_cut_end; } else { start = scp->mouse_cut_end; end = scp->mouse_cut_start - 1; } s = start; e = end; /* does the cut-mark region overlap with the update region? */ if (and_region(&s, &e, scp->start, scp->end)) { (*scp->rndr->draw)(scp, s, e - s + 1, TRUE); s = 0; e = start - 1; if (and_region(&s, &e, scp->start, scp->end)) (*scp->rndr->draw)(scp, s, e - s + 1, FALSE); s = end + 1; e = scp->xsize*scp->ysize - 1; if (and_region(&s, &e, scp->start, scp->end)) (*scp->rndr->draw)(scp, s, e - s + 1, FALSE); } else { (*scp->rndr->draw)(scp, scp->start, scp->end - scp->start + 1, FALSE); } } else { (*scp->rndr->draw)(scp, scp->start, scp->end - scp->start + 1, FALSE); } } /* we are not to show the cursor and the mouse pointer... */ if (!show_cursor) { scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; --scp->sc->videoio_in_progress; return; } /* update cursor image */ if (scp->status & CURSOR_ENABLED) { /* did cursor move since last time ? */ if (scp->cursor_pos != scp->cursor_oldpos) { /* do we need to remove old cursor image ? */ if (scp->cursor_oldpos < scp->start || scp->cursor_oldpos > scp->end) { sc_remove_cursor_image(scp); } scp->cursor_oldpos = scp->cursor_pos; sc_draw_cursor_image(scp); } else { /* cursor didn't move, has it been overwritten ? */ if (scp->cursor_pos >= scp->start && scp->cursor_pos <= scp->end) { sc_draw_cursor_image(scp); } else { /* if its a blinking cursor, we may have to update it */ if (scp->sc->flags & SC_BLINK_CURSOR) (*scp->rndr->blink_cursor)(scp, scp->cursor_pos, sc_inside_cutmark(scp, scp->cursor_pos)); } } } #ifndef SC_NO_CUTPASTE /* update "pseudo" mouse pointer image */ if (scp->status & MOUSE_VISIBLE) { /* did mouse move since last time ? */ if (scp->status & MOUSE_MOVED) { /* the previous pointer image has been removed, see above */ scp->status &= ~MOUSE_MOVED; sc_draw_mouse_image(scp); } else { /* mouse didn't move, has it been overwritten ? */ if (scp->mouse_pos + scp->xsize + 1 >= scp->start && scp->mouse_pos <= scp->end) { sc_draw_mouse_image(scp); } else if (scp->cursor_pos == scp->mouse_pos || scp->cursor_pos == scp->mouse_pos + 1 || scp->cursor_pos == scp->mouse_pos + scp->xsize || scp->cursor_pos == scp->mouse_pos + scp->xsize + 1) { sc_draw_mouse_image(scp); } } } #endif /* SC_NO_CUTPASTE */ scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; --scp->sc->videoio_in_progress; } #if NSPLASH > 0 static int scsplash_callback(int event, void *arg) { sc_softc_t *sc; int error; sc = (sc_softc_t *)arg; switch (event) { case SPLASH_INIT: if (add_scrn_saver(scsplash_saver) == 0) { sc->flags &= ~SC_SAVER_FAILED; run_scrn_saver = TRUE; if (cold && !(boothowto & (RB_VERBOSE | RB_CONFIG))) { scsplash_stick(TRUE); (*current_saver)(sc, TRUE); } } return 0; case SPLASH_TERM: if (current_saver == scsplash_saver) { scsplash_stick(FALSE); error = remove_scrn_saver(scsplash_saver); if (error) return error; } return 0; default: return EINVAL; } } static void scsplash_saver(sc_softc_t *sc, int show) { static int busy = FALSE; scr_stat *scp; if (busy) return; busy = TRUE; scp = sc->cur_scp; if (show) { if (!(sc->flags & SC_SAVER_FAILED)) { if (!(sc->flags & SC_SCRN_BLANKED)) set_scrn_saver_mode(scp, -1, NULL, 0); switch (splash(sc->adp, TRUE)) { case 0: /* succeeded */ break; case EAGAIN: /* try later */ restore_scrn_saver_mode(scp, FALSE); sc_touch_scrn_saver(); /* XXX */ break; default: sc->flags |= SC_SAVER_FAILED; scsplash_stick(FALSE); restore_scrn_saver_mode(scp, TRUE); printf("scsplash_saver(): failed to put up the image\n"); break; } } } else if (!sticky_splash) { if ((sc->flags & SC_SCRN_BLANKED) && (splash(sc->adp, FALSE) == 0)) restore_scrn_saver_mode(scp, TRUE); } busy = FALSE; } static int add_scrn_saver(void (*this_saver)(sc_softc_t *, int)) { #if 0 int error; if (current_saver != none_saver) { error = remove_scrn_saver(current_saver); if (error) return error; } #endif if (current_saver != none_saver) return EBUSY; run_scrn_saver = FALSE; saver_mode = CONS_LKM_SAVER; current_saver = this_saver; return 0; } static int remove_scrn_saver(void (*this_saver)(sc_softc_t *, int)) { if (current_saver != this_saver) return EINVAL; #if 0 /* * In order to prevent `current_saver' from being called by * the timeout routine `scrn_timer()' while we manipulate * the saver list, we shall set `current_saver' to `none_saver' * before stopping the current saver, rather than blocking by `splXX()'. */ current_saver = none_saver; if (scrn_blanked) stop_scrn_saver(this_saver); #endif /* unblank all blanked screens */ wait_scrn_saver_stop(NULL); if (scrn_blanked) return EBUSY; current_saver = none_saver; return 0; } static int set_scrn_saver_mode(scr_stat *scp, int mode, u_char *pal, int border) { int s; /* assert(scp == scp->sc->cur_scp) */ s = spltty(); if (!ISGRAPHSC(scp)) sc_remove_cursor_image(scp); scp->splash_save_mode = scp->mode; scp->splash_save_status = scp->status & (GRAPHICS_MODE | PIXEL_MODE); scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE); scp->status |= (UNKNOWN_MODE | SAVER_RUNNING); scp->sc->flags |= SC_SCRN_BLANKED; ++scrn_blanked; splx(s); if (mode < 0) return 0; scp->mode = mode; if (set_mode(scp) == 0) { if (scp->sc->adp->va_info.vi_flags & V_INFO_GRAPHICS) scp->status |= GRAPHICS_MODE; #ifndef SC_NO_PALETTE_LOADING if (pal != NULL) load_palette(scp->sc->adp, pal); #endif sc_set_border(scp, border); return 0; } else { s = spltty(); scp->mode = scp->splash_save_mode; scp->status &= ~(UNKNOWN_MODE | SAVER_RUNNING); scp->status |= scp->splash_save_status; splx(s); return 1; } } static int restore_scrn_saver_mode(scr_stat *scp, int changemode) { int mode; int status; int s; /* assert(scp == scp->sc->cur_scp) */ s = spltty(); mode = scp->mode; status = scp->status; scp->mode = scp->splash_save_mode; scp->status &= ~(UNKNOWN_MODE | SAVER_RUNNING); scp->status |= scp->splash_save_status; scp->sc->flags &= ~SC_SCRN_BLANKED; if (!changemode) { if (!ISGRAPHSC(scp)) sc_draw_cursor_image(scp); --scrn_blanked; splx(s); return 0; } if (set_mode(scp) == 0) { #ifndef SC_NO_PALETTE_LOADING load_palette(scp->sc->adp, scp->sc->palette); #endif --scrn_blanked; splx(s); return 0; } else { scp->mode = mode; scp->status = status; splx(s); return 1; } } static void stop_scrn_saver(sc_softc_t *sc, void (*saver)(sc_softc_t *, int)) { (*saver)(sc, FALSE); run_scrn_saver = FALSE; /* the screen saver may have chosen not to stop after all... */ if (sc->flags & SC_SCRN_BLANKED) return; mark_all(sc->cur_scp); if (sc->delayed_next_scr) sc_switch_scr(sc, sc->delayed_next_scr - 1); wakeup((caddr_t)&scrn_blanked); } static int wait_scrn_saver_stop(sc_softc_t *sc) { int error = 0; while (scrn_blanked > 0) { run_scrn_saver = FALSE; if (sc && !(sc->flags & SC_SCRN_BLANKED)) { error = 0; break; } error = tsleep((caddr_t)&scrn_blanked, PZERO | PCATCH, "scrsav", 0); if ((error != 0) && (error != ERESTART)) break; } run_scrn_saver = FALSE; return error; } #endif /* NSPLASH */ void sc_touch_scrn_saver(void) { scsplash_stick(FALSE); run_scrn_saver = FALSE; } int sc_switch_scr(sc_softc_t *sc, u_int next_scr) { struct tty *tp; int s; DPRINTF(5, ("sc0: sc_switch_scr() %d ", next_scr + 1)); /* delay switch if the screen is blanked or being updated */ if ((sc->flags & SC_SCRN_BLANKED) || sc->write_in_progress || sc->blink_in_progress || sc->videoio_in_progress) { sc->delayed_next_scr = next_scr + 1; sc_touch_scrn_saver(); DPRINTF(5, ("switch delayed\n")); return 0; } s = spltty(); /* we are in the middle of the vty switching process... */ if (sc->switch_in_progress && (sc->cur_scp->smode.mode == VT_PROCESS) && sc->cur_scp->proc) { if (sc->cur_scp->proc != pfind(sc->cur_scp->pid)) { /* * The controlling process has died!!. Do some clean up. * NOTE:`cur_scp->proc' and `cur_scp->smode.mode' * are not reset here yet; they will be cleared later. */ DPRINTF(5, ("cur_scp controlling process %d died, ", sc->cur_scp->pid)); if (sc->cur_scp->status & SWITCH_WAIT_REL) { /* * Force the previous switch to finish, but return now * with error. */ DPRINTF(5, ("reset WAIT_REL, ")); sc->cur_scp->status &= ~SWITCH_WAIT_REL; s = do_switch_scr(sc, s); splx(s); DPRINTF(5, ("finishing previous switch\n")); return EINVAL; } else if (sc->cur_scp->status & SWITCH_WAIT_ACQ) { /* let's assume screen switch has been completed. */ DPRINTF(5, ("reset WAIT_ACQ, ")); sc->cur_scp->status &= ~SWITCH_WAIT_ACQ; sc->switch_in_progress = 0; } else { /* * We are in between screen release and acquisition, and * reached here via scgetc() or scrn_timer() which has * interrupted exchange_scr(). Don't do anything stupid. */ DPRINTF(5, ("waiting nothing, ")); } } else { /* * The controlling process is alive, but not responding... * It is either buggy or it may be just taking time. * The following code is a gross kludge to cope with this * problem for which there is no clean solution. XXX */ if (sc->cur_scp->status & SWITCH_WAIT_REL) { switch (sc->switch_in_progress++) { case 1: break; case 2: DPRINTF(5, ("sending relsig again, ")); signal_vt_rel(sc->cur_scp); break; case 3: break; case 4: default: /* * Act as if the controlling program returned * VT_FALSE. */ DPRINTF(5, ("force reset WAIT_REL, ")); sc->cur_scp->status &= ~SWITCH_WAIT_REL; sc->switch_in_progress = 0; splx(s); DPRINTF(5, ("act as if VT_FALSE was seen\n")); return EINVAL; } } else if (sc->cur_scp->status & SWITCH_WAIT_ACQ) { switch (sc->switch_in_progress++) { case 1: break; case 2: DPRINTF(5, ("sending acqsig again, ")); signal_vt_acq(sc->cur_scp); break; case 3: break; case 4: default: /* clear the flag and finish the previous switch */ DPRINTF(5, ("force reset WAIT_ACQ, ")); sc->cur_scp->status &= ~SWITCH_WAIT_ACQ; sc->switch_in_progress = 0; break; } } } } /* * Return error if an invalid argument is given, or vty switch * is still in progress. */ if ((next_scr < sc->first_vty) || (next_scr >= sc->first_vty + sc->vtys) || sc->switch_in_progress) { splx(s); sc_bell(sc->cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error 1\n")); return EINVAL; } /* * Don't allow switching away from the graphics mode vty * if the switch mode is VT_AUTO, unless the next vty is the same * as the current or the current vty has been closed (but showing). */ tp = VIRTUAL_TTY(sc, sc->cur_scp->index); if ((sc->cur_scp->index != next_scr) && (tp->t_state & TS_ISOPEN) && (sc->cur_scp->smode.mode == VT_AUTO) && ISGRAPHSC(sc->cur_scp)) { splx(s); sc_bell(sc->cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error, graphics mode\n")); return EINVAL; } /* * Is the wanted vty open? Don't allow switching to a closed vty. * Note that we always allow the user to switch to the kernel * console even if it is closed. */ if ((sc_console == NULL) || (next_scr != sc_console->index)) { tp = VIRTUAL_TTY(sc, next_scr); if ((tp == NULL) || !(tp->t_state & TS_ISOPEN)) { splx(s); sc_bell(sc->cur_scp, bios_value.bell_pitch, BELL_DURATION); DPRINTF(5, ("error 2, requested vty isn't open!\n")); return EINVAL; } } /* this is the start of vty switching process... */ ++sc->switch_in_progress; sc->delayed_next_scr = 0; sc->old_scp = sc->cur_scp; sc->new_scp = SC_STAT(SC_DEV(sc, next_scr)); if (sc->new_scp == sc->old_scp) { sc->switch_in_progress = 0; wakeup((caddr_t)&sc->new_scp->smode); splx(s); DPRINTF(5, ("switch done (new == old)\n")); return 0; } /* has controlling process died? */ vt_proc_alive(sc->old_scp); vt_proc_alive(sc->new_scp); /* wait for the controlling process to release the screen, if necessary */ if (signal_vt_rel(sc->old_scp)) { splx(s); return 0; } /* go set up the new vty screen */ splx(s); exchange_scr(sc); s = spltty(); /* wake up processes waiting for this vty */ wakeup((caddr_t)&sc->cur_scp->smode); /* wait for the controlling process to acknowledge, if necessary */ if (signal_vt_acq(sc->cur_scp)) { splx(s); return 0; } sc->switch_in_progress = 0; if (sc->unit == sc_console_unit) cons_unavail = FALSE; splx(s); DPRINTF(5, ("switch done\n")); return 0; } static int do_switch_scr(sc_softc_t *sc, int s) { vt_proc_alive(sc->new_scp); splx(s); exchange_scr(sc); s = spltty(); /* sc->cur_scp == sc->new_scp */ wakeup((caddr_t)&sc->cur_scp->smode); /* wait for the controlling process to acknowledge, if necessary */ if (!signal_vt_acq(sc->cur_scp)) { sc->switch_in_progress = 0; if (sc->unit == sc_console_unit) cons_unavail = FALSE; } return s; } static int vt_proc_alive(scr_stat *scp) { if (scp->proc) { if (scp->proc == pfind(scp->pid)) return TRUE; scp->proc = NULL; scp->smode.mode = VT_AUTO; DPRINTF(5, ("vt controlling process %d died\n", scp->pid)); } return FALSE; } static int signal_vt_rel(scr_stat *scp) { if (scp->smode.mode != VT_PROCESS) return FALSE; scp->status |= SWITCH_WAIT_REL; psignal(scp->proc, scp->smode.relsig); DPRINTF(5, ("sending relsig to %d\n", scp->pid)); return TRUE; } static int signal_vt_acq(scr_stat *scp) { if (scp->smode.mode != VT_PROCESS) return FALSE; if (scp->sc->unit == sc_console_unit) cons_unavail = TRUE; scp->status |= SWITCH_WAIT_ACQ; psignal(scp->proc, scp->smode.acqsig); DPRINTF(5, ("sending acqsig to %d\n", scp->pid)); return TRUE; } static void exchange_scr(sc_softc_t *sc) { scr_stat *scp; /* save the current state of video and keyboard */ sc_move_cursor(sc->old_scp, sc->old_scp->xpos, sc->old_scp->ypos); if (sc->old_scp->kbd_mode == K_XLATE) save_kbd_state(sc->old_scp); /* set up the video for the new screen */ scp = sc->cur_scp = sc->new_scp; if (sc->old_scp->mode != scp->mode || ISUNKNOWNSC(sc->old_scp)) set_mode(scp); else sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize, (void *)sc->adp->va_window, FALSE); sc_move_cursor(scp, scp->xpos, scp->ypos); if (!ISGRAPHSC(scp)) sc_set_cursor_image(scp); #ifndef SC_NO_PALETTE_LOADING if (ISGRAPHSC(sc->old_scp)) load_palette(sc->adp, sc->palette); #endif sc_set_border(scp, scp->border); /* set up the keyboard for the new screen */ if (sc->old_scp->kbd_mode != scp->kbd_mode) kbd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); update_kbd_state(scp, scp->status, LOCK_MASK); mark_all(scp); } void sc_puts(scr_stat *scp, u_char *buf, int len) { #if NSPLASH > 0 /* make screensaver happy */ if (!sticky_splash && scp == scp->sc->cur_scp) run_scrn_saver = FALSE; #endif if (scp->tsw) (*scp->tsw->te_puts)(scp, buf, len); if (scp->sc->delayed_next_scr) sc_switch_scr(scp->sc, scp->sc->delayed_next_scr - 1); } void sc_draw_cursor_image(scr_stat *scp) { /* assert(scp == scp->sc->cur_scp); */ ++scp->sc->videoio_in_progress; (*scp->rndr->draw_cursor)(scp, scp->cursor_pos, scp->sc->flags & SC_BLINK_CURSOR, TRUE, sc_inside_cutmark(scp, scp->cursor_pos)); --scp->sc->videoio_in_progress; } void sc_remove_cursor_image(scr_stat *scp) { /* assert(scp == scp->sc->cur_scp); */ ++scp->sc->videoio_in_progress; (*scp->rndr->draw_cursor)(scp, scp->cursor_oldpos, scp->sc->flags & SC_BLINK_CURSOR, FALSE, sc_inside_cutmark(scp, scp->cursor_oldpos)); --scp->sc->videoio_in_progress; } static void update_cursor_image(scr_stat *scp) { int blink; if (scp->sc->flags & SC_CHAR_CURSOR) { scp->cursor_base = scp->sc->cursor_base; scp->cursor_height = imin(scp->sc->cursor_height, scp->font_size); } else { scp->cursor_base = 0; scp->cursor_height = scp->font_size; } blink = scp->sc->flags & SC_BLINK_CURSOR; /* assert(scp == scp->sc->cur_scp); */ ++scp->sc->videoio_in_progress; (*scp->rndr->draw_cursor)(scp, scp->cursor_oldpos, blink, FALSE, sc_inside_cutmark(scp, scp->cursor_pos)); (*scp->rndr->set_cursor)(scp, scp->cursor_base, scp->cursor_height, blink); (*scp->rndr->draw_cursor)(scp, scp->cursor_pos, blink, TRUE, sc_inside_cutmark(scp, scp->cursor_pos)); --scp->sc->videoio_in_progress; } void sc_set_cursor_image(scr_stat *scp) { if (scp->sc->flags & SC_CHAR_CURSOR) { scp->cursor_base = scp->sc->cursor_base; scp->cursor_height = imin(scp->sc->cursor_height, scp->font_size); } else { scp->cursor_base = 0; scp->cursor_height = scp->font_size; } /* assert(scp == scp->sc->cur_scp); */ ++scp->sc->videoio_in_progress; (*scp->rndr->set_cursor)(scp, scp->cursor_base, scp->cursor_height, scp->sc->flags & SC_BLINK_CURSOR); --scp->sc->videoio_in_progress; } static void scinit(int unit, int flags) { /* * When syscons is being initialized as the kernel console, malloc() * is not yet functional, because various kernel structures has not been * fully initialized yet. Therefore, we need to declare the following * static buffers for the console. This is less than ideal, * but is necessry evil for the time being. XXX */ static scr_stat main_console; static dev_t main_devs[MAXCONS]; static struct tty main_tty; static u_short sc_buffer[ROW*COL]; /* XXX */ #ifndef SC_NO_FONT_LOADING static u_char font_8[256*8]; static u_char font_14[256*14]; static u_char font_16[256*16]; #endif sc_softc_t *sc; scr_stat *scp; video_adapter_t *adp; int col; int row; int i; /* one time initialization */ if (init_done == COLD) sc_get_bios_values(&bios_value); init_done = WARM; /* * Allocate resources. Even if we are being called for the second * time, we must allocate them again, because they might have * disappeared... */ sc = sc_get_softc(unit, flags & SC_KERNEL_CONSOLE); adp = NULL; if (sc->adapter >= 0) { vid_release(sc->adp, (void *)&sc->adapter); adp = sc->adp; sc->adp = NULL; } if (sc->keyboard >= 0) { DPRINTF(5, ("sc%d: releasing kbd%d\n", unit, sc->keyboard)); i = kbd_release(sc->kbd, (void *)&sc->keyboard); DPRINTF(5, ("sc%d: kbd_release returned %d\n", unit, i)); if (sc->kbd != NULL) { DPRINTF(5, ("sc%d: kbd != NULL!, index:%d, unit:%d, flags:0x%x\n", unit, sc->kbd->kb_index, sc->kbd->kb_unit, sc->kbd->kb_flags)); } sc->kbd = NULL; } sc->adapter = vid_allocate("*", unit, (void *)&sc->adapter); sc->adp = vid_get_adapter(sc->adapter); /* assert((sc->adapter >= 0) && (sc->adp != NULL)) */ sc->keyboard = kbd_allocate("*", unit, (void *)&sc->keyboard, sckbdevent, sc); DPRINTF(1, ("sc%d: keyboard %d\n", unit, sc->keyboard)); sc->kbd = kbd_get_keyboard(sc->keyboard); if (sc->kbd != NULL) { DPRINTF(1, ("sc%d: kbd index:%d, unit:%d, flags:0x%x\n", unit, sc->kbd->kb_index, sc->kbd->kb_unit, sc->kbd->kb_flags)); } if (!(sc->flags & SC_INIT_DONE) || (adp != sc->adp)) { sc->initial_mode = sc->adp->va_initial_mode; #ifndef SC_NO_FONT_LOADING if (flags & SC_KERNEL_CONSOLE) { sc->font_8 = font_8; sc->font_14 = font_14; sc->font_16 = font_16; } else if (sc->font_8 == NULL) { /* assert(sc_malloc) */ sc->font_8 = malloc(sizeof(font_8), M_DEVBUF, M_WAITOK); sc->font_14 = malloc(sizeof(font_14), M_DEVBUF, M_WAITOK); sc->font_16 = malloc(sizeof(font_16), M_DEVBUF, M_WAITOK); } #endif /* extract the hardware cursor location and hide the cursor for now */ (*vidsw[sc->adapter]->read_hw_cursor)(sc->adp, &col, &row); (*vidsw[sc->adapter]->set_hw_cursor)(sc->adp, -1, -1); /* set up the first console */ sc->first_vty = unit*MAXCONS; sc->vtys = MAXCONS; /* XXX: should be configurable */ if (flags & SC_KERNEL_CONSOLE) { sc->dev = main_devs; sc->dev[0] = makedev(CDEV_MAJOR, unit*MAXCONS); sc->dev[0]->si_tty = &main_tty; ttyregister(&main_tty); scp = &main_console; init_scp(sc, sc->first_vty, scp); sc_vtb_init(&scp->vtb, VTB_MEMORY, scp->xsize, scp->ysize, (void *)sc_buffer, FALSE); if (sc_init_emulator(scp, SC_DFLT_TERM)) sc_init_emulator(scp, "*"); (*scp->tsw->te_default_attr)(scp, kernel_default.std_color, kernel_default.rev_color); } else { /* assert(sc_malloc) */ sc->dev = malloc(sizeof(dev_t)*sc->vtys, M_DEVBUF, M_WAITOK); bzero(sc->dev, sizeof(dev_t)*sc->vtys); sc->dev[0] = makedev(CDEV_MAJOR, unit*MAXCONS); sc->dev[0]->si_tty = ttymalloc(sc->dev[0]->si_tty); scp = alloc_scp(sc, sc->first_vty); } SC_STAT(sc->dev[0]) = scp; sc->cur_scp = scp; /* copy screen to temporary buffer */ sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize, (void *)scp->sc->adp->va_window, FALSE); if (ISTEXTSC(scp)) sc_vtb_copy(&scp->scr, 0, &scp->vtb, 0, scp->xsize*scp->ysize); /* move cursors to the initial positions */ scp->mouse_pos = scp->mouse_oldpos = 0; if (col >= scp->xsize) col = 0; if (row >= scp->ysize) row = scp->ysize - 1; scp->xpos = col; scp->ypos = row; scp->cursor_pos = scp->cursor_oldpos = row*scp->xsize + col; if (bios_value.cursor_end < scp->font_size) sc->cursor_base = scp->font_size - bios_value.cursor_end - 1; else sc->cursor_base = 0; i = bios_value.cursor_end - bios_value.cursor_start + 1; sc->cursor_height = imin(i, scp->font_size); if (!ISGRAPHSC(scp)) { sc_set_cursor_image(scp); sc_draw_cursor_image(scp); } /* save font and palette */ #ifndef SC_NO_FONT_LOADING sc->fonts_loaded = 0; if (ISFONTAVAIL(sc->adp->va_flags)) { #ifdef SC_DFLT_FONT bcopy(dflt_font_8, sc->font_8, sizeof(dflt_font_8)); bcopy(dflt_font_14, sc->font_14, sizeof(dflt_font_14)); bcopy(dflt_font_16, sc->font_16, sizeof(dflt_font_16)); sc->fonts_loaded = FONT_16 | FONT_14 | FONT_8; if (scp->font_size < 14) { sc_load_font(scp, 0, 8, sc->font_8, 0, 256); } else if (scp->font_size >= 16) { sc_load_font(scp, 0, 16, sc->font_16, 0, 256); } else { sc_load_font(scp, 0, 14, sc->font_14, 0, 256); } #else /* !SC_DFLT_FONT */ if (scp->font_size < 14) { sc_save_font(scp, 0, 8, sc->font_8, 0, 256); sc->fonts_loaded = FONT_8; } else if (scp->font_size >= 16) { sc_save_font(scp, 0, 16, sc->font_16, 0, 256); sc->fonts_loaded = FONT_16; } else { sc_save_font(scp, 0, 14, sc->font_14, 0, 256); sc->fonts_loaded = FONT_14; } #endif /* SC_DFLT_FONT */ /* FONT KLUDGE: always use the font page #0. XXX */ sc_show_font(scp, 0); } #endif /* !SC_NO_FONT_LOADING */ #ifndef SC_NO_PALETTE_LOADING save_palette(sc->adp, sc->palette); #endif #if NSPLASH > 0 if (!(sc->flags & SC_SPLASH_SCRN) && (flags & SC_KERNEL_CONSOLE)) { /* we are ready to put up the splash image! */ splash_init(sc->adp, scsplash_callback, sc); sc->flags |= SC_SPLASH_SCRN; } #endif /* NSPLASH */ } /* the rest is not necessary, if we have done it once */ if (sc->flags & SC_INIT_DONE) return; /* initialize mapscrn arrays to a one to one map */ for (i = 0; i < sizeof(sc->scr_map); i++) sc->scr_map[i] = sc->scr_rmap[i] = i; sc->flags |= SC_INIT_DONE; } #if __i386__ static void scterm(int unit, int flags) { sc_softc_t *sc; scr_stat *scp; sc = sc_get_softc(unit, flags & SC_KERNEL_CONSOLE); if (sc == NULL) return; /* shouldn't happen */ #if NSPLASH > 0 /* this console is no longer available for the splash screen */ if (sc->flags & SC_SPLASH_SCRN) { splash_term(sc->adp); sc->flags &= ~SC_SPLASH_SCRN; } #endif /* NSPLASH */ #if 0 /* XXX */ /* move the hardware cursor to the upper-left corner */ (*vidsw[sc->adapter]->set_hw_cursor)(sc->adp, 0, 0); #endif /* release the keyboard and the video card */ if (sc->keyboard >= 0) kbd_release(sc->kbd, &sc->keyboard); if (sc->adapter >= 0) vid_release(sc->adp, &sc->adapter); /* stop the terminal emulator, if any */ scp = SC_STAT(sc->dev[0]); if (scp->tsw) (*scp->tsw->te_term)(scp, &scp->ts); if (scp->ts != NULL) free(scp->ts, M_DEVBUF); /* clear the structure */ if (!(flags & SC_KERNEL_CONSOLE)) { /* XXX: We need delete_dev() for this */ free(sc->dev, M_DEVBUF); #if 0 /* XXX: We need a ttyunregister for this */ free(sc->tty, M_DEVBUF); #endif #ifndef SC_NO_FONT_LOADING free(sc->font_8, M_DEVBUF); free(sc->font_14, M_DEVBUF); free(sc->font_16, M_DEVBUF); #endif /* XXX vtb, history */ } bzero(sc, sizeof(*sc)); sc->keyboard = -1; sc->adapter = -1; } #endif static void scshutdown(void *arg, int howto) { /* assert(sc_console != NULL) */ sc_touch_scrn_saver(); if (!cold && sc_console && sc_console->sc->cur_scp->smode.mode == VT_AUTO && sc_console->smode.mode == VT_AUTO) sc_switch_scr(sc_console->sc, sc_console->index); shutdown_in_progress = TRUE; } int sc_clean_up(scr_stat *scp) { #if NSPLASH > 0 int error; #endif /* NSPLASH */ sc_touch_scrn_saver(); #if NSPLASH > 0 if ((error = wait_scrn_saver_stop(scp->sc))) return error; #endif /* NSPLASH */ scp->status &= ~MOUSE_VISIBLE; sc_remove_cutmarking(scp); return 0; } void sc_alloc_scr_buffer(scr_stat *scp, int wait, int discard) { sc_vtb_t new; sc_vtb_t old; old = scp->vtb; sc_vtb_init(&new, VTB_MEMORY, scp->xsize, scp->ysize, NULL, wait); if (!discard && (old.vtb_flags & VTB_VALID)) { /* retain the current cursor position and buffer contants */ scp->cursor_oldpos = scp->cursor_pos; /* * This works only if the old buffer has the same size as or larger * than the new one. XXX */ sc_vtb_copy(&old, 0, &new, 0, scp->xsize*scp->ysize); scp->vtb = new; } else { scp->vtb = new; sc_vtb_destroy(&old); } #ifndef SC_NO_SYSMOUSE /* move the mouse cursor at the center of the screen */ sc_mouse_move(scp, scp->xpixel / 2, scp->ypixel / 2); #endif } static scr_stat *alloc_scp(sc_softc_t *sc, int vty) { scr_stat *scp; /* assert(sc_malloc) */ scp = (scr_stat *)malloc(sizeof(scr_stat), M_DEVBUF, M_WAITOK); init_scp(sc, vty, scp); sc_alloc_scr_buffer(scp, TRUE, TRUE); if (sc_init_emulator(scp, SC_DFLT_TERM)) sc_init_emulator(scp, "*"); #ifndef SC_NO_SYSMOUSE if (ISMOUSEAVAIL(sc->adp->va_flags)) sc_alloc_cut_buffer(scp, TRUE); #endif #ifndef SC_NO_HISTORY sc_alloc_history_buffer(scp, 0, 0, TRUE); #endif return scp; } static void init_scp(sc_softc_t *sc, int vty, scr_stat *scp) { video_info_t info; bzero(scp, sizeof(*scp)); scp->index = vty; scp->sc = sc; scp->status = 0; scp->mode = sc->initial_mode; (*vidsw[sc->adapter]->get_info)(sc->adp, scp->mode, &info); if (info.vi_flags & V_INFO_GRAPHICS) { scp->status |= GRAPHICS_MODE; scp->xpixel = info.vi_width; scp->ypixel = info.vi_height; scp->xsize = info.vi_width/8; scp->ysize = info.vi_height/info.vi_cheight; scp->font_size = 0; scp->font = NULL; } else { scp->xsize = info.vi_width; scp->ysize = info.vi_height; scp->xpixel = scp->xsize*8; scp->ypixel = scp->ysize*info.vi_cheight; if (info.vi_cheight < 14) { scp->font_size = 8; #ifndef SC_NO_FONT_LOADING scp->font = sc->font_8; #else scp->font = NULL; #endif } else if (info.vi_cheight >= 16) { scp->font_size = 16; #ifndef SC_NO_FONT_LOADING scp->font = sc->font_16; #else scp->font = NULL; #endif } else { scp->font_size = 14; #ifndef SC_NO_FONT_LOADING scp->font = sc->font_14; #else scp->font = NULL; #endif } } sc_vtb_init(&scp->vtb, VTB_MEMORY, 0, 0, NULL, FALSE); sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, 0, 0, NULL, FALSE); scp->xoff = scp->yoff = 0; scp->xpos = scp->ypos = 0; scp->start = scp->xsize * scp->ysize - 1; scp->end = 0; scp->tsw = NULL; scp->ts = NULL; scp->rndr = NULL; scp->border = BG_BLACK; scp->cursor_base = sc->cursor_base; scp->cursor_height = imin(sc->cursor_height, scp->font_size); scp->mouse_xpos = scp->xoff*8 + scp->xsize*8/2; scp->mouse_ypos = (scp->ysize + scp->yoff)*scp->font_size/2; scp->mouse_cut_start = scp->xsize*scp->ysize; scp->mouse_cut_end = -1; scp->mouse_signal = 0; scp->mouse_pid = 0; scp->mouse_proc = NULL; scp->kbd_mode = K_XLATE; scp->bell_pitch = bios_value.bell_pitch; scp->bell_duration = BELL_DURATION; scp->status |= (bios_value.shift_state & NLKED); scp->status |= CURSOR_ENABLED; scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; scp->history = NULL; scp->history_pos = 0; scp->history_size = 0; } int sc_init_emulator(scr_stat *scp, char *name) { sc_term_sw_t *sw; sc_rndr_sw_t *rndr; void *p; int error; if (name == NULL) /* if no name is given, use the current emulator */ sw = scp->tsw; else /* ...otherwise find the named emulator */ sw = sc_term_match(name); if (sw == NULL) return EINVAL; rndr = NULL; if (strcmp(sw->te_renderer, "*") != 0) { rndr = sc_render_match(scp, sw->te_renderer, scp->status & (GRAPHICS_MODE | PIXEL_MODE)); } if (rndr == NULL) { rndr = sc_render_match(scp, scp->sc->adp->va_name, scp->status & (GRAPHICS_MODE | PIXEL_MODE)); if (rndr == NULL) return ENODEV; } if (sw == scp->tsw) { error = (*sw->te_init)(scp, &scp->ts, SC_TE_WARM_INIT); scp->rndr = rndr; sc_clear_screen(scp); /* assert(error == 0); */ return error; } if (sc_malloc && (sw->te_size > 0)) p = malloc(sw->te_size, M_DEVBUF, M_NOWAIT); else p = NULL; error = (*sw->te_init)(scp, &p, SC_TE_COLD_INIT); if (error) return error; if (scp->tsw) (*scp->tsw->te_term)(scp, &scp->ts); if (scp->ts != NULL) free(scp->ts, M_DEVBUF); scp->tsw = sw; scp->ts = p; scp->rndr = rndr; /* XXX */ (*sw->te_default_attr)(scp, user_default.std_color, user_default.rev_color); sc_clear_screen(scp); return 0; } /* * scgetc(flags) - get character from keyboard. * If flags & SCGETC_CN, then avoid harmful side effects. * If flags & SCGETC_NONBLOCK, then wait until a key is pressed, else * return NOKEY if there is nothing there. */ static u_int scgetc(sc_softc_t *sc, u_int flags) { scr_stat *scp; #ifndef SC_NO_HISTORY struct tty *tp; #endif u_int c; int this_scr; int f; int i; if (sc->kbd == NULL) return NOKEY; next_code: #if 1 /* I don't like this, but... XXX */ if (flags & SCGETC_CN) sccnupdate(sc->cur_scp); #endif scp = sc->cur_scp; /* first see if there is something in the keyboard port */ for (;;) { c = kbd_read_char(sc->kbd, !(flags & SCGETC_NONBLOCK)); if (c == ERRKEY) { if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); } else if (c == NOKEY) return c; else break; } /* make screensaver happy */ if (!(c & RELKEY)) sc_touch_scrn_saver(); #ifdef __i386__ if (!(flags & SCGETC_CN)) /* do the /dev/random device a favour */ add_keyboard_randomness(c); #endif if (scp->kbd_mode != K_XLATE) return KEYCHAR(c); /* if scroll-lock pressed allow history browsing */ if (!ISGRAPHSC(scp) && scp->history && scp->status & SLKED) { scp->status &= ~CURSOR_ENABLED; sc_remove_cursor_image(scp); #ifndef SC_NO_HISTORY if (!(scp->status & BUFFER_SAVED)) { scp->status |= BUFFER_SAVED; sc_hist_save(scp); } switch (c) { /* FIXME: key codes */ case SPCLKEY | FKEY | F(49): /* home key */ sc_remove_cutmarking(scp); sc_hist_home(scp); goto next_code; case SPCLKEY | FKEY | F(57): /* end key */ sc_remove_cutmarking(scp); sc_hist_end(scp); goto next_code; case SPCLKEY | FKEY | F(50): /* up arrow key */ sc_remove_cutmarking(scp); if (sc_hist_up_line(scp)) if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); goto next_code; case SPCLKEY | FKEY | F(58): /* down arrow key */ sc_remove_cutmarking(scp); if (sc_hist_down_line(scp)) if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); goto next_code; case SPCLKEY | FKEY | F(51): /* page up key */ sc_remove_cutmarking(scp); for (i=0; iysize; i++) if (sc_hist_up_line(scp)) { if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); break; } goto next_code; case SPCLKEY | FKEY | F(59): /* page down key */ sc_remove_cutmarking(scp); for (i=0; iysize; i++) if (sc_hist_down_line(scp)) { if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); break; } goto next_code; } #endif /* SC_NO_HISTORY */ } /* * Process and consume special keys here. Return a plain char code * or a char code with the META flag or a function key code. */ if (c & RELKEY) { /* key released */ /* goto next_code */ } else { /* key pressed */ if (c & SPCLKEY) { c &= ~SPCLKEY; switch (KEYCHAR(c)) { /* LOCKING KEYS */ case NLK: case CLK: case ALK: break; case SLK: kbd_ioctl(sc->kbd, KDGKBSTATE, (caddr_t)&f); if (f & SLKED) { scp->status |= SLKED; } else { if (scp->status & SLKED) { scp->status &= ~SLKED; #ifndef SC_NO_HISTORY if (scp->status & BUFFER_SAVED) { if (!sc_hist_restore(scp)) sc_remove_cutmarking(scp); scp->status &= ~BUFFER_SAVED; scp->status |= CURSOR_ENABLED; sc_draw_cursor_image(scp); } tp = VIRTUAL_TTY(sc, scp->index); if (tp->t_state & TS_ISOPEN) scstart(tp); #endif } } break; /* NON-LOCKING KEYS */ case NOP: case LSH: case RSH: case LCTR: case RCTR: case LALT: case RALT: case ASH: case META: break; case BTAB: if (!(sc->flags & SC_SCRN_BLANKED)) return c; break; case SPSC: #if NSPLASH > 0 /* force activatation/deactivation of the screen saver */ if (!(sc->flags & SC_SCRN_BLANKED)) { run_scrn_saver = TRUE; sc->scrn_time_stamp -= scrn_blank_time; } if (cold) { /* * While devices are being probed, the screen saver need * to be invoked explictly. XXX */ if (sc->flags & SC_SCRN_BLANKED) { scsplash_stick(FALSE); stop_scrn_saver(sc, current_saver); } else { if (!ISGRAPHSC(scp)) { scsplash_stick(TRUE); (*current_saver)(sc, TRUE); } } } #endif /* NSPLASH */ break; case RBT: #ifndef SC_DISABLE_REBOOT shutdown_nice(); #endif break; #if NAPM > 0 case SUSP: apm_suspend(PMST_SUSPEND); break; case STBY: apm_suspend(PMST_STANDBY); break; #else case SUSP: case STBY: break; #endif case DBG: #ifndef SC_DISABLE_DDBKEY #ifdef DDB Debugger("manual escape to debugger"); #else printf("No debugger in kernel\n"); #endif #else /* SC_DISABLE_DDBKEY */ /* do nothing */ #endif /* SC_DISABLE_DDBKEY */ break; case PNC: if (enable_panic_key) panic("Forced by the panic key"); break; case NEXT: this_scr = scp->index; for (i = (this_scr - sc->first_vty + 1)%sc->vtys; sc->first_vty + i != this_scr; i = (i + 1)%sc->vtys) { struct tty *tp = VIRTUAL_TTY(sc, sc->first_vty + i); if (tp && tp->t_state & TS_ISOPEN) { sc_switch_scr(scp->sc, sc->first_vty + i); break; } } break; case PREV: this_scr = scp->index; for (i = (this_scr - sc->first_vty + sc->vtys - 1)%sc->vtys; sc->first_vty + i != this_scr; i = (i + sc->vtys - 1)%sc->vtys) { struct tty *tp = VIRTUAL_TTY(sc, sc->first_vty + i); if (tp && tp->t_state & TS_ISOPEN) { sc_switch_scr(scp->sc, sc->first_vty + i); break; } } break; default: if (KEYCHAR(c) >= F_SCR && KEYCHAR(c) <= L_SCR) { sc_switch_scr(scp->sc, sc->first_vty + KEYCHAR(c) - F_SCR); break; } /* assert(c & FKEY) */ if (!(sc->flags & SC_SCRN_BLANKED)) return c; break; } /* goto next_code */ } else { /* regular keys (maybe MKEY is set) */ if (!(sc->flags & SC_SCRN_BLANKED)) return c; } } goto next_code; } int scmmap(dev_t dev, vm_offset_t offset, int nprot) { scr_stat *scp; scp = SC_STAT(dev); if (scp != scp->sc->cur_scp) return -1; return (*vidsw[scp->sc->adapter]->mmap)(scp->sc->adp, offset, nprot); } static int save_kbd_state(scr_stat *scp) { int state; int error; error = kbd_ioctl(scp->sc->kbd, KDGKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; if (error == 0) { scp->status &= ~LOCK_MASK; scp->status |= state; } return error; } static int update_kbd_state(scr_stat *scp, int new_bits, int mask) { int state; int error; if (mask != LOCK_MASK) { error = kbd_ioctl(scp->sc->kbd, KDGKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; if (error) return error; state &= ~mask; state |= new_bits & mask; } else { state = new_bits & LOCK_MASK; } error = kbd_ioctl(scp->sc->kbd, KDSKBSTATE, (caddr_t)&state); if (error == ENOIOCTL) error = ENODEV; return error; } static int update_kbd_leds(scr_stat *scp, int which) { int error; which &= LOCK_MASK; error = kbd_ioctl(scp->sc->kbd, KDSETLED, (caddr_t)&which); if (error == ENOIOCTL) error = ENODEV; return error; } int set_mode(scr_stat *scp) { video_info_t info; /* reject unsupported mode */ if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, scp->mode, &info)) return 1; /* if this vty is not currently showing, do nothing */ if (scp != scp->sc->cur_scp) return 0; /* setup video hardware for the given mode */ (*vidsw[scp->sc->adapter]->set_mode)(scp->sc->adp, scp->mode); sc_vtb_init(&scp->scr, VTB_FRAMEBUFFER, scp->xsize, scp->ysize, (void *)scp->sc->adp->va_window, FALSE); #ifndef SC_NO_FONT_LOADING /* load appropriate font */ if (!(scp->status & GRAPHICS_MODE)) { if (!(scp->status & PIXEL_MODE) && ISFONTAVAIL(scp->sc->adp->va_flags)) { if (scp->font_size < 14) { if (scp->sc->fonts_loaded & FONT_8) sc_load_font(scp, 0, 8, scp->sc->font_8, 0, 256); } else if (scp->font_size >= 16) { if (scp->sc->fonts_loaded & FONT_16) sc_load_font(scp, 0, 16, scp->sc->font_16, 0, 256); } else { if (scp->sc->fonts_loaded & FONT_14) sc_load_font(scp, 0, 14, scp->sc->font_14, 0, 256); } /* * FONT KLUDGE: * This is an interim kludge to display correct font. * Always use the font page #0 on the video plane 2. * Somehow we cannot show the font in other font pages on * some video cards... XXX */ sc_show_font(scp, 0); } mark_all(scp); } #endif /* !SC_NO_FONT_LOADING */ sc_set_border(scp, scp->border); sc_set_cursor_image(scp); return 0; } void sc_set_border(scr_stat *scp, int color) { ++scp->sc->videoio_in_progress; (*scp->rndr->draw_border)(scp, color); --scp->sc->videoio_in_progress; } #ifndef SC_NO_FONT_LOADING void sc_load_font(scr_stat *scp, int page, int size, u_char *buf, int base, int count) { sc_softc_t *sc; sc = scp->sc; sc->font_loading_in_progress = TRUE; (*vidsw[sc->adapter]->load_font)(sc->adp, page, size, buf, base, count); sc->font_loading_in_progress = FALSE; } void sc_save_font(scr_stat *scp, int page, int size, u_char *buf, int base, int count) { sc_softc_t *sc; sc = scp->sc; sc->font_loading_in_progress = TRUE; (*vidsw[sc->adapter]->save_font)(sc->adp, page, size, buf, base, count); sc->font_loading_in_progress = FALSE; } void sc_show_font(scr_stat *scp, int page) { (*vidsw[scp->sc->adapter]->show_font)(scp->sc->adp, page); } #endif /* !SC_NO_FONT_LOADING */ void sc_paste(scr_stat *scp, u_char *p, int count) { struct tty *tp; u_char *rmap; if (scp->status & MOUSE_VISIBLE) { tp = VIRTUAL_TTY(scp->sc, scp->sc->cur_scp->index); if (!(tp->t_state & TS_ISOPEN)) return; rmap = scp->sc->scr_rmap; for (; count > 0; --count) (*linesw[tp->t_line].l_rint)(rmap[*p++], tp); } } void sc_bell(scr_stat *scp, int pitch, int duration) { if (cold || shutdown_in_progress) return; if (scp != scp->sc->cur_scp && (scp->sc->flags & SC_QUIET_BELL)) return; if (scp->sc->flags & SC_VISUAL_BELL) { if (scp->sc->blink_in_progress) return; scp->sc->blink_in_progress = 3; if (scp != scp->sc->cur_scp) scp->sc->blink_in_progress += 2; blink_screen(scp->sc->cur_scp); } else { if (scp != scp->sc->cur_scp) pitch *= 2; sysbeep(pitch, duration); } } static void blink_screen(void *arg) { scr_stat *scp = arg; struct tty *tp; if (ISGRAPHSC(scp) || (scp->sc->blink_in_progress <= 1)) { scp->sc->blink_in_progress = 0; mark_all(scp); tp = VIRTUAL_TTY(scp->sc, scp->index); if (tp->t_state & TS_ISOPEN) scstart(tp); if (scp->sc->delayed_next_scr) sc_switch_scr(scp->sc, scp->sc->delayed_next_scr - 1); } else { (*scp->rndr->draw)(scp, 0, scp->xsize*scp->ysize, scp->sc->blink_in_progress & 1); scp->sc->blink_in_progress--; timeout(blink_screen, scp, hz / 10); } } - -#endif /* NSC */ Index: head/sys/dev/syscons/sysmouse.c =================================================================== --- head/sys/dev/syscons/sysmouse.c (revision 56835) +++ head/sys/dev/syscons/sysmouse.c (revision 56836) @@ -1,344 +1,339 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" #include "opt_syscons.h" -#if NSC > 0 - #include #include #include #include #include #include #include #include #include #ifndef SC_NO_SYSMOUSE #define CDEV_MAJOR 12 /* major number, shared with syscons */ #define SC_MOUSE 128 /* minor number */ static d_open_t smopen; static d_close_t smclose; static d_ioctl_t smioctl; static struct cdevsw sm_cdevsw = { /* open */ smopen, /* close */ smclose, /* read */ ttyread, /* write */ nowrite, /* ioctl */ smioctl, /* poll */ ttypoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "sysmouse", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_TTY, /* bmaj */ -1 }; /* local variables */ static struct tty *sysmouse_tty; static int mouse_level; /* sysmouse protocol level */ static mousestatus_t mouse_status; static void smstart(struct tty *tp); static int smparam(struct tty *tp, struct termios *t); static int smopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp; DPRINTF(5, ("smopen: dev:%d,%d, vty:%d\n", major(dev), minor(dev), SC_VTY(dev))); #if 0 if (SC_VTY(dev) != SC_MOUSE) return ENXIO; #endif tp = dev->si_tty = ttymalloc(dev->si_tty); if (!(tp->t_state & TS_ISOPEN)) { tp->t_oproc = smstart; tp->t_param = smparam; tp->t_stop = nottystop; tp->t_dev = dev; ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; smparam(tp, &tp->t_termios); (*linesw[tp->t_line].l_modem)(tp, 1); } else if (tp->t_state & TS_XCLUDE && suser(p)) { return EBUSY; } return (*linesw[tp->t_line].l_open)(dev, tp); } static int smclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp; int s; tp = dev->si_tty; s = spltty(); mouse_level = 0; (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); splx(s); return 0; } static void smstart(struct tty *tp) { struct clist *rbp; u_char buf[PCBURST]; int s; s = spltty(); if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { tp->t_state |= TS_BUSY; rbp = &tp->t_outq; while (rbp->c_cc) q_to_b(rbp, buf, PCBURST); tp->t_state &= ~TS_BUSY; ttwwakeup(tp); } splx(s); } static int smparam(struct tty *tp, struct termios *t) { tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; return 0; } static int smioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct tty *tp; mousehw_t *hw; mousemode_t *mode; int error; int s; tp = dev->si_tty; switch (cmd) { case MOUSE_GETHWINFO: /* get device information */ hw = (mousehw_t *)data; hw->buttons = 10; /* XXX unknown */ hw->iftype = MOUSE_IF_SYSMOUSE; hw->type = MOUSE_MOUSE; hw->model = MOUSE_MODEL_GENERIC; hw->hwid = 0; return 0; case MOUSE_GETMODE: /* get protocol/mode */ mode = (mousemode_t *)data; mode->level = mouse_level; switch (mode->level) { case 0: /* emulate MouseSystems protocol */ mode->protocol = MOUSE_PROTO_MSC; mode->rate = -1; /* unknown */ mode->resolution = -1; /* unknown */ mode->accelfactor = 0; /* disabled */ mode->packetsize = MOUSE_MSC_PACKETSIZE; mode->syncmask[0] = MOUSE_MSC_SYNCMASK; mode->syncmask[1] = MOUSE_MSC_SYNC; break; case 1: /* sysmouse protocol */ mode->protocol = MOUSE_PROTO_SYSMOUSE; mode->rate = -1; mode->resolution = -1; mode->accelfactor = 0; mode->packetsize = MOUSE_SYS_PACKETSIZE; mode->syncmask[0] = MOUSE_SYS_SYNCMASK; mode->syncmask[1] = MOUSE_SYS_SYNC; break; } return 0; case MOUSE_SETMODE: /* set protocol/mode */ mode = (mousemode_t *)data; if ((mode->level < 0) || (mode->level > 1)) return EINVAL; mouse_level = mode->level; return 0; case MOUSE_GETLEVEL: /* get operation level */ *(int *)data = mouse_level; return 0; case MOUSE_SETLEVEL: /* set operation level */ if ((*(int *)data < 0) || (*(int *)data > 1)) return EINVAL; mouse_level = *(int *)data; return 0; case MOUSE_GETSTATUS: /* get accumulated mouse events */ s = spltty(); *(mousestatus_t *)data = mouse_status; mouse_status.flags = 0; mouse_status.obutton = mouse_status.button; mouse_status.dx = 0; mouse_status.dy = 0; mouse_status.dz = 0; splx(s); return 0; #if notyet case MOUSE_GETVARS: /* get internal mouse variables */ case MOUSE_SETVARS: /* set internal mouse variables */ return ENODEV; #endif case MOUSE_READSTATE: /* read status from the device */ case MOUSE_READDATA: /* read data from the device */ return ENODEV; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; error = ttioctl(tp, cmd, data, flag); if (error != ENOIOCTL) return error; return ENOTTY; } static void sm_attach_mouse(void *unused) { dev_t dev; dev = make_dev(&sm_cdevsw, SC_MOUSE, UID_ROOT, GID_WHEEL, 0600, "sysmouse"); dev->si_tty = sysmouse_tty = ttymalloc(sysmouse_tty); /* sysmouse doesn't have scr_stat */ } SYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR, sm_attach_mouse, NULL) int sysmouse_event(mouse_info_t *info) { /* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */ static int butmap[8] = { MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON1UP, 0, }; u_char buf[8]; int x, y, z; int i; switch (info->operation) { case MOUSE_ACTION: mouse_status.button = info->u.data.buttons; /* FALL THROUGH */ case MOUSE_MOTION_EVENT: x = info->u.data.x; y = info->u.data.y; z = info->u.data.z; break; case MOUSE_BUTTON_EVENT: x = y = z = 0; if (info->u.event.value > 0) mouse_status.button |= info->u.event.id; else mouse_status.button &= ~info->u.event.id; break; default: return 0; } mouse_status.dx += x; mouse_status.dy += y; mouse_status.dz += z; mouse_status.flags |= ((x || y || z) ? MOUSE_POSCHANGED : 0) | (mouse_status.obutton ^ mouse_status.button); if (mouse_status.flags == 0) return 0; if ((sysmouse_tty == NULL) || !(sysmouse_tty->t_state & TS_ISOPEN)) return mouse_status.flags; /* the first five bytes are compatible with MouseSystems' */ buf[0] = MOUSE_MSC_SYNC | butmap[mouse_status.button & MOUSE_STDBUTTONS]; x = imax(imin(x, 255), -256); buf[1] = x >> 1; buf[3] = x - buf[1]; y = -imax(imin(y, 255), -256); buf[2] = y >> 1; buf[4] = y - buf[2]; for (i = 0; i < MOUSE_MSC_PACKETSIZE; ++i) (*linesw[sysmouse_tty->t_line].l_rint)(buf[i], sysmouse_tty); if (mouse_level >= 1) { /* extended part */ z = imax(imin(z, 127), -128); buf[5] = (z >> 1) & 0x7f; buf[6] = (z - (z >> 1)) & 0x7f; /* buttons 4-10 */ buf[7] = (~mouse_status.button >> 3) & 0x7f; for (i = MOUSE_MSC_PACKETSIZE; i < MOUSE_SYS_PACKETSIZE; ++i) (*linesw[sysmouse_tty->t_line].l_rint)(buf[i], sysmouse_tty); } return mouse_status.flags; } #endif /* !SC_NO_SYSMOUSE */ - -#endif /* NSC */ Index: head/sys/i386/isa/vesa.c =================================================================== --- head/sys/i386/isa/vesa.c (revision 56835) +++ head/sys/i386/isa/vesa.c (revision 56836) @@ -1,1645 +1,1638 @@ /*- * Copyright (c) 1998 Kazutaka YOKOTA and Michael Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "vga.h" #include "opt_vga.h" -#include "opt_vesa.h" -#include "opt_fb.h" -#ifdef VGA_NO_MODE_CHANGE -#undef VESA -#endif +#ifndef VGA_NO_MODE_CHANGE -#if (NVGA > 0 && defined(VESA)) || defined(KLD_MODULE) - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __i386__ #include #else #include #endif #ifndef VESA_DEBUG #define VESA_DEBUG 0 #endif /* VESA video adapter state buffer stub */ struct adp_state { int sig; #define V_STATE_SIG 0x61736576 u_char regs[1]; }; typedef struct adp_state adp_state_t; /* VESA video adapter */ static video_adapter_t *vesa_adp = NULL; static int vesa_state_buf_size = 0; /* VESA functions */ #if 0 static int vesa_nop(void); #endif static int vesa_error(void); static vi_probe_t vesa_probe; static vi_init_t vesa_init; static vi_get_info_t vesa_get_info; static vi_query_mode_t vesa_query_mode; static vi_set_mode_t vesa_set_mode; static vi_save_font_t vesa_save_font; static vi_load_font_t vesa_load_font; static vi_show_font_t vesa_show_font; static vi_save_palette_t vesa_save_palette; static vi_load_palette_t vesa_load_palette; static vi_set_border_t vesa_set_border; static vi_save_state_t vesa_save_state; static vi_load_state_t vesa_load_state; static vi_set_win_org_t vesa_set_origin; static vi_read_hw_cursor_t vesa_read_hw_cursor; static vi_set_hw_cursor_t vesa_set_hw_cursor; static vi_set_hw_cursor_shape_t vesa_set_hw_cursor_shape; static vi_blank_display_t vesa_blank_display; static vi_mmap_t vesa_mmap; static vi_ioctl_t vesa_ioctl; static vi_clear_t vesa_clear; static vi_fill_rect_t vesa_fill_rect; static vi_bitblt_t vesa_bitblt; static vi_diag_t vesa_diag; static int vesa_bios_info(int level); static struct vm86context vesa_vmcontext; static video_switch_t vesavidsw = { vesa_probe, vesa_init, vesa_get_info, vesa_query_mode, vesa_set_mode, vesa_save_font, vesa_load_font, vesa_show_font, vesa_save_palette, vesa_load_palette, vesa_set_border, vesa_save_state, vesa_load_state, vesa_set_origin, vesa_read_hw_cursor, vesa_set_hw_cursor, vesa_set_hw_cursor_shape, vesa_blank_display, vesa_mmap, vesa_ioctl, vesa_clear, vesa_fill_rect, vesa_bitblt, vesa_error, vesa_error, vesa_diag, }; static video_switch_t *prevvidsw; /* VESA BIOS video modes */ #define VESA_MAXMODES 64 #define EOT (-1) #define NA (-2) #define MODE_TABLE_DELTA 8 static int vesa_vmode_max = 0; static video_info_t vesa_vmode_empty = { EOT }; static video_info_t *vesa_vmode = &vesa_vmode_empty; static int vesa_init_done = FALSE; static int has_vesa_bios = FALSE; static struct vesa_info *vesa_adp_info = NULL; static u_int16_t *vesa_vmodetab = NULL; static char *vesa_oemstr = NULL; static char *vesa_venderstr = NULL; static char *vesa_prodstr = NULL; static char *vesa_revstr = NULL; /* local macros and functions */ #define BIOS_SADDRTOLADDR(p) ((((p) & 0xffff0000) >> 12) + ((p) & 0x0000ffff)) static int int10_set_mode(int mode); static int vesa_bios_get_mode(int mode, struct vesa_mode *vmode); static int vesa_bios_set_mode(int mode); static int vesa_bios_get_dac(void); static int vesa_bios_set_dac(int bits); static int vesa_bios_save_palette(int start, int colors, u_char *palette, int bits); static int vesa_bios_save_palette2(int start, int colors, u_char *r, u_char *g, u_char *b, int bits); static int vesa_bios_load_palette(int start, int colors, u_char *palette, int bits); #ifdef notyet static int vesa_bios_load_palette2(int start, int colors, u_char *r, u_char *g, u_char *b, int bits); #endif #define STATE_SIZE 0 #define STATE_SAVE 1 #define STATE_LOAD 2 #define STATE_HW (1<<0) #define STATE_DATA (1<<1) #define STATE_DAC (1<<2) #define STATE_REG (1<<3) #define STATE_MOST (STATE_HW | STATE_DATA | STATE_REG) #define STATE_ALL (STATE_HW | STATE_DATA | STATE_DAC | STATE_REG) static int vesa_bios_state_buf_size(void); static int vesa_bios_save_restore(int code, void *p, size_t size); static int vesa_bios_get_line_length(void); static int vesa_bios_set_line_length(int pixel, int *bytes, int *lines); #if 0 static int vesa_bios_get_start(int *x, int *y); #endif static int vesa_bios_set_start(int x, int y); static int vesa_map_gen_mode_num(int type, int color, int mode); static int vesa_translate_flags(u_int16_t vflags); static int vesa_translate_mmodel(u_int8_t vmodel); static void *vesa_fix_ptr(u_int32_t p, u_int16_t seg, u_int16_t off, u_char *buf); static int vesa_bios_init(void); static void vesa_clear_modes(video_info_t *info, int color); static vm_offset_t vesa_map_buffer(u_int paddr, size_t size); static void vesa_unmap_buffer(vm_offset_t vaddr, size_t size); #if 0 static int vesa_get_origin(video_adapter_t *adp, off_t *offset); #endif static void dump_buffer(u_char *buf, size_t len) { int i; for(i = 0; i < len;) { printf("%02x ", buf[i]); if ((++i % 16) == 0) printf("\n"); } } /* INT 10 BIOS calls */ static int int10_set_mode(int mode) { struct vm86frame vmf; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x0000 | mode; vm86_intcall(0x10, &vmf); return 0; } /* VESA BIOS calls */ static int vesa_bios_get_mode(int mode, struct vesa_mode *vmode) { struct vm86frame vmf; u_char *buf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f01; vmf.vmf_ecx = mode; buf = (u_char *)vm86_getpage(&vesa_vmcontext, 1); vm86_getptr(&vesa_vmcontext, (vm_offset_t)buf, &vmf.vmf_es, &vmf.vmf_di); err = vm86_datacall(0x10, &vmf, &vesa_vmcontext); if ((err != 0) || (vmf.vmf_ax != 0x4f)) return 1; bcopy(buf, vmode, sizeof(*vmode)); return 0; } static int vesa_bios_set_mode(int mode) { struct vm86frame vmf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f02; vmf.vmf_ebx = mode; err = vm86_intcall(0x10, &vmf); return ((err != 0) || (vmf.vmf_ax != 0x4f)); } static int vesa_bios_get_dac(void) { struct vm86frame vmf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f08; vmf.vmf_ebx = 1; /* get DAC width */ err = vm86_intcall(0x10, &vmf); if ((err != 0) || (vmf.vmf_ax != 0x4f)) return 6; /* XXX */ return ((vmf.vmf_ebx >> 8) & 0x00ff); } static int vesa_bios_set_dac(int bits) { struct vm86frame vmf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f08; vmf.vmf_ebx = (bits << 8); err = vm86_intcall(0x10, &vmf); if ((err != 0) || (vmf.vmf_ax != 0x4f)) return 6; /* XXX */ return ((vmf.vmf_ebx >> 8) & 0x00ff); } static int vesa_bios_save_palette(int start, int colors, u_char *palette, int bits) { struct vm86frame vmf; u_char *p; int err; int i; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f09; vmf.vmf_ebx = 1; /* get primary palette data */ vmf.vmf_ecx = colors; vmf.vmf_edx = start; p = (u_char *)vm86_getpage(&vesa_vmcontext, 1); vm86_getptr(&vesa_vmcontext, (vm_offset_t)p, &vmf.vmf_es, &vmf.vmf_di); err = vm86_datacall(0x10, &vmf, &vesa_vmcontext); if ((err != 0) || (vmf.vmf_ax != 0x4f)) return 1; bits = 8 - bits; for (i = 0; i < colors; ++i) { palette[i*3] = p[i*4 + 2] << bits; palette[i*3 + 1] = p[i*4 + 1] << bits; palette[i*3 + 2] = p[i*4] << bits; } return 0; } static int vesa_bios_save_palette2(int start, int colors, u_char *r, u_char *g, u_char *b, int bits) { struct vm86frame vmf; u_char *p; int err; int i; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f09; vmf.vmf_ebx = 1; /* get primary palette data */ vmf.vmf_ecx = colors; vmf.vmf_edx = start; p = (u_char *)vm86_getpage(&vesa_vmcontext, 1); vm86_getptr(&vesa_vmcontext, (vm_offset_t)p, &vmf.vmf_es, &vmf.vmf_di); err = vm86_datacall(0x10, &vmf, &vesa_vmcontext); if ((err != 0) || (vmf.vmf_ax != 0x4f)) return 1; bits = 8 - bits; for (i = 0; i < colors; ++i) { r[i] = p[i*4 + 2] << bits; g[i] = p[i*4 + 1] << bits; b[i] = p[i*4] << bits; } return 0; } static int vesa_bios_load_palette(int start, int colors, u_char *palette, int bits) { struct vm86frame vmf; u_char *p; int err; int i; p = (u_char *)vm86_getpage(&vesa_vmcontext, 1); bits = 8 - bits; for (i = 0; i < colors; ++i) { p[i*4] = palette[i*3 + 2] >> bits; p[i*4 + 1] = palette[i*3 + 1] >> bits; p[i*4 + 2] = palette[i*3] >> bits; p[i*4 + 3] = 0; } bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f09; vmf.vmf_ebx = 0; /* set primary palette data */ vmf.vmf_ecx = colors; vmf.vmf_edx = start; vm86_getptr(&vesa_vmcontext, (vm_offset_t)p, &vmf.vmf_es, &vmf.vmf_di); err = vm86_datacall(0x10, &vmf, &vesa_vmcontext); return ((err != 0) || (vmf.vmf_ax != 0x4f)); } #ifdef notyet static int vesa_bios_load_palette2(int start, int colors, u_char *r, u_char *g, u_char *b, int bits) { struct vm86frame vmf; u_char *p; int err; int i; p = (u_char *)vm86_getpage(&vesa_vmcontext, 1); bits = 8 - bits; for (i = 0; i < colors; ++i) { p[i*4] = b[i] >> bits; p[i*4 + 1] = g[i] >> bits; p[i*4 + 2] = r[i] >> bits; p[i*4 + 3] = 0; } bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f09; vmf.vmf_ebx = 0; /* set primary palette data */ vmf.vmf_ecx = colors; vmf.vmf_edx = start; vm86_getptr(&vesa_vmcontext, (vm_offset_t)p, &vmf.vmf_es, &vmf.vmf_di); err = vm86_datacall(0x10, &vmf, &vesa_vmcontext); return ((err != 0) || (vmf.vmf_ax != 0x4f)); } #endif static int vesa_bios_state_buf_size(void) { struct vm86frame vmf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f04; vmf.vmf_ecx = STATE_MOST; vmf.vmf_edx = STATE_SIZE; err = vm86_intcall(0x10, &vmf); if ((err != 0) || (vmf.vmf_ax != 0x4f)) return 0; return vmf.vmf_bx*64; } static int vesa_bios_save_restore(int code, void *p, size_t size) { struct vm86frame vmf; u_char *buf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f04; vmf.vmf_ecx = STATE_MOST; vmf.vmf_edx = code; /* STATE_SAVE/STATE_LOAD */ buf = (u_char *)vm86_getpage(&vesa_vmcontext, 1); vm86_getptr(&vesa_vmcontext, (vm_offset_t)buf, &vmf.vmf_es, &vmf.vmf_di); bcopy(p, buf, size); err = vm86_datacall(0x10, &vmf, &vesa_vmcontext); return ((err != 0) || (vmf.vmf_ax != 0x4f)); } static int vesa_bios_get_line_length(void) { struct vm86frame vmf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f06; vmf.vmf_ebx = 1; /* get scan line length */ err = vm86_intcall(0x10, &vmf); if ((err != 0) || (vmf.vmf_ax != 0x4f)) return -1; return vmf.vmf_bx; /* line length in bytes */ } static int vesa_bios_set_line_length(int pixel, int *bytes, int *lines) { struct vm86frame vmf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f06; vmf.vmf_ebx = 0; /* set scan line length in pixel */ vmf.vmf_ecx = pixel; err = vm86_intcall(0x10, &vmf); #if VESA_DEBUG > 1 printf("bx:%d, cx:%d, dx:%d\n", vmf.vmf_bx, vmf.vmf_cx, vmf.vmf_dx); #endif if ((err != 0) || (vmf.vmf_ax != 0x4f)) return 1; if (bytes) *bytes = vmf.vmf_bx; if (lines) *lines = vmf.vmf_dx; return 0; } #if 0 static int vesa_bios_get_start(int *x, int *y) { struct vm86frame vmf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f07; vmf.vmf_ebx = 1; /* get display start */ err = vm86_intcall(0x10, &vmf); if ((err != 0) || (vmf.vmf_ax != 0x4f)) return 1; *x = vmf.vmf_cx; *y = vmf.vmf_dx; return 0; } #endif static int vesa_bios_set_start(int x, int y) { struct vm86frame vmf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f07; vmf.vmf_ebx = 0x80; /* set display start */ vmf.vmf_edx = y; vmf.vmf_ecx = x; err = vm86_intcall(0x10, &vmf); return ((err != 0) || (vmf.vmf_ax != 0x4f)); } /* map a generic video mode to a known mode */ static int vesa_map_gen_mode_num(int type, int color, int mode) { static struct { int from; int to; } mode_map[] = { { M_TEXT_132x25, M_VESA_C132x25 }, { M_TEXT_132x43, M_VESA_C132x43 }, { M_TEXT_132x50, M_VESA_C132x50 }, { M_TEXT_132x60, M_VESA_C132x60 }, }; int i; for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) { if (mode_map[i].from == mode) return mode_map[i].to; } return mode; } static int vesa_translate_flags(u_int16_t vflags) { static struct { u_int16_t mask; int set; int reset; } ftable[] = { { V_MODECOLOR, V_INFO_COLOR, 0 }, { V_MODEGRAPHICS, V_INFO_GRAPHICS, 0 }, { V_MODELFB, V_INFO_LINEAR, 0 }, }; int flags; int i; for (flags = 0, i = 0; i < sizeof(ftable)/sizeof(ftable[0]); ++i) { flags |= (vflags & ftable[i].mask) ? ftable[i].set : ftable[i].reset; } return flags; } static int vesa_translate_mmodel(u_int8_t vmodel) { static struct { u_int8_t vmodel; int mmodel; } mtable[] = { { V_MMTEXT, V_INFO_MM_TEXT }, { V_MMCGA, V_INFO_MM_CGA }, { V_MMHGC, V_INFO_MM_HGC }, { V_MMEGA, V_INFO_MM_PLANAR }, { V_MMPACKED, V_INFO_MM_PACKED }, { V_MMDIRCOLOR, V_INFO_MM_DIRECT }, }; int i; for (i = 0; mtable[i].mmodel >= 0; ++i) { if (mtable[i].vmodel == vmodel) return mtable[i].mmodel; } return V_INFO_MM_OTHER; } static void *vesa_fix_ptr(u_int32_t p, u_int16_t seg, u_int16_t off, u_char *buf) { if (p == 0) return NULL; if (((p >> 16) == seg) && ((p & 0xffff) >= off)) return (void *)(buf + ((p & 0xffff) - off)); else { p = BIOS_SADDRTOLADDR(p); return (void *)BIOS_PADDRTOVADDR(p); } } static int vesa_bios_init(void) { static u_char buf[512]; struct vm86frame vmf; struct vesa_mode vmode; video_info_t *p; u_char *vmbuf; int modes; int err; int i; if (vesa_init_done) return 0; has_vesa_bios = FALSE; vesa_adp_info = NULL; vesa_vmode_max = 0; vesa_vmode[0].vi_mode = EOT; vmbuf = (u_char *)vm86_addpage(&vesa_vmcontext, 1, 0); bzero(&vmf, sizeof(vmf)); /* paranoia */ bcopy("VBE2", vmbuf, 4); /* try for VBE2 data */ vmf.vmf_eax = 0x4f00; vm86_getptr(&vesa_vmcontext, (vm_offset_t)vmbuf, &vmf.vmf_es, &vmf.vmf_di); err = vm86_datacall(0x10, &vmf, &vesa_vmcontext); if ((err != 0) || (vmf.vmf_ax != 0x4f) || bcmp("VESA", vmbuf, 4)) return 1; bcopy(vmbuf, buf, sizeof(buf)); vesa_adp_info = (struct vesa_info *)buf; if (bootverbose) { printf("VESA: information block\n"); dump_buffer(buf, 64); } if (vesa_adp_info->v_flags & V_NONVGA) return 1; if (vesa_adp_info->v_version < 0x0102) { printf("VESA: VBE version %d.%d is not supported; " "version 1.2 or later is required.\n", ((vesa_adp_info->v_version & 0xf000) >> 12) * 10 + ((vesa_adp_info->v_version & 0x0f00) >> 8), ((vesa_adp_info->v_version & 0x00f0) >> 4) * 10 + (vesa_adp_info->v_version & 0x000f)); return 1; } /* fix string ptrs */ vesa_oemstr = (char *)vesa_fix_ptr(vesa_adp_info->v_oemstr, vmf.vmf_es, vmf.vmf_di, buf); if (vesa_adp_info->v_version >= 0x0200) { vesa_venderstr = (char *)vesa_fix_ptr(vesa_adp_info->v_venderstr, vmf.vmf_es, vmf.vmf_di, buf); vesa_prodstr = (char *)vesa_fix_ptr(vesa_adp_info->v_prodstr, vmf.vmf_es, vmf.vmf_di, buf); vesa_revstr = (char *)vesa_fix_ptr(vesa_adp_info->v_revstr, vmf.vmf_es, vmf.vmf_di, buf); } /* obtain video mode information */ vesa_vmodetab = (u_int16_t *)vesa_fix_ptr(vesa_adp_info->v_modetable, vmf.vmf_es, vmf.vmf_di, buf); if (vesa_vmodetab == NULL) return 1; for (i = 0, modes = 0; (i < (M_VESA_MODE_MAX - M_VESA_BASE + 1)) && (vesa_vmodetab[i] != 0xffff); ++i) { if (vesa_bios_get_mode(vesa_vmodetab[i], &vmode)) continue; /* reject unsupported modes */ #if 0 if ((vmode.v_modeattr & (V_MODESUPP | V_MODEOPTINFO | V_MODENONVGA)) != (V_MODESUPP | V_MODEOPTINFO)) continue; #else if ((vmode.v_modeattr & (V_MODEOPTINFO | V_MODENONVGA)) != (V_MODEOPTINFO)) continue; #endif /* expand the array if necessary */ if (modes >= vesa_vmode_max) { vesa_vmode_max += MODE_TABLE_DELTA; p = malloc(sizeof(*vesa_vmode)*(vesa_vmode_max + 1), M_DEVBUF, M_WAITOK); #if VESA_DEBUG > 1 printf("vesa_bios_init(): modes:%d, vesa_mode_max:%d\n", modes, vesa_vmode_max); #endif if (modes > 0) { bcopy(vesa_vmode, p, sizeof(*vesa_vmode)*modes); free(vesa_vmode, M_DEVBUF); } vesa_vmode = p; } /* copy some fields */ bzero(&vesa_vmode[modes], sizeof(vesa_vmode[modes])); vesa_vmode[modes].vi_mode = vesa_vmodetab[i]; vesa_vmode[modes].vi_width = vmode.v_width; vesa_vmode[modes].vi_height = vmode.v_height; vesa_vmode[modes].vi_depth = vmode.v_bpp; vesa_vmode[modes].vi_planes = vmode.v_planes; vesa_vmode[modes].vi_cwidth = vmode.v_cwidth; vesa_vmode[modes].vi_cheight = vmode.v_cheight; vesa_vmode[modes].vi_window = (u_int)vmode.v_waseg << 4; /* XXX window B */ vesa_vmode[modes].vi_window_size = vmode.v_wsize*1024; vesa_vmode[modes].vi_window_gran = vmode.v_wgran*1024; if (vmode.v_modeattr & V_MODELFB) vesa_vmode[modes].vi_buffer = vmode.v_lfb; else vesa_vmode[modes].vi_buffer = 0; /* XXX */ vesa_vmode[modes].vi_buffer_size = vesa_adp_info->v_memsize*64*1024; #if 0 if (vmode.v_offscreen > vmode.v_lfb) vesa_vmode[modes].vi_buffer_size = vmode.v_offscreen + vmode.v_offscreensize*1024 - vmode.v_lfb; else vesa_vmode[modes].vi_buffer_size = vmode.v_offscreen + vmode.v_offscreensize*1024 #endif vesa_vmode[modes].vi_mem_model = vesa_translate_mmodel(vmode.v_memmodel); vesa_vmode[modes].vi_pixel_fields[0] = 0; vesa_vmode[modes].vi_pixel_fields[1] = 0; vesa_vmode[modes].vi_pixel_fields[2] = 0; vesa_vmode[modes].vi_pixel_fields[3] = 0; vesa_vmode[modes].vi_pixel_fsizes[0] = 0; vesa_vmode[modes].vi_pixel_fsizes[1] = 0; vesa_vmode[modes].vi_pixel_fsizes[2] = 0; vesa_vmode[modes].vi_pixel_fsizes[3] = 0; if (vesa_vmode[modes].vi_mem_model == V_INFO_MM_PACKED) { vesa_vmode[modes].vi_pixel_size = (vmode.v_bpp + 7)/8; } else if (vesa_vmode[modes].vi_mem_model == V_INFO_MM_DIRECT) { vesa_vmode[modes].vi_pixel_size = (vmode.v_bpp + 7)/8; vesa_vmode[modes].vi_pixel_fields[0] = vmode.v_redfieldpos; vesa_vmode[modes].vi_pixel_fields[1] = vmode.v_greenfieldpos; vesa_vmode[modes].vi_pixel_fields[2] = vmode.v_bluefieldpos; vesa_vmode[modes].vi_pixel_fields[3] = vmode.v_resfieldpos; vesa_vmode[modes].vi_pixel_fsizes[0] = vmode.v_redmasksize; vesa_vmode[modes].vi_pixel_fsizes[1] = vmode.v_greenmasksize; vesa_vmode[modes].vi_pixel_fsizes[2] = vmode.v_bluemasksize; vesa_vmode[modes].vi_pixel_fsizes[3] = vmode.v_resmasksize; } else { vesa_vmode[modes].vi_pixel_size = 0; } vesa_vmode[modes].vi_flags = vesa_translate_flags(vmode.v_modeattr) | V_INFO_VESA; ++modes; } vesa_vmode[modes].vi_mode = EOT; if (bootverbose) printf("VESA: %d mode(s) found\n", modes); has_vesa_bios = (modes > 0); return (has_vesa_bios ? 0 : 1); } static void vesa_clear_modes(video_info_t *info, int color) { while (info->vi_mode != EOT) { if ((info->vi_flags & V_INFO_COLOR) != color) info->vi_mode = NA; ++info; } } static vm_offset_t vesa_map_buffer(u_int paddr, size_t size) { vm_offset_t vaddr; u_int off; off = paddr - trunc_page(paddr); vaddr = (vm_offset_t)pmap_mapdev(paddr - off, size + off); #if VESA_DEBUG > 1 printf("vesa_map_buffer: paddr:%x vaddr:%x size:%x off:%x\n", paddr, vaddr, size, off); #endif return (vaddr + off); } static void vesa_unmap_buffer(vm_offset_t vaddr, size_t size) { #if VESA_DEBUG > 1 printf("vesa_unmap_buffer: vaddr:%x size:%x\n", vaddr, size); #endif kmem_free(kernel_map, vaddr, size); } /* entry points */ static int vesa_configure(int flags) { video_adapter_t *adp; int adapters; int error; int i; if (vesa_init_done) return 0; if (flags & VIO_PROBE_ONLY) return 0; /* XXX */ /* * If the VESA module has already been loaded, abort loading * the module this time. */ for (i = 0; (adp = vid_get_adapter(i)) != NULL; ++i) { if (adp->va_flags & V_ADP_VESA) return ENXIO; if (adp->va_type == KD_VGA) break; } /* * The VGA adapter is not found. This is because either * 1) the VGA driver has not been initialized, or 2) the VGA card * is not present. If 1) is the case, we shall defer * initialization for now and try again later. */ if (adp == NULL) { vga_sub_configure = vesa_configure; return ENODEV; } /* count number of registered adapters */ for (++i; vid_get_adapter(i) != NULL; ++i) ; adapters = i; /* call VESA BIOS */ vesa_adp = adp; if (vesa_bios_init()) { vesa_adp = NULL; return ENXIO; } vesa_adp->va_flags |= V_ADP_VESA; /* remove conflicting modes if we have more than one adapter */ if (adapters > 1) { vesa_clear_modes(vesa_vmode, (vesa_adp->va_flags & V_ADP_COLOR) ? V_INFO_COLOR : 0); } if ((error = vesa_load_ioctl()) == 0) { prevvidsw = vidsw[vesa_adp->va_index]; vidsw[vesa_adp->va_index] = &vesavidsw; vesa_init_done = TRUE; } else { vesa_adp = NULL; return error; } return 0; } #if 0 static int vesa_nop(void) { return 0; } #endif static int vesa_error(void) { return 1; } static int vesa_probe(int unit, video_adapter_t **adpp, void *arg, int flags) { return (*prevvidsw->probe)(unit, adpp, arg, flags); } static int vesa_init(int unit, video_adapter_t *adp, int flags) { return (*prevvidsw->init)(unit, adp, flags); } static int vesa_get_info(video_adapter_t *adp, int mode, video_info_t *info) { int i; if ((*prevvidsw->get_info)(adp, mode, info) == 0) return 0; if (adp != vesa_adp) return 1; mode = vesa_map_gen_mode_num(vesa_adp->va_type, vesa_adp->va_flags & V_ADP_COLOR, mode); for (i = 0; vesa_vmode[i].vi_mode != EOT; ++i) { if (vesa_vmode[i].vi_mode == NA) continue; if (vesa_vmode[i].vi_mode == mode) { *info = vesa_vmode[i]; return 0; } } return 1; } static int vesa_query_mode(video_adapter_t *adp, video_info_t *info) { int i; if ((*prevvidsw->query_mode)(adp, info) == 0) return 0; if (adp != vesa_adp) return ENODEV; for (i = 0; vesa_vmode[i].vi_mode != EOT; ++i) { if ((info->vi_width != 0) && (info->vi_width != vesa_vmode[i].vi_width)) continue; if ((info->vi_height != 0) && (info->vi_height != vesa_vmode[i].vi_height)) continue; if ((info->vi_cwidth != 0) && (info->vi_cwidth != vesa_vmode[i].vi_cwidth)) continue; if ((info->vi_cheight != 0) && (info->vi_cheight != vesa_vmode[i].vi_cheight)) continue; if ((info->vi_depth != 0) && (info->vi_depth != vesa_vmode[i].vi_depth)) continue; if ((info->vi_planes != 0) && (info->vi_planes != vesa_vmode[i].vi_planes)) continue; /* pixel format, memory model */ if ((info->vi_flags != 0) && (info->vi_flags != vesa_vmode[i].vi_flags)) continue; *info = vesa_vmode[i]; return 0; } return ENODEV; } static int vesa_set_mode(video_adapter_t *adp, int mode) { video_info_t info; int len; if (adp != vesa_adp) return (*prevvidsw->set_mode)(adp, mode); mode = vesa_map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode); #if VESA_DEBUG > 0 printf("VESA: set_mode(): %d(%x) -> %d(%x)\n", adp->va_mode, adp->va_mode, mode, mode); #endif /* * If the current mode is a VESA mode and the new mode is not, * restore the state of the adapter first by setting one of the * standard VGA mode, so that non-standard, extended SVGA registers * are set to the state compatible with the standard VGA modes. * Otherwise (*prevvidsw->set_mode)() may not be able to set up * the new mode correctly. */ if (VESA_MODE(adp->va_mode)) { if ((*prevvidsw->get_info)(adp, mode, &info) == 0) { int10_set_mode(adp->va_initial_bios_mode); if (adp->va_info.vi_flags & V_INFO_LINEAR) vesa_unmap_buffer(adp->va_buffer, vesa_adp_info->v_memsize*64*1024); /* * Once (*prevvidsw->get_info)() succeeded, * (*prevvidsw->set_mode)() below won't fail... */ } } /* we may not need to handle this mode after all... */ if ((*prevvidsw->set_mode)(adp, mode) == 0) return 0; /* is the new mode supported? */ if (vesa_get_info(adp, mode, &info)) return 1; /* assert(VESA_MODE(mode)); */ #if VESA_DEBUG > 0 printf("VESA: about to set a VESA mode...\n"); #endif /* don't use the linear frame buffer for text modes. XXX */ if (!(info.vi_flags & V_INFO_GRAPHICS)) info.vi_flags &= ~V_INFO_LINEAR; if (vesa_bios_set_mode(mode | ((info.vi_flags & V_INFO_LINEAR) ? 0x4000 : 0))) return 1; if (adp->va_info.vi_flags & V_INFO_LINEAR) vesa_unmap_buffer(adp->va_buffer, vesa_adp_info->v_memsize*64*1024); #if VESA_DEBUG > 0 printf("VESA: mode set!\n"); #endif vesa_adp->va_mode = mode; vesa_adp->va_flags &= ~V_ADP_COLOR; vesa_adp->va_flags |= (info.vi_flags & V_INFO_COLOR) ? V_ADP_COLOR : 0; vesa_adp->va_crtc_addr = (vesa_adp->va_flags & V_ADP_COLOR) ? COLOR_CRTC : MONO_CRTC; if (info.vi_flags & V_INFO_LINEAR) { #if VESA_DEBUG > 1 printf("VESA: setting up LFB\n"); #endif vesa_adp->va_buffer = vesa_map_buffer(info.vi_buffer, vesa_adp_info->v_memsize*64*1024); vesa_adp->va_buffer_size = info.vi_buffer_size; vesa_adp->va_window = vesa_adp->va_buffer; vesa_adp->va_window_size = info.vi_buffer_size/info.vi_planes; vesa_adp->va_window_gran = info.vi_buffer_size/info.vi_planes; } else { vesa_adp->va_buffer = 0; vesa_adp->va_buffer_size = info.vi_buffer_size; vesa_adp->va_window = BIOS_PADDRTOVADDR(info.vi_window); vesa_adp->va_window_size = info.vi_window_size; vesa_adp->va_window_gran = info.vi_window_gran; } vesa_adp->va_window_orig = 0; len = vesa_bios_get_line_length(); if (len > 0) { vesa_adp->va_line_width = len; } else if (info.vi_flags & V_INFO_GRAPHICS) { switch (info.vi_depth/info.vi_planes) { case 1: vesa_adp->va_line_width = info.vi_width/8; break; case 2: vesa_adp->va_line_width = info.vi_width/4; break; case 4: vesa_adp->va_line_width = info.vi_width/2; break; case 8: default: /* shouldn't happen */ vesa_adp->va_line_width = info.vi_width; break; } } else { vesa_adp->va_line_width = info.vi_width; } vesa_adp->va_disp_start.x = 0; vesa_adp->va_disp_start.y = 0; #if VESA_DEBUG > 0 printf("vesa_set_mode(): vi_width:%d, len:%d, line_width:%d\n", info.vi_width, len, vesa_adp->va_line_width); #endif bcopy(&info, &vesa_adp->va_info, sizeof(vesa_adp->va_info)); /* move hardware cursor out of the way */ (*vidsw[vesa_adp->va_index]->set_hw_cursor)(vesa_adp, -1, -1); return 0; } static int vesa_save_font(video_adapter_t *adp, int page, int fontsize, u_char *data, int ch, int count) { return (*prevvidsw->save_font)(adp, page, fontsize, data, ch, count); } static int vesa_load_font(video_adapter_t *adp, int page, int fontsize, u_char *data, int ch, int count) { return (*prevvidsw->load_font)(adp, page, fontsize, data, ch, count); } static int vesa_show_font(video_adapter_t *adp, int page) { return (*prevvidsw->show_font)(adp, page); } static int vesa_save_palette(video_adapter_t *adp, u_char *palette) { int bits; int error; if ((adp == vesa_adp) && (vesa_adp_info->v_flags & V_DAC8) && VESA_MODE(adp->va_mode)) { bits = vesa_bios_get_dac(); error = vesa_bios_save_palette(0, 256, palette, bits); if (error == 0) return 0; if (bits != 6) return error; } return (*prevvidsw->save_palette)(adp, palette); } static int vesa_load_palette(video_adapter_t *adp, u_char *palette) { #if notyet int bits; int error; if ((adp == vesa_adp) && (vesa_adp_info->v_flags & V_DAC8) && VESA_MODE(adp->va_mode) && ((bits = vesa_bios_set_dac(8)) > 6)) { error = vesa_bios_load_palette(0, 256, palette, bits); if (error == 0) return 0; if (vesa_bios_set_dac(6) != 6) return 1; } #endif /* notyet */ return (*prevvidsw->load_palette)(adp, palette); } static int vesa_set_border(video_adapter_t *adp, int color) { return (*prevvidsw->set_border)(adp, color); } static int vesa_save_state(video_adapter_t *adp, void *p, size_t size) { if (adp != vesa_adp) return (*prevvidsw->save_state)(adp, p, size); if (vesa_state_buf_size == 0) vesa_state_buf_size = vesa_bios_state_buf_size(); if (size == 0) return (sizeof(int) + vesa_state_buf_size); else if (size < (sizeof(int) + vesa_state_buf_size)) return 1; ((adp_state_t *)p)->sig = V_STATE_SIG; bzero(((adp_state_t *)p)->regs, vesa_state_buf_size); return vesa_bios_save_restore(STATE_SAVE, ((adp_state_t *)p)->regs, vesa_state_buf_size); } static int vesa_load_state(video_adapter_t *adp, void *p) { if ((adp != vesa_adp) || (((adp_state_t *)p)->sig != V_STATE_SIG)) return (*prevvidsw->load_state)(adp, p); return vesa_bios_save_restore(STATE_LOAD, ((adp_state_t *)p)->regs, vesa_state_buf_size); } #if 0 static int vesa_get_origin(video_adapter_t *adp, off_t *offset) { struct vm86frame vmf; int err; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f05; vmf.vmf_ebx = 0x10; /* WINDOW_A, XXX */ err = vm86_intcall(0x10, &vmf); if ((err != 0) || (vmf.vmf_ax != 0x4f)) return 1; *offset = vmf.vmf_dx*adp->va_window_gran; return 0; } #endif static int vesa_set_origin(video_adapter_t *adp, off_t offset) { struct vm86frame vmf; int err; /* * This function should return as quickly as possible to * maintain good performance of the system. For this reason, * error checking is kept minimal and let the VESA BIOS to * detect error. */ if (adp != vesa_adp) return (*prevvidsw->set_win_org)(adp, offset); /* if this is a linear frame buffer, do nothing */ if (adp->va_info.vi_flags & V_INFO_LINEAR) return 0; /* XXX */ if (adp->va_window_gran == 0) return 1; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f05; vmf.vmf_ebx = 0; /* WINDOW_A, XXX */ vmf.vmf_edx = offset/adp->va_window_gran; err = vm86_intcall(0x10, &vmf); if ((err != 0) || (vmf.vmf_ax != 0x4f)) return 1; bzero(&vmf, sizeof(vmf)); vmf.vmf_eax = 0x4f05; vmf.vmf_ebx = 1; /* WINDOW_B, XXX */ vmf.vmf_edx = offset/adp->va_window_gran; err = vm86_intcall(0x10, &vmf); adp->va_window_orig = (offset/adp->va_window_gran)*adp->va_window_gran; return 0; /* XXX */ } static int vesa_read_hw_cursor(video_adapter_t *adp, int *col, int *row) { return (*prevvidsw->read_hw_cursor)(adp, col, row); } static int vesa_set_hw_cursor(video_adapter_t *adp, int col, int row) { return (*prevvidsw->set_hw_cursor)(adp, col, row); } static int vesa_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, int celsize, int blink) { return (*prevvidsw->set_hw_cursor_shape)(adp, base, height, celsize, blink); } static int vesa_blank_display(video_adapter_t *adp, int mode) { /* XXX: use VESA DPMS */ return (*prevvidsw->blank_display)(adp, mode); } static int vesa_mmap(video_adapter_t *adp, vm_offset_t offset, int prot) { #if VESA_DEBUG > 0 printf("vesa_mmap(): window:0x%x, buffer:0x%x, offset:0x%x\n", adp->va_info.vi_window, adp->va_info.vi_buffer, offset); #endif if ((adp == vesa_adp) && (adp->va_info.vi_flags & V_INFO_LINEAR)) { /* va_window_size == va_buffer_size/vi_planes */ /* XXX: is this correct? */ if (offset > adp->va_window_size - PAGE_SIZE) return -1; #ifdef __i386__ return i386_btop(adp->va_info.vi_buffer + offset); #endif #ifdef __alpha__ /* XXX */ return alpha_btop(adp->va_info.vi_buffer + offset); #endif } else { return (*prevvidsw->mmap)(adp, offset, prot); } } static int vesa_clear(video_adapter_t *adp) { return (*prevvidsw->clear)(adp); } static int vesa_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy) { return (*prevvidsw->fill_rect)(adp, val, x, y, cx, cy); } static int vesa_bitblt(video_adapter_t *adp,...) { /* FIXME */ return 1; } static int get_palette(video_adapter_t *adp, int base, int count, u_char *red, u_char *green, u_char *blue, u_char *trans) { u_char *r; u_char *g; u_char *b; int bits; int error; if ((base < 0) || (base >= 256) || (base + count > 256)) return 1; if (!(vesa_adp_info->v_flags & V_DAC8) || !VESA_MODE(adp->va_mode)) return 1; bits = vesa_bios_get_dac(); if (bits <= 6) return 1; r = malloc(count*3, M_DEVBUF, M_WAITOK); g = r + count; b = g + count; error = vesa_bios_save_palette2(base, count, r, g, b, bits); if (error == 0) { copyout(r, red, count); copyout(g, green, count); copyout(b, blue, count); if (trans != NULL) { bzero(r, count); copyout(r, trans, count); } } free(r, M_DEVBUF); /* if error && bits != 6 at this point, we are in in trouble... XXX */ return error; } static int set_palette(video_adapter_t *adp, int base, int count, u_char *red, u_char *green, u_char *blue, u_char *trans) { return 1; #if notyet u_char *r; u_char *g; u_char *b; int bits; int error; if ((base < 0) || (base >= 256) || (base + count > 256)) return 1; if (!(vesa_adp_info->v_flags & V_DAC8) || !VESA_MODE(adp->va_mode) || ((bits = vesa_bios_set_dac(8)) <= 6)) return 1; r = malloc(count*3, M_DEVBUF, M_WAITOK); g = r + count; b = g + count; copyin(red, r, count); copyin(green, g, count); copyin(blue, b, count); error = vesa_bios_load_palette2(base, count, r, g, b, bits); free(r, M_DEVBUF); if (error == 0) return 0; /* if the following call fails, we are in trouble... XXX */ vesa_bios_set_dac(6); return 1; #endif /* notyet */ } static int vesa_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg) { int bytes; if (adp != vesa_adp) return (*prevvidsw->ioctl)(adp, cmd, arg); switch (cmd) { case FBIO_SETWINORG: /* set frame buffer window origin */ if (!VESA_MODE(adp->va_mode)) return (*prevvidsw->ioctl)(adp, cmd, arg); return (vesa_set_origin(adp, *(off_t *)arg) ? ENODEV : 0); case FBIO_SETDISPSTART: /* set display start address */ if (!VESA_MODE(adp->va_mode)) return (*prevvidsw->ioctl)(adp, cmd, arg); if (vesa_bios_set_start(((video_display_start_t *)arg)->x, ((video_display_start_t *)arg)->y)) return ENODEV; adp->va_disp_start.x = ((video_display_start_t *)arg)->x; adp->va_disp_start.y = ((video_display_start_t *)arg)->y; return 0; case FBIO_SETLINEWIDTH: /* set line length in pixel */ if (!VESA_MODE(adp->va_mode)) return (*prevvidsw->ioctl)(adp, cmd, arg); if (vesa_bios_set_line_length(*(u_int *)arg, &bytes, NULL)) return ENODEV; adp->va_line_width = bytes; #if VESA_DEBUG > 1 printf("new line width:%d\n", adp->va_line_width); #endif return 0; case FBIO_GETPALETTE: /* get color palette */ if (get_palette(adp, ((video_color_palette_t *)arg)->index, ((video_color_palette_t *)arg)->count, ((video_color_palette_t *)arg)->red, ((video_color_palette_t *)arg)->green, ((video_color_palette_t *)arg)->blue, ((video_color_palette_t *)arg)->transparent)) return (*prevvidsw->ioctl)(adp, cmd, arg); return 0; case FBIO_SETPALETTE: /* set color palette */ if (set_palette(adp, ((video_color_palette_t *)arg)->index, ((video_color_palette_t *)arg)->count, ((video_color_palette_t *)arg)->red, ((video_color_palette_t *)arg)->green, ((video_color_palette_t *)arg)->blue, ((video_color_palette_t *)arg)->transparent)) return (*prevvidsw->ioctl)(adp, cmd, arg); return 0; case FBIOGETCMAP: /* get color palette */ if (get_palette(adp, ((struct fbcmap *)arg)->index, ((struct fbcmap *)arg)->count, ((struct fbcmap *)arg)->red, ((struct fbcmap *)arg)->green, ((struct fbcmap *)arg)->blue, NULL)) return (*prevvidsw->ioctl)(adp, cmd, arg); return 0; case FBIOPUTCMAP: /* set color palette */ if (set_palette(adp, ((struct fbcmap *)arg)->index, ((struct fbcmap *)arg)->count, ((struct fbcmap *)arg)->red, ((struct fbcmap *)arg)->green, ((struct fbcmap *)arg)->blue, NULL)) return (*prevvidsw->ioctl)(adp, cmd, arg); return 0; default: return (*prevvidsw->ioctl)(adp, cmd, arg); } } static int vesa_diag(video_adapter_t *adp, int level) { int error; /* call the previous handler first */ error = (*prevvidsw->diag)(adp, level); if (error) return error; if (adp != vesa_adp) return 1; if (level <= 0) return 0; return 0; } static int vesa_bios_info(int level) { #if VESA_DEBUG > 1 struct vesa_mode vmode; int i; #endif /* general adapter information */ printf("VESA: v%d.%d, %dk memory, flags:0x%x, mode table:%p (%x)\n", ((vesa_adp_info->v_version & 0xf000) >> 12) * 10 + ((vesa_adp_info->v_version & 0x0f00) >> 8), ((vesa_adp_info->v_version & 0x00f0) >> 4) * 10 + (vesa_adp_info->v_version & 0x000f), vesa_adp_info->v_memsize * 64, vesa_adp_info->v_flags, vesa_vmodetab, vesa_adp_info->v_modetable); /* OEM string */ if (vesa_oemstr != NULL) printf("VESA: %s\n", vesa_oemstr); if (level <= 0) return 0; if (vesa_adp_info->v_version >= 0x0200) { /* vender name, product name, product revision */ printf("VESA: %s %s %s\n", (vesa_venderstr != NULL) ? vesa_venderstr : "unknown", (vesa_prodstr != NULL) ? vesa_prodstr : "unknown", (vesa_revstr != NULL) ? vesa_revstr : "?"); } #if VESA_DEBUG > 1 /* mode information */ for (i = 0; (i < (M_VESA_MODE_MAX - M_VESA_BASE + 1)) && (vesa_vmodetab[i] != 0xffff); ++i) { if (vesa_bios_get_mode(vesa_vmodetab[i], &vmode)) continue; /* print something for diagnostic purpose */ printf("VESA: mode:0x%03x, flags:0x%04x", vesa_vmodetab[i], vmode.v_modeattr); if (vmode.v_modeattr & V_MODEOPTINFO) { if (vmode.v_modeattr & V_MODEGRAPHICS) { printf(", G %dx%dx%d %d, ", vmode.v_width, vmode.v_height, vmode.v_bpp, vmode.v_planes); } else { printf(", T %dx%d, ", vmode.v_width, vmode.v_height); } printf("font:%dx%d, ", vmode.v_cwidth, vmode.v_cheight); printf("pages:%d, mem:%d", vmode.v_ipages + 1, vmode.v_memmodel); } if (vmode.v_modeattr & V_MODELFB) { printf("\nVESA: LFB:0x%x, off:0x%x, off_size:0x%x", vmode.v_lfb, vmode.v_offscreen, vmode.v_offscreensize*1024); } printf("\n"); printf("VESA: window A:0x%x (%x), window B:0x%x (%x), ", vmode.v_waseg, vmode.v_waattr, vmode.v_wbseg, vmode.v_wbattr); printf("size:%dk, gran:%dk\n", vmode.v_wsize, vmode.v_wgran); } #endif /* VESA_DEBUG > 1 */ return 0; } /* module loading */ static int vesa_load(void) { int error; int s; if (vesa_init_done) return 0; /* locate a VGA adapter */ s = spltty(); vesa_adp = NULL; error = vesa_configure(0); splx(s); if (error == 0) vesa_bios_info(bootverbose); return error; } static int vesa_unload(void) { u_char palette[256*3]; int error; int bits; int s; /* if the adapter is currently in a VESA mode, don't unload */ if ((vesa_adp != NULL) && VESA_MODE(vesa_adp->va_mode)) return EBUSY; /* * FIXME: if there is at least one vty which is in a VESA mode, * we shouldn't be unloading! XXX */ s = spltty(); if ((error = vesa_unload_ioctl()) == 0) { if (vesa_adp != NULL) { if (vesa_adp_info->v_flags & V_DAC8) { bits = vesa_bios_get_dac(); if (bits > 6) { vesa_bios_save_palette(0, 256, palette, bits); vesa_bios_set_dac(6); vesa_bios_load_palette(0, 256, palette, 6); } } vesa_adp->va_flags &= ~V_ADP_VESA; vidsw[vesa_adp->va_index] = prevvidsw; } } splx(s); return error; } static int vesa_mod_event(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: return vesa_load(); case MOD_UNLOAD: return vesa_unload(); default: break; } return 0; } static moduledata_t vesa_mod = { "vesa", vesa_mod_event, NULL, }; DECLARE_MODULE(vesa, vesa_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); -#endif /* (NVGA > 0 && VESA) || KLD_MODULE */ +#endif /* VGA_NO_MODE_CHANGE */ Index: head/sys/isa/atkbd_isa.c =================================================================== --- head/sys/isa/atkbd_isa.c (revision 56835) +++ head/sys/isa/atkbd_isa.c (revision 56836) @@ -1,128 +1,123 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "atkbd.h" #include "opt_kbd.h" -#if NATKBD > 0 - #include #include #include #include #include #include #include #include #include #include #include #include #include #include devclass_t atkbd_devclass; static int atkbdprobe(device_t dev); static int atkbdattach(device_t dev); static void atkbd_isa_intr(void *arg); static device_method_t atkbd_methods[] = { DEVMETHOD(device_probe, atkbdprobe), DEVMETHOD(device_attach, atkbdattach), { 0, 0 } }; static driver_t atkbd_driver = { ATKBD_DRIVER_NAME, atkbd_methods, 1, }; static int atkbdprobe(device_t dev) { uintptr_t port; uintptr_t irq; uintptr_t flags; device_set_desc(dev, "AT Keyboard"); /* obtain parameters */ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); /* probe the device */ return atkbd_probe_unit(device_get_unit(dev), port, irq, flags); } static int atkbdattach(device_t dev) { keyboard_t *kbd; uintptr_t port; uintptr_t irq; uintptr_t flags; struct resource *res; void *ih; int zero = 0; int error; BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); error = atkbd_attach_unit(device_get_unit(dev), &kbd, port, irq, flags); if (error) return error; /* declare our interrupt handler */ res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, INTR_TYPE_TTY, atkbd_isa_intr, kbd, &ih); return 0; } static void atkbd_isa_intr(void *arg) { keyboard_t *kbd; kbd = (keyboard_t *)arg; (*kbdsw[kbd->kb_index]->intr)(kbd, NULL); } DRIVER_MODULE(atkbd, atkbdc, atkbd_driver, atkbd_devclass, 0, 0); - -#endif /* NATKBD > 0 */ Index: head/sys/isa/atkbdc_isa.c =================================================================== --- head/sys/isa/atkbdc_isa.c (revision 56835) +++ head/sys/isa/atkbdc_isa.c (revision 56836) @@ -1,269 +1,264 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "atkbdc.h" #include "opt_kbd.h" -#if NATKBDC > 0 - #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device"); /* children */ typedef struct atkbdc_device { int flags; /* configuration flags */ int port; /* port number (same as the controller's) */ int irq; /* ISA IRQ mask */ } atkbdc_device_t; /* kbdc */ devclass_t atkbdc_devclass; static int atkbdc_probe(device_t dev); static int atkbdc_attach(device_t dev); static int atkbdc_print_child(device_t bus, device_t dev); static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val); static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val); static device_method_t atkbdc_methods[] = { DEVMETHOD(device_probe, atkbdc_probe), DEVMETHOD(device_attach, atkbdc_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(bus_print_child, atkbdc_print_child), DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t atkbdc_driver = { ATKBDC_DRIVER_NAME, atkbdc_methods, sizeof(atkbdc_softc_t *), }; static int atkbdc_probe(device_t dev) { int error; int rid; struct resource *port; /* Check isapnp ids */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "keyboard controller (i8042)"); rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_probe_unit(device_get_unit(dev), rman_get_start(port)); bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return error; } static void atkbdc_add_device(device_t dev, const char *name, int unit) { atkbdc_softc_t *sc = *(atkbdc_softc_t **)device_get_softc(dev); atkbdc_device_t *kdev; device_t child; int t; kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT); if (!kdev) return; bzero(kdev, sizeof *kdev); kdev->port = sc->port; if (resource_int_value(name, unit, "irq", &t) == 0) kdev->irq = t; else kdev->irq = -1; if (resource_int_value(name, unit, "flags", &t) == 0) kdev->flags = t; else kdev->flags = 0; child = device_add_child(dev, name, unit); device_set_ivars(child, kdev); } static int atkbdc_attach(device_t dev) { atkbdc_softc_t *sc; struct resource *port; int unit; int error; int rid; int i; unit = device_get_unit(dev); sc = *(atkbdc_softc_t **)device_get_softc(dev); if (sc == NULL) { /* * We have to maintain two copies of the kbdc_softc struct, * as the low-level console needs to have access to the * keyboard controller before kbdc is probed and attached. * kbdc_soft[] contains the default entry for that purpose. * See atkbdc.c. XXX */ sc = atkbdc_get_softc(unit); if (sc == NULL) return ENOMEM; } /* XXX should track resource in softc */ rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, IO_KBDSIZE, RF_ACTIVE); if (!port) return ENXIO; error = atkbdc_attach_unit(unit, sc, rman_get_start(port)); if (error) return error; *(atkbdc_softc_t **)device_get_softc(dev) = sc; /* * Add all devices configured to be attached to atkbdc0. */ for (i = resource_query_string(-1, "at", "atkbdc0"); i != -1; i = resource_query_string(i, "at", "atkbdc0")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } /* * and atkbdc? */ for (i = resource_query_string(-1, "at", "atkbdc"); i != -1; i = resource_query_string(i, "at", "atkbdc")) { atkbdc_add_device(dev, resource_query_name(i), resource_query_unit(i)); } bus_generic_attach(dev); return 0; } static int atkbdc_print_child(device_t bus, device_t dev) { atkbdc_device_t *kbdcdev; int retval = 0; kbdcdev = (atkbdc_device_t *)device_get_ivars(dev); retval += bus_print_child_header(bus, dev); if (kbdcdev->flags != 0) retval += printf(" flags 0x%x", kbdcdev->flags); if (kbdcdev->irq != -1) retval += printf(" irq %d", kbdcdev->irq); retval += bus_print_child_footer(bus, dev); return (retval); } static int atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: *val = (u_long)ivar->port; break; case KBDC_IVAR_IRQ: *val = (u_long)ivar->irq; break; case KBDC_IVAR_FLAGS: *val = (u_long)ivar->flags; break; default: return ENOENT; } return 0; } static int atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_PORT: ivar->port = (int)val; break; case KBDC_IVAR_IRQ: ivar->irq = (int)val; break; case KBDC_IVAR_FLAGS: ivar->flags = (int)val; break; default: return ENOENT; } return 0; } DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0); - -#endif /* NATKBDC > 0 */ Index: head/sys/isa/psm.c =================================================================== --- head/sys/isa/psm.c (revision 56835) +++ head/sys/isa/psm.c (revision 56836) @@ -1,2454 +1,2445 @@ /*- * Copyright (c) 1992, 1993 Erik Forsberg. * Copyright (c) 1996, 1997 Kazutaka YOKOTA. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * Ported to 386bsd Oct 17, 1992 * Sandi Donno, Computer Science, University of Cape Town, South Africa * Please send bug reports to sandi@cs.uct.ac.za * * Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca - * although I was only partially successful in getting the alpha release * of his "driver for the Logitech and ATI Inport Bus mice for use with * 386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless * found his code to be an invaluable reference when porting this driver * to 386bsd. * * Further modifications for latest 386BSD+patchkit and port to NetBSD, * Andrew Herbert - 8 June 1993 * * Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by * Andrew Herbert - 12 June 1993 * * Modified for PS/2 mouse by Charles Hannum * - 13 June 1993 * * Modified for PS/2 AUX mouse by Shoji Yuen * - 24 October 1993 * * Hardware access routines and probe logic rewritten by * Kazutaka Yokota * - 3, 14, 22 October 1996. * - 12 November 1996. IOCTLs and rearranging `psmread', `psmioctl'... * - 14, 30 November 1996. Uses `kbdio.c'. * - 13 December 1996. Uses queuing version of `kbdio.c'. * - January/February 1997. Tweaked probe logic for * HiNote UltraII/Latitude/Armada laptops. * - 30 July 1997. Added APM support. * - 5 March 1997. Defined driver configuration flags (PSM_CONFIG_XXX). * Improved sync check logic. * Vendor specific support routines. */ -#include "psm.h" #include "opt_psm.h" -#if NPSM > 0 - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Driver specific options: the following options may be set by * `options' statements in the kernel configuration file. */ /* debugging */ #ifndef PSM_DEBUG #define PSM_DEBUG 0 /* logging: 0: none, 1: brief, 2: verbose */ #endif /* features */ /* #define PSM_HOOKRESUME hook the system resume event */ /* #define PSM_RESETAFTERSUSPEND reset the device at the resume event */ #ifdef PSM_HOOKAPM #undef PSM_HOOKRESUME #define PSM_HOOKRESUME 1 #endif /* PSM_HOOKAPM */ #ifndef PSM_HOOKRESUME #undef PSM_RESETAFTERSUSPEND #endif /* PSM_HOOKRESUME */ /* end of driver specific options */ /* input queue */ #define PSM_BUFSIZE 960 #define PSM_SMALLBUFSIZE 240 /* operation levels */ #define PSM_LEVEL_BASE 0 #define PSM_LEVEL_STANDARD 1 #define PSM_LEVEL_NATIVE 2 #define PSM_LEVEL_MIN PSM_LEVEL_BASE #define PSM_LEVEL_MAX PSM_LEVEL_NATIVE /* Logitech PS2++ protocol */ #define MOUSE_PS2PLUS_CHECKBITS(b) \ ((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f)) #define MOUSE_PS2PLUS_PACKET_TYPE(b) \ (((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4)) /* some macros */ #define PSM_UNIT(dev) (minor(dev) >> 1) #define PSM_NBLOCKIO(dev) (minor(dev) & 1) #define PSM_MKMINOR(unit,block) (((unit) << 1) | ((block) ? 0:1)) #ifndef max #define max(x,y) ((x) > (y) ? (x) : (y)) #endif #ifndef min #define min(x,y) ((x) < (y) ? (x) : (y)) #endif #define abs(x) (((x) < 0) ? -(x) : (x)) /* ring buffer */ typedef struct ringbuf { int count; /* # of valid elements in the buffer */ int head; /* head pointer */ int tail; /* tail poiner */ unsigned char buf[PSM_BUFSIZE]; } ringbuf_t; /* driver control block */ struct psm_softc { /* Driver status information */ struct selinfo rsel; /* Process selecting for Input */ unsigned char state; /* Mouse driver state */ int config; /* driver configuration flags */ int flags; /* other flags */ KBDC kbdc; /* handle to access the keyboard controller */ int addr; /* I/O port address */ mousehw_t hw; /* hardware information */ mousemode_t mode; /* operation mode */ mousemode_t dflt_mode; /* default operation mode */ mousestatus_t status; /* accumulated mouse movement */ ringbuf_t queue; /* mouse status queue */ unsigned char ipacket[16]; /* interim input buffer */ int inputbytes; /* # of bytes in the input buffer */ int button; /* the latest button state */ int xold; /* previous absolute X position */ int yold; /* previous absolute Y position */ }; devclass_t psm_devclass; #define PSM_SOFTC(unit) ((struct psm_softc*)devclass_get_softc(psm_devclass, unit)) /* driver state flags (state) */ #define PSM_VALID 0x80 #define PSM_OPEN 1 /* Device is open */ #define PSM_ASLP 2 /* Waiting for mouse data */ /* driver configuration flags (config) */ #define PSM_CONFIG_RESOLUTION 0x000f /* resolution */ #define PSM_CONFIG_ACCEL 0x00f0 /* acceleration factor */ #define PSM_CONFIG_NOCHECKSYNC 0x0100 /* disable sync. test */ #define PSM_CONFIG_NOIDPROBE 0x0200 /* disable mouse model probe */ #define PSM_CONFIG_NORESET 0x0400 /* don't reset the mouse */ #define PSM_CONFIG_FORCETAP 0x0800 /* assume `tap' action exists */ #define PSM_CONFIG_IGNPORTERROR 0x1000 /* ignore error in aux port test */ #define PSM_CONFIG_FLAGS (PSM_CONFIG_RESOLUTION \ | PSM_CONFIG_ACCEL \ | PSM_CONFIG_NOCHECKSYNC \ | PSM_CONFIG_NOIDPROBE \ | PSM_CONFIG_NORESET \ | PSM_CONFIG_FORCETAP \ | PSM_CONFIG_IGNPORTERROR) /* other flags (flags) */ #define PSM_FLAGS_FINGERDOWN 0x0001 /* VersaPad finger down */ /* for backward compatibility */ #define OLD_MOUSE_GETHWINFO _IOR('M', 1, old_mousehw_t) #define OLD_MOUSE_GETMODE _IOR('M', 2, old_mousemode_t) #define OLD_MOUSE_SETMODE _IOW('M', 3, old_mousemode_t) typedef struct old_mousehw { int buttons; int iftype; int type; int hwid; } old_mousehw_t; typedef struct old_mousemode { int protocol; int rate; int resolution; int accelfactor; } old_mousemode_t; /* packet formatting function */ typedef int packetfunc_t __P((struct psm_softc *, unsigned char *, int *, int, mousestatus_t *)); /* function prototypes */ static int psmprobe __P((device_t)); static int psmattach __P((device_t)); static int psmresume __P((device_t)); static d_open_t psmopen; static d_close_t psmclose; static d_read_t psmread; static d_ioctl_t psmioctl; static d_poll_t psmpoll; static int enable_aux_dev __P((KBDC)); static int disable_aux_dev __P((KBDC)); static int get_mouse_status __P((KBDC, int *, int, int)); static int get_aux_id __P((KBDC)); static int set_mouse_sampling_rate __P((KBDC, int)); static int set_mouse_scaling __P((KBDC, int)); static int set_mouse_resolution __P((KBDC, int)); #ifdef PSM_RESETAFTERSUSPEND static int set_mouse_mode __P((KBDC)); #endif /* PSM_RESETAFTERSUSPEND */ static int get_mouse_buttons __P((KBDC)); static int is_a_mouse __P((int)); static void recover_from_error __P((KBDC)); static int restore_controller __P((KBDC, int)); #ifdef PSM_RESETAFTERSUSPEND static int reinitialize __P((int, mousemode_t *)); #endif static int doopen __P((int, int)); static char *model_name(int); static void psmintr(void*); /* vendor specific features */ typedef int probefunc_t __P((struct psm_softc *)); static int mouse_id_proc1 __P((KBDC, int, int, int *)); static probefunc_t enable_groller; static probefunc_t enable_gmouse; static probefunc_t enable_aglide; static probefunc_t enable_kmouse; static probefunc_t enable_msintelli; static probefunc_t enable_mmanplus; static probefunc_t enable_versapad; static int tame_mouse __P((struct psm_softc *, mousestatus_t *, unsigned char *)); static struct { int model; unsigned char syncmask; int packetsize; probefunc_t *probefunc; } vendortype[] = { { MOUSE_MODEL_NET, /* Genius NetMouse */ 0xc8, MOUSE_INTELLI_PACKETSIZE, enable_gmouse, }, { MOUSE_MODEL_NETSCROLL, /* Genius NetScroll */ 0xc8, 6, enable_groller, }, { MOUSE_MODEL_GLIDEPOINT, /* ALPS GlidePoint */ 0xc0, MOUSE_PS2_PACKETSIZE, enable_aglide, }, { MOUSE_MODEL_MOUSEMANPLUS, /* Logitech MouseMan+ */ 0x08, MOUSE_PS2_PACKETSIZE, enable_mmanplus, }, { MOUSE_MODEL_THINK, /* Kensignton ThinkingMouse */ 0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse, }, { MOUSE_MODEL_INTELLI, /* Microsoft IntelliMouse */ 0xc8, MOUSE_INTELLI_PACKETSIZE, enable_msintelli, }, { MOUSE_MODEL_VERSAPAD, /* Interlink electronics VersaPad */ 0xe8, MOUSE_PS2VERSA_PACKETSIZE, enable_versapad, }, { MOUSE_MODEL_GENERIC, 0xc0, MOUSE_PS2_PACKETSIZE, NULL, }, }; #define GENERIC_MOUSE_ENTRY 7 /* device driver declarateion */ static device_method_t psm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, psmprobe), DEVMETHOD(device_attach, psmattach), DEVMETHOD(device_resume, psmresume), { 0, 0 } }; static driver_t psm_driver = { "psm", psm_methods, sizeof(struct psm_softc), }; #define CDEV_MAJOR 21 static struct cdevsw psm_cdevsw = { /* open */ psmopen, /* close */ psmclose, /* read */ psmread, /* write */ nowrite, /* ioctl */ psmioctl, /* poll */ psmpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "psm", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; /* debug message level */ static int verbose = PSM_DEBUG; /* device I/O routines */ static int enable_aux_dev(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_ENABLE_DEV); if (verbose >= 2) log(LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res); return (res == PSM_ACK); } static int disable_aux_dev(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_DISABLE_DEV); if (verbose >= 2) log(LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res); return (res == PSM_ACK); } static int get_mouse_status(KBDC kbdc, int *status, int flag, int len) { int cmd; int res; int i; switch (flag) { case 0: default: cmd = PSMC_SEND_DEV_STATUS; break; case 1: cmd = PSMC_SEND_DEV_DATA; break; } empty_aux_buffer(kbdc, 5); res = send_aux_command(kbdc, cmd); if (verbose >= 2) log(LOG_DEBUG, "psm: SEND_AUX_DEV_%s return code:%04x\n", (flag == 1) ? "DATA" : "STATUS", res); if (res != PSM_ACK) return 0; for (i = 0; i < len; ++i) { status[i] = read_aux_data(kbdc); if (status[i] < 0) break; } if (verbose) { log(LOG_DEBUG, "psm: %s %02x %02x %02x\n", (flag == 1) ? "data" : "status", status[0], status[1], status[2]); } return i; } static int get_aux_id(KBDC kbdc) { int res; int id; empty_aux_buffer(kbdc, 5); res = send_aux_command(kbdc, PSMC_SEND_DEV_ID); if (verbose >= 2) log(LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res); if (res != PSM_ACK) return (-1); /* 10ms delay */ DELAY(10000); id = read_aux_data(kbdc); if (verbose >= 2) log(LOG_DEBUG, "psm: device ID: %04x\n", id); return id; } static int set_mouse_sampling_rate(KBDC kbdc, int rate) { int res; res = send_aux_command_and_data(kbdc, PSMC_SET_SAMPLING_RATE, rate); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res); return ((res == PSM_ACK) ? rate : -1); } static int set_mouse_scaling(KBDC kbdc, int scale) { int res; switch (scale) { case 1: default: scale = PSMC_SET_SCALING11; break; case 2: scale = PSMC_SET_SCALING21; break; } res = send_aux_command(kbdc, scale); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_SCALING%s return code:%04x\n", (scale == PSMC_SET_SCALING21) ? "21" : "11", res); return (res == PSM_ACK); } /* `val' must be 0 through PSMD_MAX_RESOLUTION */ static int set_mouse_resolution(KBDC kbdc, int val) { int res; res = send_aux_command_and_data(kbdc, PSMC_SET_RESOLUTION, val); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n", val, res); return ((res == PSM_ACK) ? val : -1); } #ifdef PSM_RESETAFTERSUSPEND /* * NOTE: once `set_mouse_mode()' is called, the mouse device must be * re-enabled by calling `enable_aux_dev()' */ static int set_mouse_mode(KBDC kbdc) { int res; res = send_aux_command(kbdc, PSMC_SET_STREAM_MODE); if (verbose >= 2) log(LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res); return (res == PSM_ACK); } #endif /* PSM_RESETAFTERSUSPEND */ static int get_mouse_buttons(KBDC kbdc) { int c = 2; /* assume two buttons by default */ int status[3]; /* * NOTE: a special sequence to obtain Logitech Mouse specific * information: set resolution to 25 ppi, set scaling to 1:1, set * scaling to 1:1, set scaling to 1:1. Then the second byte of the * mouse status bytes is the number of available buttons. * Some manufactures also support this sequence. */ if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW) return c; if (set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1) && (get_mouse_status(kbdc, status, 0, 3) >= 3)) { if (status[1] != 0) return status[1]; } return c; } /* misc subroutines */ /* * Someday, I will get the complete list of valid pointing devices and * their IDs... XXX */ static int is_a_mouse(int id) { #if 0 static int valid_ids[] = { PSM_MOUSE_ID, /* mouse */ PSM_BALLPOINT_ID, /* ballpoint device */ PSM_INTELLI_ID, /* Intellimouse */ -1 /* end of table */ }; int i; for (i = 0; valid_ids[i] >= 0; ++i) if (valid_ids[i] == id) return TRUE; return FALSE; #else return TRUE; #endif } static char * model_name(int model) { static struct { int model_code; char *model_name; } models[] = { { MOUSE_MODEL_NETSCROLL, "NetScroll Mouse" }, { MOUSE_MODEL_NET, "NetMouse" }, { MOUSE_MODEL_GLIDEPOINT, "GlidePoint" }, { MOUSE_MODEL_THINK, "ThinkingMouse" }, { MOUSE_MODEL_INTELLI, "IntelliMouse" }, { MOUSE_MODEL_MOUSEMANPLUS, "MouseMan+" }, { MOUSE_MODEL_VERSAPAD, "VersaPad" }, { MOUSE_MODEL_GENERIC, "Generic PS/2 mouse" }, { MOUSE_MODEL_UNKNOWN, NULL }, }; int i; for (i = 0; models[i].model_code != MOUSE_MODEL_UNKNOWN; ++i) { if (models[i].model_code == model) return models[i].model_name; } return "Unknown"; } static void recover_from_error(KBDC kbdc) { /* discard anything left in the output buffer */ empty_both_buffers(kbdc, 10); #if 0 /* * NOTE: KBDC_RESET_KBD may not restore the communication between the * keyboard and the controller. */ reset_kbd(kbdc); #else /* * NOTE: somehow diagnostic and keyboard port test commands bring the * keyboard back. */ if (!test_controller(kbdc)) log(LOG_ERR, "psm: keyboard controller failed.\n"); /* if there isn't a keyboard in the system, the following error is OK */ if (test_kbd_port(kbdc) != 0) { if (verbose) log(LOG_ERR, "psm: keyboard port failed.\n"); } #endif } static int restore_controller(KBDC kbdc, int command_byte) { empty_both_buffers(kbdc, 10); if (!set_controller_command_byte(kbdc, 0xff, command_byte)) { log(LOG_ERR, "psm: failed to restore the keyboard controller " "command byte.\n"); return FALSE; } else { return TRUE; } } #ifdef PSM_RESETAFTERSUSPEND /* * Re-initialize the aux port and device. The aux port must be enabled * and its interrupt must be disabled before calling this routine. * The aux device will be disabled before returning. * The keyboard controller must be locked via `kbdc_lock()' before * calling this routine. */ static int reinitialize(int unit, mousemode_t *mode) { struct psm_softc *sc = PSM_SOFTC(unit); KBDC kbdc = sc->kbdc; int stat[3]; int i; switch((i = test_aux_port(kbdc))) { case 1: /* ignore this error */ case PSM_ACK: if (verbose) log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n", unit, i); /* fall though */ case 0: /* no error */ break; case -1: /* time out */ default: /* error */ recover_from_error(kbdc); if (sc->config & PSM_CONFIG_IGNPORTERROR) break; log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n", unit, i); return FALSE; } if (sc->config & PSM_CONFIG_NORESET) { /* * Don't try to reset the pointing device. It may possibly be * left in the unknown state, though... */ } else { /* * NOTE: some controllers appears to hang the `keyboard' when * the aux port doesn't exist and `PSMC_RESET_DEV' is issued. */ if (!reset_aux_dev(kbdc)) { recover_from_error(kbdc); log(LOG_ERR, "psm%d: failed to reset the aux device.\n", unit); return FALSE; } } /* * both the aux port and the aux device is functioning, see * if the device can be enabled. */ if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) { log(LOG_ERR, "psm%d: failed to enable the aux device.\n", unit); return FALSE; } empty_both_buffers(kbdc, 10); /* remove stray data if any */ if (sc->config & PSM_CONFIG_NOIDPROBE) { i = GENERIC_MOUSE_ENTRY; } else { /* FIXME: hardware ID, mouse buttons? */ /* other parameters */ for (i = 0; vendortype[i].probefunc != NULL; ++i) { if ((*vendortype[i].probefunc)(sc)) { if (verbose >= 2) log(LOG_ERR, "psm%d: found %s\n", unit, model_name(vendortype[i].model)); break; } } } sc->hw.model = vendortype[i].model; sc->mode.packetsize = vendortype[i].packetsize; /* set mouse parameters */ if (mode != (mousemode_t *)NULL) { if (mode->rate > 0) mode->rate = set_mouse_sampling_rate(kbdc, mode->rate); if (mode->resolution >= 0) mode->resolution = set_mouse_resolution(kbdc, mode->resolution); set_mouse_scaling(kbdc, 1); set_mouse_mode(kbdc); } /* request a data packet and extract sync. bits */ if (get_mouse_status(kbdc, stat, 1, 3) < 3) { log(LOG_DEBUG, "psm%d: failed to get data (reinitialize).\n", unit); sc->mode.syncmask[0] = 0; } else { sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */ /* the NetScroll Mouse will send three more bytes... Ignore them */ empty_aux_buffer(kbdc, 5); } /* just check the status of the mouse */ if (get_mouse_status(kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (reinitialize).\n", unit); return TRUE; } #endif /* PSM_RESETAFTERSUSPEND */ static int doopen(int unit, int command_byte) { struct psm_softc *sc = PSM_SOFTC(unit); int stat[3]; /* enable the mouse device */ if (!enable_aux_dev(sc->kbdc)) { /* MOUSE ERROR: failed to enable the mouse because: * 1) the mouse is faulty, * 2) the mouse has been removed(!?) * In the latter case, the keyboard may have hung, and need * recovery procedure... */ recover_from_error(sc->kbdc); #if 0 /* FIXME: we could reset the mouse here and try to enable * it again. But it will take long time and it's not a good * idea to disable the keyboard that long... */ if (!reinitialize(unit, &sc->mode) || !enable_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); #else { #endif restore_controller(sc->kbdc, command_byte); /* mark this device is no longer available */ sc->state &= ~PSM_VALID; log(LOG_ERR, "psm%d: failed to enable the device (doopen).\n", unit); return (EIO); } } if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", unit); /* enable the aux port and interrupt */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (command_byte & KBD_KBD_CONTROL_BITS) | KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) { /* CONTROLLER ERROR */ disable_aux_dev(sc->kbdc); restore_controller(sc->kbdc, command_byte); log(LOG_ERR, "psm%d: failed to enable the aux interrupt (doopen).\n", unit); return (EIO); } return (0); } /* psm driver entry points */ #define endprobe(v) { if (bootverbose) \ --verbose; \ kbdc_set_device_mask(sc->kbdc, mask); \ kbdc_lock(sc->kbdc, FALSE); \ free(sc, M_DEVBUF); \ return (v); \ } static int psmprobe(device_t dev) { int unit = device_get_unit(dev); struct psm_softc *sc = device_get_softc(dev); uintptr_t port; uintptr_t flags; int stat[3]; int command_byte; int mask; int i; #if 0 kbdc_debug(TRUE); #endif BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); sc->addr = port; sc->kbdc = kbdc_open(sc->addr); sc->config = flags & PSM_CONFIG_FLAGS; sc->flags = 0; if (bootverbose) ++verbose; device_set_desc(dev, "PS/2 Mouse"); if (!kbdc_lock(sc->kbdc, TRUE)) { printf("psm%d: unable to lock the controller.\n", unit); if (bootverbose) --verbose; return (ENXIO); } /* * NOTE: two bits in the command byte controls the operation of the * aux port (mouse port): the aux port disable bit (bit 5) and the aux * port interrupt (IRQ 12) enable bit (bit 2). */ /* discard anything left after the keyboard initialization */ empty_both_buffers(sc->kbdc, 10); /* save the current command byte; it will be used later */ mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS; command_byte = get_controller_command_byte(sc->kbdc); if (verbose) printf("psm%d: current command byte:%04x\n", unit, command_byte); if (command_byte == -1) { /* CONTROLLER ERROR */ printf("psm%d: unable to get the current command byte value.\n", unit); endprobe(ENXIO); } /* * disable the keyboard port while probing the aux port, which must be * enabled during this routine */ if (!set_controller_command_byte(sc->kbdc, KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS, KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * this is CONTROLLER ERROR; I don't know how to recover * from this error... */ restore_controller(sc->kbdc, command_byte); printf("psm%d: unable to set the command byte.\n", unit); endprobe(ENXIO); } write_controller_command(sc->kbdc, KBDC_ENABLE_AUX_PORT); /* * NOTE: `test_aux_port()' is designed to return with zero if the aux * port exists and is functioning. However, some controllers appears * to respond with zero even when the aux port doesn't exist. (It may * be that this is only the case when the controller DOES have the aux * port but the port is not wired on the motherboard.) The keyboard * controllers without the port, such as the original AT, are * supporsed to return with an error code or simply time out. In any * case, we have to continue probing the port even when the controller * passes this test. * * XXX: some controllers erroneously return the error code 1 when * it has the perfectly functional aux port. We have to ignore this * error code. Even if the controller HAS error with the aux port, * it will be detected later... * XXX: another incompatible controller returns PSM_ACK (0xfa)... */ switch ((i = test_aux_port(sc->kbdc))) { case 1: /* ignore this error */ case PSM_ACK: if (verbose) printf("psm%d: strange result for test aux port (%d).\n", unit, i); /* fall though */ case 0: /* no error */ break; case -1: /* time out */ default: /* error */ recover_from_error(sc->kbdc); if (sc->config & PSM_CONFIG_IGNPORTERROR) break; restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: the aux port is not functioning (%d).\n", unit, i); endprobe(ENXIO); } if (sc->config & PSM_CONFIG_NORESET) { /* * Don't try to reset the pointing device. It may possibly be * left in the unknown state, though... */ } else { /* * NOTE: some controllers appears to hang the `keyboard' when the aux * port doesn't exist and `PSMC_RESET_DEV' is issued. */ if (!reset_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: failed to reset the aux device.\n", unit); endprobe(ENXIO); } } /* * both the aux port and the aux device is functioning, see if the * device can be enabled. NOTE: when enabled, the device will start * sending data; we shall immediately disable the device once we know * the device can be enabled. */ if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) { /* MOUSE ERROR */ recover_from_error(sc->kbdc); restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: failed to enable the aux device.\n", unit); endprobe(ENXIO); } /* save the default values after reset */ if (get_mouse_status(sc->kbdc, stat, 0, 3) >= 3) { sc->dflt_mode.rate = sc->mode.rate = stat[2]; sc->dflt_mode.resolution = sc->mode.resolution = stat[1]; } else { sc->dflt_mode.rate = sc->mode.rate = -1; sc->dflt_mode.resolution = sc->mode.resolution = -1; } /* hardware information */ sc->hw.iftype = MOUSE_IF_PS2; /* verify the device is a mouse */ sc->hw.hwid = get_aux_id(sc->kbdc); if (!is_a_mouse(sc->hw.hwid)) { restore_controller(sc->kbdc, command_byte); if (verbose) printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid); endprobe(ENXIO); } switch (sc->hw.hwid) { case PSM_BALLPOINT_ID: sc->hw.type = MOUSE_TRACKBALL; break; case PSM_MOUSE_ID: case PSM_INTELLI_ID: sc->hw.type = MOUSE_MOUSE; break; default: sc->hw.type = MOUSE_UNKNOWN; break; } if (sc->config & PSM_CONFIG_NOIDPROBE) { sc->hw.buttons = 2; i = GENERIC_MOUSE_ENTRY; } else { /* # of buttons */ sc->hw.buttons = get_mouse_buttons(sc->kbdc); /* other parameters */ for (i = 0; vendortype[i].probefunc != NULL; ++i) { if ((*vendortype[i].probefunc)(sc)) { if (verbose >= 2) printf("psm%d: found %s\n", unit, model_name(vendortype[i].model)); break; } } } sc->hw.model = vendortype[i].model; sc->dflt_mode.level = PSM_LEVEL_BASE; sc->dflt_mode.packetsize = MOUSE_PS2_PACKETSIZE; sc->dflt_mode.accelfactor = (sc->config & PSM_CONFIG_ACCEL) >> 4; if (sc->config & PSM_CONFIG_NOCHECKSYNC) sc->dflt_mode.syncmask[0] = 0; else sc->dflt_mode.syncmask[0] = vendortype[i].syncmask; if (sc->config & PSM_CONFIG_FORCETAP) sc->mode.syncmask[0] &= ~MOUSE_PS2_TAP; sc->dflt_mode.syncmask[1] = 0; /* syncbits */ sc->mode = sc->dflt_mode; sc->mode.packetsize = vendortype[i].packetsize; /* set mouse parameters */ #if 0 /* * A version of Logitech FirstMouse+ won't report wheel movement, * if SET_DEFAULTS is sent... Don't use this command. * This fix was found by Takashi Nishida. */ i = send_aux_command(sc->kbdc, PSMC_SET_DEFAULTS); if (verbose >= 2) printf("psm%d: SET_DEFAULTS return code:%04x\n", unit, i); #endif if (sc->config & PSM_CONFIG_RESOLUTION) { sc->mode.resolution = set_mouse_resolution(sc->kbdc, (sc->config & PSM_CONFIG_RESOLUTION) - 1); } else if (sc->mode.resolution >= 0) { sc->mode.resolution = set_mouse_resolution(sc->kbdc, sc->dflt_mode.resolution); } if (sc->mode.rate > 0) { sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, sc->dflt_mode.rate); } set_mouse_scaling(sc->kbdc, 1); /* request a data packet and extract sync. bits */ if (get_mouse_status(sc->kbdc, stat, 1, 3) < 3) { printf("psm%d: failed to get data.\n", unit); sc->mode.syncmask[0] = 0; } else { sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */ /* the NetScroll Mouse will send three more bytes... Ignore them */ empty_aux_buffer(sc->kbdc, 5); } /* just check the status of the mouse */ /* * NOTE: XXX there are some arcane controller/mouse combinations out * there, which hung the controller unless there is data transmission * after ACK from the mouse. */ if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) { printf("psm%d: failed to get status.\n", unit); } else { /* * When in its native mode, some mice operate with different * default parameters than in the PS/2 compatible mode. */ sc->dflt_mode.rate = sc->mode.rate = stat[2]; sc->dflt_mode.resolution = sc->mode.resolution = stat[1]; } /* disable the aux port for now... */ if (!set_controller_command_byte(sc->kbdc, KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS, (command_byte & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* * this is CONTROLLER ERROR; I don't know the proper way to * recover from this error... */ restore_controller(sc->kbdc, command_byte); printf("psm%d: unable to set the command byte.\n", unit); endprobe(ENXIO); } /* done */ kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS); kbdc_lock(sc->kbdc, FALSE); return (0); } static int psmattach(device_t dev) { int unit = device_get_unit(dev); struct psm_softc *sc = device_get_softc(dev); void *ih; struct resource *res; uintptr_t irq; int zero = 0; if (sc == NULL) /* shouldn't happen */ return (ENXIO); /* Setup initial state */ sc->state = PSM_VALID; /* Done */ make_dev(&psm_cdevsw, PSM_MKMINOR(unit, FALSE), 0, 0, 0666, "psm%d", unit); make_dev(&psm_cdevsw, PSM_MKMINOR(unit, TRUE), 0, 0, 0666, "bpsm%d", unit); if (!verbose) { printf("psm%d: model %s, device ID %d\n", unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff); } else { printf("psm%d: model %s, device ID %d-%02x, %d buttons\n", unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff, sc->hw.hwid >> 8, sc->hw.buttons); printf("psm%d: config:%08x, flags:%08x, packet size:%d\n", unit, sc->config, sc->flags, sc->mode.packetsize); printf("psm%d: syncmask:%02x, syncbits:%02x\n", unit, sc->mode.syncmask[0], sc->mode.syncmask[1]); } if (bootverbose) --verbose; BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1, RF_SHAREABLE | RF_ACTIVE); BUS_SETUP_INTR(device_get_parent(dev), dev, res, INTR_TYPE_TTY, psmintr, sc, &ih); return (0); } static int psmopen(dev_t dev, int flag, int fmt, struct proc *p) { int unit = PSM_UNIT(dev); struct psm_softc *sc; int command_byte; int err; int s; - /* Validate unit number */ - if (unit >= NPSM) - return (ENXIO); - /* Get device data */ sc = PSM_SOFTC(unit); if ((sc == NULL) || (sc->state & PSM_VALID) == 0) /* the device is no longer valid/functioning */ return (ENXIO); /* Disallow multiple opens */ if (sc->state & PSM_OPEN) return (EBUSY); device_busy(devclass_get_device(psm_devclass, unit)); /* Initialize state */ sc->rsel.si_flags = 0; sc->rsel.si_pid = 0; sc->mode.level = sc->dflt_mode.level; sc->mode.protocol = sc->dflt_mode.protocol; /* flush the event queue */ sc->queue.count = 0; sc->queue.head = 0; sc->queue.tail = 0; sc->status.flags = 0; sc->status.button = 0; sc->status.obutton = 0; sc->status.dx = 0; sc->status.dy = 0; sc->status.dz = 0; sc->button = 0; /* empty input buffer */ bzero(sc->ipacket, sizeof(sc->ipacket)); sc->inputbytes = 0; /* don't let timeout routines in the keyboard driver to poll the kbdc */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); /* save the current controller command byte */ s = spltty(); command_byte = get_controller_command_byte(sc->kbdc); /* enable the aux port and temporalily disable the keyboard */ if ((command_byte == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR; do you know how to get out of this? */ kbdc_lock(sc->kbdc, FALSE); splx(s); log(LOG_ERR, "psm%d: unable to set the command byte (psmopen).\n", unit); return (EIO); } /* * Now that the keyboard controller is told not to generate * the keyboard and mouse interrupts, call `splx()' to allow * the other tty interrupts. The clock interrupt may also occur, * but timeout routines will be blocked by the poll flag set * via `kbdc_lock()' */ splx(s); /* enable the mouse device */ err = doopen(unit, command_byte); /* done */ if (err == 0) sc->state |= PSM_OPEN; kbdc_lock(sc->kbdc, FALSE); return (err); } static int psmclose(dev_t dev, int flag, int fmt, struct proc *p) { int unit = PSM_UNIT(dev); struct psm_softc *sc = PSM_SOFTC(unit); int stat[3]; int command_byte; int s; /* don't let timeout routines in the keyboard driver to poll the kbdc */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); /* save the current controller command byte */ s = spltty(); command_byte = get_controller_command_byte(sc->kbdc); if (command_byte == -1) { kbdc_lock(sc->kbdc, FALSE); splx(s); return (EIO); } /* disable the aux interrupt and temporalily disable the keyboard */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { log(LOG_ERR, "psm%d: failed to disable the aux int (psmclose).\n", PSM_UNIT(dev)); /* CONTROLLER ERROR; * NOTE: we shall force our way through. Because the only * ill effect we shall see is that we may not be able * to read ACK from the mouse, and it doesn't matter much * so long as the mouse will accept the DISABLE command. */ } splx(s); /* remove anything left in the output buffer */ empty_aux_buffer(sc->kbdc, 10); /* disable the aux device, port and interrupt */ if (sc->state & PSM_VALID) { if (!disable_aux_dev(sc->kbdc)) { /* MOUSE ERROR; * NOTE: we don't return error and continue, pretending * we have successfully disabled the device. It's OK because * the interrupt routine will discard any data from the mouse * hereafter. */ log(LOG_ERR, "psm%d: failed to disable the device (psmclose).\n", PSM_UNIT(dev)); } if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) log(LOG_DEBUG, "psm%d: failed to get status (psmclose).\n", PSM_UNIT(dev)); } if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (command_byte & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR; * we shall ignore this error; see the above comment. */ log(LOG_ERR, "psm%d: failed to disable the aux port (psmclose).\n", PSM_UNIT(dev)); } /* remove anything left in the output buffer */ empty_aux_buffer(sc->kbdc, 10); /* close is almost always successful */ sc->state &= ~PSM_OPEN; kbdc_lock(sc->kbdc, FALSE); device_unbusy(devclass_get_device(psm_devclass, unit)); return (0); } static int tame_mouse(struct psm_softc *sc, mousestatus_t *status, unsigned char *buf) { static unsigned char butmapps2[8] = { 0, MOUSE_PS2_BUTTON1DOWN, MOUSE_PS2_BUTTON2DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN, MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN, }; static unsigned char butmapmsc[8] = { MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON1UP, 0, }; int mapped; int i; if (sc->mode.level == PSM_LEVEL_BASE) { mapped = status->button & ~MOUSE_BUTTON4DOWN; if (status->button & MOUSE_BUTTON4DOWN) mapped |= MOUSE_BUTTON1DOWN; status->button = mapped; buf[0] = MOUSE_PS2_SYNC | butmapps2[mapped & MOUSE_STDBUTTONS]; i = max(min(status->dx, 255), -256); if (i < 0) buf[0] |= MOUSE_PS2_XNEG; buf[1] = i; i = max(min(status->dy, 255), -256); if (i < 0) buf[0] |= MOUSE_PS2_YNEG; buf[2] = i; return MOUSE_PS2_PACKETSIZE; } else if (sc->mode.level == PSM_LEVEL_STANDARD) { buf[0] = MOUSE_MSC_SYNC | butmapmsc[status->button & MOUSE_STDBUTTONS]; i = max(min(status->dx, 255), -256); buf[1] = i >> 1; buf[3] = i - buf[1]; i = max(min(status->dy, 255), -256); buf[2] = i >> 1; buf[4] = i - buf[2]; i = max(min(status->dz, 127), -128); buf[5] = (i >> 1) & 0x7f; buf[6] = (i - (i >> 1)) & 0x7f; buf[7] = (~status->button >> 3) & 0x7f; return MOUSE_SYS_PACKETSIZE; } return sc->inputbytes;; } static int psmread(dev_t dev, struct uio *uio, int flag) { register struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); unsigned char buf[PSM_SMALLBUFSIZE]; int error = 0; int s; int l; if ((sc->state & PSM_VALID) == 0) return EIO; /* block until mouse activity occured */ s = spltty(); while (sc->queue.count <= 0) { if (PSM_NBLOCKIO(dev)) { splx(s); return EWOULDBLOCK; } sc->state |= PSM_ASLP; error = tsleep((caddr_t) sc, PZERO | PCATCH, "psmrea", 0); sc->state &= ~PSM_ASLP; if (error) { splx(s); return error; } else if ((sc->state & PSM_VALID) == 0) { /* the device disappeared! */ splx(s); return EIO; } } splx(s); /* copy data to the user land */ while ((sc->queue.count > 0) && (uio->uio_resid > 0)) { s = spltty(); l = min(sc->queue.count, uio->uio_resid); if (l > sizeof(buf)) l = sizeof(buf); if (l > sizeof(sc->queue.buf) - sc->queue.head) { bcopy(&sc->queue.buf[sc->queue.head], &buf[0], sizeof(sc->queue.buf) - sc->queue.head); bcopy(&sc->queue.buf[0], &buf[sizeof(sc->queue.buf) - sc->queue.head], l - (sizeof(sc->queue.buf) - sc->queue.head)); } else { bcopy(&sc->queue.buf[sc->queue.head], &buf[0], l); } sc->queue.count -= l; sc->queue.head = (sc->queue.head + l) % sizeof(sc->queue.buf); splx(s); error = uiomove(buf, l, uio); if (error) break; } return error; } static int block_mouse_data(struct psm_softc *sc, int *c) { int s; if (!kbdc_lock(sc->kbdc, TRUE)) return EIO; s = spltty(); *c = get_controller_command_byte(sc->kbdc); if ((*c == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* this is CONTROLLER ERROR */ splx(s); kbdc_lock(sc->kbdc, FALSE); return EIO; } /* * The device may be in the middle of status data transmission. * The transmission will be interrupted, thus, incomplete status * data must be discarded. Although the aux interrupt is disabled * at the keyboard controller level, at most one aux interrupt * may have already been pending and a data byte is in the * output buffer; throw it away. Note that the second argument * to `empty_aux_buffer()' is zero, so that the call will just * flush the internal queue. * `psmintr()' will be invoked after `splx()' if an interrupt is * pending; it will see no data and returns immediately. */ empty_aux_buffer(sc->kbdc, 0); /* flush the queue */ read_aux_data_no_wait(sc->kbdc); /* throw away data if any */ sc->inputbytes = 0; splx(s); return 0; } static int unblock_mouse_data(struct psm_softc *sc, int c) { int error = 0; /* * We may have seen a part of status data during `set_mouse_XXX()'. * they have been queued; flush it. */ empty_aux_buffer(sc->kbdc, 0); /* restore ports and interrupt */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR; this is serious, we may have * been left with the inaccessible keyboard and * the disabled mouse interrupt. */ error = EIO; } kbdc_lock(sc->kbdc, FALSE); return error; } static int psmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); mousemode_t mode; mousestatus_t status; #if (defined(MOUSE_GETVARS)) mousevar_t *var; #endif mousedata_t *data; int stat[3]; int command_byte; int error = 0; int s; /* Perform IOCTL command */ switch (cmd) { case OLD_MOUSE_GETHWINFO: s = spltty(); ((old_mousehw_t *)addr)->buttons = sc->hw.buttons; ((old_mousehw_t *)addr)->iftype = sc->hw.iftype; ((old_mousehw_t *)addr)->type = sc->hw.type; ((old_mousehw_t *)addr)->hwid = sc->hw.hwid & 0x00ff; splx(s); break; case MOUSE_GETHWINFO: s = spltty(); *(mousehw_t *)addr = sc->hw; if (sc->mode.level == PSM_LEVEL_BASE) ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC; splx(s); break; case OLD_MOUSE_GETMODE: s = spltty(); switch (sc->mode.level) { case PSM_LEVEL_BASE: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; case PSM_LEVEL_STANDARD: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; break; case PSM_LEVEL_NATIVE: ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; } ((old_mousemode_t *)addr)->rate = sc->mode.rate; ((old_mousemode_t *)addr)->resolution = sc->mode.resolution; ((old_mousemode_t *)addr)->accelfactor = sc->mode.accelfactor; splx(s); break; case MOUSE_GETMODE: s = spltty(); *(mousemode_t *)addr = sc->mode; ((mousemode_t *)addr)->resolution = MOUSE_RES_LOW - sc->mode.resolution; switch (sc->mode.level) { case PSM_LEVEL_BASE: ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; ((mousemode_t *)addr)->packetsize = MOUSE_PS2_PACKETSIZE; break; case PSM_LEVEL_STANDARD: ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE; ((mousemode_t *)addr)->packetsize = MOUSE_SYS_PACKETSIZE; ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK; ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC; break; case PSM_LEVEL_NATIVE: /* FIXME: this isn't quite correct... XXX */ ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2; break; } splx(s); break; case OLD_MOUSE_SETMODE: case MOUSE_SETMODE: if (cmd == OLD_MOUSE_SETMODE) { mode.rate = ((old_mousemode_t *)addr)->rate; /* * resolution old I/F new I/F * default 0 0 * low 1 -2 * medium low 2 -3 * medium high 3 -4 * high 4 -5 */ if (((old_mousemode_t *)addr)->resolution > 0) mode.resolution = -((old_mousemode_t *)addr)->resolution - 1; mode.accelfactor = ((old_mousemode_t *)addr)->accelfactor; mode.level = -1; } else { mode = *(mousemode_t *)addr; } /* adjust and validate parameters. */ if (mode.rate > UCHAR_MAX) return EINVAL; if (mode.rate == 0) mode.rate = sc->dflt_mode.rate; else if (mode.rate == -1) /* don't change the current setting */ ; else if (mode.rate < 0) return EINVAL; if (mode.resolution >= UCHAR_MAX) return EINVAL; if (mode.resolution >= 200) mode.resolution = MOUSE_RES_HIGH; else if (mode.resolution >= 100) mode.resolution = MOUSE_RES_MEDIUMHIGH; else if (mode.resolution >= 50) mode.resolution = MOUSE_RES_MEDIUMLOW; else if (mode.resolution > 0) mode.resolution = MOUSE_RES_LOW; if (mode.resolution == MOUSE_RES_DEFAULT) mode.resolution = sc->dflt_mode.resolution; else if (mode.resolution == -1) /* don't change the current setting */ ; else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */ mode.resolution = MOUSE_RES_LOW - mode.resolution; if (mode.level == -1) /* don't change the current setting */ mode.level = sc->mode.level; else if ((mode.level < PSM_LEVEL_MIN) || (mode.level > PSM_LEVEL_MAX)) return EINVAL; if (mode.accelfactor == -1) /* don't change the current setting */ mode.accelfactor = sc->mode.accelfactor; else if (mode.accelfactor < 0) return EINVAL; /* don't allow anybody to poll the keyboard controller */ error = block_mouse_data(sc, &command_byte); if (error) return error; /* set mouse parameters */ if (mode.rate > 0) mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate); if (mode.resolution >= 0) mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution); set_mouse_scaling(sc->kbdc, 1); get_mouse_status(sc->kbdc, stat, 0, 3); s = spltty(); sc->mode.rate = mode.rate; sc->mode.resolution = mode.resolution; sc->mode.accelfactor = mode.accelfactor; sc->mode.level = mode.level; splx(s); unblock_mouse_data(sc, command_byte); break; case MOUSE_GETLEVEL: *(int *)addr = sc->mode.level; break; case MOUSE_SETLEVEL: if ((*(int *)addr < PSM_LEVEL_MIN) || (*(int *)addr > PSM_LEVEL_MAX)) return EINVAL; sc->mode.level = *(int *)addr; break; case MOUSE_GETSTATUS: s = spltty(); status = sc->status; sc->status.flags = 0; sc->status.obutton = sc->status.button; sc->status.button = 0; sc->status.dx = 0; sc->status.dy = 0; sc->status.dz = 0; splx(s); *(mousestatus_t *)addr = status; break; #if (defined(MOUSE_GETVARS)) case MOUSE_GETVARS: var = (mousevar_t *)addr; bzero(var, sizeof(*var)); s = spltty(); var->var[0] = MOUSE_VARS_PS2_SIG; var->var[1] = sc->config; var->var[2] = sc->flags; splx(s); break; case MOUSE_SETVARS: return ENODEV; #endif /* MOUSE_GETVARS */ case MOUSE_READSTATE: case MOUSE_READDATA: data = (mousedata_t *)addr; if (data->len > sizeof(data->buf)/sizeof(data->buf[0])) return EINVAL; error = block_mouse_data(sc, &command_byte); if (error) return error; if ((data->len = get_mouse_status(sc->kbdc, data->buf, (cmd == MOUSE_READDATA) ? 1 : 0, data->len)) <= 0) error = EIO; unblock_mouse_data(sc, command_byte); break; #if (defined(MOUSE_SETRESOLUTION)) case MOUSE_SETRESOLUTION: mode.resolution = *(int *)addr; if (mode.resolution >= UCHAR_MAX) return EINVAL; else if (mode.resolution >= 200) mode.resolution = MOUSE_RES_HIGH; else if (mode.resolution >= 100) mode.resolution = MOUSE_RES_MEDIUMHIGH; else if (mode.resolution >= 50) mode.resolution = MOUSE_RES_MEDIUMLOW; else if (mode.resolution > 0) mode.resolution = MOUSE_RES_LOW; if (mode.resolution == MOUSE_RES_DEFAULT) mode.resolution = sc->dflt_mode.resolution; else if (mode.resolution == -1) mode.resolution = sc->mode.resolution; else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */ mode.resolution = MOUSE_RES_LOW - mode.resolution; error = block_mouse_data(sc, &command_byte); if (error) return error; sc->mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution); if (sc->mode.resolution != mode.resolution) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETRESOLUTION */ #if (defined(MOUSE_SETRATE)) case MOUSE_SETRATE: mode.rate = *(int *)addr; if (mode.rate > UCHAR_MAX) return EINVAL; if (mode.rate == 0) mode.rate = sc->dflt_mode.rate; else if (mode.rate < 0) mode.rate = sc->mode.rate; error = block_mouse_data(sc, &command_byte); if (error) return error; sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate); if (sc->mode.rate != mode.rate) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETRATE */ #if (defined(MOUSE_SETSCALING)) case MOUSE_SETSCALING: if ((*(int *)addr <= 0) || (*(int *)addr > 2)) return EINVAL; error = block_mouse_data(sc, &command_byte); if (error) return error; if (!set_mouse_scaling(sc->kbdc, *(int *)addr)) error = EIO; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_SETSCALING */ #if (defined(MOUSE_GETHWID)) case MOUSE_GETHWID: error = block_mouse_data(sc, &command_byte); if (error) return error; sc->hw.hwid &= ~0x00ff; sc->hw.hwid |= get_aux_id(sc->kbdc); *(int *)addr = sc->hw.hwid & 0x00ff; unblock_mouse_data(sc, command_byte); break; #endif /* MOUSE_GETHWID */ default: return ENOTTY; } return error; } static void psmintr(void *arg) { /* * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN) * into `mousestatus' button bits (MOUSE_BUTTON?DOWN). */ static int butmap[8] = { 0, MOUSE_BUTTON1DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN, MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN }; static int butmap_versapad[8] = { 0, MOUSE_BUTTON3DOWN, 0, MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN }; register struct psm_softc *sc = arg; mousestatus_t ms; int x, y, z; int c; int l; int x0, y0; /* read until there is nothing to read */ while((c = read_aux_data_no_wait(sc->kbdc)) != -1) { /* discard the byte if the device is not open */ if ((sc->state & PSM_OPEN) == 0) continue; /* * Check sync bits. We check for overflow bits and the bit 3 * for most mice. True, the code doesn't work if overflow * condition occurs. But we expect it rarely happens... */ if ((sc->inputbytes == 0) && ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1])) { log(LOG_DEBUG, "psmintr: out of sync (%04x != %04x).\n", c & sc->mode.syncmask[0], sc->mode.syncmask[1]); continue; } sc->ipacket[sc->inputbytes++] = c; if (sc->inputbytes < sc->mode.packetsize) continue; #if 0 log(LOG_DEBUG, "psmintr: %02x %02x %02x %02x %02x %02x\n", sc->ipacket[0], sc->ipacket[1], sc->ipacket[2], sc->ipacket[3], sc->ipacket[4], sc->ipacket[5]); #endif c = sc->ipacket[0]; /* * A kludge for Kensington device! * The MSB of the horizontal count appears to be stored in * a strange place. This kludge doesn't affect other mice * because the bit is the overflow bit which is, in most cases, * expected to be zero when we reach here. XXX */ if (sc->hw.model != MOUSE_MODEL_VERSAPAD) sc->ipacket[1] |= (c & MOUSE_PS2_XOVERFLOW) ? 0x80 : 0; /* ignore the overflow bits... */ x = (c & MOUSE_PS2_XNEG) ? sc->ipacket[1] - 256 : sc->ipacket[1]; y = (c & MOUSE_PS2_YNEG) ? sc->ipacket[2] - 256 : sc->ipacket[2]; z = 0; ms.obutton = sc->button; /* previous button state */ ms.button = butmap[c & MOUSE_PS2_BUTTONS]; /* `tapping' action */ if (sc->config & PSM_CONFIG_FORCETAP) ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN; switch (sc->hw.model) { case MOUSE_MODEL_INTELLI: case MOUSE_MODEL_NET: /* wheel data is in the fourth byte */ z = (char)sc->ipacket[3]; break; case MOUSE_MODEL_MOUSEMANPLUS: /* * PS2++ protocl packet * * b7 b6 b5 b4 b3 b2 b1 b0 * byte 1: * 1 p3 p2 1 * * * * byte 2: c1 c2 p1 p0 d1 d0 1 0 * * p3-p0: packet type * c1, c2: c1 & c2 == 1, if p2 == 0 * c1 & c2 == 0, if p2 == 1 * * packet type: 0 (device type) * See comments in enable_mmanplus() below. * * packet type: 1 (wheel data) * * b7 b6 b5 b4 b3 b2 b1 b0 * byte 3: h * B5 B4 s d2 d1 d0 * * h: 1, if horizontal roller data * 0, if vertical roller data * B4, B5: button 4 and 5 * s: sign bit * d2-d0: roller data * * packet type: 2 (reserved) */ if (((c & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC) && (abs(x) > 191) && MOUSE_PS2PLUS_CHECKBITS(sc->ipacket)) { /* the extended data packet encodes button and wheel events */ switch (MOUSE_PS2PLUS_PACKET_TYPE(sc->ipacket)) { case 1: /* wheel data packet */ x = y = 0; if (sc->ipacket[2] & 0x80) { /* horizontal roller count - ignore it XXX*/ } else { /* vertical roller count */ z = (sc->ipacket[2] & MOUSE_PS2PLUS_ZNEG) ? (sc->ipacket[2] & 0x0f) - 16 : (sc->ipacket[2] & 0x0f); } ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON4DOWN) ? MOUSE_BUTTON4DOWN : 0; ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON5DOWN) ? MOUSE_BUTTON5DOWN : 0; break; case 2: /* this packet type is reserved, and currently ignored */ /* FALL THROUGH */ case 0: /* device type packet - shouldn't happen */ /* FALL THROUGH */ default: x = y = 0; ms.button = ms.obutton; log(LOG_DEBUG, "psmintr: unknown PS2++ packet type %d: " "0x%02x 0x%02x 0x%02x\n", MOUSE_PS2PLUS_PACKET_TYPE(sc->ipacket), sc->ipacket[0], sc->ipacket[1], sc->ipacket[2]); break; } } else { /* preserve button states */ ms.button |= ms.obutton & MOUSE_EXTBUTTONS; } break; case MOUSE_MODEL_GLIDEPOINT: /* `tapping' action */ ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN; break; case MOUSE_MODEL_NETSCROLL: /* three addtional bytes encode button and wheel events */ ms.button |= (sc->ipacket[3] & MOUSE_PS2_BUTTON3DOWN) ? MOUSE_BUTTON4DOWN : 0; z = (sc->ipacket[3] & MOUSE_PS2_XNEG) ? sc->ipacket[4] - 256 : sc->ipacket[4]; break; case MOUSE_MODEL_THINK: /* the fourth button state in the first byte */ ms.button |= (c & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0; break; case MOUSE_MODEL_VERSAPAD: /* VersaPad PS/2 absolute mode message format * * [packet1] 7 6 5 4 3 2 1 0(LSB) * ipacket[0]: 1 1 0 A 1 L T R * ipacket[1]: H7 H6 H5 H4 H3 H2 H1 H0 * ipacket[2]: V7 V6 V5 V4 V3 V2 V1 V0 * ipacket[3]: 1 1 1 A 1 L T R * ipacket[4]:V11 V10 V9 V8 H11 H10 H9 H8 * ipacket[5]: 0 P6 P5 P4 P3 P2 P1 P0 * * [note] * R: right physical mouse button (1=on) * T: touch pad virtual button (1=tapping) * L: left physical mouse button (1=on) * A: position data is valid (1=valid) * H: horizontal data (12bit signed integer. H11 is sign bit.) * V: vertical data (12bit signed integer. V11 is sign bit.) * P: pressure data * * Tapping is mapped to MOUSE_BUTTON4. */ ms.button = butmap_versapad[c & MOUSE_PS2VERSA_BUTTONS]; ms.button |= (c & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0; x = y = 0; if (c & MOUSE_PS2VERSA_IN_USE) { x0 = sc->ipacket[1] | (((sc->ipacket[4]) & 0x0f) << 8); y0 = sc->ipacket[2] | (((sc->ipacket[4]) & 0xf0) << 4); if (x0 & 0x800) x0 -= 0x1000; if (y0 & 0x800) y0 -= 0x1000; if (sc->flags & PSM_FLAGS_FINGERDOWN) { x = sc->xold - x0; y = y0 - sc->yold; if (x < 0) /* XXX */ x++; else if (x) x--; if (y < 0) y++; else if (y) y--; } else { sc->flags |= PSM_FLAGS_FINGERDOWN; } sc->xold = x0; sc->yold = y0; } else { sc->flags &= ~PSM_FLAGS_FINGERDOWN; } c = ((x < 0) ? MOUSE_PS2_XNEG : 0) | ((y < 0) ? MOUSE_PS2_YNEG : 0); break; case MOUSE_MODEL_GENERIC: default: break; } /* scale values */ if (sc->mode.accelfactor >= 1) { if (x != 0) { x = x * x / sc->mode.accelfactor; if (x == 0) x = 1; if (c & MOUSE_PS2_XNEG) x = -x; } if (y != 0) { y = y * y / sc->mode.accelfactor; if (y == 0) y = 1; if (c & MOUSE_PS2_YNEG) y = -y; } } ms.dx = x; ms.dy = y; ms.dz = z; ms.flags = ((x || y || z) ? MOUSE_POSCHANGED : 0) | (ms.obutton ^ ms.button); if (sc->mode.level < PSM_LEVEL_NATIVE) sc->inputbytes = tame_mouse(sc, &ms, sc->ipacket); sc->status.flags |= ms.flags; sc->status.dx += ms.dx; sc->status.dy += ms.dy; sc->status.dz += ms.dz; sc->status.button = ms.button; sc->button = ms.button; /* queue data */ if (sc->queue.count + sc->inputbytes < sizeof(sc->queue.buf)) { l = min(sc->inputbytes, sizeof(sc->queue.buf) - sc->queue.tail); bcopy(&sc->ipacket[0], &sc->queue.buf[sc->queue.tail], l); if (sc->inputbytes > l) bcopy(&sc->ipacket[l], &sc->queue.buf[0], sc->inputbytes - l); sc->queue.tail = (sc->queue.tail + sc->inputbytes) % sizeof(sc->queue.buf); sc->queue.count += sc->inputbytes; } sc->inputbytes = 0; if (sc->state & PSM_ASLP) { sc->state &= ~PSM_ASLP; wakeup((caddr_t) sc); } selwakeup(&sc->rsel); } } static int psmpoll(dev_t dev, int events, struct proc *p) { struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev)); int s; int revents = 0; /* Return true if a mouse event available */ s = spltty(); if (events & (POLLIN | POLLRDNORM)) { if (sc->queue.count > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sc->rsel); } splx(s); return (revents); } /* vendor/model specific routines */ static int mouse_id_proc1(KBDC kbdc, int res, int scale, int *status) { if (set_mouse_resolution(kbdc, res) != res) return FALSE; if (set_mouse_scaling(kbdc, scale) && set_mouse_scaling(kbdc, scale) && set_mouse_scaling(kbdc, scale) && (get_mouse_status(kbdc, status, 0, 3) >= 3)) return TRUE; return FALSE; } #if notyet /* Logitech MouseMan Cordless II */ static int enable_lcordless(struct psm_softc *sc) { int status[3]; int ch; if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 2, status)) return FALSE; if (status[1] == PSMD_RES_HIGH) return FALSE; ch = (status[0] & 0x07) - 1; /* channel # */ if ((ch <= 0) || (ch > 4)) return FALSE; /* * status[1]: always one? * status[2]: battery status? (0-100) */ return TRUE; } #endif /* notyet */ /* Genius NetScroll Mouse */ static int enable_groller(struct psm_softc *sc) { int status[3]; /* * The special sequence to enable the fourth button and the * roller. Immediately after this sequence check status bytes. * if the mouse is NetScroll, the second and the third bytes are * '3' and 'D'. */ /* * If the mouse is an ordinary PS/2 mouse, the status bytes should * look like the following. * * byte 1 bit 7 always 0 * bit 6 stream mode (0) * bit 5 disabled (0) * bit 4 1:1 scaling (0) * bit 3 always 0 * bit 0-2 button status * byte 2 resolution (PSMD_RES_HIGH) * byte 3 report rate (?) */ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status)) return FALSE; if ((status[1] != '3') || (status[2] != 'D')) return FALSE; /* FIXME!! */ sc->hw.buttons = get_mouse_buttons(sc->kbdc); sc->hw.buttons = 4; return TRUE; } /* Genius NetMouse/NetMouse Pro */ static int enable_gmouse(struct psm_softc *sc) { int status[3]; /* * The special sequence to enable the middle, "rubber" button. * Immediately after this sequence check status bytes. * if the mouse is NetMouse, NetMouse Pro, or ASCII MIE Mouse, * the second and the third bytes are '3' and 'U'. * NOTE: NetMouse reports that it has three buttons although it has * two buttons and a rubber button. NetMouse Pro and MIE Mouse * say they have three buttons too and they do have a button on the * side... */ if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status)) return FALSE; if ((status[1] != '3') || (status[2] != 'U')) return FALSE; return TRUE; } /* ALPS GlidePoint */ static int enable_aglide(struct psm_softc *sc) { int status[3]; /* * The special sequence to obtain ALPS GlidePoint specific * information. Immediately after this sequence, status bytes will * contain something interesting. * NOTE: ALPS produces several models of GlidePoint. Some of those * do not respond to this sequence, thus, cannot be detected this way. */ if (set_mouse_sampling_rate(sc->kbdc, 100) != 100) return FALSE; if (!mouse_id_proc1(sc->kbdc, PSMD_RES_LOW, 2, status)) return FALSE; if ((status[1] == PSMD_RES_LOW) || (status[2] == 100)) return FALSE; return TRUE; } /* Kensington ThinkingMouse/Trackball */ static int enable_kmouse(struct psm_softc *sc) { static unsigned char rate[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; KBDC kbdc = sc->kbdc; int status[3]; int id1; int id2; int i; id1 = get_aux_id(kbdc); if (set_mouse_sampling_rate(kbdc, 10) != 10) return FALSE; /* * The device is now in the native mode? It returns a different * ID value... */ id2 = get_aux_id(kbdc); if ((id1 == id2) || (id2 != 2)) return FALSE; if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW) return FALSE; #if PSM_DEBUG >= 2 /* at this point, resolution is LOW, sampling rate is 10/sec */ if (get_mouse_status(kbdc, status, 0, 3) < 3) return FALSE; #endif /* * The special sequence to enable the third and fourth buttons. * Otherwise they behave like the first and second buttons. */ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) { if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i]) return FALSE; } /* * At this point, the device is using default resolution and * sampling rate for the native mode. */ if (get_mouse_status(kbdc, status, 0, 3) < 3) return FALSE; if ((status[1] == PSMD_RES_LOW) || (status[2] == rate[i - 1])) return FALSE; /* the device appears be enabled by this sequence, diable it for now */ disable_aux_dev(kbdc); empty_aux_buffer(kbdc, 5); return TRUE; } /* Logitech MouseMan+/FirstMouse+ */ static int enable_mmanplus(struct psm_softc *sc) { static char res[] = { -1, PSMD_RES_LOW, PSMD_RES_HIGH, PSMD_RES_MEDIUM_HIGH, PSMD_RES_MEDIUM_LOW, -1, PSMD_RES_HIGH, PSMD_RES_MEDIUM_LOW, PSMD_RES_MEDIUM_HIGH, PSMD_RES_HIGH, }; KBDC kbdc = sc->kbdc; int data[3]; int i; /* the special sequence to enable the fourth button and the roller. */ for (i = 0; i < sizeof(res)/sizeof(res[0]); ++i) { if (res[i] < 0) { if (!set_mouse_scaling(kbdc, 1)) return FALSE; } else { if (set_mouse_resolution(kbdc, res[i]) != res[i]) return FALSE; } } if (get_mouse_status(kbdc, data, 1, 3) < 3) return FALSE; /* * PS2++ protocl, packet type 0 * * b7 b6 b5 b4 b3 b2 b1 b0 * byte 1: * 1 p3 p2 1 * * * * byte 2: 1 1 p1 p0 m1 m0 1 0 * byte 3: m7 m6 m5 m4 m3 m2 m1 m0 * * p3-p0: packet type: 0 * m7-m0: model ID: MouseMan+:0x50, FirstMouse+:0x51,... */ /* check constant bits */ if ((data[0] & MOUSE_PS2PLUS_SYNCMASK) != MOUSE_PS2PLUS_SYNC) return FALSE; if ((data[1] & 0xc3) != 0xc2) return FALSE; /* check d3-d0 in byte 2 */ if (!MOUSE_PS2PLUS_CHECKBITS(data)) return FALSE; /* check p3-p0 */ if (MOUSE_PS2PLUS_PACKET_TYPE(data) != 0) return FALSE; sc->hw.hwid &= 0x00ff; sc->hw.hwid |= data[2] << 8; /* save model ID */ /* * MouseMan+ (or FirstMouse+) is now in its native mode, in which * the wheel and the fourth button events are encoded in the * special data packet. The mouse may be put in the IntelliMouse mode * if it is initialized by the IntelliMouse's method. */ return TRUE; } /* MS IntelliMouse */ static int enable_msintelli(struct psm_softc *sc) { /* * Logitech MouseMan+ and FirstMouse+ will also respond to this * probe routine and act like IntelliMouse. */ static unsigned char rate[] = { 200, 100, 80, }; KBDC kbdc = sc->kbdc; int id; int i; /* the special sequence to enable the third button and the roller. */ for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) { if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i]) return FALSE; } /* the device will give the genuine ID only after the above sequence */ id = get_aux_id(kbdc); if (id != PSM_INTELLI_ID) return FALSE; sc->hw.hwid = id; sc->hw.buttons = 3; return TRUE; } /* Interlink electronics VersaPad */ static int enable_versapad(struct psm_softc *sc) { KBDC kbdc = sc->kbdc; int data[3]; set_mouse_resolution(kbdc, PSMD_RES_MEDIUM_HIGH); /* set res. 2 */ set_mouse_sampling_rate(kbdc, 100); /* set rate 100 */ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */ set_mouse_scaling(kbdc, 1); /* set scale 1:1 */ if (get_mouse_status(kbdc, data, 0, 3) < 3) /* get status */ return FALSE; if (data[2] != 0xa || data[1] != 0 ) /* rate == 0xa && res. == 0 */ return FALSE; set_mouse_scaling(kbdc, 1); /* set scale 1:1 */ return TRUE; /* PS/2 absolute mode */ } static int psmresume(device_t dev) { #ifdef PSM_HOOKRESUME struct psm_softc *sc = device_get_softc(dev); int unit = device_get_unit(dev); int err = 0; int s; int c; if (verbose >= 2) log(LOG_NOTICE, "psm%d: system resume hook called.\n", unit); /* don't let anybody mess with the aux device */ if (!kbdc_lock(sc->kbdc, TRUE)) return (EIO); s = spltty(); /* save the current controller command byte */ empty_both_buffers(sc->kbdc, 10); c = get_controller_command_byte(sc->kbdc); if (verbose >= 2) log(LOG_DEBUG, "psm%d: current command byte: %04x (psmresume).\n", unit, c); /* enable the aux port but disable the aux interrupt and the keyboard */ if ((c == -1) || !set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ splx(s); kbdc_lock(sc->kbdc, FALSE); log(LOG_ERR, "psm%d: unable to set the command byte (psmresume).\n", unit); return (EIO); } /* flush any data */ if (sc->state & PSM_VALID) { disable_aux_dev(sc->kbdc); /* this may fail; but never mind... */ empty_aux_buffer(sc->kbdc, 10); } sc->inputbytes = 0; #ifdef PSM_RESETAFTERSUSPEND /* try to detect the aux device; are you still there? */ if (reinitialize(unit, &sc->mode)) { /* yes */ sc->state |= PSM_VALID; } else { /* the device has gone! */ restore_controller(sc->kbdc, c); sc->state &= ~PSM_VALID; log(LOG_ERR, "psm%d: the aux device has gone! (psmresume).\n", unit); err = ENXIO; } #endif /* PSM_RESETAFTERSUSPEND */ splx(s); /* restore the driver state */ if ((sc->state & PSM_OPEN) && (err == 0)) { /* enable the aux device and the port again */ err = doopen(unit, c); if (err != 0) log(LOG_ERR, "psm%d: failed to enable the device (psmresume).\n", unit); } else { /* restore the keyboard port and disable the aux port */ if (!set_controller_command_byte(sc->kbdc, kbdc_get_device_mask(sc->kbdc), (c & KBD_KBD_CONTROL_BITS) | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ log(LOG_ERR, "psm%d: failed to disable the aux port (psmresume).\n", unit); err = EIO; } } /* done */ kbdc_lock(sc->kbdc, FALSE); if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) { /* * Release the blocked process; it must be notified that the device * cannot be accessed anymore. */ sc->state &= ~PSM_ASLP; wakeup((caddr_t)sc); } if (verbose >= 2) log(LOG_DEBUG, "psm%d: system resume hook exiting.\n", unit); return (err); #else /* !PSM_HOOKRESUME */ return (0); #endif /* PSM_HOOKRESUME */ } DRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass, 0, 0); - -#endif /* NPSM > 0 */ Index: head/sys/isa/syscons_isa.c =================================================================== --- head/sys/isa/syscons_isa.c (revision 56835) +++ head/sys/isa/syscons_isa.c (revision 56836) @@ -1,246 +1,243 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "sc.h" #include "opt_syscons.h" -#if NSC > 0 - #include #include #include #include #include #include #include #ifdef __i386__ #include #include #include #include #include #include #define BIOS_CLKED (1 << 6) #define BIOS_NLKED (1 << 5) #define BIOS_SLKED (1 << 4) #define BIOS_ALKED 0 #endif /* __i386__ */ #include #include #include static devclass_t sc_devclass; static int scprobe(device_t dev); static int scattach(device_t dev); static int scresume(device_t dev); static device_method_t sc_methods[] = { DEVMETHOD(device_probe, scprobe), DEVMETHOD(device_attach, scattach), DEVMETHOD(device_resume, scresume), { 0, 0 } }; static driver_t sc_driver = { SC_DRIVER_NAME, sc_methods, sizeof(sc_softc_t), }; static sc_softc_t main_softc; static int scprobe(device_t dev) { /* No pnp support */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "System console"); return sc_probe_unit(device_get_unit(dev), device_get_flags(dev)); } static int scattach(device_t dev) { return sc_attach_unit(device_get_unit(dev), device_get_flags(dev)); } static int scresume(device_t dev) { return sc_resume_unit(device_get_unit(dev)); } int sc_max_unit(void) { return devclass_get_maxunit(sc_devclass); } sc_softc_t *sc_get_softc(int unit, int flags) { sc_softc_t *sc; - if ((unit < 0) || (unit >= NSC)) + if (unit < 0) return NULL; if (flags & SC_KERNEL_CONSOLE) { /* FIXME: clear if it is wired to another unit! */ sc = &main_softc; } else { sc = (sc_softc_t *)device_get_softc(devclass_get_device(sc_devclass, unit)); + if (sc == NULL) + return NULL; } sc->unit = unit; if (!(sc->flags & SC_INIT_DONE)) { sc->keyboard = -1; sc->adapter = -1; sc->mouse_char = SC_MOUSE_CHAR; } return sc; } sc_softc_t *sc_find_softc(struct video_adapter *adp, struct keyboard *kbd) { sc_softc_t *sc; int units; int i; sc = &main_softc; if (((adp == NULL) || (adp == sc->adp)) && ((kbd == NULL) || (kbd == sc->kbd))) return sc; units = devclass_get_maxunit(sc_devclass); for (i = 0; i < units; ++i) { sc = (sc_softc_t *)device_get_softc(devclass_get_device(sc_devclass, i)); if (sc == NULL) continue; if (((adp == NULL) || (adp == sc->adp)) && ((kbd == NULL) || (kbd == sc->kbd))) return sc; } return NULL; } int sc_get_cons_priority(int *unit, int *flags) { int disabled; int u, f; int i; *unit = -1; for (i = -1; (i = resource_locate(i, SC_DRIVER_NAME)) >= 0;) { u = resource_query_unit(i); if ((resource_int_value(SC_DRIVER_NAME, u, "disabled", &disabled) == 0) && disabled) continue; if (resource_int_value(SC_DRIVER_NAME, u, "flags", &f) != 0) f = 0; if (f & SC_KERNEL_CONSOLE) { /* the user designates this unit to be the console */ *unit = u; *flags = f; break; } if (*unit < 0) { /* ...otherwise remember the first found unit */ *unit = u; *flags = f; } } if ((i < 0) && (*unit < 0)) return CN_DEAD; #if 0 return ((*flags & SC_KERNEL_CONSOLE) ? CN_INTERNAL : CN_NORMAL); #endif return CN_INTERNAL; } void sc_get_bios_values(bios_values_t *values) { #ifdef __i386__ u_int8_t shift; values->cursor_start = *(u_int8_t *)BIOS_PADDRTOVADDR(0x461); values->cursor_end = *(u_int8_t *)BIOS_PADDRTOVADDR(0x460); shift = *(u_int8_t *)BIOS_PADDRTOVADDR(0x417); values->shift_state = ((shift & BIOS_CLKED) ? CLKED : 0) | ((shift & BIOS_NLKED) ? NLKED : 0) | ((shift & BIOS_SLKED) ? SLKED : 0) | ((shift & BIOS_ALKED) ? ALKED : 0); values->bell_pitch = BELL_PITCH; #else /* !__i386__ */ values->cursor_start = 0; values->cursor_end = 32; values->shift_state = 0; values->bell_pitch = BELL_PITCH; #endif /* __i386__ */ } int sc_tone(int herz) { #ifdef __i386__ int pitch; if (herz) { /* set command for counter 2, 2 byte write */ if (acquire_timer2(TIMER_16BIT | TIMER_SQWAVE)) return EBUSY; /* set pitch */ pitch = timer_freq/herz; outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, pitch >> 8); /* enable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); } else { /* disable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) & 0xFC); release_timer2(); } #endif /* __i386__ */ return 0; } DRIVER_MODULE(sc, isa, sc_driver, sc_devclass, 0, 0); - -#endif /* NSC > 0 */ Index: head/sys/isa/vga_isa.c =================================================================== --- head/sys/isa/vga_isa.c (revision 56835) +++ head/sys/isa/vga_isa.c (revision 56836) @@ -1,223 +1,218 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -#include "vga.h" #include "opt_vga.h" #include "opt_fb.h" #include "opt_syscons.h" /* should be removed in the future, XXX */ -#if NVGA > 0 - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VGA_SOFTC(unit) \ ((vga_softc_t *)devclass_get_softc(isavga_devclass, unit)) static devclass_t isavga_devclass; static int isavga_probe(device_t dev); static int isavga_attach(device_t dev); static device_method_t isavga_methods[] = { DEVMETHOD(device_probe, isavga_probe), DEVMETHOD(device_attach, isavga_attach), DEVMETHOD(bus_print_child, bus_generic_print_child), { 0, 0 } }; static driver_t isavga_driver = { VGA_DRIVER_NAME, isavga_methods, sizeof(vga_softc_t), }; DRIVER_MODULE(vga, isa, isavga_driver, isavga_devclass, 0, 0); #ifdef FB_INSTALL_CDEV static d_open_t isavga_open; static d_close_t isavga_close; static d_read_t isavga_read; static d_write_t isavga_write; static d_ioctl_t isavga_ioctl; static d_mmap_t isavga_mmap; static struct cdevsw isavga_cdevsw = { /* open */ isavga_open, /* close */ isavga_close, /* read */ isavga_read, /* write */ isavga_write, /* ioctl */ isavga_ioctl, /* poll */ nopoll, /* mmap */ isavga_mmap, /* strategy */ nostrategy, /* name */ VGA_DRIVER_NAME, /* maj */ -1, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; #endif /* FB_INSTALL_CDEV */ static int isavga_probe(device_t dev) { video_adapter_t adp; device_t bus; int error; /* No pnp support */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "Generic ISA VGA"); error = vga_probe_unit(device_get_unit(dev), &adp, device_get_flags(dev)); if (error == 0) { bus = device_get_parent(dev); bus_set_resource(dev, SYS_RES_IOPORT, 0, adp.va_io_base, adp.va_io_size); bus_set_resource(dev, SYS_RES_MEMORY, 0, adp.va_mem_base, adp.va_mem_size); #if 0 isa_set_port(dev, adp.va_io_base); isa_set_portsize(dev, adp.va_io_size); isa_set_maddr(dev, adp.va_mem_base); isa_set_msize(dev, adp.va_mem_size); #endif } return error; } static int isavga_attach(device_t dev) { vga_softc_t *sc; struct resource *port; struct resource *mem; int unit; int rid; int error; unit = device_get_unit(dev); sc = device_get_softc(dev); rid = 0; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 0, RF_ACTIVE | RF_SHAREABLE); rid = 0; mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 0, RF_ACTIVE | RF_SHAREABLE); error = vga_attach_unit(unit, sc, device_get_flags(dev)); if (error) return error; #ifdef FB_INSTALL_CDEV /* attach a virtual frame buffer device */ error = fb_attach(makedev(0, VGA_MKMINOR(unit)), sc->adp, &isavga_cdevsw); if (error) return error; #endif /* FB_INSTALL_CDEV */ if (bootverbose) (*vidsw[sc->adp->va_index]->diag)(sc->adp, bootverbose); #if experimental device_add_child(dev, "fb", -1); bus_generic_attach(dev); #endif return 0; } #ifdef FB_INSTALL_CDEV static int isavga_open(dev_t dev, int flag, int mode, struct proc *p) { return vga_open(dev, VGA_SOFTC(VGA_UNIT(dev)), flag, mode, p); } static int isavga_close(dev_t dev, int flag, int mode, struct proc *p) { return vga_close(dev, VGA_SOFTC(VGA_UNIT(dev)), flag, mode, p); } static int isavga_read(dev_t dev, struct uio *uio, int flag) { return vga_read(dev, VGA_SOFTC(VGA_UNIT(dev)), uio, flag); } static int isavga_write(dev_t dev, struct uio *uio, int flag) { return vga_write(dev, VGA_SOFTC(VGA_UNIT(dev)), uio, flag); } static int isavga_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) { return vga_ioctl(dev, VGA_SOFTC(VGA_UNIT(dev)), cmd, arg, flag, p); } static int isavga_mmap(dev_t dev, vm_offset_t offset, int prot) { return vga_mmap(dev, VGA_SOFTC(VGA_UNIT(dev)), offset, prot); } #endif /* FB_INSTALL_CDEV */ - -#endif /* NVGA > 0 */ Index: head/sys/modules/vesa/Makefile =================================================================== --- head/sys/modules/vesa/Makefile (revision 56835) +++ head/sys/modules/vesa/Makefile (revision 56836) @@ -1,19 +1,8 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../dev/syscons ${.CURDIR}/../../i386/isa KMOD = vesa -SRCS = vesa.c scvesactl.c sc.h vga.h opt_syscons.h opt_vga.h \ - opt_vesa.h opt_fb.h +SRCS = vesa.c scvesactl.c opt_vga.h NOMAN = -CLEANFILES = sc.h vga.h - -sc.h: - echo "#define NSC 1" > sc.h - -vga.h: - echo "#define NVGA 1" > vga.h - -opt_vesa.h: - echo "#define VESA 1" > opt_vesa.h .include