/* * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany * Copyright 1993 by David Dawes <dawes@XFree86.org> * Copyright 1999 by David Holland <davidh@iquest.net) * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the names of Thomas Roell, David Dawes, and David Holland not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. Thomas Roell, David Dawes, and * David Holland make no representations about the suitability of this software * for any purpose. It is provided "as is" without express or implied * warranty. * * THOMAS ROELL, DAVID DAWES, AND DAVID HOLLAND DISCLAIM ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THOMAS ROELL, DAVID DAWES, OR DAVID HOLLAND * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Copyright (c) 2004-2009, Oracle and/or its affiliates. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <xorg-server.h> #include "xf86.h" #include "xf86Priv.h" #include "xf86_OSlib.h" #include "xf86OSKbd.h" #include "sun_kbd.h" #include <sys/stropts.h> #include <sys/vuid_event.h> #include <sys/kbd.h> #include <sys/note.h> /* needed before including older versions of hid.h */ #include <sys/usb/clients/hid/hid.h> static int KbdOn(InputInfoPtr pInfo, int what); static Bool OpenKeyboard(InputInfoPtr pInfo); static void CloseKeyboard(InputInfoPtr pInfo); static void sunKbdSetLeds(InputInfoPtr pInfo, int leds) { int i; uchar_t setleds = (uchar_t) (leds & 0xFF); SYSCALL(i = ioctl(pInfo->fd, KIOCSLED, &setleds)); if (i < 0) { xf86Msg(X_ERROR, "%s: Failed to set keyboard LED's: %s\n", pInfo->name, strerror(errno)); } } static int sunKbdGetLeds(InputInfoPtr pInfo) { int i; uchar_t leds = 0; SYSCALL(i = ioctl(pInfo->fd, KIOCGLED, &leds)); if (i < 0) { xf86Msg(X_ERROR, "%s: Failed to get keyboard LED's: %s\n", pInfo->name, strerror(errno)); } return (int) leds; } /* * Save initial keyboard state. This is called at the start of each server * generation. */ static int KbdInit(InputInfoPtr pInfo, int what) { KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; sunKbdPrivPtr priv = (sunKbdPrivPtr) pKbd->private; pointer options = pInfo->options; int ktype, klayout, i; const char *ktype_name; priv->kbdActive = FALSE; priv->otranslation = -1; priv->odirect = -1; if (options != NULL) { priv->strmod = xf86SetStrOption(options, "StreamsModule", NULL); } else { priv->strmod = NULL; } i = KbdOn(pInfo, DEVICE_INIT); if (i != Success) { return i; } SYSCALL(i = ioctl(pInfo->fd, KIOCTYPE, &ktype)); if (i < 0) { xf86Msg(X_ERROR, "%s: Unable to determine keyboard type: %s\n", pInfo->name, strerror(errno)); return BadImplementation; } SYSCALL(i = ioctl(pInfo->fd, KIOCLAYOUT, &klayout)); if (i < 0) { xf86Msg(X_ERROR, "%s: Unable to determine keyboard layout: %s\n", pInfo->name, strerror(errno)); return BadImplementation; } switch (ktype) { case KB_SUN3: ktype_name = "Sun Type 3"; break; case KB_SUN4: ktype_name = "Sun Type 4/5/6"; break; case KB_USB: ktype_name = "USB"; break; case KB_PC: ktype_name = "PC"; break; default: ktype_name = "Unknown"; break; } xf86Msg(X_PROBED, "%s: Keyboard type: %s (%d)\n", pInfo->name, ktype_name, ktype); xf86Msg(X_PROBED, "%s: Keyboard layout: %d\n", pInfo->name, klayout); priv->ktype = ktype; return Success; } static int KbdOn(InputInfoPtr pInfo, int what) { KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; sunKbdPrivPtr priv = (sunKbdPrivPtr) pKbd->private; int ktrans, kdirect, i; int io_get_direct = KIOCGDIRECT; int io_set_direct = KIOCSDIRECT; if (priv->kbdActive) { return Success; } if (pInfo->fd == -1) { if (!OpenKeyboard(pInfo)) { return BadImplementation; } } if (priv->strmod) { /* Check to see if module is already pushed */ SYSCALL(i = ioctl(pInfo->fd, I_FIND, priv->strmod)); if (i == 0) { /* Not already pushed */ SYSCALL(i = ioctl(pInfo->fd, I_PUSH, priv->strmod)); if (i < 0) { xf86Msg(X_ERROR, "%s: cannot push module '%s' onto " "keyboard device: %s\n", pInfo->name, priv->strmod, strerror(errno)); } } #ifdef HIDIOCKMSDIRECT if (strcmp(priv->strmod, "usbkbm") == 0) { io_get_direct = HIDIOCKMGDIRECT; io_set_direct = HIDIOCKMSDIRECT; } #endif } SYSCALL(i = ioctl(pInfo->fd, io_get_direct, &kdirect)); if (i < 0) { xf86Msg(X_ERROR, "%s: Unable to determine keyboard direct setting: %s\n", pInfo->name, strerror(errno)); return BadImplementation; } priv->odirect = kdirect; kdirect = 1; SYSCALL(i = ioctl(pInfo->fd, io_set_direct, &kdirect)); if (i < 0) { xf86Msg(X_ERROR, "%s: Failed turning keyboard direct mode on: %s\n", pInfo->name, strerror(errno)); return BadImplementation; } /* Setup translation */ SYSCALL(i = ioctl(pInfo->fd, KIOCGTRANS, &ktrans)); if (i < 0) { xf86Msg(X_ERROR, "%s: Unable to determine keyboard translation mode: %s\n", pInfo->name, strerror(errno)); return BadImplementation; } priv->otranslation = ktrans; ktrans = TR_UNTRANS_EVENT; SYSCALL(i = ioctl(pInfo->fd, KIOCTRANS, &ktrans)); if (i < 0) { xf86Msg(X_ERROR, "%s: Failed setting keyboard translation mode: %s\n", pInfo->name, strerror(errno)); return BadImplementation; } priv->oleds = sunKbdGetLeds(pInfo); /* Allocate here so we don't alloc in ReadInput which may be called from SIGIO handler. */ priv->remove_timer = TimerSet(priv->remove_timer, 0, 0, NULL, NULL); priv->kbdActive = TRUE; return Success; } static int KbdOff(InputInfoPtr pInfo, int what) { KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; sunKbdPrivPtr priv = (sunKbdPrivPtr) pKbd->private; int i; int io_set_direct, kdirect; if (priv->remove_timer) { TimerFree(priv->remove_timer); priv->remove_timer = NULL; } if (!priv->kbdActive) { return Success; } if (pInfo->fd == -1) { priv->kbdActive = FALSE; return Success; } /* restore original state */ if (priv->oleds != -1) { sunKbdSetLeds(pInfo, priv->oleds); priv->oleds = -1; } if (priv->otranslation != -1) { SYSCALL(i = ioctl(pInfo->fd, KIOCTRANS, &priv->otranslation)); if (i < 0) { xf86Msg(X_ERROR, "%s: Unable to restore keyboard translation mode: %s\n", pInfo->name, strerror(errno)); return BadImplementation; } priv->otranslation = -1; } io_set_direct = KIOCSDIRECT; kdirect = priv->odirect; #ifdef HIDIOCKMSDIRECT if ((priv->strmod != NULL) && (strcmp(priv->strmod, "usbkbm") == 0)) { io_set_direct = HIDIOCKMSDIRECT; kdirect = 0; } #endif if (kdirect != -1) { SYSCALL(i = ioctl(pInfo->fd, io_set_direct, &kdirect)); if (i < 0) { xf86Msg(X_ERROR, "%s: Unable to restore keyboard direct setting: %s\n", pInfo->name, strerror(errno)); return BadImplementation; } priv->odirect = -1; } if (priv->strmod) { SYSCALL(i = ioctl(pInfo->fd, I_POP, priv->strmod)); if (i < 0) { xf86Msg(X_WARNING, "%s: cannot pop module '%s' off keyboard device: %s\n", pInfo->name, priv->strmod, strerror(errno)); } } CloseKeyboard(pInfo); return Success; } static void SoundKbdBell(InputInfoPtr pInfo, int loudness, int pitch, int duration) { int kbdCmd, i; #ifdef KIOCMKTONE int cycles; int mktonevalue; #endif if (loudness && pitch) { #ifdef KIOCMKTONE if (pitch == 0) cycles = UINT16_MAX; else if (pitch >= UINT16_MAX) cycles = 0; else { cycles = (PIT_HZ + pitch / 2) / pitch; if (cycles > UINT16_MAX) cycles = UINT16_MAX; } mktonevalue = cycles | (((duration * loudness * 20) / 1000) << 16); errno = 0; SYSCALL(i = ioctl (pInfo->fd, KIOCMKTONE, mktonevalue)); if (i == 0) return; if (errno != EINVAL) { if (errno != EAGAIN) xf86Msg(X_ERROR, "%s: Failed to activate bell: %s\n", pInfo->name, strerror(errno)); return; } #endif kbdCmd = KBD_CMD_BELL; SYSCALL(i = ioctl (pInfo->fd, KIOCCMD, &kbdCmd)); if (i < 0) { xf86Msg(X_ERROR, "%s: Failed to activate bell: %s\n", pInfo->name, strerror(errno)); } usleep(duration * loudness * 20); kbdCmd = KBD_CMD_NOBELL; SYSCALL(i = ioctl (pInfo->fd, KIOCCMD, &kbdCmd)); if (i < 0) { xf86Msg(X_ERROR, "%s: Failed to deactivate bell: %s\n", pInfo->name, strerror(errno)); } } } static void SetKbdLeds(InputInfoPtr pInfo, int leds) { int real_leds = sunKbdGetLeds(pInfo); real_leds &= ~(LED_CAPS_LOCK | LED_NUM_LOCK | LED_SCROLL_LOCK | LED_COMPOSE); if (leds & XLED1) real_leds |= LED_CAPS_LOCK; if (leds & XLED2) real_leds |= LED_NUM_LOCK; if (leds & XLED3) real_leds |= LED_SCROLL_LOCK; if (leds & XLED4) real_leds |= LED_COMPOSE; sunKbdSetLeds(pInfo, real_leds); } static int GetKbdLeds(InputInfoPtr pInfo) { int leds = 0; int real_leds = sunKbdGetLeds(pInfo); if (real_leds & LED_CAPS_LOCK) leds |= XLED1; if (real_leds & LED_NUM_LOCK) leds |= XLED2; if (real_leds & LED_SCROLL_LOCK) leds |= XLED3; if (real_leds & LED_COMPOSE) leds |= XLED4; return leds; } static void CloseKeyboard(InputInfoPtr pInfo) { KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; sunKbdPrivPtr priv = (sunKbdPrivPtr) pKbd->private; close(pInfo->fd); pInfo->fd = -1; priv->kbdActive = FALSE; } /* Called from OsTimer callback, since removing a device from the device list or changing pInfo->fd while xf86Wakeup is looping through the list causes server crashes */ static CARD32 RemoveKeyboard(OsTimerPtr timer, CARD32 time, pointer arg) { InputInfoPtr pInfo = (InputInfoPtr) arg; CloseKeyboard(pInfo); xf86DisableDevice(pInfo->dev, TRUE); return 0; /* All done, don't set to run again */ } static void ReadInput(InputInfoPtr pInfo) { KbdDevPtr pKbd = (KbdDevPtr) pInfo->private; sunKbdPrivPtr priv = (sunKbdPrivPtr) pKbd->private; Firm_event event[64]; int nBytes, i; while (TRUE) { /* I certainly hope it's not possible to read partial events */ nBytes = read(pInfo->fd, (char *)event, sizeof(event)); if (nBytes > 0) { for (i = 0; i < (nBytes / sizeof(Firm_event)); i++) { pKbd->PostEvent(pInfo, event[i].id & 0xFF, event[i].value == VKEY_DOWN ? TRUE : FALSE); } } else if (nBytes == -1) { switch (errno) { case EAGAIN: /* Nothing to read now */ return; case EINTR: /* Interrupted, try again */ break; case ENODEV: /* May happen when USB kbd is unplugged */ /* We use X_NONE here because it didn't alloc since we may be called from SIGIO handler. No longer true for sigsafe logging, but matters for older servers */ LogMessageVerbSigSafe(X_NONE, 0, "%s: Device no longer present - removing.\n", pInfo->name); xf86RemoveEnabledDevice(pInfo); priv->remove_timer = TimerSet(priv->remove_timer, 0, 1, RemoveKeyboard, pInfo); return; default: /* All other errors */ /* We use X_NONE here because it didn't alloc since we may be called from SIGIO handler. No longer true for sigsafe logging, but matters for older servers */ LogMessageVerbSigSafe(X_NONE, 0, "%s: Read error: %s\n", pInfo->name, strerror(errno)); return; } } else { /* nBytes == 0, so nothing more to read */ return; } } } static Bool OpenKeyboard(InputInfoPtr pInfo) { char *kbdPath = xf86SetStrOption(pInfo->options, "Device", "/dev/kbd"); Bool ret; pInfo->fd = open(kbdPath, O_RDONLY | O_NONBLOCK); if (pInfo->fd == -1) { xf86Msg(X_ERROR, "%s: cannot open \"%s\"\n", pInfo->name, kbdPath); ret = FALSE; } else { xf86MsgVerb(X_INFO, 3, "%s: Opened device \"%s\"\n", pInfo->name, kbdPath); pInfo->read_input = ReadInput; ret = TRUE; /* in case it wasn't set and we fell back to default */ xf86ReplaceStrOption(pInfo->options, "Device", kbdPath); } free(kbdPath); return ret; } _X_EXPORT Bool xf86OSKbdPreInit(InputInfoPtr pInfo) { KbdDevPtr pKbd = pInfo->private; pKbd->KbdInit = KbdInit; pKbd->KbdOn = KbdOn; pKbd->KbdOff = KbdOff; pKbd->Bell = SoundKbdBell; pKbd->SetLeds = SetKbdLeds; pKbd->GetLeds = GetKbdLeds; pKbd->KbdGetMapping = KbdGetMapping; pKbd->RemapScanCode = NULL; pKbd->OpenKeyboard = OpenKeyboard; pKbd->private = calloc(sizeof(sunKbdPrivRec), 1); if (pKbd->private == NULL) { xf86Msg(X_ERROR,"can't allocate keyboard OS private data\n"); return FALSE; } else { sunKbdPrivPtr priv = (sunKbdPrivPtr) pKbd->private; priv->otranslation = -1; priv->odirect = -1; } return TRUE; }