Edit

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

Branch :

  • Show log

    Commit

  • Author : shadchin
    Date : 2013-05-03 09:32:18
    Hash : 83ceffd3
    Message : Update to xf86-input-keyboard 1.7.0. ok matthieu@

  • driver/xf86-input-keyboard/src/sun_kbd.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;
    }