From 7fe60435bce6595a9c58a9bfd8244d74b5320e96 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Tue, 15 Jan 2013 08:46:13 +0100 Subject: Import DirectFB141_2k11R3_beta5 --- Source/DirectFB/inputdrivers/ps2mouse/ps2mouse.c | 576 +++++++++++++++++++++++ 1 file changed, 576 insertions(+) create mode 100755 Source/DirectFB/inputdrivers/ps2mouse/ps2mouse.c (limited to 'Source/DirectFB/inputdrivers/ps2mouse/ps2mouse.c') diff --git a/Source/DirectFB/inputdrivers/ps2mouse/ps2mouse.c b/Source/DirectFB/inputdrivers/ps2mouse/ps2mouse.c new file mode 100755 index 0000000..5b0fe2d --- /dev/null +++ b/Source/DirectFB/inputdrivers/ps2mouse/ps2mouse.c @@ -0,0 +1,576 @@ +/* + (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) + (c) Copyright 2000-2004 Convergence (integrated media) GmbH + + All rights reserved. + + Written by Denis Oliver Kropp , + Andreas Hundt , + Sven Neumann , + Ville Syrjälä and + Claudio Ciccani . + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + + +DFB_INPUT_DRIVER( ps2mouse ) + + +/* Stolen from the linux kernel (pc_keyb.h) */ +#define PS2_SET_RES 0xE8 /* Set resolution */ +#define PS2_SET_SCALE11 0xE6 /* Set 1:1 scaling */ +#define PS2_SET_SCALE21 0xE7 /* Set 2:1 scaling */ +#define PS2_GET_SCALE 0xE9 /* Get scaling factor */ +#define PS2_SET_STREAM 0xEA /* Set stream mode */ +#define PS2_SET_SAMPLE 0xF3 /* Set sample rate */ +#define PS2_ENABLE_DEV 0xF4 /* Enable aux device */ +#define PS2_DISABLE_DEV 0xF5 /* Disable aux device */ +#define PS2_RESET 0xFF /* Reset aux device */ +#define PS2_ACK 0xFA /* Command byte ACK */ + +/*** mouse commands ***/ + +#define PS2_SEND_ID 0xF2 +#define PS2_ID_ERROR -1 +#define PS2_ID_PS2 0 +#define PS2_ID_IMPS2 3 + +static int ndev; +static const char *devlist[8]; + +typedef struct { + int fd; + CoreInputDevice *device; + DirectThread *thread; + + int mouseId; + int packetLength; + + int dx; + int dy; +} PS2MouseData; + + +static void +flush_xy( PS2MouseData *data ) +{ + DFBInputEvent evt; + + if (data->dx) { + evt.type = DIET_AXISMOTION; + evt.flags = DIEF_AXISREL; + evt.axis = DIAI_X; + evt.axisrel = data->dx; + + /* Signal immediately following event. */ + if (data->dy) + evt.flags |= DIEF_FOLLOW; + + dfb_input_dispatch( data->device, &evt ); + + data->dx = 0; + } + + if (data->dy) { + evt.type = DIET_AXISMOTION; + evt.flags = DIEF_AXISREL; + evt.axis = DIAI_Y; + evt.axisrel = data->dy; + + dfb_input_dispatch( data->device, &evt ); + + data->dy = 0; + } +} + +static void* +ps2mouseEventThread( DirectThread *thread, void *driver_data ) +{ + PS2MouseData *data = (PS2MouseData*) driver_data; + + unsigned char packet[4]; + unsigned char pos = 0; + unsigned char last_buttons = 0; + + int readlen; + unsigned char buf[256]; + + + while ( (readlen = read(data->fd, buf, 256)) > 0 ) { + int i; + + direct_thread_testcancel( thread ); + + for ( i = 0; i < readlen; i++ ) { + + if ( pos == 0 && (buf[i] & 0xc0) ) { + continue; + } + packet[pos++] = buf[i]; + if ( pos == data->packetLength ) { + int dx, dy, dz; + int buttons; + + pos = 0; + + if ( !(packet[0] & 0x08) ) { + /* We've lost sync! */ + i--; /* does this make sense? oh well, + it will resync eventually (will it ?)*/ + continue; + } + + buttons = packet[0] & 0x07; + dx = (packet[0] & 0x10) ? packet[1]-256 : packet[1]; + dy = (packet[0] & 0x20) ? -(packet[2]-256) : -packet[2]; + if (data->mouseId == PS2_ID_IMPS2) { + /* Just strip off the extra buttons if present + and sign extend the 4 bit value */ + dz = (s8)((packet[3] & 0x80) ? + packet[3] | 0xf0 : packet[3] & 0x0F); + if (dz) { + DFBInputEvent evt; + + evt.type = DIET_AXISMOTION; + evt.flags = DIEF_AXISREL; + evt.axis = DIAI_Z; + evt.axisrel = dz; + + flush_xy( data ); + + dfb_input_dispatch( data->device, &evt ); + } + } + else { + dz = 0; + } + + data->dx += dx; + data->dy += dy; + + if ( !dfb_config->mouse_motion_compression ) + flush_xy( data ); + + if ( last_buttons != buttons ) { + DFBInputEvent evt; + unsigned char changed_buttons; + + changed_buttons = last_buttons ^ buttons; + + /* make sure the compressed motion event is dispatched + before any button change */ + flush_xy( data ); + + if ( changed_buttons & 0x01 ) { + evt.type = (buttons & 0x01) ? + DIET_BUTTONPRESS : DIET_BUTTONRELEASE; + evt.flags = DIEF_NONE; + evt.button = DIBI_LEFT; + dfb_input_dispatch( data->device, &evt ); + } + if ( changed_buttons & 0x02 ) { + evt.type = (buttons & 0x02) ? + DIET_BUTTONPRESS : DIET_BUTTONRELEASE; + evt.flags = DIEF_NONE; + evt.button = DIBI_RIGHT; + dfb_input_dispatch( data->device, &evt ); + } + if ( changed_buttons & 0x04 ) { + evt.type = (buttons & 0x04) ? + DIET_BUTTONPRESS : DIET_BUTTONRELEASE; + evt.flags = DIEF_NONE; + evt.button = DIBI_MIDDLE; + dfb_input_dispatch( data->device, &evt ); + } + + last_buttons = buttons; + } + } + } + /* make sure the compressed motion event is dispatched, + necessary if the last packet was a motion event */ + flush_xy( data ); + } + + if ( readlen <= 0 && errno != EINTR ) + D_PERROR ("psmouse thread died\n"); + + return NULL; +} + + +static int +ps2WriteChar( int fd, unsigned char c, bool verbose ) +{ + struct timeval tv; + fd_set fds; + + tv.tv_sec = 0; + tv.tv_usec = 200000; /* timeout 200 ms */ + + FD_ZERO( &fds ); + FD_SET( fd, &fds ); + + write( fd, &c, 1 ); + + if ( select(fd+1, &fds, NULL, NULL, &tv) == 0 ) { + if ( verbose ) + D_ERROR( "DirectFB/PS2Mouse: timeout waiting for ack!!\n" ); + return -1; + } + + read( fd, &c, 1 ); + + if ( c != PS2_ACK ) + return -2; + + return 0; +} + + +static int +ps2GetId( int fd, bool verbose ) +{ + unsigned char c; + + if ( ps2WriteChar(fd, PS2_SEND_ID, verbose) < 0 ) + return PS2_ID_ERROR; + + read( fd, &c, 1 ); + + return( c ); +} + + +static int +ps2Write( int fd, const unsigned char *data, size_t len, bool verbose) +{ + size_t i; + int error = 0; + + for ( i = 0; i < len; i++ ) { + if ( ps2WriteChar(fd, data[i], verbose) < 0 ) { + if ( verbose ) + D_ERROR( "DirectFB/PS2Mouse: error @byte %zu\n", i ); + error++; + } + } + + if ( error && verbose ) + D_ERROR( "DirectFB/PS2Mouse: missed %i ack's!\n", error); + + return( error ); +} + + +static int +init_ps2( int fd, bool verbose ) +{ + static const unsigned char basic_init[] = + { PS2_ENABLE_DEV, PS2_SET_SAMPLE, 100 }; + static const unsigned char imps2_init[] = + { PS2_SET_SAMPLE, 200, PS2_SET_SAMPLE, 100, PS2_SET_SAMPLE, 80 }; + static const unsigned char ps2_init[] = + { PS2_SET_SCALE11, PS2_ENABLE_DEV, PS2_SET_SAMPLE, 100, PS2_SET_RES, 3 }; + int mouseId; + + struct timeval tv; + fd_set fds; + int count = 100; + + /* read all data from the file descriptor before initializing the mouse */ + while (true) { + unsigned char buf[64]; + + tv.tv_sec = 0; + tv.tv_usec = 50000; /* timeout 1/50 sec */ + + FD_ZERO( &fds ); + FD_SET( fd, &fds ); + + if (select( fd+1, &fds, NULL, NULL, &tv )) + read( fd, buf, sizeof(buf) ); + else + break; + + if (! --count) { + D_ERROR( "DirectFB/PS2Mouse: " + "PS/2 mouse keeps sending data, " + "initialization failed\n" ); + return -1; + } + } + + ps2Write( fd, basic_init, sizeof (basic_init), verbose ); + /* Do a basic init in case the mouse is confused */ + if (ps2Write( fd, basic_init, sizeof (basic_init), verbose ) != 0) { + if (verbose) + D_ERROR( "DirectFB/PS2Mouse: PS/2 mouse failed init\n" ); + return -1; + } + + ps2Write( fd, ps2_init, sizeof (ps2_init), verbose ); + + if (ps2Write(fd, imps2_init, sizeof (imps2_init), verbose) != 0) { + if (verbose) + D_ERROR ("DirectFB/PS2Mouse: mouse failed IMPS/2 init\n"); + return -2; + } + + if ((mouseId = ps2GetId( fd, verbose )) < 0) + return mouseId; + + if ( mouseId != PS2_ID_IMPS2 ) /* unknown id, assume PS/2 */ + mouseId = PS2_ID_PS2; + + return mouseId; +} + +/**************************************************************************************************/ + +static void +check_devices( const char *devnames[], int num ) +{ + int i, fd; + + for (i=0; imouse_protocol && + (strcasecmp( dfb_config->mouse_protocol, "PS/2") == 0 || + strcasecmp( dfb_config->mouse_protocol, "IMPS/2") == 0)) + { + int fd = open( dfb_config->mouse_source, O_RDONLY | O_NONBLOCK ); + + if (fd < 0) { + D_PERROR( "DirectFB/PS2Mouse: opening %s failed!\n", + dfb_config->mouse_source ); + return 0; + } + + devlist[0] = dfb_config->mouse_source; + close( fd ); + return 1; + } + + if (uname( &uts ) < 0) + D_PERROR( "DirectFB/PS2Mouse: uname() failed!\n" ); + else if (!strncmp( uts.release, "2.6.", 4 ) || !strncmp( uts.release, "2.5.", 4 )) + check_psaux = false; + + if (check_psaux) + CHECK_DEVICES( dev_psaux ); + + if (check_input) + CHECK_DEVICES( dev_input ); + + return ndev; +} + +static void +driver_get_info( InputDriverInfo *info ) +{ + /* fill driver info structure */ + snprintf( info->name, + DFB_INPUT_DRIVER_INFO_NAME_LENGTH, "PS/2 Mouse Driver" ); + + snprintf( info->vendor, + DFB_INPUT_DRIVER_INFO_VENDOR_LENGTH, "directfb.org" ); + + info->version.major = 1; + info->version.minor = 0; +} + +static DFBResult +driver_open_device( CoreInputDevice *device, + unsigned int number, + InputDeviceInfo *info, + void **driver_data ) +{ + int fd; + int mouseId = -1; + int flags; + PS2MouseData *data; + + /* open device */ + + flags = (dfb_config->mouse_gpm_source) + ? (O_RDONLY | O_NONBLOCK) + : (O_RDWR | O_SYNC | O_EXCL); + + fd = open( devlist[number], flags ); + if (fd < 0) { + D_PERROR( "DirectFB/PS2Mouse: failed opening `%s' !\n", + devlist[number] ); + close( fd ); + return DFB_INIT; + } + + fcntl( fd, F_SETFL, fcntl ( fd, F_GETFL ) & ~O_NONBLOCK ); + + if (!dfb_config->mouse_gpm_source) { + mouseId = init_ps2( fd, true ); + + if (mouseId < 0) { + D_PERROR( "DirectFB/PS2Mouse: could not initialize mouse on `%s'!\n", + devlist[number] ); + close( fd ); + return DFB_INIT; + } + } + + if (dfb_config->mouse_protocol) { + if (strcasecmp( dfb_config->mouse_protocol, "IMPS/2" ) == 0) { + mouseId = PS2_ID_IMPS2; + } + else if (strcasecmp( dfb_config->mouse_protocol, "PS/2" ) == 0) { + mouseId = PS2_ID_PS2; + } + else { + D_ERROR( "DirectFB/PS2Mouse: unsupported protocol `%s' !\n", + dfb_config->mouse_protocol ); + close( fd ); + return DFB_INIT; + } + } + + /* fill device info structure */ + snprintf( info->desc.name, DFB_INPUT_DEVICE_DESC_NAME_LENGTH, + (mouseId == PS2_ID_IMPS2) ? "IMPS/2 Mouse" : "PS/2 Mouse" ); + + snprintf( info->desc.vendor, + DFB_INPUT_DEVICE_DESC_VENDOR_LENGTH, "Unknown" ); + + info->prefered_id = DIDID_MOUSE; + info->desc.type = DIDTF_MOUSE; + info->desc.caps = DICAPS_AXES | DICAPS_BUTTONS; + info->desc.max_axis = (mouseId == PS2_ID_IMPS2) ? DIAI_Z : DIAI_Y; + info->desc.max_button = DIBI_MIDDLE; /* TODO: probe!? */ + + /* allocate and fill private data */ + data = D_CALLOC( 1, sizeof(PS2MouseData) ); + if (!data) { + close( fd ); + return D_OOM(); + } + + data->fd = fd; + data->device = device; + data->mouseId = mouseId; + data->packetLength = (mouseId == PS2_ID_IMPS2) ? 4 : 3; + + /* start input thread */ + data->thread = direct_thread_create( DTT_INPUT, ps2mouseEventThread, data, "PS/2 Input" ); + + /* set private data pointer */ + *driver_data = data; + + return DFB_OK; +} + +/* + * Fetch one entry from the device's keymap if supported. + */ +static DFBResult +driver_get_keymap_entry( CoreInputDevice *device, + void *driver_data, + DFBInputDeviceKeymapEntry *entry ) +{ + return DFB_UNSUPPORTED; +} + +static void +driver_close_device( void *driver_data ) +{ + PS2MouseData *data = (PS2MouseData*) driver_data; + + /* stop input thread */ + direct_thread_cancel( data->thread ); + direct_thread_join( data->thread ); + direct_thread_destroy( data->thread ); + + /* close device */ + close( data->fd ); + + /* free private data */ + D_FREE( data ); +} -- cgit