xenocara/driver/xf86-input-keyboard/src/sun_kbd.c

538 lines
14 KiB
C

/*
* 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;
}