l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

3
src/l4/pkg/input/Control Normal file
View File

@@ -0,0 +1,3 @@
provides: input input-dummy
requires: libirq libio l4re_c libpthread
Maintainer: adam@os.inf.tu-dresden.de

13
src/l4/pkg/input/LEGAL Normal file
View File

@@ -0,0 +1,13 @@
# Format: (all on one line)
# Source file : Copyright Owner(s)
# Please use "TUD" when referring to TU Dresden.
# If sole owner is TUD, the license field defaults to GPL and can be
# left empty.
# Source-file spec can be a directory name or a wildcard like "src/*.c".
doc : TUD
examples : TUD
include : Linux 2.6, TUD : GPL
lib/contrib : Linux 2.6 : GPL
lib/include : Linux 2.6, TUD : GPL
lib/src : Linux 2.6, TUD : GPL
lib/src-ux : TUD

View File

@@ -0,0 +1,4 @@
PKGDIR ?= .
L4DIR ?= $(PKGDIR)/../..
include $(L4DIR)/mk/subdir.mk

14
src/l4/pkg/input/README Normal file
View File

@@ -0,0 +1,14 @@
Input driver library -- L4INPUT.
This package provides an API to input device drivers. The drivers are part of a
library, i.e. colocated with the application using it. Packages using L4INPUT
are CON and DOpE. L4INPUT supports PS/2 aux and kbd.
It's a bad idea to use two applications linked with input in one system as
there should only be one device driver per specific device.
HINT: Fiasco must not be started with deprecated "-esc" command line switch;
use "-serial_esc" instead. Otherwise, L4INPUT cannot initialize i8042 or
behaves really strange because Fiasco also programs input ports.
ALL Linux 2.6 sources included in this package are GPLed (as you know).

8
src/l4/pkg/input/TODO Normal file
View File

@@ -0,0 +1,8 @@
- other devices (for now only PS/2 mouse and keyboards are supported)
- USB stub
- serial mice
- dynamic event lists (means: no static buffers
and memcpy)
BUT: one can use callback mode
- dox
- jiffies thread is not necessary in L4IO mode

View File

@@ -0,0 +1,28 @@
ARCHITECTURE OF LINUX INPUT SUBSYSTEM
+---------+
| HANDLER |
| evdev |
+---------+
|
+-------+
+--------------| input |--------------+
| +-------+ |
| | |
+----------------+ +----------------+ +----------------+
| DEVICE | | DEVICE | | DEVICE |
| psmouse-base.c | | atkbd.c | | hid.c |
+----------------+ +----------------+ +----------------+
\ / |
+---------+ +-------+
| serio | | usb |
+---------+ +-------+
| |
+--------------+ ...
| PORT |
| i8042.c |
+--------------+
i8042.c --(raw)--> atkbd.c --(events)--> input.c
usb.c --(raw)--> hid.c --(events)--> input.c

View File

@@ -0,0 +1,4 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../..
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,33 @@
#pragma once
#include <l4/sys/compiler.h>
#include <l4/sys/types.h>
L4_BEGIN_DECLS
typedef struct {
short unsigned type;
short unsigned code;
int value;
} Input_event;
typedef void (*Input_handler)(Input_event event, void *priv);
struct arm_input_ops {
const char * (*get_info)(void);
int (*probe)(const char *name);
void (*attach)(Input_handler handler, void *priv);
void (*enable)(void);
void (*disable)(void);
};
struct arm_input_ops *arm_input_probe(const char *name);
void arm_input_register_driver(struct arm_input_ops *);
/* Callable once per file (should be enough?) */
#define arm_input_register(ops) \
static void __attribute__((constructor)) __register_ops(void) \
{ arm_input_register_driver(ops); }
L4_END_DECLS

View File

@@ -0,0 +1,88 @@
/*****************************************************************************/
/**
* \file input/include/libinput.h
* \brief Input event library (L4INPUT) API
*
* \date 11/20/2003
* \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de>
*
*/
/* (c) 2003 Technische Universitaet Dresden
* This file is part of DROPS, which is distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*
* Original copyright notice from include/linux/input.h follows...
*/
/*
* Copyright (c) 1999-2002 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef __INPUT_INCLUDE_LIBINPUT_H_
#define __INPUT_INCLUDE_LIBINPUT_H_
#include <l4/sys/compiler.h>
#include <l4/input/macros.h>
#include <l4/re/event.h>
L4_BEGIN_DECLS
struct l4input {
long long time; ///< unused on bare hardware
unsigned short type;
unsigned short code;
int value;
unsigned long stream_id;
};
/** Initialize input driver library.
*
* \param prio if != L4THREAD_DEFAULT_PRIO use as prio for irq threads
* \param handler if !NULL use this function on event occurence as
* callback
*
* libinput works in 2 different modes:
*
* -# input events are stored in library local ring buffers until
* l4input_flush() is called.
*
* -# on each event occurrence \a handler is called and no local event
* buffering is done.
*/
L4_CV int l4input_init(int prio, L4_CV void (*handler)(struct l4input *));
/** Query event status.
*
* \return 0 if there are no pending events; !0 otherwise
*/
L4_CV int l4input_ispending(void);
/** Get events.
*
* \param buffer event return buffer
* \param count max number of events to return
*
* \return number of flushed events
*
* Returns up to \a count events into buffer.
*/
L4_CV int l4input_flush(void *buffer, int count);
/** Program PC speaker
*
* \param tone tone value (0 switches off)
*/
L4_CV int l4input_pcspkr(int tone);
L4_CV int l4evdev_stream_info_for_id(l4_umword_t id, l4re_event_stream_info_t *si);
L4_CV int l4evdev_absinfo(l4_umword_t id, unsigned naxes, const unsigned *axes, l4re_event_absinfo_t *infos);
L4_END_DECLS
#endif

View File

@@ -0,0 +1,576 @@
/*****************************************************************************/
/**
* \file input/include/macros.h
* \brief Input event library (L4INPUT) API (event macros)
*
* \date 11/20/2003
* \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de>
*
*/
/* (c) 2003 Technische Universitaet Dresden
* This file is part of DROPS, which is distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*
* Original copyright notice from include/linux/input.h follows...
*/
/*
* Copyright (c) 1999-2002 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef __INPUT_INCLUDE_MACROS_H_
#define __INPUT_INCLUDE_MACROS_H_
/* XXX Leave TABs here. This way we can diff against Linux and easily insert
new macros. */
/*
* Event types
*/
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_WINDOW 0x18
#define EV_MAX 0x1f
/*
* Synchronization events.
*/
#define SYN_REPORT 0
#define SYN_CONFIG 1
/*
* Keys and buttons
*/
#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
#define KEY_3 4
#define KEY_4 5
#define KEY_5 6
#define KEY_6 7
#define KEY_7 8
#define KEY_8 9
#define KEY_9 10
#define KEY_0 11
#define KEY_MINUS 12
#define KEY_EQUAL 13
#define KEY_BACKSPACE 14
#define KEY_TAB 15
#define KEY_Q 16
#define KEY_W 17
#define KEY_E 18
#define KEY_R 19
#define KEY_T 20
#define KEY_Y 21
#define KEY_U 22
#define KEY_I 23
#define KEY_O 24
#define KEY_P 25
#define KEY_LEFTBRACE 26
#define KEY_RIGHTBRACE 27
#define KEY_ENTER 28
#define KEY_LEFTCTRL 29
#define KEY_A 30
#define KEY_S 31
#define KEY_D 32
#define KEY_F 33
#define KEY_G 34
#define KEY_H 35
#define KEY_J 36
#define KEY_K 37
#define KEY_L 38
#define KEY_SEMICOLON 39
#define KEY_APOSTROPHE 40
#define KEY_GRAVE 41
#define KEY_LEFTSHIFT 42
#define KEY_BACKSLASH 43
#define KEY_Z 44
#define KEY_X 45
#define KEY_C 46
#define KEY_V 47
#define KEY_B 48
#define KEY_N 49
#define KEY_M 50
#define KEY_COMMA 51
#define KEY_DOT 52
#define KEY_SLASH 53
#define KEY_RIGHTSHIFT 54
#define KEY_KPASTERISK 55
#define KEY_LEFTALT 56
#define KEY_SPACE 57
#define KEY_CAPSLOCK 58
#define KEY_F1 59
#define KEY_F2 60
#define KEY_F3 61
#define KEY_F4 62
#define KEY_F5 63
#define KEY_F6 64
#define KEY_F7 65
#define KEY_F8 66
#define KEY_F9 67
#define KEY_F10 68
#define KEY_NUMLOCK 69
#define KEY_SCROLLLOCK 70
#define KEY_KP7 71
#define KEY_KP8 72
#define KEY_KP9 73
#define KEY_KPMINUS 74
#define KEY_KP4 75
#define KEY_KP5 76
#define KEY_KP6 77
#define KEY_KPPLUS 78
#define KEY_KP1 79
#define KEY_KP2 80
#define KEY_KP3 81
#define KEY_KP0 82
#define KEY_KPDOT 83
#define KEY_ZENKAKUHANKAKU 85
#define KEY_102ND 86
#define KEY_F11 87
#define KEY_F12 88
#define KEY_RO 89
#define KEY_KATAKANA 90
#define KEY_HIRAGANA 91
#define KEY_HENKAN 92
#define KEY_KATAKANAHIRAGANA 93
#define KEY_MUHENKAN 94
#define KEY_KPJPCOMMA 95
#define KEY_KPENTER 96
#define KEY_RIGHTCTRL 97
#define KEY_KPSLASH 98
#define KEY_SYSRQ 99
#define KEY_RIGHTALT 100
#define KEY_LINEFEED 101
#define KEY_HOME 102
#define KEY_UP 103
#define KEY_PAGEUP 104
#define KEY_LEFT 105
#define KEY_RIGHT 106
#define KEY_END 107
#define KEY_DOWN 108
#define KEY_PAGEDOWN 109
#define KEY_INSERT 110
#define KEY_DELETE 111
#define KEY_MACRO 112
#define KEY_MUTE 113
#define KEY_VOLUMEDOWN 114
#define KEY_VOLUMEUP 115
#define KEY_POWER 116
#define KEY_KPEQUAL 117
#define KEY_KPPLUSMINUS 118
#define KEY_PAUSE 119
#define KEY_KPCOMMA 121
#define KEY_HANGEUL 122
#define KEY_HANGUEL KEY_HANGEUL
#define KEY_HANJA 123
#define KEY_YEN 124
#define KEY_LEFTMETA 125
#define KEY_RIGHTMETA 126
#define KEY_COMPOSE 127
#define KEY_STOP 128
#define KEY_AGAIN 129
#define KEY_PROPS 130
#define KEY_UNDO 131
#define KEY_FRONT 132
#define KEY_COPY 133
#define KEY_OPEN 134
#define KEY_PASTE 135
#define KEY_FIND 136
#define KEY_CUT 137
#define KEY_HELP 138
#define KEY_MENU 139
#define KEY_CALC 140
#define KEY_SETUP 141
#define KEY_SLEEP 142
#define KEY_WAKEUP 143
#define KEY_FILE 144
#define KEY_SENDFILE 145
#define KEY_DELETEFILE 146
#define KEY_XFER 147
#define KEY_PROG1 148
#define KEY_PROG2 149
#define KEY_WWW 150
#define KEY_MSDOS 151
#define KEY_COFFEE 152
#define KEY_DIRECTION 153
#define KEY_CYCLEWINDOWS 154
#define KEY_MAIL 155
#define KEY_BOOKMARKS 156
#define KEY_COMPUTER 157
#define KEY_BACK 158
#define KEY_FORWARD 159
#define KEY_CLOSECD 160
#define KEY_EJECTCD 161
#define KEY_EJECTCLOSECD 162
#define KEY_NEXTSONG 163
#define KEY_PLAYPAUSE 164
#define KEY_PREVIOUSSONG 165
#define KEY_STOPCD 166
#define KEY_RECORD 167
#define KEY_REWIND 168
#define KEY_PHONE 169
#define KEY_ISO 170
#define KEY_CONFIG 171
#define KEY_HOMEPAGE 172
#define KEY_REFRESH 173
#define KEY_EXIT 174
#define KEY_MOVE 175
#define KEY_EDIT 176
#define KEY_SCROLLUP 177
#define KEY_SCROLLDOWN 178
#define KEY_KPLEFTPAREN 179
#define KEY_KPRIGHTPAREN 180
#define KEY_NEW 181
#define KEY_REDO 182
#define KEY_F13 183
#define KEY_F14 184
#define KEY_F15 185
#define KEY_F16 186
#define KEY_F17 187
#define KEY_F18 188
#define KEY_F19 189
#define KEY_F20 190
#define KEY_F21 191
#define KEY_F22 192
#define KEY_F23 193
#define KEY_F24 194
#define KEY_PLAYCD 200
#define KEY_PAUSECD 201
#define KEY_PROG3 202
#define KEY_PROG4 203
#define KEY_SUSPEND 205
#define KEY_CLOSE 206
#define KEY_PLAY 207
#define KEY_FASTFORWARD 208
#define KEY_BASSBOOST 209
#define KEY_PRINT 210
#define KEY_HP 211
#define KEY_CAMERA 212
#define KEY_SOUND 213
#define KEY_QUESTION 214
#define KEY_EMAIL 215
#define KEY_CHAT 216
#define KEY_SEARCH 217
#define KEY_CONNECT 218
#define KEY_FINANCE 219
#define KEY_SPORT 220
#define KEY_SHOP 221
#define KEY_ALTERASE 222
#define KEY_CANCEL 223
#define KEY_BRIGHTNESSDOWN 224
#define KEY_BRIGHTNESSUP 225
#define KEY_MEDIA 226
#define KEY_SWITCHVIDEOMODE 227
#define KEY_KBDILLUMTOGGLE 228
#define KEY_KBDILLUMDOWN 229
#define KEY_KBDILLUMUP 230
#define KEY_SEND 231
#define KEY_REPLY 232
#define KEY_FORWARDMAIL 233
#define KEY_SAVE 234
#define KEY_DOCUMENTS 235
#define KEY_UNKNOWN 240
#define BTN_MISC 0x100
#define BTN_0 0x100
#define BTN_1 0x101
#define BTN_2 0x102
#define BTN_3 0x103
#define BTN_4 0x104
#define BTN_5 0x105
#define BTN_6 0x106
#define BTN_7 0x107
#define BTN_8 0x108
#define BTN_9 0x109
#define BTN_MOUSE 0x110
#define BTN_LEFT 0x110
#define BTN_RIGHT 0x111
#define BTN_MIDDLE 0x112
#define BTN_SIDE 0x113
#define BTN_EXTRA 0x114
#define BTN_FORWARD 0x115
#define BTN_BACK 0x116
#define BTN_TASK 0x117
#define BTN_JOYSTICK 0x120
#define BTN_TRIGGER 0x120
#define BTN_THUMB 0x121
#define BTN_THUMB2 0x122
#define BTN_TOP 0x123
#define BTN_TOP2 0x124
#define BTN_PINKIE 0x125
#define BTN_BASE 0x126
#define BTN_BASE2 0x127
#define BTN_BASE3 0x128
#define BTN_BASE4 0x129
#define BTN_BASE5 0x12a
#define BTN_BASE6 0x12b
#define BTN_DEAD 0x12f
#define BTN_GAMEPAD 0x130
#define BTN_A 0x130
#define BTN_B 0x131
#define BTN_C 0x132
#define BTN_X 0x133
#define BTN_Y 0x134
#define BTN_Z 0x135
#define BTN_TL 0x136
#define BTN_TR 0x137
#define BTN_TL2 0x138
#define BTN_TR2 0x139
#define BTN_SELECT 0x13a
#define BTN_START 0x13b
#define BTN_MODE 0x13c
#define BTN_THUMBL 0x13d
#define BTN_THUMBR 0x13e
#define BTN_DIGI 0x140
#define BTN_TOOL_PEN 0x140
#define BTN_TOOL_RUBBER 0x141
#define BTN_TOOL_BRUSH 0x142
#define BTN_TOOL_PENCIL 0x143
#define BTN_TOOL_AIRBRUSH 0x144
#define BTN_TOOL_FINGER 0x145
#define BTN_TOOL_MOUSE 0x146
#define BTN_TOOL_LENS 0x147
#define BTN_TOUCH 0x14a
#define BTN_STYLUS 0x14b
#define BTN_STYLUS2 0x14c
#define BTN_TOOL_DOUBLETAP 0x14d
#define BTN_TOOL_TRIPLETAP 0x14e
#define BTN_WHEEL 0x150
#define BTN_GEAR_DOWN 0x150
#define BTN_GEAR_UP 0x151
#define KEY_OK 0x160
#define KEY_SELECT 0x161
#define KEY_GOTO 0x162
#define KEY_CLEAR 0x163
#define KEY_POWER2 0x164
#define KEY_OPTION 0x165
#define KEY_INFO 0x166
#define KEY_TIME 0x167
#define KEY_VENDOR 0x168
#define KEY_ARCHIVE 0x169
#define KEY_PROGRAM 0x16a
#define KEY_CHANNEL 0x16b
#define KEY_FAVORITES 0x16c
#define KEY_EPG 0x16d
#define KEY_PVR 0x16e
#define KEY_MHP 0x16f
#define KEY_LANGUAGE 0x170
#define KEY_TITLE 0x171
#define KEY_SUBTITLE 0x172
#define KEY_ANGLE 0x173
#define KEY_ZOOM 0x174
#define KEY_MODE 0x175
#define KEY_KEYBOARD 0x176
#define KEY_SCREEN 0x177
#define KEY_PC 0x178
#define KEY_TV 0x179
#define KEY_TV2 0x17a
#define KEY_VCR 0x17b
#define KEY_VCR2 0x17c
#define KEY_SAT 0x17d
#define KEY_SAT2 0x17e
#define KEY_CD 0x17f
#define KEY_TAPE 0x180
#define KEY_RADIO 0x181
#define KEY_TUNER 0x182
#define KEY_PLAYER 0x183
#define KEY_TEXT 0x184
#define KEY_DVD 0x185
#define KEY_AUX 0x186
#define KEY_MP3 0x187
#define KEY_AUDIO 0x188
#define KEY_VIDEO 0x189
#define KEY_DIRECTORY 0x18a
#define KEY_LIST 0x18b
#define KEY_MEMO 0x18c
#define KEY_CALENDAR 0x18d
#define KEY_RED 0x18e
#define KEY_GREEN 0x18f
#define KEY_YELLOW 0x190
#define KEY_BLUE 0x191
#define KEY_CHANNELUP 0x192
#define KEY_CHANNELDOWN 0x193
#define KEY_FIRST 0x194
#define KEY_LAST 0x195
#define KEY_AB 0x196
#define KEY_NEXT 0x197
#define KEY_RESTART 0x198
#define KEY_SLOW 0x199
#define KEY_SHUFFLE 0x19a
#define KEY_BREAK 0x19b
#define KEY_PREVIOUS 0x19c
#define KEY_DIGITS 0x19d
#define KEY_TEEN 0x19e
#define KEY_TWEN 0x19f
#define KEY_DEL_EOL 0x1c0
#define KEY_DEL_EOS 0x1c1
#define KEY_INS_LINE 0x1c2
#define KEY_DEL_LINE 0x1c3
#define KEY_FN 0x1d0
#define KEY_FN_ESC 0x1d1
#define KEY_FN_F1 0x1d2
#define KEY_FN_F2 0x1d3
#define KEY_FN_F3 0x1d4
#define KEY_FN_F4 0x1d5
#define KEY_FN_F5 0x1d6
#define KEY_FN_F6 0x1d7
#define KEY_FN_F7 0x1d8
#define KEY_FN_F8 0x1d9
#define KEY_FN_F9 0x1da
#define KEY_FN_F10 0x1db
#define KEY_FN_F11 0x1dc
#define KEY_FN_F12 0x1dd
#define KEY_FN_1 0x1de
#define KEY_FN_2 0x1df
#define KEY_FN_D 0x1e0
#define KEY_FN_E 0x1e1
#define KEY_FN_F 0x1e2
#define KEY_FN_S 0x1e3
#define KEY_FN_B 0x1e4
#define KEY_MAX 0x1ff
/*
* Relative axes
*/
#define REL_X 0x00
#define REL_Y 0x01
#define REL_Z 0x02
#define REL_RX 0x03
#define REL_RY 0x04
#define REL_RZ 0x05
#define REL_HWHEEL 0x06
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
#define REL_MAX 0x0f
/*
* Absolute axes
*/
#define ABS_X 0x00
#define ABS_Y 0x01
#define ABS_Z 0x02
#define ABS_RX 0x03
#define ABS_RY 0x04
#define ABS_RZ 0x05
#define ABS_THROTTLE 0x06
#define ABS_RUDDER 0x07
#define ABS_WHEEL 0x08
#define ABS_GAS 0x09
#define ABS_BRAKE 0x0a
#define ABS_HAT0X 0x10
#define ABS_HAT0Y 0x11
#define ABS_HAT1X 0x12
#define ABS_HAT1Y 0x13
#define ABS_HAT2X 0x14
#define ABS_HAT2Y 0x15
#define ABS_HAT3X 0x16
#define ABS_HAT3Y 0x17
#define ABS_PRESSURE 0x18
#define ABS_DISTANCE 0x19
#define ABS_TILT_X 0x1a
#define ABS_TILT_Y 0x1b
#define ABS_TOOL_WIDTH 0x1c
#define ABS_VOLUME 0x20
#define ABS_MISC 0x28
#define ABS_MAX 0x3f
/*
* Switch events
*/
#define SW_0 0x00
#define SW_1 0x01
#define SW_2 0x02
#define SW_3 0x03
#define SW_4 0x04
#define SW_5 0x05
#define SW_6 0x06
#define SW_7 0x07
#define SW_MAX 0x0f
/*
* Misc events
*/
#define MSC_SERIAL 0x00
#define MSC_PULSELED 0x01
#define MSC_GESTURE 0x02
#define MSC_RAW 0x03
#define MSC_SCAN 0x04
#define MSC_MAX 0x07
/*
* LEDs
*/
#define LED_NUML 0x00
#define LED_CAPSL 0x01
#define LED_SCROLLL 0x02
#define LED_COMPOSE 0x03
#define LED_KANA 0x04
#define LED_SLEEP 0x05
#define LED_SUSPEND 0x06
#define LED_MUTE 0x07
#define LED_MISC 0x08
#define LED_MAIL 0x09
#define LED_CHARGING 0x0a
#define LED_MAX 0x0f
/*
* Autorepeat values
*/
#define REP_DELAY 0x00
#define REP_PERIOD 0x01
#define REP_MAX 0x01
/*
* Sounds
*/
#define SND_CLICK 0x00
#define SND_BELL 0x01
#define SND_TONE 0x02
#define SND_MAX 0x07
#endif

View File

@@ -0,0 +1,6 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../..
TARGET = src src-dummy
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,6 @@
L4 input library implementation
Subdirectories
contrib: Linux 2.6.11.8 Input driver sources
src: emulation code & library implementation

View File

@@ -0,0 +1,6 @@
L4 input library implementation
Files in this subtree were taken from Linux 2.6 sources (drivers/ directory)
and slightly modified to permit easy integration into L4INPUT library.
Current version: linux-2.6.11.8

View File

@@ -0,0 +1,771 @@
/*
* The input core
*
* Copyright (c) 1999-2002 Vojtech Pavlik
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/major.h>
#include <linux/proc_fs.h>
#include <linux/kobject_uevent.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/devfs_fs_kernel.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("Input core");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(input_register_device);
EXPORT_SYMBOL(input_unregister_device);
EXPORT_SYMBOL(input_register_handler);
EXPORT_SYMBOL(input_unregister_handler);
EXPORT_SYMBOL(input_grab_device);
EXPORT_SYMBOL(input_release_device);
EXPORT_SYMBOL(input_open_device);
EXPORT_SYMBOL(input_close_device);
EXPORT_SYMBOL(input_accept_process);
EXPORT_SYMBOL(input_flush_device);
EXPORT_SYMBOL(input_event);
EXPORT_SYMBOL(input_class);
#define INPUT_DEVICES 256
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
static struct input_handler *input_table[8];
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_bus_input_dir;
DECLARE_WAIT_QUEUE_HEAD(input_devices_poll_wait);
static int input_devices_state;
#endif
static inline unsigned int ms_to_jiffies(unsigned int ms)
{
unsigned int j;
j = (ms * HZ + 500) / 1000;
return (j > 0) ? j : 1;
}
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
if (type > EV_MAX || !test_bit(type, dev->evbit))
return;
add_input_randomness(type, code, value);
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
if (dev->event) dev->event(dev, type, code, value);
break;
case SYN_REPORT:
if (dev->sync) return;
dev->sync = 1;
break;
}
break;
case EV_KEY:
if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
return;
if (value == 2)
break;
change_bit(code, dev->key);
if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->timer.data && value) {
dev->repeat_key = code;
mod_timer(&dev->timer, jiffies + ms_to_jiffies(dev->rep[REP_DELAY]));
}
break;
case EV_ABS:
if (code > ABS_MAX || !test_bit(code, dev->absbit))
return;
if (dev->absfuzz[code]) {
if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
(value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
return;
if ((value > dev->abs[code] - dev->absfuzz[code]) &&
(value < dev->abs[code] + dev->absfuzz[code]))
value = (dev->abs[code] * 3 + value) >> 2;
if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
(value < dev->abs[code] + (dev->absfuzz[code] << 1)))
value = (dev->abs[code] + value) >> 1;
}
if (dev->abs[code] == value)
return;
dev->abs[code] = value;
break;
case EV_REL:
if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
return;
break;
case EV_MSC:
if (code > MSC_MAX || !test_bit(code, dev->mscbit))
return;
if (dev->event) dev->event(dev, type, code, value);
break;
case EV_LED:
if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
return;
change_bit(code, dev->led);
if (dev->event) dev->event(dev, type, code, value);
break;
case EV_SND:
if (code > SND_MAX || !test_bit(code, dev->sndbit))
return;
if (dev->event) dev->event(dev, type, code, value);
break;
case EV_REP:
if (code > REP_MAX || value < 0 || dev->rep[code] == value) return;
dev->rep[code] = value;
if (dev->event) dev->event(dev, type, code, value);
break;
case EV_FF:
if (dev->event) dev->event(dev, type, code, value);
break;
}
if (type != EV_SYN)
dev->sync = 0;
if (dev->grab)
dev->grab->handler->event(dev->grab, type, code, value);
else
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
}
static void input_repeat_key(unsigned long data)
{
struct input_dev *dev = (void *) data;
if (!test_bit(dev->repeat_key, dev->key))
return;
input_event(dev, EV_KEY, dev->repeat_key, 2);
input_sync(dev);
mod_timer(&dev->timer, jiffies + ms_to_jiffies(dev->rep[REP_PERIOD]));
}
int input_accept_process(struct input_handle *handle, struct file *file)
{
if (handle->dev->accept)
return handle->dev->accept(handle->dev, file);
return 0;
}
int input_grab_device(struct input_handle *handle)
{
if (handle->dev->grab)
return -EBUSY;
handle->dev->grab = handle;
return 0;
}
void input_release_device(struct input_handle *handle)
{
if (handle->dev->grab == handle)
handle->dev->grab = NULL;
}
int input_open_device(struct input_handle *handle)
{
handle->open++;
if (handle->dev->open)
return handle->dev->open(handle->dev);
return 0;
}
int input_flush_device(struct input_handle* handle, struct file* file)
{
if (handle->dev->flush)
return handle->dev->flush(handle->dev, file);
return 0;
}
void input_close_device(struct input_handle *handle)
{
input_release_device(handle);
if (handle->dev->close)
handle->dev->close(handle->dev);
handle->open--;
}
static void input_link_handle(struct input_handle *handle)
{
list_add_tail(&handle->d_node, &handle->dev->h_list);
list_add_tail(&handle->h_node, &handle->handler->h_list);
}
#define MATCH_BIT(bit, max) \
for (i = 0; i < NBITS(max); i++) \
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
break; \
if (i != NBITS(max)) \
continue;
static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
{
int i;
for (; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->id.bustype != dev->id.bustype)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->id.vendor != dev->id.vendor)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->id.product != dev->id.product)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->id.version != dev->id.version)
continue;
MATCH_BIT(evbit, EV_MAX);
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit, FF_MAX);
return id;
}
return NULL;
}
/*
* Input hotplugging interface - loading event handlers based on
* device bitfields.
*/
#ifdef CONFIG_HOTPLUG
/*
* Input hotplugging invokes what /proc/sys/kernel/hotplug says
* (normally /sbin/hotplug) when input devices get added or removed.
*
* This invokes a user mode policy agent, typically helping to load driver
* or other modules, configure the device, and more. Drivers can provide
* a MODULE_DEVICE_TABLE to help with module loading subtasks.
*
*/
#define SPRINTF_BIT_A(bit, name, max) \
do { \
envp[i++] = scratch; \
scratch += sprintf(scratch, name); \
for (j = NBITS(max) - 1; j >= 0; j--) \
if (dev->bit[j]) break; \
for (; j >= 0; j--) \
scratch += sprintf(scratch, "%lx ", dev->bit[j]); \
scratch++; \
} while (0)
#define SPRINTF_BIT_A2(bit, name, max, ev) \
do { \
if (test_bit(ev, dev->evbit)) \
SPRINTF_BIT_A(bit, name, max); \
} while (0)
static void input_call_hotplug(char *verb, struct input_dev *dev)
{
char *argv[3], **envp, *buf, *scratch;
int i = 0, j, value;
if (!hotplug_path[0]) {
printk(KERN_ERR "input.c: calling hotplug without a hotplug agent defined\n");
return;
}
if (in_interrupt()) {
printk(KERN_ERR "input.c: calling hotplug from interrupt\n");
return;
}
if (!current->fs->root) {
printk(KERN_WARNING "input.c: calling hotplug without valid filesystem\n");
return;
}
if (!(envp = (char **) kmalloc(20 * sizeof(char *), GFP_KERNEL))) {
printk(KERN_ERR "input.c: not enough memory allocating hotplug environment\n");
return;
}
if (!(buf = kmalloc(1024, GFP_KERNEL))) {
kfree (envp);
printk(KERN_ERR "input.c: not enough memory allocating hotplug environment\n");
return;
}
argv[0] = hotplug_path;
argv[1] = "input";
argv[2] = NULL;
envp[i++] = "HOME=/";
envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
scratch = buf;
envp[i++] = scratch;
scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
envp[i++] = scratch;
scratch += sprintf(scratch, "PRODUCT=%x/%x/%x/%x",
dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version) + 1;
if (dev->name) {
envp[i++] = scratch;
scratch += sprintf(scratch, "NAME=%s", dev->name) + 1;
}
if (dev->phys) {
envp[i++] = scratch;
scratch += sprintf(scratch, "PHYS=%s", dev->phys) + 1;
}
SPRINTF_BIT_A(evbit, "EV=", EV_MAX);
SPRINTF_BIT_A2(keybit, "KEY=", KEY_MAX, EV_KEY);
SPRINTF_BIT_A2(relbit, "REL=", REL_MAX, EV_REL);
SPRINTF_BIT_A2(absbit, "ABS=", ABS_MAX, EV_ABS);
SPRINTF_BIT_A2(mscbit, "MSC=", MSC_MAX, EV_MSC);
SPRINTF_BIT_A2(ledbit, "LED=", LED_MAX, EV_LED);
SPRINTF_BIT_A2(sndbit, "SND=", SND_MAX, EV_SND);
SPRINTF_BIT_A2(ffbit, "FF=", FF_MAX, EV_FF);
envp[i++] = NULL;
#ifdef INPUT_DEBUG
printk(KERN_DEBUG "input.c: calling %s %s [%s %s %s %s %s]\n",
argv[0], argv[1], envp[0], envp[1], envp[2], envp[3], envp[4]);
#endif
value = call_usermodehelper(argv [0], argv, envp, 0);
kfree(buf);
kfree(envp);
#ifdef INPUT_DEBUG
if (value != 0)
printk(KERN_DEBUG "input.c: hotplug returned %d\n", value);
#endif
}
#endif
void input_register_device(struct input_dev *dev)
{
struct input_handle *handle;
struct input_handler *handler;
struct input_device_id *id;
set_bit(EV_SYN, dev->evbit);
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer);
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
INIT_LIST_HEAD(&dev->h_list);
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))
if ((handle = handler->connect(handler, dev, id)))
input_link_handle(handle);
#ifdef CONFIG_HOTPLUG
input_call_hotplug("add", dev);
#endif
#ifdef CONFIG_PROC_FS
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
}
void input_unregister_device(struct input_dev *dev)
{
struct list_head * node, * next;
if (!dev) return;
del_timer_sync(&dev->timer);
list_for_each_safe(node, next, &dev->h_list) {
struct input_handle * handle = to_handle(node);
list_del_init(&handle->d_node);
list_del_init(&handle->h_node);
handle->handler->disconnect(handle);
}
#ifdef CONFIG_HOTPLUG
input_call_hotplug("remove", dev);
#endif
list_del_init(&dev->node);
#ifdef CONFIG_PROC_FS
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
}
void input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
struct input_handle *handle;
struct input_device_id *id;
if (!handler) return;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL)
input_table[handler->minor >> 5] = handler;
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))
if ((handle = handler->connect(handler, dev, id)))
input_link_handle(handle);
#ifdef CONFIG_PROC_FS
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
}
void input_unregister_handler(struct input_handler *handler)
{
struct list_head * node, * next;
list_for_each_safe(node, next, &handler->h_list) {
struct input_handle * handle = to_handle_h(node);
list_del_init(&handle->h_node);
list_del_init(&handle->d_node);
handler->disconnect(handle);
}
list_del_init(&handler->node);
if (handler->fops != NULL)
input_table[handler->minor >> 5] = NULL;
#ifdef CONFIG_PROC_FS
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
}
#ifndef L4INPUT
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[iminor(inode) >> 5];
struct file_operations *old_fops, *new_fops = NULL;
int err;
/* No load-on-demand here? */
if (!handler || !(new_fops = fops_get(handler->fops)))
return -ENODEV;
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open) {
fops_put(new_fops);
return -ENODEV;
}
old_fops = file->f_op;
file->f_op = new_fops;
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}
static struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
#else
static int input_open_file(struct inode *inode, struct file *file)
{
return -EPERM;
}
static struct file_operations input_fops = {
.open = input_open_file,
};
#endif
#ifdef CONFIG_PROC_FS
#define SPRINTF_BIT_B(bit, name, max) \
do { \
len += sprintf(buf + len, "B: %s", name); \
for (i = NBITS(max) - 1; i >= 0; i--) \
if (dev->bit[i]) break; \
for (; i >= 0; i--) \
len += sprintf(buf + len, "%lx ", dev->bit[i]); \
len += sprintf(buf + len, "\n"); \
} while (0)
#define SPRINTF_BIT_B2(bit, name, max, ev) \
do { \
if (test_bit(ev, dev->evbit)) \
SPRINTF_BIT_B(bit, name, max); \
} while (0)
static unsigned int input_devices_poll(struct file *file, poll_table *wait)
{
int state = input_devices_state;
poll_wait(file, &input_devices_poll_wait, wait);
if (state != input_devices_state)
return POLLIN | POLLRDNORM;
return 0;
}
static int input_devices_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
{
struct input_dev *dev;
struct input_handle *handle;
off_t at = 0;
int i, len, cnt = 0;
list_for_each_entry(dev, &input_dev_list, node) {
len = sprintf(buf, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",
dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);
len += sprintf(buf + len, "N: Name=\"%s\"\n", dev->name ? dev->name : "");
len += sprintf(buf + len, "P: Phys=%s\n", dev->phys ? dev->phys : "");
len += sprintf(buf + len, "H: Handlers=");
list_for_each_entry(handle, &dev->h_list, d_node)
len += sprintf(buf + len, "%s ", handle->name);
len += sprintf(buf + len, "\n");
SPRINTF_BIT_B(evbit, "EV=", EV_MAX);
SPRINTF_BIT_B2(keybit, "KEY=", KEY_MAX, EV_KEY);
SPRINTF_BIT_B2(relbit, "REL=", REL_MAX, EV_REL);
SPRINTF_BIT_B2(absbit, "ABS=", ABS_MAX, EV_ABS);
SPRINTF_BIT_B2(mscbit, "MSC=", MSC_MAX, EV_MSC);
SPRINTF_BIT_B2(ledbit, "LED=", LED_MAX, EV_LED);
SPRINTF_BIT_B2(sndbit, "SND=", SND_MAX, EV_SND);
SPRINTF_BIT_B2(ffbit, "FF=", FF_MAX, EV_FF);
len += sprintf(buf + len, "\n");
at += len;
if (at >= pos) {
if (!*start) {
*start = buf + (pos - (at - len));
cnt = at - pos;
} else cnt += len;
buf += len;
if (cnt >= count)
break;
}
}
if (&dev->node == &input_dev_list)
*eof = 1;
return (count > cnt) ? cnt : count;
}
static int input_handlers_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
{
struct input_handler *handler;
off_t at = 0;
int len = 0, cnt = 0;
int i = 0;
list_for_each_entry(handler, &input_handler_list, node) {
if (handler->fops)
len = sprintf(buf, "N: Number=%d Name=%s Minor=%d\n",
i++, handler->name, handler->minor);
else
len = sprintf(buf, "N: Number=%d Name=%s\n",
i++, handler->name);
at += len;
if (at >= pos) {
if (!*start) {
*start = buf + (pos - (at - len));
cnt = at - pos;
} else cnt += len;
buf += len;
if (cnt >= count)
break;
}
}
if (&handler->node == &input_handler_list)
*eof = 1;
return (count > cnt) ? cnt : count;
}
static int __init input_proc_init(void)
{
struct proc_dir_entry *entry;
proc_bus_input_dir = proc_mkdir("input", proc_bus);
if (proc_bus_input_dir == NULL)
return -ENOMEM;
proc_bus_input_dir->owner = THIS_MODULE;
entry = create_proc_read_entry("devices", 0, proc_bus_input_dir, input_devices_read, NULL);
if (entry == NULL) {
remove_proc_entry("input", proc_bus);
return -ENOMEM;
}
entry->owner = THIS_MODULE;
entry->proc_fops->poll = input_devices_poll;
entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL);
if (entry == NULL) {
remove_proc_entry("devices", proc_bus_input_dir);
remove_proc_entry("input", proc_bus);
return -ENOMEM;
}
entry->owner = THIS_MODULE;
return 0;
}
#else /* !CONFIG_PROC_FS */
static inline int input_proc_init(void) { return 0; }
#endif
struct class_simple *input_class;
static int __init input_init(void)
{
int retval = -ENOMEM;
input_class = class_simple_create(THIS_MODULE, "input");
#ifndef L4INPUT
if (IS_ERR(input_class))
return PTR_ERR(input_class);
#endif
input_proc_init();
retval = register_chrdev(INPUT_MAJOR, "input", &input_fops);
if (retval) {
printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
remove_proc_entry("devices", proc_bus_input_dir);
remove_proc_entry("handlers", proc_bus_input_dir);
remove_proc_entry("input", proc_bus);
class_simple_destroy(input_class);
return retval;
}
retval = devfs_mk_dir("input");
if (retval) {
remove_proc_entry("devices", proc_bus_input_dir);
remove_proc_entry("handlers", proc_bus_input_dir);
remove_proc_entry("input", proc_bus);
unregister_chrdev(INPUT_MAJOR, "input");
class_simple_destroy(input_class);
}
return retval;
}
static void __exit input_exit(void)
{
remove_proc_entry("devices", proc_bus_input_dir);
remove_proc_entry("handlers", proc_bus_input_dir);
remove_proc_entry("input", proc_bus);
devfs_remove("input");
unregister_chrdev(INPUT_MAJOR, "input");
class_simple_destroy(input_class);
}
subsys_initcall(input_init);
module_exit(input_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,99 @@
/*
* PC Speaker beeper driver for Linux
*
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 1992 Orest Zborowski
*
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <asm/8253pit.h>
#include <asm/io.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("PC Speaker beeper driver");
MODULE_LICENSE("GPL");
static char pcspkr_name[] = "PC Speaker";
static char pcspkr_phys[] = "isa0061/input0";
static struct input_dev pcspkr_dev;
DEFINE_SPINLOCK(i8253_beep_lock);
static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
unsigned int count = 0;
unsigned long flags;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL: if (value) value = 1000;
case SND_TONE: break;
default: return -1;
}
if (value > 20 && value < 32767)
count = PIT_TICK_RATE / value;
#ifdef L4INPUT
spin_lock_irqsave(&i8253_beep_lock, flags);
#endif
if (count) {
/* enable counter 2 */
outb_p(inb_p(0x61) | 3, 0x61);
/* set command for counter 2, 2 byte write */
outb_p(0xB6, 0x43);
/* select desired HZ */
outb_p(count & 0xff, 0x42);
outb((count >> 8) & 0xff, 0x42);
} else {
/* disable counter 2 */
outb(inb_p(0x61) & 0xFC, 0x61);
}
spin_unlock_irqrestore(&i8253_beep_lock, flags);
return 0;
}
static int __init pcspkr_init(void)
{
spin_lock_init(&i8253_beep_lock);
pcspkr_dev.evbit[0] = BIT(EV_SND);
pcspkr_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
pcspkr_dev.event = pcspkr_event;
pcspkr_dev.name = pcspkr_name;
pcspkr_dev.phys = pcspkr_phys;
pcspkr_dev.id.bustype = BUS_ISA;
pcspkr_dev.id.vendor = 0x001f;
pcspkr_dev.id.product = 0x0001;
pcspkr_dev.id.version = 0x0100;
input_register_device(&pcspkr_dev);
printk(KERN_INFO "input: %s\n", pcspkr_name);
return 0;
}
static void __exit pcspkr_exit(void)
{
input_unregister_device(&pcspkr_dev);
}
module_init(pcspkr_init);
module_exit(pcspkr_exit);

View File

@@ -0,0 +1,424 @@
/*
* ALPS touchpad PS/2 mouse driver
*
* Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
* Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
* Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
*
* ALPS detection, tap switching and status querying info is taken from
* tpconfig utility (by C. Scott Ananian and Bruce Kall).
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include "psmouse.h"
#include "alps.h"
#undef DEBUG
#ifdef DEBUG
#define dbg(format, arg...) printk(KERN_INFO "alps.c: " format "\n", ## arg)
#else
#define dbg(format, arg...) do {} while (0)
#endif
#define ALPS_MODEL_GLIDEPOINT 1
#define ALPS_MODEL_DUALPOINT 2
struct alps_model_info {
unsigned char signature[3];
unsigned char model;
} alps_model_data[] = {
/* { { 0x33, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT }, */
{ { 0x53, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },
{ { 0x53, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT },
{ { 0x63, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },
{ { 0x63, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT },
{ { 0x73, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },
{ { 0x73, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT },
{ { 0x63, 0x02, 0x28 }, ALPS_MODEL_GLIDEPOINT },
/* { { 0x63, 0x02, 0x3c }, ALPS_MODEL_GLIDEPOINT }, */
/* { { 0x63, 0x02, 0x50 }, ALPS_MODEL_GLIDEPOINT }, */
{ { 0x63, 0x02, 0x64 }, ALPS_MODEL_GLIDEPOINT },
{ { 0x20, 0x02, 0x0e }, ALPS_MODEL_DUALPOINT },
{ { 0x22, 0x02, 0x0a }, ALPS_MODEL_DUALPOINT },
{ { 0x22, 0x02, 0x14 }, ALPS_MODEL_DUALPOINT },
{ { 0x63, 0x03, 0xc8 }, ALPS_MODEL_DUALPOINT },
};
/*
* ALPS abolute Mode
* byte 0: 1 1 1 1 1 mid0 rig0 lef0
* byte 1: 0 x6 x5 x4 x3 x2 x1 x0
* byte 2: 0 x10 x9 x8 x7 up1 fin ges
* byte 3: 0 y9 y8 y7 1 mid1 rig1 lef1
* byte 4: 0 y6 y5 y4 y3 y2 y1 y0
* byte 5: 0 z6 z5 z4 z3 z2 z1 z0
*
* On a dualpoint, {mid,rig,lef}0 are the stick, 1 are the pad.
* We just 'or' them together for now.
*
* We used to send 'ges'tures as BTN_TOUCH but this made it impossible
* to disable tap events in the synaptics driver since the driver
* was unable to distinguish a gesture tap from an actual button click.
* A tap gesture now creates an emulated touch that the synaptics
* driver can interpret as a tap event, if MaxTapTime=0 and
* MaxTapMove=0 then the driver will ignore taps.
*
* The touchpad on an 'Acer Aspire' has 4 buttons:
* left,right,up,down.
* This device always sets {mid,rig,lef}0 to 1 and
* reflects left,right,down,up in lef1,rig1,mid1,up1.
*/
static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs)
{
unsigned char *packet = psmouse->packet;
struct input_dev *dev = &psmouse->dev;
int x, y, z;
int left = 0, right = 0, middle = 0;
input_regs(dev, regs);
if ((packet[0] & 0xc8) == 0x08) { /* 3-byte PS/2 packet */
x = packet[1];
if (packet[0] & 0x10)
x = x - 256;
y = packet[2];
if (packet[0] & 0x20)
y = y - 256;
left = (packet[0] ) & 1;
right = (packet[0] >> 1) & 1;
input_report_rel(dev, REL_X, x);
input_report_rel(dev, REL_Y, -y);
input_report_key(dev, BTN_A, left);
input_report_key(dev, BTN_B, right);
input_sync(dev);
return;
}
x = (packet[1] & 0x7f) | ((packet[2] & 0x78)<<(7-3));
y = (packet[4] & 0x7f) | ((packet[3] & 0x70)<<(7-4));
z = packet[5];
if (z == 127) { /* DualPoint stick is relative, not absolute */
if (x > 383)
x = x - 768;
if (y > 255)
y = y - 512;
left = packet[3] & 1;
right = (packet[3] >> 1) & 1;
input_report_rel(dev, REL_X, x);
input_report_rel(dev, REL_Y, -y);
input_report_key(dev, BTN_LEFT, left);
input_report_key(dev, BTN_RIGHT, right);
input_sync(dev);
return;
}
if (z > 30) input_report_key(dev, BTN_TOUCH, 1);
if (z < 25) input_report_key(dev, BTN_TOUCH, 0);
if (z > 0) {
input_report_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
}
input_report_abs(dev, ABS_PRESSURE, z);
input_report_key(dev, BTN_TOOL_FINGER, z > 0);
left |= (packet[2] ) & 1;
left |= (packet[3] ) & 1;
right |= (packet[3] >> 1) & 1;
if (packet[0] == 0xff) {
int back = (packet[3] >> 2) & 1;
int forward = (packet[2] >> 2) & 1;
if (back && forward) {
middle = 1;
back = 0;
forward = 0;
}
input_report_key(dev, BTN_BACK, back);
input_report_key(dev, BTN_FORWARD, forward);
} else {
left |= (packet[0] ) & 1;
right |= (packet[0] >> 1) & 1;
middle |= (packet[0] >> 2) & 1;
middle |= (packet[3] >> 2) & 1;
}
input_report_key(dev, BTN_LEFT, left);
input_report_key(dev, BTN_RIGHT, right);
input_report_key(dev, BTN_MIDDLE, middle);
input_sync(dev);
}
static psmouse_ret_t alps_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
{
if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
if (psmouse->pktcnt == 3) {
alps_process_packet(psmouse, regs);
return PSMOUSE_FULL_PACKET;
}
return PSMOUSE_GOOD_DATA;
}
/* ALPS absolute mode packets start with 0b11111mrl */
if ((psmouse->packet[0] & 0xf8) != 0xf8)
return PSMOUSE_BAD_DATA;
/* Bytes 2 - 6 should have 0 in the highest bit */
if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 &&
(psmouse->packet[psmouse->pktcnt-1] & 0x80))
return PSMOUSE_BAD_DATA;
if (psmouse->pktcnt == 6) {
alps_process_packet(psmouse, regs);
return PSMOUSE_FULL_PACKET;
}
return PSMOUSE_GOOD_DATA;
}
int alps_get_model(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
int i;
/*
* First try "E6 report".
* ALPS should return 0x00,0x00,0x0a or 0x00,0x00,0x64
*/
param[0] = 0;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
return -1;
param[0] = param[1] = param[2] = 0xff;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
if (param[0] != 0x00 || param[1] != 0x00 || (param[2] != 0x0a && param[2] != 0x64))
return -1;
/* Now try "E7 report". ALPS should return 0x33 in byte 1 */
param[0] = 0;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21))
return -1;
param[0] = param[1] = param[2] = 0xff;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
for (i = 0; i < ARRAY_SIZE(alps_model_data); i++)
if (!memcmp(param, alps_model_data[i].signature, sizeof(alps_model_data[i].signature)))
return alps_model_data[i].model;
return -1;
}
/*
* For DualPoint devices select the device that should respond to
* subsequent commands. It looks like glidepad is behind stickpointer,
* I'd thought it would be other way around...
*/
static int alps_passthrough_mode(struct psmouse *psmouse, int enable)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[3];
int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
if (ps2_command(ps2dev, NULL, cmd) ||
ps2_command(ps2dev, NULL, cmd) ||
ps2_command(ps2dev, NULL, cmd) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
return -1;
/* we may get 3 more bytes, just ignore them */
ps2_command(ps2dev, param, 0x0300);
return 0;
}
static int alps_absolute_mode(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
/* Try ALPS magic knock - 4 disable before enable */
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
return -1;
/*
* Switch mouse to poll (remote) mode so motion data will not
* get in our way
*/
return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
}
static int alps_get_status(struct psmouse *psmouse, char *param)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
/* Get status: 0xF5 0xF5 0xF5 0xE9 */
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
return 0;
}
/*
* Turn touchpad tapping on or off. The sequences are:
* 0xE9 0xF5 0xF5 0xF3 0x0A to enable,
* 0xE9 0xF5 0xF5 0xE8 0x00 to disable.
* My guess that 0xE9 (GetInfo) is here as a sync point.
* For models that also have stickpointer (DualPoints) its tapping
* is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but
* we don't fiddle with it.
*/
static int alps_tap_mode(struct psmouse *psmouse, int enable)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES;
unsigned char tap_arg = enable ? 0x0A : 0x00;
unsigned char param[4];
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
ps2_command(ps2dev, &tap_arg, cmd))
return -1;
if (alps_get_status(psmouse, param))
return -1;
return 0;
}
static int alps_reconnect(struct psmouse *psmouse)
{
int model;
unsigned char param[4];
if ((model = alps_get_model(psmouse)) < 0)
return -1;
if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1))
return -1;
if (alps_get_status(psmouse, param))
return -1;
if (param[0] & 0x04)
alps_tap_mode(psmouse, 0);
if (alps_absolute_mode(psmouse)) {
printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
return -1;
}
if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0))
return -1;
return 0;
}
static void alps_disconnect(struct psmouse *psmouse)
{
psmouse_reset(psmouse);
}
int alps_init(struct psmouse *psmouse)
{
unsigned char param[4];
int model;
if ((model = alps_get_model(psmouse)) < 0)
return -1;
printk(KERN_INFO "ALPS Touchpad (%s) detected\n",
model == ALPS_MODEL_GLIDEPOINT ? "Glidepoint" : "Dualpoint");
if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1))
return -1;
if (alps_get_status(psmouse, param)) {
printk(KERN_ERR "alps.c: touchpad status report request failed\n");
return -1;
}
if (param[0] & 0x04) {
printk(KERN_INFO " Disabling hardware tapping\n");
if (alps_tap_mode(psmouse, 0))
printk(KERN_WARNING "alps.c: Failed to disable hardware tapping\n");
}
if (alps_absolute_mode(psmouse)) {
printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
return -1;
}
if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0))
return -1;
psmouse->dev.evbit[LONG(EV_REL)] |= BIT(EV_REL);
psmouse->dev.relbit[LONG(REL_X)] |= BIT(REL_X);
psmouse->dev.relbit[LONG(REL_Y)] |= BIT(REL_Y);
psmouse->dev.keybit[LONG(BTN_A)] |= BIT(BTN_A);
psmouse->dev.keybit[LONG(BTN_B)] |= BIT(BTN_B);
psmouse->dev.evbit[LONG(EV_ABS)] |= BIT(EV_ABS);
input_set_abs_params(&psmouse->dev, ABS_X, 0, 1023, 0, 0);
input_set_abs_params(&psmouse->dev, ABS_Y, 0, 1023, 0, 0);
input_set_abs_params(&psmouse->dev, ABS_PRESSURE, 0, 127, 0, 0);
psmouse->dev.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
psmouse->dev.keybit[LONG(BTN_TOOL_FINGER)] |= BIT(BTN_TOOL_FINGER);
psmouse->dev.keybit[LONG(BTN_FORWARD)] |= BIT(BTN_FORWARD);
psmouse->dev.keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK);
psmouse->protocol_handler = alps_process_byte;
psmouse->disconnect = alps_disconnect;
psmouse->reconnect = alps_reconnect;
psmouse->pktsize = 6;
return 0;
}
int alps_detect(struct psmouse *psmouse, int set_properties)
{
if (alps_get_model(psmouse) < 0)
return -1;
if (set_properties) {
psmouse->vendor = "ALPS";
psmouse->name = "TouchPad";
}
return 0;
}

View File

@@ -0,0 +1,17 @@
/*
* ALPS touchpad PS/2 mouse driver
*
* Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _ALPS_H
#define _ALPS_H
int alps_detect(struct psmouse *psmouse, int set_properties);
int alps_init(struct psmouse *psmouse);
#endif

View File

@@ -0,0 +1,394 @@
/*
* Logitech PS/2++ mouse driver
*
* Copyright (c) 1999-2003 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2003 Eric Wong <eric@yhbt.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include "psmouse.h"
#include "logips2pp.h"
/* Logitech mouse types */
#define PS2PP_KIND_WHEEL 1
#define PS2PP_KIND_MX 2
#define PS2PP_KIND_TP3 3
/* Logitech mouse features */
#define PS2PP_WHEEL 0x01
#define PS2PP_HWHEEL 0x02
#define PS2PP_SIDE_BTN 0x04
#define PS2PP_EXTRA_BTN 0x08
#define PS2PP_TASK_BTN 0x10
#define PS2PP_NAV_BTN 0x20
struct ps2pp_info {
const int model;
unsigned const int kind;
unsigned const int features;
};
/*
* Process a PS2++ or PS2T++ packet.
*/
static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
{
struct input_dev *dev = &psmouse->dev;
unsigned char *packet = psmouse->packet;
if (psmouse->pktcnt < 3)
return PSMOUSE_GOOD_DATA;
/*
* Full packet accumulated, process it
*/
input_regs(dev, regs);
if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) {
/* Logitech extended packet */
switch ((packet[1] >> 4) | (packet[0] & 0x30)) {
case 0x0d: /* Mouse extra info */
input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
(int) (packet[2] & 8) - (int) (packet[2] & 7));
input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
break;
case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
input_report_key(dev, BTN_SIDE, (packet[2]) & 1);
input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1);
input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1);
input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1);
input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1);
break;
case 0x0f: /* TouchPad extra info */
input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
(int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7));
packet[0] = packet[2] | 0x08;
break;
#ifdef DEBUG
default:
printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n",
(packet[1] >> 4) | (packet[0] & 0x30));
#endif
}
} else {
/* Standard PS/2 motion data */
input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
}
input_report_key(dev, BTN_LEFT, packet[0] & 1);
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
input_sync(dev);
return PSMOUSE_FULL_PACKET;
}
/*
* ps2pp_cmd() sends a PS2++ command, sliced into two bit
* pieces through the SETRES command. This is needed to send extended
* commands to mice on notebooks that try to understand the PS/2 protocol
* Ugly.
*/
static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command)
{
if (psmouse_sliced_command(psmouse, command))
return -1;
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL))
return -1;
return 0;
}
/*
* SmartScroll / CruiseControl for some newer Logitech mice Defaults to
* enabled if we do nothing to it. Of course I put this in because I want it
* disabled :P
* 1 - enabled (if previously disabled, also default)
* 0 - disabled
*/
static void ps2pp_set_smartscroll(struct psmouse *psmouse, unsigned int smartscroll)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
if (smartscroll > 1)
smartscroll = 1;
ps2pp_cmd(psmouse, param, 0x32);
param[0] = 0;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
param[0] = smartscroll;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
}
#ifndef L4INPUT
static ssize_t psmouse_attr_show_smartscroll(struct psmouse *psmouse, char *buf)
{
return sprintf(buf, "%d\n", psmouse->smartscroll ? 1 : 0);
}
static ssize_t psmouse_attr_set_smartscroll(struct psmouse *psmouse, const char *buf, size_t count)
{
unsigned long value;
char *rest;
value = simple_strtoul(buf, &rest, 10);
if (*rest || value > 1)
return -EINVAL;
ps2pp_set_smartscroll(psmouse, value);
psmouse->smartscroll = value;
return count;
}
PSMOUSE_DEFINE_ATTR(smartscroll);
#endif
/*
* Support 800 dpi resolution _only_ if the user wants it (there are good
* reasons to not use it even if the mouse supports it, and of course there are
* also good reasons to use it, let the user decide).
*/
static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
if (resolution > 400) {
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param = 3;
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
psmouse->resolution = 800;
} else
psmouse_set_resolution(psmouse, resolution);
}
static void ps2pp_disconnect(struct psmouse *psmouse)
{
device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll);
}
static struct ps2pp_info *get_model_info(unsigned char model)
{
static struct ps2pp_info ps2pp_list[] = {
{ 12, 0, PS2PP_SIDE_BTN},
{ 13, 0, 0 },
{ 40, 0, PS2PP_SIDE_BTN },
{ 41, 0, PS2PP_SIDE_BTN },
{ 42, 0, PS2PP_SIDE_BTN },
{ 43, 0, PS2PP_SIDE_BTN },
{ 50, 0, 0 },
{ 51, 0, 0 },
{ 52, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL },
{ 53, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 61, PS2PP_KIND_MX,
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, /* MX700 */
{ 73, 0, PS2PP_SIDE_BTN },
{ 75, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 76, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 80, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL },
{ 81, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 83, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 88, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
{ 96, 0, 0 },
{ 97, PS2PP_KIND_TP3, PS2PP_WHEEL | PS2PP_HWHEEL },
{ 100, PS2PP_KIND_MX,
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, /* MX510 */
{ 112, PS2PP_KIND_MX,
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, /* MX500 */
{ 114, PS2PP_KIND_MX,
PS2PP_WHEEL | PS2PP_SIDE_BTN |
PS2PP_TASK_BTN | PS2PP_EXTRA_BTN }, /* M310 */
{ }
};
int i;
for (i = 0; ps2pp_list[i].model; i++)
if (model == ps2pp_list[i].model)
return &ps2pp_list[i];
return NULL;
}
/*
* Set up input device's properties based on the detected mouse model.
*/
static void ps2pp_set_model_properties(struct psmouse *psmouse, struct ps2pp_info *model_info,
int using_ps2pp)
{
if (model_info->features & PS2PP_SIDE_BTN)
set_bit(BTN_SIDE, psmouse->dev.keybit);
if (model_info->features & PS2PP_EXTRA_BTN)
set_bit(BTN_EXTRA, psmouse->dev.keybit);
if (model_info->features & PS2PP_TASK_BTN)
set_bit(BTN_TASK, psmouse->dev.keybit);
if (model_info->features & PS2PP_NAV_BTN) {
set_bit(BTN_FORWARD, psmouse->dev.keybit);
set_bit(BTN_BACK, psmouse->dev.keybit);
}
if (model_info->features & PS2PP_WHEEL)
set_bit(REL_WHEEL, psmouse->dev.relbit);
if (model_info->features & PS2PP_HWHEEL)
set_bit(REL_HWHEEL, psmouse->dev.relbit);
switch (model_info->kind) {
case PS2PP_KIND_WHEEL:
psmouse->name = "Wheel Mouse";
break;
case PS2PP_KIND_MX:
psmouse->name = "MX Mouse";
break;
case PS2PP_KIND_TP3:
psmouse->name = "TouchPad 3";
break;
default:
/*
* Set name to "Mouse" only when using PS2++,
* otherwise let other protocols define suitable
* name
*/
if (using_ps2pp)
psmouse->name = "Mouse";
break;
}
}
/*
* Logitech magic init. Detect whether the mouse is a Logitech one
* and its exact model and try turning on extended protocol for ones
* that support it.
*/
int ps2pp_init(struct psmouse *psmouse, int set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
unsigned char model, buttons;
struct ps2pp_info *model_info;
int use_ps2pp = 0;
param[0] = 0;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
param[1] = 0;
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
if (!param[1])
return -1;
model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
buttons = param[1];
if ((model_info = get_model_info(model)) != NULL) {
/*
* Do Logitech PS2++ / PS2T++ magic init.
*/
if (model == 97) { /* Touch Pad 3 */
/* Unprotect RAM */
param[0] = 0x11; param[1] = 0x04; param[2] = 0x68;
ps2_command(ps2dev, param, 0x30d1);
/* Enable features */
param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b;
ps2_command(ps2dev, param, 0x30d1);
/* Enable PS2++ */
param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3;
ps2_command(ps2dev, param, 0x30d1);
param[0] = 0;
if (!ps2_command(ps2dev, param, 0x13d1) &&
param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) {
use_ps2pp = 1;
}
} else {
param[0] = param[1] = param[2] = 0;
ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */
ps2pp_cmd(psmouse, param, 0xDB);
if ((param[0] & 0x78) == 0x48 &&
(param[1] & 0xf3) == 0xc2 &&
(param[2] & 0x03) == ((param[1] >> 2) & 3)) {
ps2pp_set_smartscroll(psmouse, psmouse->smartscroll);
use_ps2pp = 1;
}
}
}
if (set_properties) {
psmouse->vendor = "Logitech";
psmouse->model = model;
if (use_ps2pp) {
psmouse->protocol_handler = ps2pp_process_byte;
psmouse->pktsize = 3;
if (model_info->kind != PS2PP_KIND_TP3) {
psmouse->set_resolution = ps2pp_set_resolution;
psmouse->disconnect = ps2pp_disconnect;
device_create_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll);
}
}
if (buttons < 3)
clear_bit(BTN_MIDDLE, psmouse->dev.keybit);
if (buttons < 2)
clear_bit(BTN_RIGHT, psmouse->dev.keybit);
if (model_info)
ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
}
return use_ps2pp ? 0 : -1;
}

View File

@@ -0,0 +1,16 @@
/*
* Logitech PS/2++ mouse driver header
*
* Copyright (c) 2003 Vojtech Pavlik <vojtech@suse.cz>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _LOGIPS2PP_H
#define _LOGIPS2PP_H
int ps2pp_init(struct psmouse *psmouse, int set_properties);
#endif

View File

@@ -0,0 +1,978 @@
/*
* PS/2 mouse driver
*
* Copyright (c) 1999-2002 Vojtech Pavlik
* Copyright (c) 2003-2004 Dmitry Torokhov
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
#include <linux/libps2.h>
#include "psmouse.h"
#include "synaptics.h"
#include "logips2pp.h"
#include "alps.h"
#define DRIVER_DESC "PS/2 mouse driver"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static char *psmouse_proto;
static unsigned int psmouse_max_proto = -1U;
module_param_named(proto, psmouse_proto, charp, 0);
MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps). Useful for KVM switches.");
static unsigned int psmouse_resolution = 200;
module_param_named(resolution, psmouse_resolution, uint, 0);
MODULE_PARM_DESC(resolution, "Resolution, in dpi.");
static unsigned int psmouse_rate = 100;
module_param_named(rate, psmouse_rate, uint, 0);
MODULE_PARM_DESC(rate, "Report rate, in reports per second.");
static unsigned int psmouse_smartscroll = 1;
module_param_named(smartscroll, psmouse_smartscroll, bool, 0);
MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
static unsigned int psmouse_resetafter;
module_param_named(resetafter, psmouse_resetafter, uint, 0);
MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
#ifndef L4INPUT
PSMOUSE_DEFINE_ATTR(rate);
PSMOUSE_DEFINE_ATTR(resolution);
PSMOUSE_DEFINE_ATTR(resetafter);
#endif
__obsolete_setup("psmouse_noext");
__obsolete_setup("psmouse_resolution=");
__obsolete_setup("psmouse_smartscroll=");
__obsolete_setup("psmouse_resetafter=");
__obsolete_setup("psmouse_rate=");
static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "ThinkPS/2", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2", "AlpsPS/2" };
/*
* psmouse_process_byte() analyzes the PS/2 data stream and reports
* relevant events to the input module once full packet has arrived.
*/
static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
{
struct input_dev *dev = &psmouse->dev;
unsigned char *packet = psmouse->packet;
if (psmouse->pktcnt < psmouse->pktsize)
return PSMOUSE_GOOD_DATA;
/*
* Full packet accumulated, process it
*/
input_regs(dev, regs);
/*
* Scroll wheel on IntelliMice, scroll buttons on NetMice
*/
if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS)
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
/*
* Scroll wheel and buttons on IntelliMouse Explorer
*/
if (psmouse->type == PSMOUSE_IMEX) {
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7));
input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
}
/*
* Extra buttons on Genius NewNet 3D
*/
if (psmouse->type == PSMOUSE_GENPS) {
input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
}
/*
* Extra button on ThinkingMouse
*/
if (psmouse->type == PSMOUSE_THINKPS) {
input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1);
/* Without this bit of weirdness moving up gives wildly high Y changes. */
packet[1] |= (packet[0] & 0x40) << 1;
}
/*
* Generic PS/2 Mouse
*/
input_report_key(dev, BTN_LEFT, packet[0] & 1);
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
input_sync(dev);
return PSMOUSE_FULL_PACKET;
}
/*
* psmouse_interrupt() handles incoming characters, either gathering them into
* packets or passing them to the command routine as command output.
*/
static irqreturn_t psmouse_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
struct psmouse *psmouse = serio->private;
psmouse_ret_t rc;
if (psmouse->state == PSMOUSE_IGNORE)
goto out;
if (flags & (SERIO_PARITY|SERIO_TIMEOUT)) {
if (psmouse->state == PSMOUSE_ACTIVATED)
printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n",
flags & SERIO_TIMEOUT ? " timeout" : "",
flags & SERIO_PARITY ? " bad parity" : "");
ps2_cmd_aborted(&psmouse->ps2dev);
goto out;
}
if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK))
if (ps2_handle_ack(&psmouse->ps2dev, data))
goto out;
if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_CMD))
if (ps2_handle_response(&psmouse->ps2dev, data))
goto out;
if (psmouse->state == PSMOUSE_INITIALIZING)
goto out;
if (psmouse->state == PSMOUSE_ACTIVATED &&
psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
psmouse->name, psmouse->phys, psmouse->pktcnt);
psmouse->pktcnt = 0;
}
psmouse->last = jiffies;
psmouse->packet[psmouse->pktcnt++] = data;
if (psmouse->packet[0] == PSMOUSE_RET_BAT) {
if (psmouse->pktcnt == 1)
goto out;
if (psmouse->pktcnt == 2) {
if (psmouse->packet[1] == PSMOUSE_RET_ID) {
psmouse->state = PSMOUSE_IGNORE;
serio_reconnect(serio);
goto out;
}
if (psmouse->type == PSMOUSE_SYNAPTICS) {
/* neither 0xAA nor 0x00 are valid first bytes
* for a packet in absolute mode
*/
psmouse->pktcnt = 0;
goto out;
}
}
}
rc = psmouse->protocol_handler(psmouse, regs);
switch (rc) {
case PSMOUSE_BAD_DATA:
printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n",
psmouse->name, psmouse->phys, psmouse->pktcnt);
psmouse->pktcnt = 0;
if (++psmouse->out_of_sync == psmouse->resetafter) {
psmouse->state = PSMOUSE_IGNORE;
printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n");
serio_reconnect(psmouse->ps2dev.serio);
}
break;
case PSMOUSE_FULL_PACKET:
psmouse->pktcnt = 0;
if (psmouse->out_of_sync) {
psmouse->out_of_sync = 0;
printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n",
psmouse->name, psmouse->phys);
}
break;
case PSMOUSE_GOOD_DATA:
break;
}
out:
return IRQ_HANDLED;
}
/*
* psmouse_sliced_command() sends an extended PS/2 command to the mouse
* using sliced syntax, understood by advanced devices, such as Logitech
* or Synaptics touchpads. The command is encoded as:
* 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
* is the command.
*/
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
{
int i;
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
return -1;
for (i = 6; i >= 0; i -= 2) {
unsigned char d = (command >> i) & 3;
if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES))
return -1;
}
return 0;
}
/*
* psmouse_reset() resets the mouse into power-on state.
*/
int psmouse_reset(struct psmouse *psmouse)
{
unsigned char param[2];
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT))
return -1;
if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID)
return -1;
return 0;
}
/*
* Genius NetMouse magic init.
*/
static int genius_detect(struct psmouse *psmouse, int set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
param[0] = 3;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55)
return -1;
if (set_properties) {
set_bit(BTN_EXTRA, psmouse->dev.keybit);
set_bit(BTN_SIDE, psmouse->dev.keybit);
set_bit(REL_WHEEL, psmouse->dev.relbit);
psmouse->vendor = "Genius";
psmouse->name = "Wheel Mouse";
psmouse->pktsize = 4;
}
return 0;
}
/*
* IntelliMouse magic init.
*/
static int intellimouse_detect(struct psmouse *psmouse, int set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2];
param[0] = 200;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
param[0] = 100;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
param[0] = 80;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 3)
return -1;
if (set_properties) {
set_bit(REL_WHEEL, psmouse->dev.relbit);
if (!psmouse->vendor) psmouse->vendor = "Generic";
if (!psmouse->name) psmouse->name = "Wheel Mouse";
psmouse->pktsize = 4;
}
return 0;
}
/*
* Try IntelliMouse/Explorer magic init.
*/
static int im_explorer_detect(struct psmouse *psmouse, int set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2];
intellimouse_detect(psmouse, 0);
param[0] = 200;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
param[0] = 200;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
param[0] = 80;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 4)
return -1;
if (set_properties) {
set_bit(REL_WHEEL, psmouse->dev.relbit);
set_bit(BTN_SIDE, psmouse->dev.keybit);
set_bit(BTN_EXTRA, psmouse->dev.keybit);
if (!psmouse->vendor) psmouse->vendor = "Generic";
if (!psmouse->name) psmouse->name = "Explorer Mouse";
psmouse->pktsize = 4;
}
return 0;
}
/*
* Kensington ThinkingMouse / ExpertMouse magic init.
*/
static int thinking_detect(struct psmouse *psmouse, int set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2];
unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20, 0 };
int i;
param[0] = 10;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
param[0] = 0;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
for (i = 0; seq[i]; i++)
ps2_command(ps2dev, seq + i, PSMOUSE_CMD_SETRATE);
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 2)
return -1;
if (set_properties) {
set_bit(BTN_EXTRA, psmouse->dev.keybit);
psmouse->vendor = "Kensington";
psmouse->name = "ThinkingMouse";
}
return 0;
}
/*
* Bare PS/2 protocol "detection". Always succeeds.
*/
static int ps2bare_detect(struct psmouse *psmouse, int set_properties)
{
if (!psmouse->vendor) psmouse->vendor = "Generic";
if (!psmouse->name) psmouse->name = "Mouse";
return 0;
}
/*
* psmouse_extensions() probes for any extensions to the basic PS/2 protocol
* the mouse may have.
*/
static int psmouse_extensions(struct psmouse *psmouse,
unsigned int max_proto, int set_properties)
{
int synaptics_hardware = 0;
/*
* Try Kensington ThinkingMouse (we try first, because synaptics probe
* upsets the thinkingmouse).
*/
if (max_proto > PSMOUSE_PS2 && thinking_detect(psmouse, set_properties) == 0)
return PSMOUSE_THINKPS;
/*
* Try Synaptics TouchPad
*/
if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) {
synaptics_hardware = 1;
if (max_proto > PSMOUSE_IMEX) {
if (!set_properties || synaptics_init(psmouse) == 0)
return PSMOUSE_SYNAPTICS;
/*
* Some Synaptics touchpads can emulate extended protocols (like IMPS/2).
* Unfortunately Logitech/Genius probes confuse some firmware versions so
* we'll have to skip them.
*/
max_proto = PSMOUSE_IMEX;
}
/*
* Make sure that touchpad is in relative mode, gestures (taps) are enabled
*/
synaptics_reset(psmouse);
}
/*
* Try ALPS TouchPad
*/
if (max_proto > PSMOUSE_IMEX) {
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
if (alps_detect(psmouse, set_properties) == 0) {
if (!set_properties || alps_init(psmouse) == 0)
return PSMOUSE_ALPS;
/*
* Init failed, try basic relative protocols
*/
max_proto = PSMOUSE_IMEX;
}
}
if (max_proto > PSMOUSE_IMEX && genius_detect(psmouse, set_properties) == 0)
return PSMOUSE_GENPS;
if (max_proto > PSMOUSE_IMEX && ps2pp_init(psmouse, set_properties) == 0)
return PSMOUSE_PS2PP;
/*
* Reset to defaults in case the device got confused by extended
* protocol probes.
*/
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0)
return PSMOUSE_IMEX;
if (max_proto >= PSMOUSE_IMPS && intellimouse_detect(psmouse, set_properties) == 0)
return PSMOUSE_IMPS;
/*
* Okay, all failed, we have a standard mouse here. The number of the buttons
* is still a question, though. We assume 3.
*/
ps2bare_detect(psmouse, set_properties);
if (synaptics_hardware) {
/*
* We detected Synaptics hardware but it did not respond to IMPS/2 probes.
* We need to reset the touchpad because if there is a track point on the
* pass through port it could get disabled while probing for protocol
* extensions.
*/
psmouse_reset(psmouse);
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
}
return PSMOUSE_PS2;
}
/*
* psmouse_probe() probes for a PS/2 mouse.
*/
static int psmouse_probe(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2];
/*
* First, we check if it's a mouse. It should send 0x00 or 0x03
* in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
*/
param[0] = 0xa5;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
return -1;
if (param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04)
return -1;
/*
* Then we reset and disable the mouse so that it doesn't generate events.
*/
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
printk(KERN_WARNING "psmouse.c: Failed to reset mouse on %s\n", ps2dev->serio->phys);
return 0;
}
/*
* Here we set the mouse resolution.
*/
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
unsigned char params[] = { 0, 1, 2, 2, 3 };
if (resolution == 0 || resolution > 200)
resolution = 200;
ps2_command(&psmouse->ps2dev, &params[resolution / 50], PSMOUSE_CMD_SETRES);
psmouse->resolution = 25 << params[resolution / 50];
}
/*
* Here we set the mouse report rate.
*/
static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
{
unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
int i = 0;
while (rates[i] > rate) i++;
ps2_command(&psmouse->ps2dev, &rates[i], PSMOUSE_CMD_SETRATE);
psmouse->rate = rates[i];
}
/*
* psmouse_initialize() initializes the mouse to a sane state.
*/
static void psmouse_initialize(struct psmouse *psmouse)
{
/*
* We set the mouse into streaming mode.
*/
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM);
/*
* We set the mouse report rate, resolution and scaling.
*/
if (psmouse_max_proto != PSMOUSE_PS2) {
psmouse->set_rate(psmouse, psmouse->rate);
psmouse->set_resolution(psmouse, psmouse->resolution);
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
}
}
/*
* psmouse_set_state() sets new psmouse state and resets all flags and
* counters while holding serio lock so fighting with interrupt handler
* is not a concern.
*/
static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
{
serio_pause_rx(psmouse->ps2dev.serio);
psmouse->state = new_state;
psmouse->pktcnt = psmouse->out_of_sync = 0;
psmouse->ps2dev.flags = 0;
serio_continue_rx(psmouse->ps2dev.serio);
}
/*
* psmouse_activate() enables the mouse so that we get motion reports from it.
*/
static void psmouse_activate(struct psmouse *psmouse)
{
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE))
printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n",
psmouse->ps2dev.serio->phys);
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
}
/*
* psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
* reports from it unless we explicitely request it.
*/
static void psmouse_deactivate(struct psmouse *psmouse)
{
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE))
printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n",
psmouse->ps2dev.serio->phys);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
}
/*
* psmouse_cleanup() resets the mouse into power-on state.
*/
static void psmouse_cleanup(struct serio *serio)
{
struct psmouse *psmouse = serio->private;
psmouse_reset(psmouse);
}
/*
* psmouse_disconnect() closes and frees.
*/
static void psmouse_disconnect(struct serio *serio)
{
struct psmouse *psmouse, *parent;
device_remove_file(&serio->dev, &psmouse_attr_rate);
device_remove_file(&serio->dev, &psmouse_attr_resolution);
device_remove_file(&serio->dev, &psmouse_attr_resetafter);
psmouse = serio->private;
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
if (parent->pt_deactivate)
parent->pt_deactivate(parent);
}
if (psmouse->disconnect)
psmouse->disconnect(psmouse);
psmouse_set_state(psmouse, PSMOUSE_IGNORE);
input_unregister_device(&psmouse->dev);
serio_close(serio);
kfree(psmouse);
}
/*
* psmouse_connect() is a callback from the serio module when
* an unhandled serio port is found.
*/
static void psmouse_connect(struct serio *serio, struct serio_driver *drv)
{
struct psmouse *psmouse, *parent = NULL;
if ((serio->type & SERIO_TYPE) != SERIO_8042 &&
(serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU)
return;
/*
* If this is a pass-through port deactivate parent so the device
* connected to this port can be successfully identified
*/
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
psmouse_deactivate(parent);
}
if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL)))
goto out;
memset(psmouse, 0, sizeof(struct psmouse));
ps2_init(&psmouse->ps2dev, serio);
psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
psmouse->dev.private = psmouse;
psmouse->dev.dev = &serio->dev;
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
serio->private = psmouse;
if (serio_open(serio, drv)) {
kfree(psmouse);
serio->private = NULL;
goto out;
}
if (psmouse_probe(psmouse) < 0) {
serio_close(serio);
kfree(psmouse);
serio->private = NULL;
goto out;
}
psmouse->rate = psmouse_rate;
psmouse->resolution = psmouse_resolution;
psmouse->resetafter = psmouse_resetafter;
psmouse->smartscroll = psmouse_smartscroll;
psmouse->set_rate = psmouse_set_rate;
psmouse->set_resolution = psmouse_set_resolution;
psmouse->protocol_handler = psmouse_process_byte;
psmouse->pktsize = 3;
psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1);
sprintf(psmouse->devname, "%s %s %s",
psmouse_protocols[psmouse->type], psmouse->vendor, psmouse->name);
snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0",
serio->phys);
psmouse->dev.name = psmouse->devname;
psmouse->dev.phys = psmouse->phys;
psmouse->dev.id.bustype = BUS_I8042;
psmouse->dev.id.vendor = 0x0002;
psmouse->dev.id.product = psmouse->type;
psmouse->dev.id.version = psmouse->model;
input_register_device(&psmouse->dev);
printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
psmouse_initialize(psmouse);
if (parent && parent->pt_activate)
parent->pt_activate(parent);
device_create_file(&serio->dev, &psmouse_attr_rate);
device_create_file(&serio->dev, &psmouse_attr_resolution);
device_create_file(&serio->dev, &psmouse_attr_resetafter);
if (serio->child) {
/*
* Nothing to be done here, serio core will detect that
* the driver set serio->child and will register it for us.
*/
printk(KERN_INFO "serio: %s port at %s\n", serio->child->name, psmouse->phys);
}
psmouse_activate(psmouse);
out:
/* If this is a pass-through port the parent awaits to be activated */
if (parent)
psmouse_activate(parent);
}
static int psmouse_reconnect(struct serio *serio)
{
struct psmouse *psmouse = serio->private;
struct psmouse *parent = NULL;
struct serio_driver *drv = serio->drv;
int rc = -1;
if (!drv || !psmouse) {
printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n");
return -1;
}
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
psmouse_deactivate(parent);
}
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
if (psmouse->reconnect) {
if (psmouse->reconnect(psmouse))
goto out;
} else if (psmouse_probe(psmouse) < 0 ||
psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0))
goto out;
/* ok, the device type (and capabilities) match the old one,
* we can continue using it, complete intialization
*/
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
psmouse_initialize(psmouse);
if (parent && parent->pt_activate)
parent->pt_activate(parent);
psmouse_activate(psmouse);
rc = 0;
out:
/* If this is a pass-through port the parent waits to be activated */
if (parent)
psmouse_activate(parent);
return rc;
}
static struct serio_driver psmouse_drv = {
.driver = {
.name = "psmouse",
},
.description = DRIVER_DESC,
.interrupt = psmouse_interrupt,
.connect = psmouse_connect,
.reconnect = psmouse_reconnect,
.disconnect = psmouse_disconnect,
.cleanup = psmouse_cleanup,
};
#ifndef L4INPUT
ssize_t psmouse_attr_show_helper(struct device *dev, char *buf,
ssize_t (*handler)(struct psmouse *, char *))
{
struct serio *serio = to_serio_port(dev);
int retval;
retval = serio_pin_driver(serio);
if (retval)
return retval;
if (serio->drv != &psmouse_drv) {
retval = -ENODEV;
goto out;
}
retval = handler(serio->private, buf);
out:
serio_unpin_driver(serio);
return retval;
}
ssize_t psmouse_attr_set_helper(struct device *dev, const char *buf, size_t count,
ssize_t (*handler)(struct psmouse *, const char *, size_t))
{
struct serio *serio = to_serio_port(dev);
struct psmouse *psmouse = serio->private, *parent = NULL;
int retval;
retval = serio_pin_driver(serio);
if (retval)
return retval;
if (serio->drv != &psmouse_drv) {
retval = -ENODEV;
goto out;
}
if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) {
parent = serio->parent->private;
psmouse_deactivate(parent);
}
psmouse_deactivate(psmouse);
retval = handler(psmouse, buf, count);
psmouse_activate(psmouse);
if (parent)
psmouse_activate(parent);
out:
serio_unpin_driver(serio);
return retval;
}
static ssize_t psmouse_attr_show_rate(struct psmouse *psmouse, char *buf)
{
return sprintf(buf, "%d\n", psmouse->rate);
}
static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, const char *buf, size_t count)
{
unsigned long value;
char *rest;
value = simple_strtoul(buf, &rest, 10);
if (*rest)
return -EINVAL;
psmouse->set_rate(psmouse, value);
return count;
}
static ssize_t psmouse_attr_show_resolution(struct psmouse *psmouse, char *buf)
{
return sprintf(buf, "%d\n", psmouse->resolution);
}
static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, const char *buf, size_t count)
{
unsigned long value;
char *rest;
value = simple_strtoul(buf, &rest, 10);
if (*rest)
return -EINVAL;
psmouse->set_resolution(psmouse, value);
return count;
}
static ssize_t psmouse_attr_show_resetafter(struct psmouse *psmouse, char *buf)
{
return sprintf(buf, "%d\n", psmouse->resetafter);
}
static ssize_t psmouse_attr_set_resetafter(struct psmouse *psmouse, const char *buf, size_t count)
{
unsigned long value;
char *rest;
value = simple_strtoul(buf, &rest, 10);
if (*rest)
return -EINVAL;
psmouse->resetafter = value;
return count;
}
#endif
static inline void psmouse_parse_proto(void)
{
if (psmouse_proto) {
if (!strcmp(psmouse_proto, "bare"))
psmouse_max_proto = PSMOUSE_PS2;
else if (!strcmp(psmouse_proto, "imps"))
psmouse_max_proto = PSMOUSE_IMPS;
else if (!strcmp(psmouse_proto, "exps"))
psmouse_max_proto = PSMOUSE_IMEX;
else
printk(KERN_ERR "psmouse: unknown protocol type '%s'\n", psmouse_proto);
}
}
int __init psmouse_init(void)
{
psmouse_parse_proto();
serio_register_driver(&psmouse_drv);
return 0;
}
void __exit psmouse_exit(void)
{
serio_unregister_driver(&psmouse_drv);
}
module_init(psmouse_init);
module_exit(psmouse_exit);

View File

@@ -0,0 +1,106 @@
#ifndef _PSMOUSE_H
#define _PSMOUSE_H
#define PSMOUSE_CMD_SETSCALE11 0x00e6
#define PSMOUSE_CMD_SETSCALE21 0x00e7
#define PSMOUSE_CMD_SETRES 0x10e8
#define PSMOUSE_CMD_GETINFO 0x03e9
#define PSMOUSE_CMD_SETSTREAM 0x00ea
#define PSMOUSE_CMD_SETPOLL 0x00f0
#define PSMOUSE_CMD_POLL 0x03eb
#define PSMOUSE_CMD_GETID 0x02f2
#define PSMOUSE_CMD_SETRATE 0x10f3
#define PSMOUSE_CMD_ENABLE 0x00f4
#define PSMOUSE_CMD_DISABLE 0x00f5
#define PSMOUSE_CMD_RESET_DIS 0x00f6
#define PSMOUSE_CMD_RESET_BAT 0x02ff
#define PSMOUSE_RET_BAT 0xaa
#define PSMOUSE_RET_ID 0x00
#define PSMOUSE_RET_ACK 0xfa
#define PSMOUSE_RET_NAK 0xfe
enum psmouse_state {
PSMOUSE_IGNORE,
PSMOUSE_INITIALIZING,
PSMOUSE_CMD_MODE,
PSMOUSE_ACTIVATED,
};
/* psmouse protocol handler return codes */
typedef enum {
PSMOUSE_BAD_DATA,
PSMOUSE_GOOD_DATA,
PSMOUSE_FULL_PACKET
} psmouse_ret_t;
struct psmouse {
void *private;
struct input_dev dev;
struct ps2dev ps2dev;
char *vendor;
char *name;
unsigned char packet[8];
unsigned char pktcnt;
unsigned char pktsize;
unsigned char type;
unsigned char model;
unsigned long last;
unsigned long out_of_sync;
enum psmouse_state state;
char devname[64];
char phys[40];
unsigned int rate;
unsigned int resolution;
unsigned int resetafter;
unsigned int smartscroll; /* Logitech only */
psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs);
void (*set_rate)(struct psmouse *psmouse, unsigned int rate);
void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution);
int (*reconnect)(struct psmouse *psmouse);
void (*disconnect)(struct psmouse *psmouse);
void (*pt_activate)(struct psmouse *psmouse);
void (*pt_deactivate)(struct psmouse *psmouse);
};
enum psmouse_type {
PSMOUSE_NONE,
PSMOUSE_PS2,
PSMOUSE_PS2PP,
PSMOUSE_THINKPS,
PSMOUSE_GENPS,
PSMOUSE_IMPS,
PSMOUSE_IMEX,
PSMOUSE_SYNAPTICS,
PSMOUSE_ALPS,
};
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
int psmouse_reset(struct psmouse *psmouse);
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
ssize_t psmouse_attr_show_helper(struct device *dev, char *buf,
ssize_t (*handler)(struct psmouse *, char *));
ssize_t psmouse_attr_set_helper(struct device *dev, const char *buf, size_t count,
ssize_t (*handler)(struct psmouse *, const char *, size_t));
#define PSMOUSE_DEFINE_ATTR(_name) \
static ssize_t psmouse_attr_show_##_name(struct psmouse *, char *); \
static ssize_t psmouse_attr_set_##_name(struct psmouse *, const char *, size_t);\
static ssize_t psmouse_do_show_##_name(struct device *d, char *b) \
{ \
return psmouse_attr_show_helper(d, b, psmouse_attr_show_##_name); \
} \
static ssize_t psmouse_do_set_##_name(struct device *d, const char *b, size_t s)\
{ \
return psmouse_attr_set_helper(d, b, s, psmouse_attr_set_##_name); \
} \
static struct device_attribute psmouse_attr_##_name = \
__ATTR(_name, S_IWUSR | S_IRUGO, \
psmouse_do_show_##_name, psmouse_do_set_##_name);
#endif /* _PSMOUSE_H */

View File

@@ -0,0 +1,647 @@
/*
* Synaptics TouchPad PS/2 mouse driver
*
* 2003 Dmitry Torokhov <dtor@mail.ru>
* Added support for pass-through port. Special thanks to Peter Berg Larsen
* for explaining various Synaptics quirks.
*
* 2003 Peter Osterlund <petero2@telia.com>
* Ported to 2.5 input device infrastructure.
*
* Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch>
* start merging tpconfig and gpm code to a xfree-input module
* adding some changes and extensions (ex. 3rd and 4th button)
*
* Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu>
* Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com>
* code for the special synaptics commands (from the tpconfig-source)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* Trademarks are the property of their respective owners.
*/
#include <linux/module.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include "psmouse.h"
#include "synaptics.h"
/*
* The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
* section 2.3.2, which says that they should be valid regardless of the
* actual size of the sensor.
*/
#define XMIN_NOMINAL 1472
#define XMAX_NOMINAL 5472
#define YMIN_NOMINAL 1408
#define YMAX_NOMINAL 4448
/*****************************************************************************
* Synaptics communications functions
****************************************************************************/
/*
* Send a command to the synpatics touchpad by special commands
*/
static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
{
if (psmouse_sliced_command(psmouse, c))
return -1;
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
return -1;
return 0;
}
/*
* Set the synaptics touchpad mode byte by special commands
*/
static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
{
unsigned char param[1];
if (psmouse_sliced_command(psmouse, mode))
return -1;
param[0] = SYN_PS_SET_MODE2;
if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
return -1;
return 0;
}
/*
* Read the model-id bytes from the touchpad
* see also SYN_MODEL_* macros
*/
static int synaptics_model_id(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
unsigned char mi[3];
if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
return -1;
priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
return 0;
}
/*
* Read the capability-bits from the touchpad
* see also the SYN_CAP_* macros
*/
static int synaptics_capability(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
unsigned char cap[3];
if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
return -1;
priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
priv->ext_cap = 0;
if (!SYN_CAP_VALID(priv->capabilities))
return -1;
/*
* Unless capExtended is set the rest of the flags should be ignored
*/
if (!SYN_CAP_EXTENDED(priv->capabilities))
priv->capabilities = 0;
if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
printk(KERN_ERR "Synaptics claims to have extended capabilities,"
" but I'm not able to read them.");
} else {
priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
/*
* if nExtBtn is greater than 8 it should be considered
* invalid and treated as 0
*/
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
priv->ext_cap &= 0xff0fff;
}
}
return 0;
}
/*
* Identify Touchpad
* See also the SYN_ID_* macros
*/
static int synaptics_identify(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
unsigned char id[3];
if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
return -1;
priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
if (SYN_ID_IS_SYNAPTICS(priv->identity))
return 0;
return -1;
}
static void print_ident(struct synaptics_data *priv)
{
printk(KERN_INFO "Synaptics Touchpad, model: %ld\n", SYN_ID_MODEL(priv->identity));
printk(KERN_INFO " Firmware: %ld.%ld\n", SYN_ID_MAJOR(priv->identity),
SYN_ID_MINOR(priv->identity));
if (SYN_MODEL_ROT180(priv->model_id))
printk(KERN_INFO " 180 degree mounted touchpad\n");
if (SYN_MODEL_PORTRAIT(priv->model_id))
printk(KERN_INFO " portrait touchpad\n");
printk(KERN_INFO " Sensor: %ld\n", SYN_MODEL_SENSOR(priv->model_id));
if (SYN_MODEL_NEWABS(priv->model_id))
printk(KERN_INFO " new absolute packet format\n");
if (SYN_MODEL_PEN(priv->model_id))
printk(KERN_INFO " pen detection\n");
if (SYN_CAP_EXTENDED(priv->capabilities)) {
printk(KERN_INFO " Touchpad has extended capability bits\n");
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
printk(KERN_INFO " -> %d multi-buttons, i.e. besides standard buttons\n",
(int)(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)));
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
printk(KERN_INFO " -> middle button\n");
if (SYN_CAP_FOUR_BUTTON(priv->capabilities))
printk(KERN_INFO " -> four buttons\n");
if (SYN_CAP_MULTIFINGER(priv->capabilities))
printk(KERN_INFO " -> multifinger detection\n");
if (SYN_CAP_PALMDETECT(priv->capabilities))
printk(KERN_INFO " -> palm detection\n");
if (SYN_CAP_PASS_THROUGH(priv->capabilities))
printk(KERN_INFO " -> pass-through port\n");
}
}
static int synaptics_query_hardware(struct psmouse *psmouse)
{
int retries = 0;
while ((retries++ < 3) && psmouse_reset(psmouse))
printk(KERN_ERR "synaptics reset failed\n");
if (synaptics_identify(psmouse))
return -1;
if (synaptics_model_id(psmouse))
return -1;
if (synaptics_capability(psmouse))
return -1;
return 0;
}
static int synaptics_set_absolute_mode(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
priv->mode = SYN_BIT_ABSOLUTE_MODE;
if (SYN_ID_MAJOR(priv->identity) >= 4)
priv->mode |= SYN_BIT_DISABLE_GESTURE;
if (SYN_CAP_EXTENDED(priv->capabilities))
priv->mode |= SYN_BIT_W_MODE;
if (synaptics_mode_cmd(psmouse, priv->mode))
return -1;
return 0;
}
static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
{
struct synaptics_data *priv = psmouse->private;
if (rate >= 80) {
priv->mode |= SYN_BIT_HIGH_RATE;
psmouse->rate = 80;
} else {
priv->mode &= ~SYN_BIT_HIGH_RATE;
psmouse->rate = 40;
}
synaptics_mode_cmd(psmouse, priv->mode);
}
/*****************************************************************************
* Synaptics pass-through PS/2 port support
****************************************************************************/
static int synaptics_pt_write(struct serio *serio, unsigned char c)
{
struct psmouse *parent = serio->parent->private;
char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
if (psmouse_sliced_command(parent, c))
return -1;
if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE))
return -1;
return 0;
}
static inline int synaptics_is_pt_packet(unsigned char *buf)
{
return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
}
static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet)
{
struct psmouse *child = ptport->private;
if (child && child->state == PSMOUSE_ACTIVATED) {
serio_interrupt(ptport, packet[1], 0, NULL);
serio_interrupt(ptport, packet[4], 0, NULL);
serio_interrupt(ptport, packet[5], 0, NULL);
if (child->type >= PSMOUSE_GENPS)
serio_interrupt(ptport, packet[2], 0, NULL);
} else
serio_interrupt(ptport, packet[1], 0, NULL);
}
static void synaptics_pt_activate(struct psmouse *psmouse)
{
struct psmouse *child = psmouse->ps2dev.serio->child->private;
struct synaptics_data *priv = psmouse->private;
/* adjust the touchpad to child's choice of protocol */
if (child) {
if (child->type >= PSMOUSE_GENPS)
priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT;
else
priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT;
if (synaptics_mode_cmd(psmouse, priv->mode))
printk(KERN_INFO "synaptics: failed to switch guest protocol\n");
}
}
static void synaptics_pt_create(struct psmouse *psmouse)
{
struct serio *serio;
serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
if (!serio) {
printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
return;
}
memset(serio, 0, sizeof(struct serio));
serio->type = SERIO_PS_PSTHRU;
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
serio->write = synaptics_pt_write;
serio->parent = psmouse->ps2dev.serio;
psmouse->pt_activate = synaptics_pt_activate;
psmouse->ps2dev.serio->child = serio;
}
/*****************************************************************************
* Functions to interpret the absolute mode packets
****************************************************************************/
static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
{
memset(hw, 0, sizeof(struct synaptics_hw_state));
if (SYN_MODEL_NEWABS(priv->model_id)) {
hw->x = (((buf[3] & 0x10) << 8) |
((buf[1] & 0x0f) << 8) |
buf[4]);
hw->y = (((buf[3] & 0x20) << 7) |
((buf[1] & 0xf0) << 4) |
buf[5]);
hw->z = buf[2];
hw->w = (((buf[0] & 0x30) >> 2) |
((buf[0] & 0x04) >> 1) |
((buf[3] & 0x04) >> 2));
hw->left = (buf[0] & 0x01) ? 1 : 0;
hw->right = (buf[0] & 0x02) ? 1 : 0;
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
((buf[0] ^ buf[3]) & 0x02)) {
switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
default:
/*
* if nExtBtn is greater than 8 it should be
* considered invalid and treated as 0
*/
break;
case 8:
hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0;
hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0;
case 6:
hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0;
hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0;
case 4:
hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0;
hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0;
case 2:
hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0;
hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0;
}
}
} else {
hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
hw->y = (((buf[4] & 0x1f) << 8) | buf[5]);
hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F));
hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1));
hw->left = (buf[0] & 0x01) ? 1 : 0;
hw->right = (buf[0] & 0x02) ? 1 : 0;
}
}
/*
* called for each full received packet from the touchpad
*/
static void synaptics_process_packet(struct psmouse *psmouse)
{
struct input_dev *dev = &psmouse->dev;
struct synaptics_data *priv = psmouse->private;
struct synaptics_hw_state hw;
int num_fingers;
int finger_width;
int i;
synaptics_parse_hw_state(psmouse->packet, priv, &hw);
if (hw.z > 0) {
num_fingers = 1;
finger_width = 5;
if (SYN_CAP_EXTENDED(priv->capabilities)) {
switch (hw.w) {
case 0 ... 1:
if (SYN_CAP_MULTIFINGER(priv->capabilities))
num_fingers = hw.w + 2;
break;
case 2:
if (SYN_MODEL_PEN(priv->model_id))
; /* Nothing, treat a pen as a single finger */
break;
case 4 ... 15:
if (SYN_CAP_PALMDETECT(priv->capabilities))
finger_width = hw.w;
break;
}
}
} else {
num_fingers = 0;
finger_width = 0;
}
/* Post events
* BTN_TOUCH has to be first as mousedev relies on it when doing
* absolute -> relative conversion
*/
if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
if (hw.z > 0) {
input_report_abs(dev, ABS_X, hw.x);
input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
}
input_report_abs(dev, ABS_PRESSURE, hw.z);
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
input_report_key(dev, BTN_LEFT, hw.left);
input_report_key(dev, BTN_RIGHT, hw.right);
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
input_report_key(dev, BTN_MIDDLE, hw.middle);
if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
input_report_key(dev, BTN_FORWARD, hw.up);
input_report_key(dev, BTN_BACK, hw.down);
}
for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
input_sync(dev);
}
static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type)
{
static unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
static unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
static unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
static unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
static unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
if (idx < 0 || idx > 4)
return 0;
switch (pkt_type) {
case SYN_NEWABS:
case SYN_NEWABS_RELAXED:
return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx];
case SYN_NEWABS_STRICT:
return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx];
case SYN_OLDABS:
return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
default:
printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type);
return 0;
}
}
static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)
{
int i;
for (i = 0; i < 5; i++)
if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) {
printk(KERN_INFO "synaptics: using relaxed packet validation\n");
return SYN_NEWABS_RELAXED;
}
return SYN_NEWABS_STRICT;
}
static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
{
struct input_dev *dev = &psmouse->dev;
struct synaptics_data *priv = psmouse->private;
input_regs(dev, regs);
if (psmouse->pktcnt >= 6) { /* Full packet received */
if (unlikely(priv->pkt_type == SYN_NEWABS))
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
if (psmouse->ps2dev.serio->child)
synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
} else
synaptics_process_packet(psmouse);
return PSMOUSE_FULL_PACKET;
}
return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ?
PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
}
/*****************************************************************************
* Driver initialization/cleanup functions
****************************************************************************/
static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
{
int i;
set_bit(EV_ABS, dev->evbit);
input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
set_bit(ABS_TOOL_WIDTH, dev->absbit);
set_bit(EV_KEY, dev->evbit);
set_bit(BTN_TOUCH, dev->keybit);
set_bit(BTN_TOOL_FINGER, dev->keybit);
set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
set_bit(BTN_LEFT, dev->keybit);
set_bit(BTN_RIGHT, dev->keybit);
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
set_bit(BTN_MIDDLE, dev->keybit);
if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
set_bit(BTN_FORWARD, dev->keybit);
set_bit(BTN_BACK, dev->keybit);
}
for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
set_bit(BTN_0 + i, dev->keybit);
clear_bit(EV_REL, dev->evbit);
clear_bit(REL_X, dev->relbit);
clear_bit(REL_Y, dev->relbit);
}
void synaptics_reset(struct psmouse *psmouse)
{
/* reset touchpad back to relative mode, gestures enabled */
synaptics_mode_cmd(psmouse, 0);
}
static void synaptics_disconnect(struct psmouse *psmouse)
{
synaptics_reset(psmouse);
kfree(psmouse->private);
}
static int synaptics_reconnect(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
struct synaptics_data old_priv = *priv;
if (synaptics_detect(psmouse, 0))
return -1;
if (synaptics_query_hardware(psmouse)) {
printk(KERN_ERR "Unable to query Synaptics hardware.\n");
return -1;
}
if (old_priv.identity != priv->identity ||
old_priv.model_id != priv->model_id ||
old_priv.capabilities != priv->capabilities ||
old_priv.ext_cap != priv->ext_cap)
return -1;
if (synaptics_set_absolute_mode(psmouse)) {
printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
return -1;
}
return 0;
}
int synaptics_detect(struct psmouse *psmouse, int set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
param[0] = 0;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
if (param[1] != 0x47)
return -1;
if (set_properties) {
psmouse->vendor = "Synaptics";
psmouse->name = "TouchPad";
}
return 0;
}
int synaptics_init(struct psmouse *psmouse)
{
struct synaptics_data *priv;
psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL);
if (!priv)
return -1;
memset(priv, 0, sizeof(struct synaptics_data));
if (synaptics_query_hardware(psmouse)) {
printk(KERN_ERR "Unable to query Synaptics hardware.\n");
goto init_fail;
}
if (synaptics_set_absolute_mode(psmouse)) {
printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
goto init_fail;
}
priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
if (SYN_CAP_PASS_THROUGH(priv->capabilities))
synaptics_pt_create(psmouse);
print_ident(priv);
set_input_params(&psmouse->dev, priv);
psmouse->protocol_handler = synaptics_process_byte;
psmouse->set_rate = synaptics_set_rate;
psmouse->disconnect = synaptics_disconnect;
psmouse->reconnect = synaptics_reconnect;
psmouse->pktsize = 6;
return 0;
init_fail:
kfree(priv);
return -1;
}

View File

@@ -0,0 +1,108 @@
/*
* Synaptics TouchPad PS/2 mouse driver
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _SYNAPTICS_H
#define _SYNAPTICS_H
extern int synaptics_detect(struct psmouse *psmouse, int set_properties);
extern int synaptics_init(struct psmouse *psmouse);
extern void synaptics_reset(struct psmouse *psmouse);
/* synaptics queries */
#define SYN_QUE_IDENTIFY 0x00
#define SYN_QUE_MODES 0x01
#define SYN_QUE_CAPABILITIES 0x02
#define SYN_QUE_MODEL 0x03
#define SYN_QUE_SERIAL_NUMBER_PREFIX 0x06
#define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07
#define SYN_QUE_RESOLUTION 0x08
#define SYN_QUE_EXT_CAPAB 0x09
/* synatics modes */
#define SYN_BIT_ABSOLUTE_MODE (1 << 7)
#define SYN_BIT_HIGH_RATE (1 << 6)
#define SYN_BIT_SLEEP_MODE (1 << 3)
#define SYN_BIT_DISABLE_GESTURE (1 << 2)
#define SYN_BIT_FOUR_BYTE_CLIENT (1 << 1)
#define SYN_BIT_W_MODE (1 << 0)
/* synaptics model ID bits */
#define SYN_MODEL_ROT180(m) ((m) & (1 << 23))
#define SYN_MODEL_PORTRAIT(m) ((m) & (1 << 22))
#define SYN_MODEL_SENSOR(m) (((m) >> 16) & 0x3f)
#define SYN_MODEL_HARDWARE(m) (((m) >> 9) & 0x7f)
#define SYN_MODEL_NEWABS(m) ((m) & (1 << 7))
#define SYN_MODEL_PEN(m) ((m) & (1 << 6))
#define SYN_MODEL_SIMPLIC(m) ((m) & (1 << 5))
#define SYN_MODEL_GEOMETRY(m) ((m) & 0x0f)
/* synaptics capability bits */
#define SYN_CAP_EXTENDED(c) ((c) & (1 << 23))
#define SYN_CAP_MIDDLE_BUTTON(c) ((c) & (1 << 18))
#define SYN_CAP_PASS_THROUGH(c) ((c) & (1 << 7))
#define SYN_CAP_SLEEP(c) ((c) & (1 << 4))
#define SYN_CAP_FOUR_BUTTON(c) ((c) & (1 << 3))
#define SYN_CAP_MULTIFINGER(c) ((c) & (1 << 1))
#define SYN_CAP_PALMDETECT(c) ((c) & (1 << 0))
#define SYN_CAP_VALID(c) ((((c) & 0x00ff00) >> 8) == 0x47)
#define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20)
#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
#define SYN_MODE_RATE(m) ((m) & (1 << 6))
#define SYN_MODE_BAUD_SLEEP(m) ((m) & (1 << 3))
#define SYN_MODE_DISABLE_GESTURE(m) ((m) & (1 << 2))
#define SYN_MODE_PACKSIZE(m) ((m) & (1 << 1))
#define SYN_MODE_WMODE(m) ((m) & (1 << 0))
/* synaptics identify query bits */
#define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f)
#define SYN_ID_MAJOR(i) ((i) & 0x0f)
#define SYN_ID_MINOR(i) (((i) >> 16) & 0xff)
#define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47)
/* synaptics special commands */
#define SYN_PS_SET_MODE2 0x14
#define SYN_PS_CLIENT_CMD 0x28
/* synaptics packet types */
#define SYN_NEWABS 0
#define SYN_NEWABS_STRICT 1
#define SYN_NEWABS_RELAXED 2
#define SYN_OLDABS 3
/*
* A structure to describe the state of the touchpad hardware (buttons and pad)
*/
struct synaptics_hw_state {
int x;
int y;
int z;
int w;
unsigned int left:1;
unsigned int right:1;
unsigned int middle:1;
unsigned int up:1;
unsigned int down:1;
unsigned char ext_buttons;
};
struct synaptics_data {
/* Data read from the touchpad */
unsigned long int model_id; /* Model-ID */
unsigned long int capabilities; /* Capabilities */
unsigned long int ext_cap; /* Extended Capabilities */
unsigned long int identity; /* Identification */
unsigned char pkt_type; /* packet type - old, new, etc */
unsigned char mode; /* current mode byte */
};
#endif /* _SYNAPTICS_H */

View File

@@ -0,0 +1,225 @@
/*
* linux/drivers/input/serio/ambakmi.c
*
* Copyright (C) 2000-2003 Deep Blue Solutions Ltd.
* Copyright (C) 2002 Russell King.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <asm/io.h>
#include <asm/hardware/amba_kmi.h>
#include <l4/io/io.h>
#define AMBA_NR_IRQS 2
struct amba_device {
struct device dev;
struct resource res;
u64 dma_mask;
unsigned int periphid;
unsigned int irq[AMBA_NR_IRQS];
};
static struct amba_device dev_kmi_k;
static struct amba_device dev_kmi_m;
enum {
// DATA
DATA_RESET = 0xff,
DATA_RESET_RESPONSE = 0xaa,
// CLKDIV
CLKDIV_DIVISOR = 2, /* 8MHz = 24MHz / (1 + CLKDIV_DIVISOR) */
};
#define KMI_BASE (kmi->base)
struct amba_kmi_port {
struct serio *io;
struct clk *clk;
void *base;
unsigned int irq;
unsigned int divisor;
unsigned int open;
};
static irqreturn_t amba_kmi_int(int irq, void *dev_id, struct pt_regs *regs)
{
struct amba_kmi_port *kmi = dev_id;
unsigned int status = readb(KMIIR);
int handled = IRQ_NONE;
while (status & KMIIR_RXINTR) {
serio_interrupt(kmi->io, readb(KMIDATA), 0, regs);
status = readb(KMIIR);
handled = IRQ_HANDLED;
}
return handled;
}
static int amba_kmi_write(struct serio *io, unsigned char val)
{
struct amba_kmi_port *kmi = io->port_data;
unsigned int timeleft = 10000; /* timeout in 100ms */
while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && timeleft--)
udelay(10);
if (timeleft)
writeb(val, KMIDATA);
return timeleft ? 0 : SERIO_TIMEOUT;
}
static int amba_kmi_open(struct serio *io)
{
struct amba_kmi_port *kmi = io->port_data;
unsigned int divisor;
int ret;
divisor = CLKDIV_DIVISOR;
writeb(divisor, KMICLKDIV);
writeb(KMICR_EN, KMICR);
ret = request_irq(kmi->irq, amba_kmi_int, 0, "kmi-pl050", kmi);
if (ret) {
printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq);
writeb(0, KMICR);
return ret;
}
writeb(KMICR_EN | KMICR_RXINTREN, KMICR);
return 0;
}
static void amba_kmi_close(struct serio *io)
{
struct amba_kmi_port *kmi = io->port_data;
writeb(0, KMICR);
free_irq(kmi->irq, kmi);
}
static int amba_kmi_probe(struct amba_device *dev, void *id)
{
struct amba_kmi_port *kmi;
struct serio *io;
int ret;
kmi = kmalloc(sizeof(struct amba_kmi_port), GFP_KERNEL);
io = kmalloc(sizeof(struct serio), GFP_KERNEL);
if (!kmi || !io) {
ret = -ENOMEM;
goto out;
}
memset(kmi, 0, sizeof(struct amba_kmi_port));
memset(io, 0, sizeof(struct serio));
io->type = SERIO_8042;
io->write = amba_kmi_write;
io->open = amba_kmi_open;
io->close = amba_kmi_close;
strlcpy(io->name, dev->dev.bus_id, sizeof(io->name));
strlcpy(io->phys, dev->dev.bus_id, sizeof(io->phys));
io->port_data = kmi;
//io->dev.parent = &dev->dev;
if (KMI_SIZE > 0x1000) {
printk("KMI_SIZE greater than expected (%x)\n", KMI_SIZE);
return -1;
}
kmi->io = io;
if (l4io_request_iomem(dev->res.start, 0x1000,
L4IO_MEM_NONCACHED, (l4_addr_t *)&kmi->base)) {
printf("l4io_request_mem_region(%lx, 0x1000, 0) failed\n",
dev->res.start);
return -1;
}
printf("pl050: got memory %lx, virtual base at %p, IRQ %d\n",
dev->res.start, kmi->base, dev->irq[0]);
kmi->irq = dev->irq[0];
serio_register_port(io);
return 0;
out:
kfree(io);
return ret;
}
static int __init amba_kmi_pre_probe(const char *name, struct amba_device *d)
{
l4io_resource_t res;
l4io_device_handle_t dh;
l4io_resource_handle_t reshandle, tmp;
if (l4io_lookup_device(name, &dh, 0, &reshandle))
return -ENODEV;
tmp = reshandle;
if (l4io_lookup_resource(dh, L4IO_RESOURCE_IRQ,
&tmp, &res))
return -ENODEV;
d->irq[0] = res.start;
tmp = reshandle;
if (l4io_lookup_resource(dh, L4IO_RESOURCE_MEM,
&tmp, &res))
return -ENODEV;
d->res.start = res.start;
d->res.end = res.end;
strncpy(d->dev.bus_id, name, sizeof(d->dev.bus_id));
d->dev.bus_id[sizeof(d->dev.bus_id) - 1] = 0;
return amba_kmi_probe(d, NULL);
}
static int __init amba_kmi_init_k(void)
{
return amba_kmi_pre_probe("AMBA KMI Kbd", &dev_kmi_k);
}
static int __init amba_kmi_init_m(void)
{
return amba_kmi_pre_probe("AMBA KMI mou", &dev_kmi_m);
}
static void __exit amba_kmi_exit_k(void)
{
// XXX: return resources
}
static void __exit amba_kmi_exit_m(void)
{
// XXX: return resources
}
module_init(amba_kmi_init_k);
module_init(amba_kmi_init_m);
module_exit(amba_kmi_exit_k);
module_exit(amba_kmi_exit_m);
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("AMBA KMI controller driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,96 @@
#ifndef _I8042_IO_H
#define _I8042_IO_H
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
/*
* Names.
*/
#define I8042_KBD_PHYS_DESC "isa0060/serio0"
#define I8042_AUX_PHYS_DESC "isa0060/serio1"
#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
/*
* IRQs.
*/
#ifdef __alpha__
# define I8042_KBD_IRQ 1
# define I8042_AUX_IRQ (RTC_PORT(0) == 0x170 ? 9 : 12) /* Jensen is special */
#elif defined(__arm__)
/* defined in include/asm-arm/arch-xxx/irqs.h */
#include <asm/irq.h>
#elif defined(CONFIG_SUPERH64)
#include <asm/irq.h>
#else
# define I8042_KBD_IRQ 1
# define I8042_AUX_IRQ 12
#endif
/*
* Register numbers.
*/
#define I8042_COMMAND_REG 0x64
#define I8042_STATUS_REG 0x64
#define I8042_DATA_REG 0x60
static inline int i8042_read_data(void)
{
return inb(I8042_DATA_REG);
}
static inline int i8042_read_status(void)
{
return inb(I8042_STATUS_REG);
}
static inline void i8042_write_data(int val)
{
outb(val, I8042_DATA_REG);
}
static inline void i8042_write_command(int val)
{
outb(val, I8042_COMMAND_REG);
}
static inline int i8042_platform_init(void)
{
/*
* On some platforms touching the i8042 data register region can do really
* bad things. Because of this the region is always reserved on such boxes.
*/
#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__) && !defined(CONFIG_PPC64)
//l4/if (!request_region(I8042_DATA_REG, 16, "i8042"))
if (!request_region(I8042_DATA_REG, 1, "i8042"))
return -1;
if (!request_region(I8042_COMMAND_REG, 1, "i8042"))
return -1;
#endif
i8042_reset = 1;
#if defined(CONFIG_PPC64)
if (check_legacy_ioport(I8042_DATA_REG))
return -1;
if (!request_region(I8042_DATA_REG, 16, "i8042"))
return -1;
#endif
return 0;
}
static inline void i8042_platform_exit(void)
{
#if !defined(__sh__) && !defined(__alpha__) && !defined(CONFIG_PPC64)
release_region(I8042_DATA_REG, 16);
#endif
}
#endif /* _I8042_IO_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,133 @@
#ifndef _I8042_H
#define _I8042_H
#include <linux/config.h>
/*
* Copyright (c) 1999-2002 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
/*
* Arch-dependent inline functions and defines.
*/
#if defined(CONFIG_MACH_JAZZ)
#include "i8042-jazzio.h"
#elif defined(CONFIG_SGI_IP22)
#include "i8042-ip22io.h"
#elif defined(CONFIG_PPC)
#include "i8042-ppcio.h"
#elif defined(CONFIG_SPARC32) || defined(CONFIG_SPARC64)
#include "i8042-sparcio.h"
#elif defined(CONFIG_X86) || defined(CONFIG_IA64)
#include "i8042-x86ia64io.h"
#else
#include "i8042-io.h"
#endif
/*
* This is in 50us units, the time we wait for the i8042 to react. This
* has to be long enough for the i8042 itself to timeout on sending a byte
* to a non-existent mouse.
*/
#define I8042_CTL_TIMEOUT 10000
/*
* When the device isn't opened and it's interrupts aren't used, we poll it at
* regular intervals to see if any characters arrived. If yes, we can start
* probing for any mouse / keyboard connected. This is the period of the
* polling.
*/
#define I8042_POLL_PERIOD HZ/20
/*
* Status register bits.
*/
#define I8042_STR_PARITY 0x80
#define I8042_STR_TIMEOUT 0x40
#define I8042_STR_AUXDATA 0x20
#define I8042_STR_KEYLOCK 0x10
#define I8042_STR_CMDDAT 0x08
#define I8042_STR_MUXERR 0x04
#define I8042_STR_IBF 0x02
#define I8042_STR_OBF 0x01
/*
* Control register bits.
*/
#define I8042_CTR_KBDINT 0x01
#define I8042_CTR_AUXINT 0x02
#define I8042_CTR_IGNKEYLOCK 0x08
#define I8042_CTR_KBDDIS 0x10
#define I8042_CTR_AUXDIS 0x20
#define I8042_CTR_XLATE 0x40
/*
* Commands.
*/
#define I8042_CMD_CTL_RCTR 0x0120
#define I8042_CMD_CTL_WCTR 0x1060
#define I8042_CMD_CTL_TEST 0x01aa
#define I8042_CMD_KBD_DISABLE 0x00ad
#define I8042_CMD_KBD_ENABLE 0x00ae
#define I8042_CMD_KBD_TEST 0x01ab
#define I8042_CMD_KBD_LOOP 0x11d2
#define I8042_CMD_AUX_DISABLE 0x00a7
#define I8042_CMD_AUX_ENABLE 0x00a8
#define I8042_CMD_AUX_TEST 0x01a9
#define I8042_CMD_AUX_SEND 0x10d4
#define I8042_CMD_AUX_LOOP 0x11d3
#define I8042_CMD_MUX_PFX 0x0090
#define I8042_CMD_MUX_SEND 0x1090
/*
* Return codes.
*/
#define I8042_RET_CTL_TEST 0x55
/*
* Expected maximum internal i8042 buffer size. This is used for flushing
* the i8042 buffers. 32 should be more than enough.
*/
#define I8042_BUFFER_SIZE 32
/*
* Number of AUX ports on controllers supporting active multiplexing
* specification
*/
#define I8042_NUM_MUX_PORTS 4
/*
* Debug.
*/
#ifdef DEBUG
static unsigned long i8042_start;
#define dbg_init() do { i8042_start = jiffies; } while (0)
#define dbg(format, arg...) \
do { \
if (i8042_debug) \
printk(KERN_DEBUG __FILE__ ": " format " [%d]\n" , \
## arg, (int) (jiffies - i8042_start)); \
} while (0)
#else
#define dbg_init() do { } while (0)
#define dbg(format, arg...) do {} while (0)
#endif
#endif /* _I8042_H */

View File

@@ -0,0 +1,311 @@
/*
* PS/2 driver library
*
* Copyright (c) 1999-2002 Vojtech Pavlik
* Copyright (c) 2004 Dmitry Torokhov
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
#include <linux/libps2.h>
#define DRIVER_DESC "PS/2 driver library"
MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("PS/2 driver library");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ps2_init);
EXPORT_SYMBOL(ps2_sendbyte);
EXPORT_SYMBOL(ps2_command);
EXPORT_SYMBOL(ps2_schedule_command);
EXPORT_SYMBOL(ps2_handle_ack);
EXPORT_SYMBOL(ps2_handle_response);
EXPORT_SYMBOL(ps2_cmd_aborted);
/* Work structure to schedule execution of a command */
struct ps2work {
struct work_struct work;
struct ps2dev *ps2dev;
int command;
unsigned char param[0];
};
/*
* ps2_sendbyte() sends a byte to the mouse, and waits for acknowledge.
* It doesn't handle retransmission, though it could - because when there would
* be need for retransmissions, the mouse has to be replaced anyway.
*
* ps2_sendbyte() can only be called from a process context
*/
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
{
serio_pause_rx(ps2dev->serio);
ps2dev->nak = 1;
ps2dev->flags |= PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
if (serio_write(ps2dev->serio, byte) == 0)
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_ACK),
msecs_to_jiffies(timeout));
serio_pause_rx(ps2dev->serio);
ps2dev->flags &= ~PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
return -ps2dev->nak;
}
/*
* ps2_command() sends a command and its parameters to the mouse,
* then waits for the response and puts it in the param array.
*
* ps2_command() can only be called from a process context
*/
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
int timeout;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
int rc = -1;
int i;
down(&ps2dev->cmd_sem);
serio_pause_rx(ps2dev->serio);
ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
ps2dev->cmdcnt = receive;
if (receive && param)
for (i = 0; i < receive; i++)
ps2dev->cmdbuf[(receive - 1) - i] = param[i];
serio_continue_rx(ps2dev->serio);
/*
* Some devices (Synaptics) peform the reset before
* ACKing the reset command, and so it can take a long
* time before the ACK arrrives.
*/
if (command & 0xff)
if (ps2_sendbyte(ps2dev, command & 0xff,
command == PS2_CMD_RESET_BAT ? 1000 : 200))
goto out;
for (i = 0; i < send; i++)
if (ps2_sendbyte(ps2dev, param[i], 200))
goto out;
/*
* The reset command takes a long time to execute.
*/
timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500);
timeout = wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD1), timeout);
if (ps2dev->cmdcnt && timeout > 0) {
if (command == PS2_CMD_RESET_BAT && timeout > msecs_to_jiffies(100)) {
/*
* Device has sent the first response byte
* after a reset command, reset is thus done,
* shorten the timeout. The next byte will come
* soon (keyboard) or not at all (mouse).
*/
timeout = msecs_to_jiffies(100);
}
if (command == PS2_CMD_GETID &&
ps2dev->cmdbuf[receive - 1] != 0xab && /* Regular keyboards */
ps2dev->cmdbuf[receive - 1] != 0xac && /* NCD Sun keyboard */
ps2dev->cmdbuf[receive - 1] != 0x2b && /* Trust keyboard, translated */
ps2dev->cmdbuf[receive - 1] != 0x5d && /* Trust keyboard */
ps2dev->cmdbuf[receive - 1] != 0x60 && /* NMB SGI keyboard, translated */
ps2dev->cmdbuf[receive - 1] != 0x47) { /* NMB SGI keyboard */
/*
* Device behind the port is not a keyboard
* so we don't need to wait for the 2nd byte
* of ID response.
*/
serio_pause_rx(ps2dev->serio);
ps2dev->flags = ps2dev->cmdcnt = 0;
serio_continue_rx(ps2dev->serio);
}
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD), timeout);
}
if (param)
for (i = 0; i < receive; i++)
param[i] = ps2dev->cmdbuf[(receive - 1) - i];
if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1))
goto out;
rc = 0;
out:
serio_pause_rx(ps2dev->serio);
ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio);
up(&ps2dev->cmd_sem);
return rc;
}
/*
* ps2_execute_scheduled_command() sends a command, previously scheduled by
* ps2_schedule_command(), to a PS/2 device (keyboard, mouse, etc.)
*/
#ifndef L4INPUT
static void ps2_execute_scheduled_command(void *data)
{
struct ps2work *ps2work = data;
ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command);
kfree(ps2work);
}
#endif
/*
* ps2_schedule_command() allows to schedule delayed execution of a PS/2
* command and can be used to issue a command from an interrupt or softirq
* context.
*/
int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
#ifdef L4INPUT
ps2_command(ps2dev, param, command);
#else
struct ps2work *ps2work;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
if (!(ps2work = kmalloc(sizeof(struct ps2work) + max(send, receive), GFP_ATOMIC)))
return -1;
memset(ps2work, 0, sizeof(struct ps2work));
ps2work->ps2dev = ps2dev;
ps2work->command = command;
memcpy(ps2work->param, param, send);
INIT_WORK(&ps2work->work, ps2_execute_scheduled_command, ps2work);
if (!schedule_work(&ps2work->work)) {
kfree(ps2work);
return -1;
}
#endif
return 0;
}
/*
* ps2_init() initializes ps2dev structure
*/
void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
{
init_MUTEX(&ps2dev->cmd_sem);
init_waitqueue_head(&ps2dev->wait);
ps2dev->serio = serio;
}
/*
* ps2_handle_ack() is supposed to be used in interrupt handler
* to properly process ACK/NAK of a command from a PS/2 device.
*/
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
{
switch (data) {
case PS2_RET_ACK:
ps2dev->nak = 0;
break;
case PS2_RET_NAK:
ps2dev->nak = 1;
break;
/*
* Workaround for mice which don't ACK the Get ID command.
* These are valid mouse IDs that we recognize.
*/
case 0x00:
case 0x03:
case 0x04:
if (ps2dev->flags & PS2_FLAG_WAITID) {
ps2dev->nak = 0;
break;
}
/* Fall through */
default:
return 0;
}
if (!ps2dev->nak && ps2dev->cmdcnt)
ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
ps2dev->flags &= ~PS2_FLAG_ACK;
wake_up(&ps2dev->wait);
if (data != PS2_RET_ACK)
ps2_handle_response(ps2dev, data);
return 1;
}
/*
* ps2_handle_response() is supposed to be used in interrupt handler
* to properly store device's response to a command and notify process
* waiting for completion of the command.
*/
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
{
if (ps2dev->cmdcnt)
ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;
if (ps2dev->flags & PS2_FLAG_CMD1) {
ps2dev->flags &= ~PS2_FLAG_CMD1;
if (ps2dev->cmdcnt)
wake_up(&ps2dev->wait);
}
if (!ps2dev->cmdcnt) {
ps2dev->flags &= ~PS2_FLAG_CMD;
wake_up(&ps2dev->wait);
}
return 1;
}
void ps2_cmd_aborted(struct ps2dev *ps2dev)
{
if (ps2dev->flags & PS2_FLAG_ACK)
ps2dev->nak = 1;
if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
wake_up(&ps2dev->wait);
ps2dev->flags = 0;
}

View File

@@ -0,0 +1,754 @@
/*
* The Serio abstraction module
*
* Copyright (c) 1999-2004 Vojtech Pavlik
* Copyright (c) 2004 Dmitry Torokhov
* Copyright (c) 2003 Daniele Bellucci
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/serio.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/completion.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/slab.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Serio abstraction core");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(serio_interrupt);
EXPORT_SYMBOL(serio_register_port);
EXPORT_SYMBOL(serio_register_port_delayed);
EXPORT_SYMBOL(serio_unregister_port);
EXPORT_SYMBOL(serio_unregister_port_delayed);
EXPORT_SYMBOL(serio_register_driver);
EXPORT_SYMBOL(serio_unregister_driver);
EXPORT_SYMBOL(serio_open);
EXPORT_SYMBOL(serio_close);
EXPORT_SYMBOL(serio_rescan);
EXPORT_SYMBOL(serio_reconnect);
static DECLARE_MUTEX(serio_sem); /* protects serio_list and serio_diriver_list */
static LIST_HEAD(serio_list);
static LIST_HEAD(serio_driver_list);
static unsigned int serio_no;
struct bus_type serio_bus = {
.name = "serio",
};
#ifndef L4INPUT
static void serio_find_driver(struct serio *serio);
static void serio_create_port(struct serio *serio);
static void serio_destroy_port(struct serio *serio);
static void serio_connect_port(struct serio *serio, struct serio_driver *drv);
static void serio_reconnect_port(struct serio *serio);
#endif
static void serio_disconnect_port(struct serio *serio);
static int serio_bind_driver(struct serio *serio, struct serio_driver *drv)
{
get_driver(&drv->driver);
drv->connect(serio, drv);
if (serio->drv) {
#ifndef L4INPUT
down_write(&serio_bus.subsys.rwsem);
#endif
serio->dev.driver = &drv->driver;
device_bind_driver(&serio->dev);
#ifndef L4INPUT
up_write(&serio_bus.subsys.rwsem);
#endif
return 1;
}
put_driver(&drv->driver);
return 0;
}
/* serio_find_driver() must be called with serio_sem down. */
static void serio_find_driver(struct serio *serio)
{
struct serio_driver *drv;
list_for_each_entry(drv, &serio_driver_list, node)
if (!drv->manual_bind)
if (serio_bind_driver(serio, drv))
break;
}
#ifndef L4INPUT
/*
* Serio event processing.
*/
struct serio_event {
int type;
struct serio *serio;
struct list_head node;
};
enum serio_event_type {
SERIO_RESCAN,
SERIO_RECONNECT,
SERIO_REGISTER_PORT,
SERIO_UNREGISTER_PORT,
};
#endif
#ifndef L4INPUT
static DEFINE_SPINLOCK(serio_event_lock); /* protects serio_event_list */
#endif
static LIST_HEAD(serio_event_list);
#ifndef L4INPUT
static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
static DECLARE_COMPLETION(serio_exited);
static int serio_pid;
#endif
#ifndef L4INPUT
static void serio_queue_event(struct serio *serio, int event_type)
{
unsigned long flags;
struct serio_event *event;
spin_lock_irqsave(&serio_event_lock, flags);
if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) {
event->type = event_type;
event->serio = serio;
list_add_tail(&event->node, &serio_event_list);
wake_up(&serio_wait);
}
spin_unlock_irqrestore(&serio_event_lock, flags);
}
static struct serio_event *serio_get_event(void)
{
struct serio_event *event;
struct list_head *node;
unsigned long flags;
spin_lock_irqsave(&serio_event_lock, flags);
if (list_empty(&serio_event_list)) {
spin_unlock_irqrestore(&serio_event_lock, flags);
return NULL;
}
node = serio_event_list.next;
event = container_of(node, struct serio_event, node);
list_del_init(node);
spin_unlock_irqrestore(&serio_event_lock, flags);
return event;
}
static void serio_handle_events(void)
{
struct serio_event *event;
while ((event = serio_get_event())) {
down(&serio_sem);
switch (event->type) {
case SERIO_REGISTER_PORT :
serio_create_port(event->serio);
serio_connect_port(event->serio, NULL);
break;
case SERIO_UNREGISTER_PORT :
serio_disconnect_port(event->serio);
serio_destroy_port(event->serio);
break;
case SERIO_RECONNECT :
serio_reconnect_port(event->serio);
break;
case SERIO_RESCAN :
serio_disconnect_port(event->serio);
serio_connect_port(event->serio, NULL);
break;
default:
break;
}
up(&serio_sem);
kfree(event);
}
}
#endif
#ifndef L4INPUT
static void serio_remove_pending_events(struct serio *serio)
{
struct list_head *node, *next;
struct serio_event *event;
unsigned long flags;
spin_lock_irqsave(&serio_event_lock, flags);
list_for_each_safe(node, next, &serio_event_list) {
event = container_of(node, struct serio_event, node);
if (event->serio == serio) {
list_del_init(node);
kfree(event);
}
}
spin_unlock_irqrestore(&serio_event_lock, flags);
}
static int serio_thread(void *nothing)
{
lock_kernel();
daemonize("kseriod");
allow_signal(SIGTERM);
do {
serio_handle_events();
wait_event_interruptible(serio_wait, !list_empty(&serio_event_list));
try_to_freeze(PF_FREEZE);
} while (!signal_pending(current));
printk(KERN_DEBUG "serio: kseriod exiting\n");
unlock_kernel();
complete_and_exit(&serio_exited, 0);
}
/*
* Serio port operations
*/
static ssize_t serio_show_description(struct device *dev, char *buf)
{
struct serio *serio = to_serio_port(dev);
return sprintf(buf, "%s\n", serio->name);
}
static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t count)
{
struct serio *serio = to_serio_port(dev);
struct device_driver *drv;
int retval;
retval = down_interruptible(&serio_sem);
if (retval)
return retval;
retval = count;
if (!strncmp(buf, "none", count)) {
serio_disconnect_port(serio);
} else if (!strncmp(buf, "reconnect", count)) {
serio_reconnect_port(serio);
} else if (!strncmp(buf, "rescan", count)) {
serio_disconnect_port(serio);
serio_connect_port(serio, NULL);
} else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
serio_disconnect_port(serio);
serio_connect_port(serio, to_serio_driver(drv));
put_driver(drv);
} else {
retval = -EINVAL;
}
up(&serio_sem);
return retval;
}
static ssize_t serio_show_bind_mode(struct device *dev, char *buf)
{
struct serio *serio = to_serio_port(dev);
return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto");
}
static ssize_t serio_set_bind_mode(struct device *dev, const char *buf, size_t count)
{
struct serio *serio = to_serio_port(dev);
int retval;
retval = count;
if (!strncmp(buf, "manual", count)) {
serio->manual_bind = 1;
} else if (!strncmp(buf, "auto", count)) {
serio->manual_bind = 0;
} else {
retval = -EINVAL;
}
return retval;
}
static struct device_attribute serio_device_attrs[] = {
__ATTR(description, S_IRUGO, serio_show_description, NULL),
__ATTR(drvctl, S_IWUSR, NULL, serio_rebind_driver),
__ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode),
__ATTR_NULL
};
#endif
static void serio_release_port(struct device *dev)
{
struct serio *serio = to_serio_port(dev);
kfree(serio);
module_put(THIS_MODULE);
}
static void serio_create_port(struct serio *serio)
{
try_module_get(THIS_MODULE);
spin_lock_init(&serio->lock);
init_MUTEX(&serio->drv_sem);
list_add_tail(&serio->node, &serio_list);
snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id), "serio%d", serio_no++);
serio->dev.bus = &serio_bus;
serio->dev.release = serio_release_port;
if (serio->parent)
serio->dev.parent = &serio->parent->dev;
#ifndef L4INPUT
device_register(&serio->dev);
#endif
}
/*
* serio_destroy_port() completes deregistration process and removes
* port from the system
*/
static void serio_destroy_port(struct serio *serio)
{
struct serio_driver *drv = serio->drv;
unsigned long flags;
#ifndef L4INPUT
serio_remove_pending_events(serio);
#endif
list_del_init(&serio->node);
if (drv) {
drv->disconnect(serio);
#ifndef L4INPUT
down_write(&serio_bus.subsys.rwsem);
device_release_driver(&serio->dev);
up_write(&serio_bus.subsys.rwsem);
#endif
put_driver(&drv->driver);
}
if (serio->parent) {
spin_lock_irqsave(&serio->parent->lock, flags);
serio->parent->child = NULL;
spin_unlock_irqrestore(&serio->parent->lock, flags);
}
#ifndef L4INPUT
device_unregister(&serio->dev);
#endif
}
/*
* serio_connect_port() tries to bind the port and possible all its
* children to appropriate drivers. If driver passed in the function will not
* try otehr drivers when binding parent port.
*/
static void serio_connect_port(struct serio *serio, struct serio_driver *drv)
{
WARN_ON(serio->drv);
WARN_ON(serio->child);
if (drv)
serio_bind_driver(serio, drv);
else if (!serio->manual_bind)
serio_find_driver(serio);
/* Ok, now bind children, if any */
while (serio->child) {
serio = serio->child;
WARN_ON(serio->drv);
WARN_ON(serio->child);
serio_create_port(serio);
if (!serio->manual_bind) {
/*
* With children we just _prefer_ passed in driver,
* but we will try other options in case preferred
* is not the one
*/
if (!drv || !serio_bind_driver(serio, drv))
serio_find_driver(serio);
}
}
}
#ifndef L4INPUT
/*
*
*/
static void serio_reconnect_port(struct serio *serio)
{
do {
if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) {
serio_disconnect_port(serio);
serio_connect_port(serio, NULL);
/* Ok, old children are now gone, we are done */
break;
}
serio = serio->child;
} while (serio);
}
#endif
/*
* serio_disconnect_port() unbinds a port from its driver. As a side effect
* all child ports are unbound and destroyed.
*/
static void serio_disconnect_port(struct serio *serio)
{
struct serio_driver *drv = serio->drv;
struct serio *s;
if (serio->child) {
/*
* Children ports should be disconnected and destroyed
* first, staring with the leaf one, since we don't want
* to do recursion
*/
do {
s = serio->child;
} while (s->child);
while (s != serio) {
s = s->parent;
serio_destroy_port(s->child);
}
}
/*
* Ok, no children left, now disconnect this port
*/
if (drv) {
drv->disconnect(serio);
#ifndef L4INPUT
down_write(&serio_bus.subsys.rwsem);
device_release_driver(&serio->dev);
up_write(&serio_bus.subsys.rwsem);
#endif
put_driver(&drv->driver);
}
}
#ifndef L4INPUT
void serio_rescan(struct serio *serio)
{
serio_queue_event(serio, SERIO_RESCAN);
}
void serio_reconnect(struct serio *serio)
{
serio_queue_event(serio, SERIO_RECONNECT);
}
#else
void serio_rescan(struct serio *serio)
{
down(&serio_sem);
if (serio->drv && serio->drv->disconnect)
serio->drv->disconnect(serio);
serio_find_driver(serio);
up(&serio_sem);
}
void serio_reconnect(struct serio *serio)
{
down(&serio_sem);
if (serio->drv && serio->drv->reconnect)
if (serio->drv->reconnect(serio) == 0)
return;
/* reconnect failed - fall through to rescan */
if (serio->drv && serio->drv->disconnect)
serio->drv->disconnect(serio);
serio_find_driver(serio);
up(&serio_sem);
}
#endif
void serio_register_port(struct serio *serio)
{
down(&serio_sem);
serio_create_port(serio);
serio_connect_port(serio, NULL);
up(&serio_sem);
}
/*
* Submits register request to kseriod for subsequent execution.
* Can be used when it is not obvious whether the serio_sem is
* taken or not and when delayed execution is feasible.
*/
void serio_register_port_delayed(struct serio *serio)
{
#ifndef L4INPUT
serio_queue_event(serio, SERIO_REGISTER_PORT);
#else
serio_register_port(serio);
#endif
}
void serio_unregister_port(struct serio *serio)
{
down(&serio_sem);
serio_disconnect_port(serio);
serio_destroy_port(serio);
up(&serio_sem);
}
/*
* Submits unregister request to kseriod for subsequent execution.
* Can be used when it is not obvious whether the serio_sem is
* taken or not and when delayed execution is feasible.
*/
void serio_unregister_port_delayed(struct serio *serio)
{
#ifndef L4INPUT
serio_queue_event(serio, SERIO_UNREGISTER_PORT);
#else
serio_unregister_port(serio);
#endif
}
#ifndef L4INPUT
/*
* Serio driver operations
*/
static ssize_t serio_driver_show_description(struct device_driver *drv, char *buf)
{
struct serio_driver *driver = to_serio_driver(drv);
return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
}
static ssize_t serio_driver_show_bind_mode(struct device_driver *drv, char *buf)
{
struct serio_driver *serio_drv = to_serio_driver(drv);
return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto");
}
static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char *buf, size_t count)
{
struct serio_driver *serio_drv = to_serio_driver(drv);
int retval;
retval = count;
if (!strncmp(buf, "manual", count)) {
serio_drv->manual_bind = 1;
} else if (!strncmp(buf, "auto", count)) {
serio_drv->manual_bind = 0;
} else {
retval = -EINVAL;
}
return retval;
}
static struct driver_attribute serio_driver_attrs[] = {
__ATTR(description, S_IRUGO, serio_driver_show_description, NULL),
__ATTR(bind_mode, S_IWUSR | S_IRUGO,
serio_driver_show_bind_mode, serio_driver_set_bind_mode),
__ATTR_NULL
};
#endif
void serio_register_driver(struct serio_driver *drv)
{
struct serio *serio;
down(&serio_sem);
list_add_tail(&drv->node, &serio_driver_list);
drv->driver.bus = &serio_bus;
driver_register(&drv->driver);
if (drv->manual_bind)
goto out;
start_over:
list_for_each_entry(serio, &serio_list, node) {
if (!serio->drv) {
serio_connect_port(serio, drv);
/*
* if new child appeared then the list is changed,
* we need to start over
*/
if (serio->child)
goto start_over;
}
}
out:
up(&serio_sem);
}
void serio_unregister_driver(struct serio_driver *drv)
{
struct serio *serio;
down(&serio_sem);
list_del_init(&drv->node);
start_over:
list_for_each_entry(serio, &serio_list, node) {
if (serio->drv == drv) {
serio_disconnect_port(serio);
serio_connect_port(serio, NULL);
/* we could've deleted some ports, restart */
goto start_over;
}
}
driver_unregister(&drv->driver);
up(&serio_sem);
}
static void serio_set_drv(struct serio *serio, struct serio_driver *drv)
{
down(&serio->drv_sem);
serio_pause_rx(serio);
serio->drv = drv;
serio_continue_rx(serio);
up(&serio->drv_sem);
}
/* called from serio_driver->connect/disconnect methods under serio_sem */
int serio_open(struct serio *serio, struct serio_driver *drv)
{
serio_set_drv(serio, drv);
if (serio->open && serio->open(serio)) {
serio_set_drv(serio, NULL);
return -1;
}
return 0;
}
/* called from serio_driver->connect/disconnect methods under serio_sem */
void serio_close(struct serio *serio)
{
if (serio->close)
serio->close(serio);
serio_set_drv(serio, NULL);
}
irqreturn_t serio_interrupt(struct serio *serio,
unsigned char data, unsigned int dfl, struct pt_regs *regs)
{
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
spin_lock_irqsave(&serio->lock, flags);
if (likely(serio->drv)) {
ret = serio->drv->interrupt(serio, data, dfl, regs);
} else {
if (!dfl) {
if ((serio->type != SERIO_8042 &&
serio->type != SERIO_8042_XL) || (data == 0xaa)) {
serio_rescan(serio);
ret = IRQ_HANDLED;
}
}
}
spin_unlock_irqrestore(&serio->lock, flags);
return ret;
}
static int __init serio_init(void)
{
#ifndef L4INPUT
if (!(serio_pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL))) {
printk(KERN_WARNING "serio: Failed to start kseriod\n");
return -1;
}
serio_bus.dev_attrs = serio_device_attrs;
serio_bus.drv_attrs = serio_driver_attrs;
bus_register(&serio_bus);
#else
pthread_mutexattr_t a;
if (pthread_mutexattr_init(&a) != 0)
return -1;
if (pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE) != 0)
return -1;
if (pthread_mutex_init(&serio_sem.l4_lock, &a) != 0)
return -1;
pthread_mutexattr_destroy(&a);
#endif
return 0;
}
static void __exit serio_exit(void)
{
#ifndef L4INPUT
bus_unregister(&serio_bus);
kill_proc(serio_pid, SIGTERM, 1);
wait_for_completion(&serio_exited);
#endif
}
module_init(serio_init);
module_exit(serio_exit);

View File

@@ -0,0 +1,16 @@
#ifndef _I386_BITOPS_H
#define _I386_BITOPS_H
#include <l4/util/bitops.h>
#define set_bit(nr, addr) \
l4util_set_bit((nr), (addr))
#define clear_bit(nr, addr) \
l4util_clear_bit((nr), (addr))
#define change_bit(nr, addr) \
l4util_complement_bit((nr),(addr))
#define test_bit(nr, addr) \
l4util_test_bit((nr),(addr))
#endif

View File

@@ -0,0 +1,7 @@
#ifndef _I386_DELAY_H
#define _I386_DELAY_H
extern void udelay(unsigned long usecs);
#endif

View File

@@ -0,0 +1,12 @@
#ifndef _ASM_ERRNO_H
#define _ASM_ERRNO_H
#define EPERM 1 /* Operation not permitted */
#define EIO 5
#define ENOMEM 12 /* Out of memory */
#define EBUSY 16 /* Device or resource busy */
#define ENODEV 19 /* No such device */
#define EINVAL 22
#endif

View File

@@ -0,0 +1,92 @@
/*
* linux/include/asm-arm/hardware/amba_kmi.h
*
* Internal header file for AMBA KMI ports
*
* Copyright (C) 2000 Deep Blue Solutions Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* ---------------------------------------------------------------------------
* From ARM PrimeCell(tm) PS2 Keyboard/Mouse Interface (PL050) Technical
* Reference Manual - ARM DDI 0143B - see http://www.arm.com/
* ---------------------------------------------------------------------------
*/
#ifndef ASM_ARM_HARDWARE_AMBA_KMI_H
#define ASM_ARM_HARDWARE_AMBA_KMI_H
/*
* KMI control register:
* KMICR_TYPE 0 = PS2/AT mode, 1 = No line control bit mode
* KMICR_RXINTREN 1 = enable RX interrupts
* KMICR_TXINTREN 1 = enable TX interrupts
* KMICR_EN 1 = enable KMI
* KMICR_FD 1 = force KMI data low
* KMICR_FC 1 = force KMI clock low
*/
#define KMICR (KMI_BASE + 0x00)
#define KMICR_TYPE (1 << 5)
#define KMICR_RXINTREN (1 << 4)
#define KMICR_TXINTREN (1 << 3)
#define KMICR_EN (1 << 2)
#define KMICR_FD (1 << 1)
#define KMICR_FC (1 << 0)
/*
* KMI status register:
* KMISTAT_TXEMPTY 1 = transmitter register empty
* KMISTAT_TXBUSY 1 = currently sending data
* KMISTAT_RXFULL 1 = receiver register ready to be read
* KMISTAT_RXBUSY 1 = currently receiving data
* KMISTAT_RXPARITY parity of last databyte received
* KMISTAT_IC current level of KMI clock input
* KMISTAT_ID current level of KMI data input
*/
#define KMISTAT (KMI_BASE + 0x04)
#define KMISTAT_TXEMPTY (1 << 6)
#define KMISTAT_TXBUSY (1 << 5)
#define KMISTAT_RXFULL (1 << 4)
#define KMISTAT_RXBUSY (1 << 3)
#define KMISTAT_RXPARITY (1 << 2)
#define KMISTAT_IC (1 << 1)
#define KMISTAT_ID (1 << 0)
/*
* KMI data register
*/
#define KMIDATA (KMI_BASE + 0x08)
/*
* KMI clock divisor: to generate 8MHz internal clock
* div = (ref / 8MHz) - 1; 0 <= div <= 15
*/
#define KMICLKDIV (KMI_BASE + 0x0c)
/*
* KMI interrupt register:
* KMIIR_TXINTR 1 = transmit interrupt asserted
* KMIIR_RXINTR 1 = receive interrupt asserted
*/
#define KMIIR (KMI_BASE + 0x10)
#define KMIIR_TXINTR (1 << 1)
#define KMIIR_RXINTR (1 << 0)
/*
* The size of the KMI primecell
*/
#define KMI_SIZE (0x100)
#endif

View File

@@ -0,0 +1,24 @@
#ifndef _ASM_IO_H
#define _ASM_IO_H
#ifdef ARCH_arm
#define readb(addr) (*(volatile unsigned char *)(addr))
#define writeb(val, addr) do { *(volatile unsigned char *)(addr) = (val); } while (0)
#else
#include <l4/util/port_io.h>
#include <l4/util/util.h>
#include <l4/sys/ipc.h>
#define iodelay() do { l4_sleep(1); } while (0)
#define inb(port) l4util_in8(port)
#define outb(value, port) l4util_out8(value, port)
#define inb_p(port) ({ iodelay(); l4util_in8(port); })
#define outb_p(value, port) ({ iodelay(); l4util_out8(value, port); })
#endif
#endif

View File

@@ -0,0 +1,8 @@
#ifndef _ASMi386_PARAM_H
#define _ASMi386_PARAM_H
//extern unsigned long HZ;
#define HZ 1000000
#endif

View File

@@ -0,0 +1,38 @@
#ifndef _I386_SEMAPHORE_H
#define _I386_SEMAPHORE_H
#include <linux/wait.h>
#include <pthread.h>
/* XXX Semaphores are used only for mutual exclusion in input for now. Given
* this fact and because "kseriod" is not desirable we use L4 locks that can
* be reentered by one and the same thread. */
struct semaphore {
pthread_mutex_t l4_lock;
};
#define DECLARE_MUTEX(name) \
struct semaphore name;
static inline void init_MUTEX (struct semaphore * sem)
{
pthread_mutexattr_t a;
pthread_mutexattr_init(&a);
pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&sem->l4_lock, &a);
pthread_mutexattr_destroy(&a);
}
static inline void down(struct semaphore * sem)
{
pthread_mutex_lock(&sem->l4_lock);
}
static inline void up(struct semaphore * sem)
{
pthread_mutex_unlock(&sem->l4_lock);
}
#endif

View File

@@ -0,0 +1,7 @@
#ifndef _ASMi386_SIGNAL_H
#define _ASMi386_SIGNAL_H
#define SA_SHIRQ 0x04000000
#endif

View File

@@ -0,0 +1,31 @@
#ifndef _I386_TYPES_H
#define _I386_TYPES_H
//typedef signed char __s8;
//typedef unsigned char __u8;
typedef signed short __s16;
typedef unsigned short __u16;
typedef signed int __s32;
typedef unsigned int __u32;
//typedef signed long long __s64;
//typedef unsigned long long __u64;
#define BITS_PER_LONG (__SIZEOF_LONG__ * 8)
//typedef signed char s8;
typedef unsigned char u8;
//typedef signed short s16;
//typedef unsigned short u16;
//typedef signed int s32;
typedef unsigned int u32;
//typedef signed long long s64;
typedef unsigned long long u64;
#endif

View File

@@ -0,0 +1,63 @@
/*****************************************************************************/
/**
* \file input/lib/include/internal.h
* \brief L4INPUT internal interface
*
* \date 11/20/2003
* \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de>
*
*/
/*
* (c) 2003-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#ifndef __INPUT_LIB_INCLUDE_INTERNAL_H_
#define __INPUT_LIB_INCLUDE_INTERNAL_H_
#include <l4/input/libinput.h>
/** SUBSYSTEMS/EMULATION **/
void l4input_internal_irq_init(int prio);
void l4input_internal_wait_init(void);
struct l4input_ops {
int(*ispending)(void);
int(*flush)(void *buf, int count);
int(*pcspkr)(int tone);
};
/** L4EVDEV input event handler (hardware drivers) **/
struct l4input_ops * l4input_internal_l4evdev_init(L4_CV void (*cb)(struct l4input *));
void l4input_internal_l4evdev_exit(void);
/** INPUT SYSTEM **/
int l4input_internal_atkbd_init(void);
int l4input_internal_psmouse_init(void);
int l4input_internal_i8042_init(void);
int l4input_internal_input_init(void);
int l4input_internal_serio_init(void);
int l4input_internal_pcspkr_init(void);
int l4input_internal_proxy_init(int prio);
int l4input_internal_amba_kmi_init_k(void);
int l4input_internal_amba_kmi_init_m(void);
int l4input_internal_l4drv_init(void);
int l4input_internal_l4bus_init(void);
void l4input_internal_atkbd_exit(void);
void l4input_internal_psmouse_exit(void);
void l4input_internal_i8042_exit(void);
void l4input_internal_input_exit(void);
void l4input_internal_serio_exit(void);
void l4input_internal_pcspkr_exit(void);
void l4input_internal_proxy_exit(void);
void l4input_internal_amba_kmi_exit_k(void);
void l4input_internal_amba_kmi_exit_m(void);
#endif

View File

@@ -0,0 +1 @@
#include <asm/bitops.h>

View File

@@ -0,0 +1,7 @@
#ifndef _LINUX_CONFIG_H
#define _LINUX_CONFIG_H
#undef CONFIG_PROC_FS
#undef CONFIG_HOTPLUG /* XXX maybe useful later */
#endif

View File

@@ -0,0 +1,7 @@
#ifndef _LINUX_DELAY_H
#define _LINUX_DELAY_H
#include <asm/delay.h>
#endif

View File

@@ -0,0 +1,13 @@
#ifndef _LINUX_DEVFS_FS_KERNEL_H
#define _LINUX_DEVFS_FS_KERNEL_H
static inline int devfs_mk_dir(const char *fmt, ...)
{
return 0;
}
static inline void devfs_remove(const char *fmt, ...)
{
}
#endif

View File

@@ -0,0 +1,56 @@
#ifndef _DEVICE_H_
#define _DEVICE_H_
#include <linux/types.h>
#include <linux/errno.h>
#include <asm/semaphore.h>
struct module;
static inline struct class_simple *class_simple_create(struct module *owner, char *name)
{
return NULL;
}
#define class_simple_destroy(x)
#define device_create_file(d,e)
#define device_remove_file(d,a)
struct bus_type {
char * name;
};
struct device {
struct device * parent;
struct device_driver *driver;
char bus_id[20];
struct bus_type * bus;
void (*release)(struct device *dev);
};
struct device_driver {
char * name;
struct bus_type * bus;
};
struct platform_device {
char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
#define get_driver(x)
#define put_driver(x)
#define device_bind_driver(x)
#define driver_unregister(x)
#define platform_device_unregister(x)
static int inline driver_register(struct device_driver *drv)
{
return 0;
}
#endif

View File

View File

@@ -0,0 +1 @@
#include <asm/errno.h>

View File

@@ -0,0 +1,20 @@
#ifndef _LINUX_FS_H
#define _LINUX_FS_H
struct file;
struct inode;
struct file_operations {
int (*open) (struct inode *, struct file *);
};
static inline int register_chrdev(unsigned int i, const char *c,
struct file_operations *f) {return 0;};
static inline int unregister_chrdev(unsigned int i, const char *c) {return 0;};
#define fops_get(fops) (fops)
#define fops_put(fops) do {} while (0)
#define iminor(x) (1)
#endif

View File

@@ -0,0 +1,26 @@
#ifndef _LINUX_INIT_H
#define _LINUX_INIT_H
#define __init /* ignore */
#define __exit /* ignore */
#define __obsolete_setup(str) /* ignore */
#define subsys_initcall(x) module_init(x)
#define module_init(x) \
int l4input_internal_##x(void) { return x(); }
#define module_exit(x) \
void l4input_internal_##x(void) { x(); }
/* XXX Could be useful for tweaking */
#define __setup(str, fn) \
static int(*__setup_##fn)(char*) __attribute__ ((unused)) = fn
#if __GNUC__ == 2 && __GNUC_MINOR__ < 96
#define __builtin_expect(x, expected_value) (x)
#endif
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif

View File

@@ -0,0 +1,970 @@
#ifndef _INPUT_H
#define _INPUT_H
/*
* Copyright (c) 1999-2002 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifdef __KERNEL__
#include <linux/time.h>
#include <linux/list.h>
#else
#include <sys/time.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#endif
#ifdef L4INPUT
#include <asm/bitops.h>
#include <linux/slab.h>
#endif
/*
* The event structure itself
*/
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
/*
* Protocol version.
*/
#define EV_VERSION 0x010000
/*
* IOCTLs (0x00 - 0x7f)
*/
struct input_id {
__u16 bustype;
__u16 vendor;
__u16 product;
__u16 version;
};
struct input_absinfo {
__s32 value;
__s32 minimum;
__s32 maximum;
__s32 fuzz;
__s32 flat;
};
#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */
#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */
#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */
#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */
#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */
#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */
#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */
#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */
#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */
#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */
#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */
#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */
#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */
#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */
#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */
#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */
#ifndef L4INPUT
/*
* Event types
*/
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
#define EV_MSC 0x04
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
/*
* Synchronization events.
*/
#define SYN_REPORT 0
#define SYN_CONFIG 1
/*
* Keys and buttons
*/
#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
#define KEY_3 4
#define KEY_4 5
#define KEY_5 6
#define KEY_6 7
#define KEY_7 8
#define KEY_8 9
#define KEY_9 10
#define KEY_0 11
#define KEY_MINUS 12
#define KEY_EQUAL 13
#define KEY_BACKSPACE 14
#define KEY_TAB 15
#define KEY_Q 16
#define KEY_W 17
#define KEY_E 18
#define KEY_R 19
#define KEY_T 20
#define KEY_Y 21
#define KEY_U 22
#define KEY_I 23
#define KEY_O 24
#define KEY_P 25
#define KEY_LEFTBRACE 26
#define KEY_RIGHTBRACE 27
#define KEY_ENTER 28
#define KEY_LEFTCTRL 29
#define KEY_A 30
#define KEY_S 31
#define KEY_D 32
#define KEY_F 33
#define KEY_G 34
#define KEY_H 35
#define KEY_J 36
#define KEY_K 37
#define KEY_L 38
#define KEY_SEMICOLON 39
#define KEY_APOSTROPHE 40
#define KEY_GRAVE 41
#define KEY_LEFTSHIFT 42
#define KEY_BACKSLASH 43
#define KEY_Z 44
#define KEY_X 45
#define KEY_C 46
#define KEY_V 47
#define KEY_B 48
#define KEY_N 49
#define KEY_M 50
#define KEY_COMMA 51
#define KEY_DOT 52
#define KEY_SLASH 53
#define KEY_RIGHTSHIFT 54
#define KEY_KPASTERISK 55
#define KEY_LEFTALT 56
#define KEY_SPACE 57
#define KEY_CAPSLOCK 58
#define KEY_F1 59
#define KEY_F2 60
#define KEY_F3 61
#define KEY_F4 62
#define KEY_F5 63
#define KEY_F6 64
#define KEY_F7 65
#define KEY_F8 66
#define KEY_F9 67
#define KEY_F10 68
#define KEY_NUMLOCK 69
#define KEY_SCROLLLOCK 70
#define KEY_KP7 71
#define KEY_KP8 72
#define KEY_KP9 73
#define KEY_KPMINUS 74
#define KEY_KP4 75
#define KEY_KP5 76
#define KEY_KP6 77
#define KEY_KPPLUS 78
#define KEY_KP1 79
#define KEY_KP2 80
#define KEY_KP3 81
#define KEY_KP0 82
#define KEY_KPDOT 83
#define KEY_ZENKAKUHANKAKU 85
#define KEY_102ND 86
#define KEY_F11 87
#define KEY_F12 88
#define KEY_RO 89
#define KEY_KATAKANA 90
#define KEY_HIRAGANA 91
#define KEY_HENKAN 92
#define KEY_KATAKANAHIRAGANA 93
#define KEY_MUHENKAN 94
#define KEY_KPJPCOMMA 95
#define KEY_KPENTER 96
#define KEY_RIGHTCTRL 97
#define KEY_KPSLASH 98
#define KEY_SYSRQ 99
#define KEY_RIGHTALT 100
#define KEY_LINEFEED 101
#define KEY_HOME 102
#define KEY_UP 103
#define KEY_PAGEUP 104
#define KEY_LEFT 105
#define KEY_RIGHT 106
#define KEY_END 107
#define KEY_DOWN 108
#define KEY_PAGEDOWN 109
#define KEY_INSERT 110
#define KEY_DELETE 111
#define KEY_MACRO 112
#define KEY_MUTE 113
#define KEY_VOLUMEDOWN 114
#define KEY_VOLUMEUP 115
#define KEY_POWER 116
#define KEY_KPEQUAL 117
#define KEY_KPPLUSMINUS 118
#define KEY_PAUSE 119
#define KEY_KPCOMMA 121
#define KEY_HANGUEL 122
#define KEY_HANJA 123
#define KEY_YEN 124
#define KEY_LEFTMETA 125
#define KEY_RIGHTMETA 126
#define KEY_COMPOSE 127
#define KEY_STOP 128
#define KEY_AGAIN 129
#define KEY_PROPS 130
#define KEY_UNDO 131
#define KEY_FRONT 132
#define KEY_COPY 133
#define KEY_OPEN 134
#define KEY_PASTE 135
#define KEY_FIND 136
#define KEY_CUT 137
#define KEY_HELP 138
#define KEY_MENU 139
#define KEY_CALC 140
#define KEY_SETUP 141
#define KEY_SLEEP 142
#define KEY_WAKEUP 143
#define KEY_FILE 144
#define KEY_SENDFILE 145
#define KEY_DELETEFILE 146
#define KEY_XFER 147
#define KEY_PROG1 148
#define KEY_PROG2 149
#define KEY_WWW 150
#define KEY_MSDOS 151
#define KEY_COFFEE 152
#define KEY_DIRECTION 153
#define KEY_CYCLEWINDOWS 154
#define KEY_MAIL 155
#define KEY_BOOKMARKS 156
#define KEY_COMPUTER 157
#define KEY_BACK 158
#define KEY_FORWARD 159
#define KEY_CLOSECD 160
#define KEY_EJECTCD 161
#define KEY_EJECTCLOSECD 162
#define KEY_NEXTSONG 163
#define KEY_PLAYPAUSE 164
#define KEY_PREVIOUSSONG 165
#define KEY_STOPCD 166
#define KEY_RECORD 167
#define KEY_REWIND 168
#define KEY_PHONE 169
#define KEY_ISO 170
#define KEY_CONFIG 171
#define KEY_HOMEPAGE 172
#define KEY_REFRESH 173
#define KEY_EXIT 174
#define KEY_MOVE 175
#define KEY_EDIT 176
#define KEY_SCROLLUP 177
#define KEY_SCROLLDOWN 178
#define KEY_KPLEFTPAREN 179
#define KEY_KPRIGHTPAREN 180
#define KEY_F13 183
#define KEY_F14 184
#define KEY_F15 185
#define KEY_F16 186
#define KEY_F17 187
#define KEY_F18 188
#define KEY_F19 189
#define KEY_F20 190
#define KEY_F21 191
#define KEY_F22 192
#define KEY_F23 193
#define KEY_F24 194
#define KEY_PLAYCD 200
#define KEY_PAUSECD 201
#define KEY_PROG3 202
#define KEY_PROG4 203
#define KEY_SUSPEND 205
#define KEY_CLOSE 206
#define KEY_PLAY 207
#define KEY_FASTFORWARD 208
#define KEY_BASSBOOST 209
#define KEY_PRINT 210
#define KEY_HP 211
#define KEY_CAMERA 212
#define KEY_SOUND 213
#define KEY_QUESTION 214
#define KEY_EMAIL 215
#define KEY_CHAT 216
#define KEY_SEARCH 217
#define KEY_CONNECT 218
#define KEY_FINANCE 219
#define KEY_SPORT 220
#define KEY_SHOP 221
#define KEY_ALTERASE 222
#define KEY_CANCEL 223
#define KEY_BRIGHTNESSDOWN 224
#define KEY_BRIGHTNESSUP 225
#define KEY_MEDIA 226
#define KEY_UNKNOWN 240
#define BTN_MISC 0x100
#define BTN_0 0x100
#define BTN_1 0x101
#define BTN_2 0x102
#define BTN_3 0x103
#define BTN_4 0x104
#define BTN_5 0x105
#define BTN_6 0x106
#define BTN_7 0x107
#define BTN_8 0x108
#define BTN_9 0x109
#define BTN_MOUSE 0x110
#define BTN_LEFT 0x110
#define BTN_RIGHT 0x111
#define BTN_MIDDLE 0x112
#define BTN_SIDE 0x113
#define BTN_EXTRA 0x114
#define BTN_FORWARD 0x115
#define BTN_BACK 0x116
#define BTN_TASK 0x117
#define BTN_JOYSTICK 0x120
#define BTN_TRIGGER 0x120
#define BTN_THUMB 0x121
#define BTN_THUMB2 0x122
#define BTN_TOP 0x123
#define BTN_TOP2 0x124
#define BTN_PINKIE 0x125
#define BTN_BASE 0x126
#define BTN_BASE2 0x127
#define BTN_BASE3 0x128
#define BTN_BASE4 0x129
#define BTN_BASE5 0x12a
#define BTN_BASE6 0x12b
#define BTN_DEAD 0x12f
#define BTN_GAMEPAD 0x130
#define BTN_A 0x130
#define BTN_B 0x131
#define BTN_C 0x132
#define BTN_X 0x133
#define BTN_Y 0x134
#define BTN_Z 0x135
#define BTN_TL 0x136
#define BTN_TR 0x137
#define BTN_TL2 0x138
#define BTN_TR2 0x139
#define BTN_SELECT 0x13a
#define BTN_START 0x13b
#define BTN_MODE 0x13c
#define BTN_THUMBL 0x13d
#define BTN_THUMBR 0x13e
#define BTN_DIGI 0x140
#define BTN_TOOL_PEN 0x140
#define BTN_TOOL_RUBBER 0x141
#define BTN_TOOL_BRUSH 0x142
#define BTN_TOOL_PENCIL 0x143
#define BTN_TOOL_AIRBRUSH 0x144
#define BTN_TOOL_FINGER 0x145
#define BTN_TOOL_MOUSE 0x146
#define BTN_TOOL_LENS 0x147
#define BTN_TOUCH 0x14a
#define BTN_STYLUS 0x14b
#define BTN_STYLUS2 0x14c
#define BTN_TOOL_DOUBLETAP 0x14d
#define BTN_TOOL_TRIPLETAP 0x14e
#define BTN_WHEEL 0x150
#define BTN_GEAR_DOWN 0x150
#define BTN_GEAR_UP 0x151
#define KEY_OK 0x160
#define KEY_SELECT 0x161
#define KEY_GOTO 0x162
#define KEY_CLEAR 0x163
#define KEY_POWER2 0x164
#define KEY_OPTION 0x165
#define KEY_INFO 0x166
#define KEY_TIME 0x167
#define KEY_VENDOR 0x168
#define KEY_ARCHIVE 0x169
#define KEY_PROGRAM 0x16a
#define KEY_CHANNEL 0x16b
#define KEY_FAVORITES 0x16c
#define KEY_EPG 0x16d
#define KEY_PVR 0x16e
#define KEY_MHP 0x16f
#define KEY_LANGUAGE 0x170
#define KEY_TITLE 0x171
#define KEY_SUBTITLE 0x172
#define KEY_ANGLE 0x173
#define KEY_ZOOM 0x174
#define KEY_MODE 0x175
#define KEY_KEYBOARD 0x176
#define KEY_SCREEN 0x177
#define KEY_PC 0x178
#define KEY_TV 0x179
#define KEY_TV2 0x17a
#define KEY_VCR 0x17b
#define KEY_VCR2 0x17c
#define KEY_SAT 0x17d
#define KEY_SAT2 0x17e
#define KEY_CD 0x17f
#define KEY_TAPE 0x180
#define KEY_RADIO 0x181
#define KEY_TUNER 0x182
#define KEY_PLAYER 0x183
#define KEY_TEXT 0x184
#define KEY_DVD 0x185
#define KEY_AUX 0x186
#define KEY_MP3 0x187
#define KEY_AUDIO 0x188
#define KEY_VIDEO 0x189
#define KEY_DIRECTORY 0x18a
#define KEY_LIST 0x18b
#define KEY_MEMO 0x18c
#define KEY_CALENDAR 0x18d
#define KEY_RED 0x18e
#define KEY_GREEN 0x18f
#define KEY_YELLOW 0x190
#define KEY_BLUE 0x191
#define KEY_CHANNELUP 0x192
#define KEY_CHANNELDOWN 0x193
#define KEY_FIRST 0x194
#define KEY_LAST 0x195
#define KEY_AB 0x196
#define KEY_NEXT 0x197
#define KEY_RESTART 0x198
#define KEY_SLOW 0x199
#define KEY_SHUFFLE 0x19a
#define KEY_BREAK 0x19b
#define KEY_PREVIOUS 0x19c
#define KEY_DIGITS 0x19d
#define KEY_TEEN 0x19e
#define KEY_TWEN 0x19f
#define KEY_DEL_EOL 0x1c0
#define KEY_DEL_EOS 0x1c1
#define KEY_INS_LINE 0x1c2
#define KEY_DEL_LINE 0x1c3
#define KEY_MAX 0x1ff
/*
* Relative axes
*/
#define REL_X 0x00
#define REL_Y 0x01
#define REL_Z 0x02
#define REL_HWHEEL 0x06
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
#define REL_MAX 0x0f
/*
* Absolute axes
*/
#define ABS_X 0x00
#define ABS_Y 0x01
#define ABS_Z 0x02
#define ABS_RX 0x03
#define ABS_RY 0x04
#define ABS_RZ 0x05
#define ABS_THROTTLE 0x06
#define ABS_RUDDER 0x07
#define ABS_WHEEL 0x08
#define ABS_GAS 0x09
#define ABS_BRAKE 0x0a
#define ABS_HAT0X 0x10
#define ABS_HAT0Y 0x11
#define ABS_HAT1X 0x12
#define ABS_HAT1Y 0x13
#define ABS_HAT2X 0x14
#define ABS_HAT2Y 0x15
#define ABS_HAT3X 0x16
#define ABS_HAT3Y 0x17
#define ABS_PRESSURE 0x18
#define ABS_DISTANCE 0x19
#define ABS_TILT_X 0x1a
#define ABS_TILT_Y 0x1b
#define ABS_TOOL_WIDTH 0x1c
#define ABS_VOLUME 0x20
#define ABS_MISC 0x28
#define ABS_MAX 0x3f
/*
* Misc events
*/
#define MSC_SERIAL 0x00
#define MSC_PULSELED 0x01
#define MSC_GESTURE 0x02
#define MSC_RAW 0x03
#define MSC_SCAN 0x04
#define MSC_MAX 0x07
/*
* LEDs
*/
#define LED_NUML 0x00
#define LED_CAPSL 0x01
#define LED_SCROLLL 0x02
#define LED_COMPOSE 0x03
#define LED_KANA 0x04
#define LED_SLEEP 0x05
#define LED_SUSPEND 0x06
#define LED_MUTE 0x07
#define LED_MISC 0x08
#define LED_MAX 0x0f
/*
* Autorepeat values
*/
#define REP_DELAY 0x00
#define REP_PERIOD 0x01
#define REP_MAX 0x01
/*
* Sounds
*/
#define SND_CLICK 0x00
#define SND_BELL 0x01
#define SND_TONE 0x02
#define SND_MAX 0x07
#else /* L4INPUT */
#include <l4/input/macros.h>
#endif
/*
* IDs.
*/
#define ID_BUS 0
#define ID_VENDOR 1
#define ID_PRODUCT 2
#define ID_VERSION 3
#define BUS_PCI 0x01
#define BUS_ISAPNP 0x02
#define BUS_USB 0x03
#define BUS_HIL 0x04
#define BUS_BLUETOOTH 0x05
#define BUS_ISA 0x10
#define BUS_I8042 0x11
#define BUS_XTKBD 0x12
#define BUS_RS232 0x13
#define BUS_GAMEPORT 0x14
#define BUS_PARPORT 0x15
#define BUS_AMIGA 0x16
#define BUS_ADB 0x17
#define BUS_I2C 0x18
#define BUS_HOST 0x19
/*
* Values describing the status of an effect
*/
#define FF_STATUS_STOPPED 0x00
#define FF_STATUS_PLAYING 0x01
#define FF_STATUS_MAX 0x01
/*
* Structures used in ioctls to upload effects to a device
* The first structures are not passed directly by using ioctls.
* They are sub-structures of the actually sent structure (called ff_effect)
*/
struct ff_replay {
__u16 length; /* Duration of an effect in ms. All other times are also expressed in ms */
__u16 delay; /* Time to wait before to start playing an effect */
};
struct ff_trigger {
__u16 button; /* Number of button triggering an effect */
__u16 interval; /* Time to wait before an effect can be re-triggered (ms) */
};
struct ff_envelope {
__u16 attack_length; /* Duration of attack (ms) */
__u16 attack_level; /* Level at beginning of attack */
__u16 fade_length; /* Duration of fade (ms) */
__u16 fade_level; /* Level at end of fade */
};
/* FF_CONSTANT */
struct ff_constant_effect {
__s16 level; /* Strength of effect. Negative values are OK */
struct ff_envelope envelope;
};
/* FF_RAMP */
struct ff_ramp_effect {
__s16 start_level;
__s16 end_level;
struct ff_envelope envelope;
};
/* FF_SPRING of FF_FRICTION */
struct ff_condition_effect {
__u16 right_saturation; /* Max level when joystick is on the right */
__u16 left_saturation; /* Max level when joystick in on the left */
__s16 right_coeff; /* Indicates how fast the force grows when the
joystick moves to the right */
__s16 left_coeff; /* Same for left side */
__u16 deadband; /* Size of area where no force is produced */
__s16 center; /* Position of dead zone */
};
/* FF_PERIODIC */
struct ff_periodic_effect {
__u16 waveform; /* Kind of wave (sine, square...) */
__u16 period; /* in ms */
__s16 magnitude; /* Peak value */
__s16 offset; /* Mean value of wave (roughly) */
__u16 phase; /* 'Horizontal' shift */
struct ff_envelope envelope;
/* Only used if waveform == FF_CUSTOM */
__u32 custom_len; /* Number of samples */
__s16 *custom_data; /* Buffer of samples */
/* Note: the data pointed by custom_data is copied by the driver. You can
* therefore dispose of the memory after the upload/update */
};
/* FF_RUMBLE */
/* Some rumble pads have two motors of different weight.
strong_magnitude represents the magnitude of the vibration generated
by the heavy motor.
*/
struct ff_rumble_effect {
__u16 strong_magnitude; /* Magnitude of the heavy motor */
__u16 weak_magnitude; /* Magnitude of the light one */
};
/*
* Structure sent through ioctl from the application to the driver
*/
struct ff_effect {
__u16 type;
/* Following field denotes the unique id assigned to an effect.
* If user sets if to -1, a new effect is created, and its id is returned in the same field
* Else, the user sets it to the effect id it wants to update.
*/
__s16 id;
__u16 direction; /* Direction. 0 deg -> 0x0000 (down)
90 deg -> 0x4000 (left)
180 deg -> 0x8000 (up)
270 deg -> 0xC000 (right)
*/
struct ff_trigger trigger;
struct ff_replay replay;
union {
struct ff_constant_effect constant;
struct ff_ramp_effect ramp;
struct ff_periodic_effect periodic;
struct ff_condition_effect condition[2]; /* One for each axis */
struct ff_rumble_effect rumble;
} u;
};
/*
* Force feedback effect types
*/
#define FF_RUMBLE 0x50
#define FF_PERIODIC 0x51
#define FF_CONSTANT 0x52
#define FF_SPRING 0x53
#define FF_FRICTION 0x54
#define FF_DAMPER 0x55
#define FF_INERTIA 0x56
#define FF_RAMP 0x57
/*
* Force feedback periodic effect types
*/
#define FF_SQUARE 0x58
#define FF_TRIANGLE 0x59
#define FF_SINE 0x5a
#define FF_SAW_UP 0x5b
#define FF_SAW_DOWN 0x5c
#define FF_CUSTOM 0x5d
/*
* Set ff device properties
*/
#define FF_GAIN 0x60
#define FF_AUTOCENTER 0x61
#define FF_MAX 0x7f
#ifdef __KERNEL__
/*
* In-kernel definitions.
*/
#include <linux/fs.h>
#include <linux/timer.h>
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define BIT(x) (1UL<<((x)%BITS_PER_LONG))
#define LONG(x) ((x)/BITS_PER_LONG)
#define INPUT_KEYCODE(dev, scancode) ((dev->keycodesize == 1) ? ((u8*)dev->keycode)[scancode] : \
((dev->keycodesize == 2) ? ((u16*)dev->keycode)[scancode] : (((u32*)dev->keycode)[scancode])))
#define init_input_dev(dev) do { INIT_LIST_HEAD(&((dev)->h_list)); INIT_LIST_HEAD(&((dev)->node)); } while (0)
#define SET_INPUT_KEYCODE(dev, scancode, val) \
({ unsigned __old; \
switch (dev->keycodesize) { \
case 1: { \
u8 *k = (u8 *)dev->keycode; \
__old = k[scancode]; \
k[scancode] = val; \
break; \
} \
case 2: { \
u16 *k = (u16 *)dev->keycode; \
__old = k[scancode]; \
k[scancode] = val; \
break; \
} \
default: { \
u32 *k = (u32 *)dev->keycode; \
__old = k[scancode]; \
k[scancode] = val; \
break; \
} \
} \
__old; })
struct input_dev {
void *private;
char *name;
char *phys;
char *uniq;
struct input_id id;
unsigned long evbit[NBITS(EV_MAX)];
unsigned long keybit[NBITS(KEY_MAX)];
unsigned long relbit[NBITS(REL_MAX)];
unsigned long absbit[NBITS(ABS_MAX)];
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
int ff_effects_max;
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
unsigned int repeat_key;
struct timer_list timer;
struct pm_dev *pm_dev;
struct pt_regs *regs;
int state;
int sync;
int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];
unsigned long key[NBITS(KEY_MAX)];
unsigned long led[NBITS(LED_MAX)];
unsigned long snd[NBITS(SND_MAX)];
int absmax[ABS_MAX + 1];
int absmin[ABS_MAX + 1];
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*accept)(struct input_dev *dev, struct file *file);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
int (*erase_effect)(struct input_dev *dev, int effect_id);
struct input_handle *grab;
struct device *dev;
struct list_head h_list;
struct list_head node;
};
/*
* Structure for hotplug & device<->driver matching.
*/
#define INPUT_DEVICE_ID_MATCH_BUS 1
#define INPUT_DEVICE_ID_MATCH_VENDOR 2
#define INPUT_DEVICE_ID_MATCH_PRODUCT 4
#define INPUT_DEVICE_ID_MATCH_VERSION 8
#define INPUT_DEVICE_ID_MATCH_EVBIT 0x010
#define INPUT_DEVICE_ID_MATCH_KEYBIT 0x020
#define INPUT_DEVICE_ID_MATCH_RELBIT 0x040
#define INPUT_DEVICE_ID_MATCH_ABSBIT 0x080
#define INPUT_DEVICE_ID_MATCH_MSCIT 0x100
#define INPUT_DEVICE_ID_MATCH_LEDBIT 0x200
#define INPUT_DEVICE_ID_MATCH_SNDBIT 0x400
#define INPUT_DEVICE_ID_MATCH_FFBIT 0x800
#define INPUT_DEVICE_ID_MATCH_DEVICE\
(INPUT_DEVICE_ID_MATCH_BUS | INPUT_DEVICE_ID_MATCH_VENDOR | INPUT_DEVICE_ID_MATCH_PRODUCT)
#define INPUT_DEVICE_ID_MATCH_DEVICE_AND_VERSION\
(INPUT_DEVICE_ID_MATCH_DEVICE | INPUT_DEVICE_ID_MATCH_VERSION)
struct input_device_id {
unsigned long flags;
struct input_id id;
unsigned long evbit[NBITS(EV_MAX)];
unsigned long keybit[NBITS(KEY_MAX)];
unsigned long relbit[NBITS(REL_MAX)];
unsigned long absbit[NBITS(ABS_MAX)];
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
unsigned long driver_info;
};
struct input_handle;
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
struct file_operations *fops;
int minor;
char *name;
struct input_device_id *id_table;
struct input_device_id *blacklist;
struct list_head h_list;
struct list_head node;
};
struct input_handle {
void *private;
int open;
char *name;
struct input_dev *dev;
struct input_handler *handler;
struct list_head d_node;
struct list_head h_node;
};
#define to_dev(n) container_of(n,struct input_dev,node)
#define to_handler(n) container_of(n,struct input_handler,node);
#define to_handle(n) container_of(n,struct input_handle,d_node)
#define to_handle_h(n) container_of(n,struct input_handle,h_node)
void input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *);
void input_register_handler(struct input_handler *);
void input_unregister_handler(struct input_handler *);
int input_grab_device(struct input_handle *);
void input_release_device(struct input_handle *);
int input_open_device(struct input_handle *);
void input_close_device(struct input_handle *);
int input_accept_process(struct input_handle *handle, struct file *file);
int input_flush_device(struct input_handle* handle, struct file* file);
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
#define input_report_key(a,b,c) input_event(a, EV_KEY, b, !!(c))
#define input_report_rel(a,b,c) input_event(a, EV_REL, b, c)
#define input_report_abs(a,b,c) input_event(a, EV_ABS, b, c)
#define input_report_ff(a,b,c) input_event(a, EV_FF, b, c)
#define input_report_ff_status(a,b,c) input_event(a, EV_FF_STATUS, b, c)
#define input_regs(a,b) do { (a)->regs = (b); } while (0)
#define input_sync(a) do { input_event(a, EV_SYN, SYN_REPORT, 0); (a)->regs = NULL; } while (0)
static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)
{
dev->absmin[axis] = min;
dev->absmax[axis] = max;
dev->absfuzz[axis] = fuzz;
dev->absflat[axis] = flat;
dev->absbit[LONG(axis)] |= BIT(axis);
}
extern struct class_simple *input_class;
#endif
#endif

View File

@@ -0,0 +1,38 @@
#ifndef _LINUX_INTERRUPT_H
#define _LINUX_INTERRUPT_H
#include <linux/bitops.h>
/* from asm-i386/ptrace.h */
struct pt_regs;
/* from asm-i386/mach-default/irq-vectors.h */
#ifdef ARCH_arm
#define NR_IRQS 64
#else
#define NR_IRQS 16
#endif
typedef int irqreturn_t;
#define IRQ_NONE (0)
#define IRQ_HANDLED (1)
#define IRQ_RETVAL(x) ((x) != 0)
struct irqaction {
irqreturn_t (*handler)(int, void *, struct pt_regs *);
unsigned long flags;
unsigned long mask;
const char *name;
void *dev_id;
struct irqaction *next;
};
//extern irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs);
extern int request_irq(unsigned int,
irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long, const char *, void *);
extern void free_irq(unsigned int, void *);
#endif

View File

@@ -0,0 +1,14 @@
/*
* Resources are tree-like, allowing
* nesting etc..
*/
struct resource {
const char *name;
unsigned long start, end;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
extern void * request_region(unsigned long start, unsigned long len, const char *name);
extern void release_region(unsigned long start, unsigned long len);

View File

@@ -0,0 +1,16 @@
#ifndef _LINUX_JIFFIES_H
#define _LINUX_JIFFIES_H
#include <asm/param.h>
extern unsigned long volatile jiffies;
#define time_after(a,b) ((long)(b) - (long)(a) < 0)
#define time_before(a,b) time_after(b,a)
static inline unsigned int msecs_to_jiffies(const unsigned int m)
{
return (m * HZ) / 1000;
}
#endif

View File

@@ -0,0 +1,46 @@
#ifndef _LINUX_KERNEL_H
#define _LINUX_KERNEL_H
#include <stddef.h>
#include <string.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define printk printf
#define KERN_EMERG "" /* system is unusable */
#define KERN_ALERT "" /* action must be taken immediately */
#define KERN_CRIT "" /* critical conditions */
#define KERN_ERR "" /* error conditions */
#define KERN_WARNING "" /* warning conditions */
#define KERN_NOTICE "" /* normal but significant condition */
#define KERN_INFO "" /* informational */
#define KERN_DEBUG "" /* debug-level messages */
extern int printf(const char *format, ...);
extern int sprintf(char * buf, const char * fmt, ...);
static inline int get_option(char **str, int *pint)
{
/* no int in sring */
return 0;
}
static inline char *get_options(const char *str, int nints, int *ints)
{
/* no ints found - start pointer breaks search */
*ints = 0;
return (char *)str;
}
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#endif

View File

@@ -0,0 +1 @@
#include <linux/errno.h>

View File

@@ -0,0 +1,49 @@
#ifndef _LIBPS2_H
#define _LIBPS2_H
/*
* Copyright (C) 1999-2002 Vojtech Pavlik
* Copyright (C) 2004 Dmitry Torokhov
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#define PS2_CMD_GETID 0x02f2
#define PS2_CMD_RESET_BAT 0x02ff
#define PS2_RET_BAT 0xaa
#define PS2_RET_ID 0x00
#define PS2_RET_ACK 0xfa
#define PS2_RET_NAK 0xfe
#define PS2_FLAG_ACK 1 /* Waiting for ACK/NAK */
#define PS2_FLAG_CMD 2 /* Waiting for command to finish */
#define PS2_FLAG_CMD1 4 /* Waiting for the first byte of command response */
#define PS2_FLAG_WAITID 8 /* Command execiting is GET ID */
struct ps2dev {
struct serio *serio;
/* Ensures that only one command is executing at a time */
struct semaphore cmd_sem;
/* Used to signal completion from interrupt handler */
wait_queue_head_t wait;
unsigned long flags;
unsigned char cmdbuf[6];
unsigned char cmdcnt;
unsigned char nak;
};
void ps2_init(struct ps2dev *ps2dev, struct serio *serio);
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout);
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command);
int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command);
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data);
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data);
void ps2_cmd_aborted(struct ps2dev *ps2dev);
#endif /* _LIBPS2_H */

View File

@@ -0,0 +1,587 @@
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
#ifdef __KERNEL__
#ifndef L4INPUT
#include <linux/stddef.h>
#include <linux/prefetch.h>
#include <asm/system.h>
#else
#include <linux/kernel.h>
#include <linux/prefetch.h>
#endif
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x00100100)
#define LIST_POISON2 ((void *) 0x00200200)
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_add_rcu(struct list_head * new,
struct list_head * prev,
struct list_head * next)
{
new->next = next;
new->prev = prev;
#ifndef L4INPUT
smp_wmb();
#endif
next->prev = new;
prev->next = new;
}
/**
* list_add_rcu - add a new entry to rcu-protected list
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static __inline__ void list_add_rcu(struct list_head *new, struct list_head *head)
{
__list_add_rcu(new, head, head->next);
}
/**
* list_add_tail_rcu - add a new entry to rcu-protected list
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static __inline__ void list_add_tail_rcu(struct list_head *new, struct list_head *head)
{
__list_add_rcu(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_del_rcu - deletes entry from list without re-initialization
* @entry: the element to delete from the list.
*
* Note: list_empty on entry does not return true after this,
* the entry is in an undefined state. It is useful for RCU based
* lockfree traversal.
*
* In particular, it means that we can not poison the forward
* pointers that may still be used for walking the list.
*/
static inline void list_del_rcu(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->prev = LIST_POISON2;
}
/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del(list->prev, list->next);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del(list->prev, list->next);
list_add_tail(list, head);
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
static inline void __list_splice(struct list_head *list,
struct list_head *head)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
/**
* list_splice - join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head);
}
/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head);
INIT_LIST_HEAD(list);
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
pos = pos->next, prefetch(pos->next))
/**
* __list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*
* This variant differs from list_for_each() in that it's the
* simplest possible list iteration code, no prefetching is done.
* Use this for code that knows the list to be very short (empty
* or 1 entry) most of the time.
*/
#define __list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
pos = pos->prev, prefetch(pos->prev))
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop counter.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_entry((head)->prev, typeof(*pos), member), \
prefetch(pos->member.prev); \
&pos->member != (head); \
pos = list_entry(pos->member.prev, typeof(*pos), member), \
prefetch(pos->member.prev))
/**
* list_for_each_entry_continue - iterate over list of given type
* continuing after existing point
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
/**
* list_for_each_rcu - iterate over an rcu-protected list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each_rcu(pos, head) \
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
#define __list_for_each_rcu(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
/**
* list_for_each_safe_rcu - iterate over an rcu-protected list safe
* against removal of list entry
* @pos: the &struct list_head to use as a loop counter.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe_rcu(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
/**
* list_for_each_entry_rcu - iterate over rcu list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_rcu(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member), \
({ smp_read_barrier_depends(); 0;}), \
prefetch(pos->member.next))
/**
* list_for_each_continue_rcu - iterate over an rcu-protected list
* continuing after existing point.
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each_continue_rcu(pos, head) \
for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
(pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*/
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
static __inline__ int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static __inline__ int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
static __inline__ void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}
static __inline__ void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
/**
* hlist_del_rcu - deletes entry from hash list without re-initialization
* @n: the element to delete from the hash list.
*
* Note: list_unhashed() on entry does not return true after this,
* the entry is in an undefined state. It is useful for RCU based
* lockfree traversal.
*
* In particular, it means that we can not poison the forward
* pointers that may still be used for walking the hash list.
*/
static inline void hlist_del_rcu(struct hlist_node *n)
{
__hlist_del(n);
n->pprev = LIST_POISON2;
}
static __inline__ void hlist_del_init(struct hlist_node *n)
{
if (n->pprev) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
#define hlist_del_rcu_init hlist_del_init
static __inline__ void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
static __inline__ void hlist_add_head_rcu(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
n->pprev = &h->first;
#ifndef L4INPUT
smp_wmb();
#endif
if (first)
first->pprev = &n->next;
h->first = n;
}
/* next must be != NULL */
static __inline__ void hlist_add_before(struct hlist_node *n, struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
*(n->pprev) = n;
}
static __inline__ void hlist_add_after(struct hlist_node *n,
struct hlist_node *next)
{
next->next = n->next;
*(next->pprev) = n;
n->next = next;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
/* Cannot easily do prefetch unfortunately */
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; n = pos ? pos->next : 0, pos; \
pos = n)
/**
* hlist_for_each_entry - iterate over list of given type
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(tpos, pos, head, member) \
for (pos = (head)->first; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_continue(tpos, pos, member) \
for (pos = (pos)->next; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_from - iterate over a hlist continuing from existing point
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_from(tpos, pos, member) \
for (; pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @n: another &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
for (pos = (head)->first; \
pos && ({ n = pos->next; 1; }) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = n)
#else
#warning "don't include kernel headers in userspace"
#endif /* __KERNEL__ */
#endif

View File

@@ -0,0 +1,7 @@
#ifndef _LINUX_MAJOR_H
#define _LINUX_MAJOR_H
#define INPUT_MAJOR 13
#endif

View File

@@ -0,0 +1,25 @@
#ifndef _LINUX_MODULE_H
#define _LINUX_MODULE_H
#include <linux/spinlock.h>
#include <linux/sched.h>
struct module;
#define MODULE_LICENSE(x)
#define MODULE_AUTHOR(x)
#define MODULE_DESCRIPTION(x)
/* XXX Consider to export this or provide setup funcs */
#define MODULE_PARM(x, y)
#define MODULE_PARM_DESC(x,y)
#define EXPORT_SYMBOL(sym)
#define THIS_MODULE ((struct module *)0)
#define try_module_get(x)
#define module_put(x)
#endif

View File

@@ -0,0 +1,11 @@
#ifndef _LINUX_MODULE_PARAMS_H
#define _LINUX_MODULE_PARAMS_H
#include <linux/init.h>
#include <linux/kernel.h>
#define module_param(name, type, perm)
#define module_param_named(name, value, type, perm)
#endif

View File

@@ -0,0 +1,24 @@
#ifndef _LINUX_NOTIFIER_H
#define _LINUX_NOTIFIER_H
#include <linux/errno.h>
struct notifier_block
{
int (*notifier_call)(struct notifier_block *self, unsigned long, void *);
struct notifier_block *next;
int priority;
};
#define NOTIFY_DONE 0x0000 /* Don't care */
#define NOTIFY_OK 0x0001 /* Suits me */
#define NOTIFY_STOP_MASK 0x8000 /* Don't call further */
#define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002) /* Bad/Veto action */
#define SYS_DOWN 0x0001 /* Notify of system down */
#define SYS_RESTART SYS_DOWN
#define SYS_HALT 0x0002 /* Notify of system halt */
#define SYS_POWER_OFF 0x0003 /* Notify of system power off */
#endif

View File

@@ -0,0 +1,75 @@
#ifndef _LINUX_PM_H
#define _LINUX_PM_H
/*
* Power management requests
*/
enum
{
PM_SUSPEND, /* enter D1-D3 */
PM_RESUME, /* enter D0 */
PM_SAVE_STATE, /* save device's state */
/* enable wake-on */
PM_SET_WAKEUP,
/* bus resource management */
PM_GET_RESOURCES,
PM_SET_RESOURCES,
/* base station management */
PM_EJECT,
PM_LOCK,
};
typedef int pm_request_t;
/*
* Device types
*/
enum
{
PM_UNKNOWN_DEV = 0, /* generic */
PM_SYS_DEV, /* system device (fan, KB controller, ...) */
PM_PCI_DEV, /* PCI device */
PM_USB_DEV, /* USB device */
PM_SCSI_DEV, /* SCSI device */
PM_ISA_DEV, /* ISA device */
PM_MTD_DEV, /* Memory Technology Device */
};
typedef int pm_dev_t;
/*
* System device hardware ID (PnP) values
*/
enum
{
PM_SYS_UNKNOWN = 0x00000000, /* generic */
PM_SYS_KBC = 0x41d00303, /* keyboard controller */
PM_SYS_COM = 0x41d00500, /* serial port */
PM_SYS_IRDA = 0x41d00510, /* IRDA controller */
PM_SYS_FDC = 0x41d00700, /* floppy controller */
PM_SYS_VGA = 0x41d00900, /* VGA controller */
PM_SYS_PCMCIA = 0x41d00e00, /* PCMCIA controller */
};
struct pm_dev {
void *data;
};
typedef int (*pm_callback)(struct pm_dev *dev, pm_request_t rqst, void *data);
static inline struct pm_dev *pm_register(pm_dev_t type, unsigned long id,
pm_callback callback)
{
return 0;
}
static inline void pm_unregister(struct pm_dev *dev) {}
static inline void pm_access(struct pm_dev *dev) {}
static inline void pm_dev_idle(struct pm_dev *dev) {}
#endif

View File

@@ -0,0 +1,7 @@
#ifndef _LINUX_PREFETCH_H
#define _LINUX_PREFETCH_H
static inline void prefetch(const void *x) {;}
#endif

View File

@@ -0,0 +1,7 @@
#ifndef _LINUX_PROC_FS_H
#define _LINUX_PROC_FS_H
#define remove_proc_entry(name, parent) do {} while (0)
#endif

View File

@@ -0,0 +1,8 @@
#ifndef _LINUX_RANDOM_H
#define _LINUX_RANDOM_H
#define add_mouse_randomness(x)
#define add_input_randomness(x,y,z)
#endif

View File

@@ -0,0 +1,16 @@
#ifndef _LINUX_REBOOT_H
#define _LINUX_REBOOT_H
#include <linux/notifier.h>
static inline int register_reboot_notifier(struct notifier_block *n)
{
return 0;
}
static inline int unregister_reboot_notifier(struct notifier_block *n)
{
return 0;
}
#endif

View File

@@ -0,0 +1,21 @@
#include <linux/jiffies.h>
#include <linux/spinlock.h>
#include <linux/signal.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <asm/semaphore.h>
#include <l4/sys/kdebug.h> /* for enter_kdebug() */
#ifndef PIT_TICK_RATE
# define PIT_TICK_RATE 1193182
#endif
#define WARN_ON(x) \
do { \
if (x) \
enter_kdebug("WARN_ON"); \
} while (0)

View File

@@ -0,0 +1,190 @@
#ifndef _SERIO_H
#define _SERIO_H
/*
* Copyright (C) 1999-2002 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <stdio.h>
#include <linux/ioctl.h>
#include <linux/interrupt.h>
#define SPIOCSTYPE _IOW('q', 0x01, unsigned long)
#ifdef __KERNEL__
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#define strlcpy(dst,src,size) snprintf(dst,size,"%s",src)
struct serio {
void *private;
void *port_data;
char name[32];
char phys[32];
unsigned int manual_bind;
unsigned short idbus;
unsigned short idvendor;
unsigned short idproduct;
unsigned short idversion;
unsigned long type;
unsigned long event;
spinlock_t lock;
int (*write)(struct serio *, unsigned char);
int (*open)(struct serio *);
void (*close)(struct serio *);
struct serio *parent, *child;
struct serio_driver *drv;
struct semaphore drv_sem;
struct device dev;
struct list_head node;
};
#define to_serio_port(d) container_of(d, struct serio, dev)
struct serio_driver {
void *private;
char *description;
unsigned int manual_bind;
void (*write_wakeup)(struct serio *);
irqreturn_t (*interrupt)(struct serio *, unsigned char,
unsigned int, struct pt_regs *);
void (*connect)(struct serio *, struct serio_driver *drv);
int (*reconnect)(struct serio *);
void (*disconnect)(struct serio *);
void (*cleanup)(struct serio *);
struct device_driver driver;
struct list_head node;
};
int serio_open(struct serio *serio, struct serio_driver *drv);
void serio_close(struct serio *serio);
void serio_rescan(struct serio *serio);
void serio_reconnect(struct serio *serio);
irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs);
void serio_register_port(struct serio *serio);
void serio_register_port_delayed(struct serio *serio);
void __serio_register_port(struct serio *serio);
void serio_unregister_port(struct serio *serio);
void serio_unregister_port_delayed(struct serio *serio);
void __serio_unregister_port(struct serio *serio);
void serio_register_driver(struct serio_driver *drv);
void serio_unregister_driver(struct serio_driver *drv);
static __inline__ int serio_write(struct serio *serio, unsigned char data)
{
if (serio->write)
return serio->write(serio, data);
else
return -1;
}
static __inline__ void serio_dev_write_wakeup(struct serio *serio)
{
if (serio->drv && serio->drv->write_wakeup)
serio->drv->write_wakeup(serio);
}
static __inline__ void serio_cleanup(struct serio *serio)
{
if (serio->drv && serio->drv->cleanup)
serio->drv->cleanup(serio);
}
/*
* Use the following fucntions to protect critical sections in
* driver code from port's interrupt handler
*/
static __inline__ void serio_pause_rx(struct serio *serio)
{
spin_lock_irq(&serio->lock);
}
static __inline__ void serio_continue_rx(struct serio *serio)
{
spin_unlock_irq(&serio->lock);
}
/*
* Use the following fucntions to pin serio's driver in process context
*/
static __inline__ int serio_pin_driver(struct serio *serio)
{
// return down_interruptible(&serio->drv_sem);
down(&serio->drv_sem);
return 0;
}
static __inline__ void serio_unpin_driver(struct serio *serio)
{
up(&serio->drv_sem);
}
#endif
/*
* bit masks for use in "interrupt" flags (3rd argument)
*/
#define SERIO_TIMEOUT 1
#define SERIO_PARITY 2
#define SERIO_FRAME 4
#define SERIO_TYPE 0xff000000UL
#define SERIO_XT 0x00000000UL
#define SERIO_8042 0x01000000UL
#define SERIO_RS232 0x02000000UL
#define SERIO_HIL_MLC 0x03000000UL
#define SERIO_PC9800 0x04000000UL
#define SERIO_PS_PSTHRU 0x05000000UL
#define SERIO_8042_XL 0x06000000UL
#define SERIO_L4 0xff000000UL
#define SERIO_PROTO 0xFFUL
#define SERIO_MSC 0x01
#define SERIO_SUN 0x02
#define SERIO_MS 0x03
#define SERIO_MP 0x04
#define SERIO_MZ 0x05
#define SERIO_MZP 0x06
#define SERIO_MZPP 0x07
#define SERIO_SUNKBD 0x10
#define SERIO_WARRIOR 0x18
#define SERIO_SPACEORB 0x19
#define SERIO_MAGELLAN 0x1a
#define SERIO_SPACEBALL 0x1b
#define SERIO_GUNZE 0x1c
#define SERIO_IFORCE 0x1d
#define SERIO_STINGER 0x1e
#define SERIO_NEWTON 0x1f
#define SERIO_STOWAWAY 0x20
#define SERIO_H3600 0x21
#define SERIO_PS2SER 0x22
#define SERIO_TWIDKBD 0x23
#define SERIO_TWIDJOY 0x24
#define SERIO_HIL 0x25
#define SERIO_SNES232 0x26
#define SERIO_SEMTECH 0x27
#define SERIO_ID 0xff00UL
#define SERIO_EXTRA 0xff0000UL
#endif

View File

@@ -0,0 +1 @@
#include <asm/signal.h>

View File

@@ -0,0 +1,26 @@
#ifndef _LINUX_SLAB_H
#define _LINUX_SLAB_H
/* Hmm: stdlib.h defines a structure with __init as member... */
#undef __init
#include <stdlib.h>
#include <stddef.h>
#include <linux/init.h>
#define __init
#define GFP_KERNEL 0xabcde
static inline void *kmalloc(size_t size, int flags)
{
return malloc(size);
}
static inline void kfree(void *p)
{
free(p);
}
#endif

View File

@@ -0,0 +1,53 @@
#ifndef __LINUX_SPINLOCK_H
#define __LINUX_SPINLOCK_H
#include <pthread.h>
typedef pthread_mutex_t spinlock_t;
#define DEFINE_SPINLOCK(x) spinlock_t x
static inline void spin_unlock(spinlock_t *lock)
{
pthread_mutex_unlock(lock);
}
static inline void spin_lock(spinlock_t *lock)
{
pthread_mutex_lock(lock);
}
#define spin_lock_init(x) \
do { \
pthread_mutexattr_t a; \
pthread_mutexattr_init(&a); \
pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE); \
pthread_mutex_init(x, &a); \
pthread_mutexattr_destroy(&a); \
} while (0)
/* XXX lock irq threads here??? */
#define spin_lock_irqsave(lock, flags) \
do { \
(void)flags; \
spin_lock(lock); \
} while (0)
#define spin_unlock_irqrestore(lock, flags) \
do { \
(void)flags; \
spin_unlock(lock); \
} while (0)
#define spin_lock_irq(lock) \
do { \
spin_lock(lock); \
} while (0)
#define spin_unlock_irq(lock) \
do { \
spin_unlock(lock); \
} while (0)
#endif

View File

@@ -0,0 +1 @@
#include <stddef.h>

View File

@@ -0,0 +1 @@
#include <linux/init.h>

View File

@@ -0,0 +1,15 @@
#ifndef _LINUX_TIME_H
#define _LINUX_TIME_H
#include <asm/param.h>
#include <linux/types.h>
#include <sys/time.h> /* for struct timeval */
#if 0
struct timeval {
long tv_sec;
long tv_usec;
};
#endif
#endif

View File

@@ -0,0 +1,50 @@
#ifndef _LINUX_TIMER_H
#define _LINUX_TIMER_H
#include <linux/config.h>
#include <linux/list.h>
//#include <linux/spinlock.h>
struct timer_list {
// struct list_head entry;
// unsigned long expires;
// spinlock_t lock;
// unsigned long magic;
void (*function)(unsigned long);
unsigned long data;
// struct tvec_t_base_s *base;
};
static inline void init_timer(struct timer_list * timer)
{
#if 0
timer->base = NULL;
timer->magic = TIMER_MAGIC;
spin_lock_init(&timer->lock);
#endif
}
#if 0
static inline int timer_pending(const struct timer_list * timer)
{
return timer->base != NULL;
}
#endif
//#define add_timer_on(x, y)
static inline int del_timer(struct timer_list *timer)
{
return 0;
}
static inline int mod_timer(struct timer_list *timer, unsigned long expires)
{
return 0;
}
#define add_timer(x)
#define del_timer_sync(x) del_timer(x)
#endif

View File

@@ -0,0 +1,10 @@
#ifndef _LINUX_TYPES
#define _LINUX_TYPES
#include <asm/types.h>
#ifdef USE_OSKIT
typedef signed long ssize_t;
#endif
#endif

View File

@@ -0,0 +1,56 @@
#ifndef _LINUX_WAIT_H
#define _LINUX_WAIT_H
struct __wait_queue_entry_t {
l4_cap_idx_t tid;
struct __wait_queue_entry_t *next;
};
struct __wait_queue_head_t {
struct __wait_queue_entry_t *first;
struct __wait_queue_head_t *next;
};
typedef struct __wait_queue_entry_t wait_queue_entry_t;
typedef struct __wait_queue_head_t wait_queue_head_t;
extern l4_cap_idx_t wait_thread;
void wake_up(wait_queue_head_t *wq);
#define init_waitqueue_head(wq) \
do { \
(wq)->first = 0; \
(wq)->next = 0; \
} while (0)
#include <l4/sys/ipc.h>
#include <pthread.h>
#include <pthread-l4.h>
#define wait_event_timeout(wq, condition, timeout) \
({ \
unsigned stop, end = jiffies + timeout; \
l4_umword_t dummy; \
l4_msgtag_t r; \
wait_queue_entry_t wqe; \
while (!(condition) && (end - jiffies < 1000000000)) \
{ \
l4_utcb_mr()->mr[0] = (l4_umword_t)&(wq); \
l4_utcb_mr()->mr[1] = (l4_umword_t)&(wqe); \
r = l4_ipc(wait_thread, l4_utcb(), L4_SYSF_CALL, \
pthread_l4_cap(pthread_self()), \
l4_msgtag(0, 2, 0, 0), \
&dummy, L4_IPC_NEVER); \
if (l4_ipc_error(r, l4_utcb())) \
enter_kdebug("wait_timeout"); \
} \
stop = jiffies; \
if (stop < end) \
end -= stop; \
else \
end = 0; \
end; \
})
#endif

View File

@@ -0,0 +1,7 @@
#ifndef INPUT_WORKQUEUE_H__
#define INPUT_WORKQUEUE_H__
struct work_struct {
};
#endif

View File

@@ -0,0 +1,8 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
TARGET = libinput-dummy.a
SRC_C = main.c
PC_FILENAME = input-dummy
include $(L4DIR)/mk/lib.mk

View File

@@ -0,0 +1,41 @@
/*
* Dummy inputlib with no functionality.
*
* by Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*/
/*
* (c) 2004-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Christian Helmuth <ch12@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include <l4/input/libinput.h>
#include <l4/sys/err.h>
L4_CV int l4input_ispending(void)
{
return 0;
}
L4_CV int l4input_flush(void *buffer, int count)
{
(void)buffer; (void)count;
return 0;
}
L4_CV int l4input_pcspkr(int tone)
{
(void)tone;
return -L4_ENODEV;
}
L4_CV int l4input_init(int prio, L4_CV void (*handler)(struct l4input *))
{
(void)prio; (void)handler;
return 0;
}

View File

@@ -0,0 +1,26 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
TARGET = libinput.a
SRC_S = jiffie_hack.S
SRC_C = emul_time.c emul_irq.c emul_wait.c emul_res.c \
l4evdev.c init.c l4drv.c l4drv_reg.c l4bus.c
REQUIRES_LIBS = libio libirq l4util libc libpthread
PRIVATE_INCDIR = $(PKGDIR)/lib/include
DEFINES = -D__KERNEL__ -DL4INPUT
WARNINGS = -Wall -Wstrict-prototypes $(call checkcc,-Wno-pointer-sign)
# gather Linux sources
VPATH += $(PKGDIR)/lib/contrib
SRC_C += input/input.c input/keyboard/atkbd.c input/mouse/psmouse-base.c \
input/mouse/logips2pp.c input/mouse/synaptics.c input/mouse/alps.c \
input/serio/serio.c input/serio/libps2.c
SRC_C_x86-l4f += input/serio/i8042.c input/misc/pcspkr.c
SRC_C_amd64-l4f += input/serio/i8042.c input/misc/pcspkr.c
SRC_C_arm-l4f += input/serio/ambakmi.c
include $(L4DIR)/mk/lib.mk

View File

@@ -0,0 +1,4 @@
L4 input library implementation
1. Emulation of some essential Linux functions.
2. Libinput API

View File

@@ -0,0 +1,247 @@
/*****************************************************************************/
/**
* \file input/lib/src/emul_irq.c
* \brief L4INPUT: Linux IRQ handling emulation
*
* \date 11/20/2003
* \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de>
*
*/
/*
* (c) 2003-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include <stdio.h>
#include <string.h>
#include <l4/sys/ipc.h>
#include <l4/sys/types.h>
//#include <l4/util/irq.h>
#include <l4/irq/irq.h>
#if defined(ARCH_x86) || defined(ARCH_amd64)
#include <l4/util/port_io.h>
#endif
#include <pthread.h>
/* Linux */
#include <linux/interrupt.h>
#include "internal.h"
#define DEBUG_IRQ 0
#define DEBUG_IRQ_VERBOSE 0
/* return within 50ms even if there was no interrupt */
#define IRQ_TIMEOUT l4_ipc_timeout(0,0,781,6)
/* INTERRUPT HANDLING EMULATION */
static struct irq_desc {
int active;
int num;
void *cookie;
pthread_t t;
pthread_mutex_t startup;
irqreturn_t (*handler)(int, void *, struct pt_regs *);
} handlers[NR_IRQS];
static int irq_prio = -1;
/* OMEGA0 STUFF */
#define OM_MASK 0x00000001
#define OM_UNMASK 0x00000002
#define OM_CONSUME 0x00000004
#define OM_AGAIN 0x00000008
/** Attach IRQ line.
*
* \param irq IRQ number
* \retval handle IRQ handle
*
* \return 0 on success (attached interrupt), -1 if attach failed.
*/
static inline int __omega0_attach(unsigned int irq, l4irq_t **handle)
{
return (*handle = l4irq_attach(irq)) ? 0 : -1;
}
/** Wait for IRQ notification.
*
* \param irq IRQ number
* \param handle IRQ line handle
* \param flags Flags:
* - \c OM_MASK request IRQ mask
* - \c OM_UNMASK request IRQ unmask
* - \c OM_CONSUME IRQ consumed
* \return 0 for success
* L4_IPC_RETIMEOUT if no IRQ was received (timeout)
*/
static inline int __omega0_wait(unsigned int irq, l4irq_t *handle,
unsigned int flags)
{
return l4irq_wait(handle);
}
/** IRQ HANDLER THREAD.
*
* \param irq_desc IRQ handling descriptor (IRQ number and handler routine)
*/
static void *__irq_handler(void *_data)
{
struct irq_desc *irq_desc = (struct irq_desc *)_data;
unsigned int irq = irq_desc->num; /* save irq number */
void *cookie = irq_desc->cookie; /* save cookie/dev_id pointer */
long ret;
l4irq_t *irq_handle;
unsigned int om_flags;
/* get permission for attaching to IRQ */
ret = __omega0_attach(irq, &irq_handle);
if (ret < 0)
{
fprintf(stderr, "failed to attach IRQ %d!\n", irq);
return NULL;
}
/* we are up */
ret = pthread_mutex_unlock(&handlers[irq].startup);
#if DEBUG_IRQ
printf("emul_irq.c: __irq_handler %p for IRQ %d running\n",
irq_desc->handler, irq);
#endif
if (ret)
{
fprintf(stderr, "IRQ thread startup failed!\n");
return NULL;
}
om_flags = OM_UNMASK;
for (;;) {
ret = __omega0_wait(irq, irq_handle, om_flags);
switch (ret) {
case 0:
#if DEBUG_IRQ_VERBOSE
printf("emul_irq.c: got IRQ %d\n", irq);
#endif
if (irq_desc->active)
irq_desc->handler(irq, cookie, NULL);
om_flags = 0;
break;
#if 0
case L4_IPC_RETIMEOUT:
if (irq_desc->active)
irq_desc->handler(irq, cookie, NULL);
om_flags = OM_AGAIN;
break;
#endif
default:
fprintf(stderr, "error receiving irq\n");
return NULL;
}
}
}
/** Request Interrupt.
* \ingroup grp_irq
*
* \param irq interrupt number
* \param handler interrupt handler -> top half
* \param ...
* \param cookie cookie pointer passed back
*
* \return 0 on success; error code otherwise
*/
int request_irq(unsigned int irq,
irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long flags, const char *devname, void *cookie)
{
pthread_t irq_tid;
char buf[7];
if (irq >= NR_IRQS)
return -L4_EINVAL;
if (!handler)
return -L4_EINVAL;
handlers[irq].num = irq;
handlers[irq].cookie = cookie;
handlers[irq].handler = handler;
handlers[irq].active = 1;
if(handlers[irq].t) {
#if DEBUG_IRQ
printf("emul_irq.c: reattached to irq %d\n", irq);
#endif
return 0;
}
sprintf(buf, ".irq%.2X", irq);
/* create IRQ handler thread */
pthread_mutex_init(&handlers[irq].startup, NULL);
pthread_mutex_lock(&handlers[irq].startup);
pthread_attr_t a;
pthread_attr_init(&a);
if (irq_prio != -1) {
struct sched_param sp;
sp.sched_priority = irq_prio;
pthread_attr_setschedpolicy(&a, SCHED_L4);
pthread_attr_setschedparam(&a, &sp);
pthread_attr_setinheritsched(&a, PTHREAD_EXPLICIT_SCHED);
}
if (pthread_create(&irq_tid, &a, __irq_handler, (void *)
&handlers[irq]))
{
printf("emul_irq.c: Creating irq-thread failed\n");
pthread_mutex_unlock(&handlers[irq].startup);
return 0;
}
pthread_mutex_lock(&handlers[irq].startup);
pthread_mutex_unlock(&handlers[irq].startup);
#if DEBUG_IRQ
printf("emul_irq.c: attached to irq %d\n", irq);
#endif
handlers[irq].t = irq_tid;
return 0;
}
/** Release Interrupt.
*
* \param irq interrupt number
* \param cookie cookie pointer passed back
*
* \todo Implementation.
*/
void free_irq(unsigned int irq, void *cookie)
{
handlers[irq].active = 0;
#if DEBUG_IRQ
printf("emul_irq.c: attempt to free irq %d\n", irq);
#endif
}
/* INTERRUPT EMULATION INITIALIZATION */
void l4input_internal_irq_init(int prio)
{
irq_prio = prio;
memset(&handlers, 0, sizeof(handlers));
}

View File

@@ -0,0 +1,30 @@
/**
* \file input/lib/src/emul_res.c
* \brief L4INPUT: Linux I/O resource management emulation
*
* \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
*
* Currently we only support I/O port requests.
*/
/*
* (c) 2007-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include <l4/io/io.h>
void * request_region(unsigned long start, unsigned long len, const char *name)
{
int err = l4io_request_ioport(start, len);
return err ? 0 : (void *)1;
}
void release_region(unsigned long start, unsigned long len)
{
l4io_release_ioport(start, len);
}

View File

@@ -0,0 +1,34 @@
/*****************************************************************************/
/**
* \file input/lib/src/emul_time.c
* \brief L4INPUT: Linux time emulation
*
* \date 2007-05-29
* \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de>
*
*/
/*
* (c) 2007-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
/* L4 */
#if defined(ARCH_x86) || defined(ARCH_amd64)
#include <l4/util/rdtsc.h> /* XXX x86 specific */
#endif
#include <l4/util/util.h>
/* UDELAY */
void udelay(unsigned long usecs)
{
#if defined(ARCH_amd64) || defined(ARCH_x86)
l4_busy_wait_us(usecs);
#else
l4_sleep(usecs/1000); // XXX
#endif
}

View File

@@ -0,0 +1,224 @@
/*****************************************************************************/
/**
* \file input/lib/src/emul_wait
* \brief L4INPUT: Linux Wait queue emulation
*
* \date 2005/05/24
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de>
*
* I've no idea if this is really needed.
*/
/*
* (c) 2005-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include <stdio.h>
#include <l4/sys/ipc.h>
#include <l4/util/l4_macros.h>
#include <linux/wait.h>
#include <linux/jiffies.h>
#include <pthread.h>
#include <pthread-l4.h>
l4_cap_idx_t wait_thread;
//#define dbg(format...) printf(format)
#define dbg(format...)
/** Enqueue entry into list. */
static void
enqueue_entry(wait_queue_head_t *wqh, wait_queue_entry_t *e)
{
if (!wqh->first)
wqh->first = e;
else
{
wait_queue_entry_t *w;
for (w=wqh->first; w->next; w=w->next)
;
w->next = e;
}
}
/** Enqueue list into main list. */
static void
enqueue_head(wait_queue_head_t **wqm, wait_queue_head_t *h)
{
if (!*wqm)
*wqm = h;
else
{
wait_queue_head_t *w = *wqm;
for (;;)
{
if (w == h)
return;
if (!w->next)
{
w->next = h;
return;
}
w = w->next;
}
}
}
/** Dequeue list from main list. */
static void
dequeue_head(wait_queue_head_t **wqm, wait_queue_head_t *h)
{
for (; *wqm; *wqm=(*wqm)->next)
{
if (*wqm == h)
{
*wqm = h->next;
return;
}
}
}
/** Wakeup all threads enqueued in list. */
static void
wakeup(wait_queue_head_t *h)
{
wait_queue_entry_t *e;
l4_cap_idx_t tid;
l4_umword_t dummy;
for (e=h->first; e;)
{
dbg(" wakeup entry %p\n", e);
dbg(" ("l4util_idfmt", next=%p)\n",
l4util_idstr(e->tid), e->next);
tid = e->tid;
e = e->next;
l4_ipc(tid, l4_utcb(), L4_SYSF_SEND, pthread_l4_cap(pthread_self()),
l4_msgtag(0, 0, 0, 0), &dummy, L4_IPC_NEVER);
}
h->first = 0;
}
/** The waiter thread. */
static void *
__wait_thread(void *ignore)
{
l4_cap_idx_t src;
l4_umword_t dw1, dw2;
l4_umword_t dummy;
l4_msgtag_t r;
l4_timeout_t to = l4_timeout(L4_IPC_TIMEOUT_NEVER,
l4_timeout_from_us(10000));
int error;
wait_queue_head_t *main_queue = 0;
//l4thread_started(NULL);
for (;;)
{
r = l4_ipc_wait(l4_utcb(), &src, main_queue ? to : L4_IPC_NEVER);
dw1 = l4_utcb_mr()->mr[0];
dw2 = l4_utcb_mr()->mr[1];
error = l4_ipc_error(r, l4_utcb());
if (error == 0)
{
/* got request */
if (dw1 != 0)
{
/* go sleep, append at wait queue */
wait_queue_head_t *h = (wait_queue_head_t*)dw1;
wait_queue_entry_t *e = (wait_queue_entry_t*)dw2;
dbg("enqueue "l4util_idfmt" into queue %p entry %p\n", l4util_idstr(src), (void*)dw1, (void*)dw2);
e->tid = src;
e->next = 0;
/* append entry to wait queue */
enqueue_entry(h, e);
/* append wait queue to main queue if necessary */
enqueue_head(&main_queue, h);
dbg(" queue now ");
for (e=h->first; e; e=e->next)
dbg("%p ", e);
dbg("\n");
}
else
{
/* wakeup */
wait_queue_head_t *h = ((wait_queue_head_t*)dw2);
dbg("wakeup queue %p\n", (void*)dw2);
/* wakeup waiting threads of wait queue */
wakeup(h);
/* dequeue wait queue from main queue */
dequeue_head(&main_queue, h);
dbg(" main=%p\n", (void*)main_queue);
l4_ipc(src, l4_utcb(), L4_SYSF_SEND,
pthread_l4_cap(pthread_self()),
l4_msgtag(0, 0, 0, 0), &dummy, L4_IPC_NEVER);
}
}
else if (error == L4_IPC_RETIMEOUT)
{
/* timeout, wakeup all queues */
wait_queue_head_t *h;
dbg("wakup all queues\n");
for (h=main_queue; h; h=h->next)
{
dbg(" wakeup queue %p\n", h);
wakeup(h);
}
main_queue = 0;
}
else
{
/* ??? */
}
}
return NULL;
}
/** Called if a specific wait queue should be woken up. */
void
wake_up(wait_queue_head_t *wq)
{
l4_umword_t dummy;
l4_utcb_mr()->mr[0] = 0;
l4_utcb_mr()->mr[1] = (l4_umword_t)(wq);
l4_ipc(wait_thread, l4_utcb(), L4_SYSF_CALL,
pthread_l4_cap(pthread_self()),
l4_msgtag(0, 2, 0, 0),
&dummy, L4_IPC_NEVER);
}
/** Initialization. */
void
l4input_internal_wait_init(void)
{
pthread_t t;
pthread_attr_t a;
struct sched_param sp;
pthread_attr_init(&a);
sp.sched_priority = 255;
pthread_attr_setschedpolicy(&a, SCHED_L4);
pthread_attr_setschedparam(&a, &sp);
pthread_attr_setinheritsched(&a, PTHREAD_EXPLICIT_SCHED);
if (pthread_create(&t, &a, __wait_thread, NULL))
return;
wait_thread = pthread_l4_cap(t);
pthread_attr_destroy(&a);
}

View File

@@ -0,0 +1,132 @@
/*****************************************************************************/
/**
* \file input/lib/src/init.c
* \brief L4INPUT: Initialization
*
* \date 11/20/2003
* \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de>
*
*/
/*
* (c) 2003-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
/* L4 */
#include <l4/sys/err.h>
#include <l4/re/env.h>
#include <l4/util/kip.h>
#include <l4/io/io.h>
#if defined(ARCH_x86) || defined(ARCH_amd64)
#include <l4/util/rdtsc.h> /* XXX x86 specific */
#endif
/* C */
#include <stdio.h>
/* local */
#include <l4/input/libinput.h>
#include "internal.h"
/** Backend operations */
static struct l4input_ops *ops;
int l4input_ispending()
{
if (ops->ispending)
return ops->ispending();
else
return 0;
}
L4_CV int l4input_flush(void *buf, int count)
{
if (ops->flush)
return ops->flush(buf, count);
else
return 0;
}
L4_CV int l4input_pcspkr(int tone)
{
if (ops->pcspkr)
return ops->pcspkr(tone);
else
return -L4_EINVAL;
}
/* Okay ...
We have to initialize requested devices only here because there is only one
general event device.
After discussions with Norman I also think one event device is fine. Maybe
we could add device ids to the event struct later and inject UPDATE events
in the case of hotplugging.
*/
#define CHECK(x) if ((error=x)) { \
printf("%s:%d: failed " #x ": %d\n", __FILE__, __LINE__, error); \
return error; }
/** L4INPUT LIBRARY INITIALIZATION **/
L4_CV int l4input_init(int prio, L4_CV void (*handler)(struct l4input *))
{
printf("L4INPUT native mode activated\n");
/* for usleep */
#if defined(ARCH_x86) || defined(ARCH_amd64)
l4_calibrate_tsc(l4re_kip());
#endif
/* lib state */
l4input_internal_irq_init(prio);
l4input_internal_wait_init();
printf("L4INPUT: !!! W A R N I N G !!!\n"
"L4INPUT: Please, do not use Fiasco's \"-esc\" with L4INPUT.\n"
"L4INPUT: !!! W A R N I N G !!!\n");
if (handler)
printf("L4INPUT: Registered %p for callbacks.\n", handler);
int error;
#if 0
#if defined(ARCH_x86) || defined(ARCH_amd64)
CHECK(l4io_request_ioport(0x80, 1));
#endif
#endif
CHECK(l4input_internal_input_init());
#if defined(ARCH_x86) || defined(ARCH_amd64)
CHECK(l4input_internal_i8042_init());
#endif
CHECK(l4input_internal_psmouse_init());
CHECK(l4input_internal_atkbd_init());
#ifdef ARCH_arm
l4input_internal_amba_kmi_init_k();
l4input_internal_amba_kmi_init_m();
CHECK(l4input_internal_serio_init());
l4input_internal_l4drv_init();
l4input_internal_l4bus_init();
#endif
#if defined(ARCH_x86) || defined(ARCH_amd64)
// reprogrammes PIT, only enable when you think you
// know what you're doing
//CHECK(l4input_internal_pcspkr_init());
#endif
if (!(ops = l4input_internal_l4evdev_init(handler))) {
printf("L4INPUT: evdev initialization failed\n");
return -L4_ENODEV;
}
return 0;
}

View File

@@ -0,0 +1,9 @@
.global jiffies
#ifdef ARCH_amd64
jiffies = 0x6ffff000 + 0xa0
#elif ARCH_riscv
jiffies = 0x7fff0000 + 0xa0
#else
jiffies = 0xaffff000 + 0xa0
#endif

View File

@@ -0,0 +1,115 @@
/*
* (c) 2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <l4/io/io.h>
struct l4bus_port {
struct serio *io;
unsigned int irq;
};
#if 0
static irqreturn_t l4bus_int(int irq, void *dev_id, struct pt_regs *regs)
{
struct l4bus_port *port = dev_id;
int handled = IRQ_NONE;
(void)port;
return handled;
}
#endif
static int l4bus_write(struct serio *io, unsigned char val)
{
struct l4bus_port *port = io->port_data;
(void)port;
return 0;
}
static int l4bus_open(struct serio *io)
{
struct l4bus_port *port = io->port_data;
(void)port;
return 0;
}
static void l4bus_close(struct serio *io)
{
//struct l4bus_port *port = io->port_data;
}
static int l4bus_probe(const char *name)
{
struct l4bus_port *port;
struct serio *io;
int ret;
port = kmalloc(sizeof(struct l4bus_port), GFP_KERNEL);
io = kmalloc(sizeof(struct serio), GFP_KERNEL);
if (!port || !io) {
ret = -ENOMEM;
goto out;
}
memset(port, 0, sizeof(struct l4bus_port));
memset(io, 0, sizeof(struct serio));
io->type = SERIO_L4;
io->write = l4bus_write;
//io->read = 0;
io->open = l4bus_open;
io->close = l4bus_close;
strlcpy(io->name, name, sizeof(io->name));
strlcpy(io->phys, name, sizeof(io->phys));
io->port_data = port;
//io->dev.parent = &dev->dev;
port->io = io;
port->irq = -1;
serio_register_port(io);
return 0;
out:
kfree(io);
kfree(port);
return ret;
}
static int __init l4bus_init(void)
{
// FIXME: iterate over vbus to find devices
// ... indeed: disabled those two probe functions because they emit a
// warning and someone might think there could be working omap input stuff
if (0)
{
l4bus_probe("OMAP_TSC");
l4bus_probe("OMAP_KP");
}
return 0;
}
module_init(l4bus_init);
MODULE_AUTHOR("Torsten Frenzel");
MODULE_DESCRIPTION("L4 bus driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,217 @@
/*
* (c) 2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* This file is part of TUD:OS and distributed under the terms of the
* GNU General Public License 2.
* Please see the COPYING-GPL-2 file for details.
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/workqueue.h>
#include <l4/input/drv_reg.h>
#include <l4/sys/err.h>
#include <l4/re/env.h>
#include <stdio.h>
#define DRIVER_DESC "L4 driver"
MODULE_AUTHOR("Torsten Frenzel");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
struct l4drv_data {
char name[64];
char phys[40];
struct input_dev dev;
unsigned char enabled;
void *private;
};
static void input_handler(Input_event event, void *private)
{
struct input_dev *dev = &((struct l4drv_data *)private)->dev;
input_event(dev, event.type, event.code, event.value);
input_sync(dev);
}
/* XXX this is just a dummy */
static irqreturn_t l4drv_interrupt(struct serio *serio, unsigned char data,
unsigned int flags, struct pt_regs *regs)
{
return IRQ_HANDLED;
}
static int l4drv_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
return -1;
}
static void l4drv_set_device_attrs(struct l4drv_data *data)
{
memset(&data->dev, 0, sizeof(struct input_dev));
init_input_dev(&data->dev);
data->dev.name = data->name;
data->dev.phys = data->phys;
data->dev.id.bustype = 0;
data->dev.id.vendor = 0x1;
data->dev.id.product = 0x1;
data->dev.id.version = 0x1;
data->dev.event = l4drv_event;
data->dev.private = data;
data->dev.dev = 0;
// FIXME: this information should come from the driver!
data->dev.evbit[0] = BIT(EV_KEY)|BIT(EV_ABS);
set_bit(BTN_LEFT, data->dev.keybit);
set_bit(KEY_1, data->dev.keybit);
set_bit(KEY_2, data->dev.keybit);
set_bit(KEY_3, data->dev.keybit);
set_bit(KEY_4, data->dev.keybit);
set_bit(KEY_5, data->dev.keybit);
set_bit(KEY_6, data->dev.keybit);
set_bit(KEY_7, data->dev.keybit);
set_bit(KEY_8, data->dev.keybit);
set_bit(KEY_9, data->dev.keybit);
set_bit(KEY_0, data->dev.keybit);
set_bit(KEY_A, data->dev.keybit);
set_bit(KEY_B, data->dev.keybit);
set_bit(KEY_C, data->dev.keybit);
set_bit(KEY_D, data->dev.keybit);
set_bit(KEY_E, data->dev.keybit);
set_bit(KEY_F, data->dev.keybit);
set_bit(KEY_ENTER, data->dev.keybit);
}
static inline void l4drv_enable(struct l4drv_data *data)
{
}
static inline void l4drv_disable(struct l4drv_data *data)
{
}
static int l4drv_probe(struct l4drv_data *data)
{
struct arm_input_ops *input_ops;
if (!(input_ops = arm_input_probe(data->name)))
{
printk(KERN_INFO "l4drv: Could not find driver for %s.\n", data->name);
return 1;
}
input_ops->enable();
input_ops->attach(input_handler, data);
data->private = input_ops;
return 0;
}
static void l4drv_connect(struct serio *serio, struct serio_driver *drv)
{
struct l4drv_data *data;
if (!(data = kmalloc(sizeof(struct l4drv_data), GFP_KERNEL)))
return;
memset(data, 0, sizeof(struct l4drv_data));
serio->private = data;
if (serio_open(serio, drv)) {
kfree(data);
return;
}
strcpy(data->name, serio->name);
snprintf(data->phys, sizeof(data->phys), "%s/input0", serio->phys);
l4drv_set_device_attrs(data);
data->enabled = 1;
if (l4drv_probe(data)) {
serio_close(serio);
serio->private = NULL;
kfree(data);
return;
}
input_register_device(&data->dev);
printk(KERN_INFO "l4drv: %s on %s\n", data->name, serio->phys);
}
static int l4drv_reconnect(struct serio *serio)
{
struct l4drv_data *data = serio->private;
struct serio_driver *drv = serio->drv;
if (!data || !drv) {
printk(KERN_DEBUG "l4drv: reconnect request, but serio is disconnected, ignoring...\n");
return -1;
}
l4drv_disable(data);
l4drv_enable(data);
return 0;
}
static void l4drv_disconnect(struct serio *serio)
{
struct l4drv_data *data = serio->private;
l4drv_disable(data);
input_unregister_device(&data->dev);
serio_close(serio);
kfree(data);
}
static void l4drv_cleanup(struct serio *serio)
{}
static struct serio_driver l4drv = {
.driver = {
.name = "l4drv",
},
.description = DRIVER_DESC,
.interrupt = l4drv_interrupt,
.connect = l4drv_connect,
.reconnect = l4drv_reconnect,
.disconnect = l4drv_disconnect,
.cleanup = l4drv_cleanup,
};
int __init l4drv_init(void)
{
serio_register_driver(&l4drv);
return 0;
}
void __exit l4drv_exit(void)
{
serio_unregister_driver(&l4drv);
}
module_init(l4drv_init);
module_exit(l4drv_exit);

View File

@@ -0,0 +1,26 @@
/*
* Some generic functions for input drivers.
*/
#include <l4/input/drv_reg.h>
enum { nr_drivers = 10 };
static struct arm_input_ops *ops[nr_drivers];
static int ops_alloced;
struct arm_input_ops *arm_input_probe(const char *name)
{
int i;
for (i = 0; i < ops_alloced; i++)
if (ops[i]->probe && ops[i]->probe(name))
return ops[i];
return NULL;
}
void arm_input_register_driver(struct arm_input_ops *_ops)
{
if (ops_alloced < nr_drivers)
ops[ops_alloced++] = _ops;
}

View File

@@ -0,0 +1,619 @@
/*****************************************************************************/
/**
* \file input/lib/src/l4evdev.c
* \brief L4INPUT Event Device
*
* \date 05/28/2003
* \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de>
*
* This is roughly a Linux /dev/event implementation adopted from evdev.
*
* Original copyright notice from drivers/input/evdev.c follows...
*/
/*
* Event char devices, giving access to raw input device events.
*
* Copyright (c) 1999-2002 Vojtech Pavlik
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
/* L4 */
#include <string.h>
#include <l4/sys/err.h>
#include <l4/input/libinput.h>
#include <l4/re/c/util/cap_alloc.h>
#include <l4/re/env.h>
#include <linux/kernel.h>
#include <pthread.h>
/* C */
#include <stdio.h>
/* Linux */
#include <linux/input.h>
#include <linux/jiffies.h>
#include "internal.h"
/* Okay ...
There can be up to L4EVDEV_DEVICES managed.
So we _don't_ need one event queue/list per device to support dedicated
event handlers per device -> we use one general event queue (for DOpE) with
up to L4EVDEV_BUFFER l4evdev_event structures. CON uses callbacks and
therefore needs no ring buffer.
*/
#define L4EVDEV_DEVICES 16
#define L4EVDEV_BUFFER 512
#define DEBUG_L4EVDEV 0
/** l4evdev event structure */
struct l4evdev_event {
struct l4evdev *evdev;
struct l4input l4event;
};
/** l4evdev device structure */
struct l4evdev {
int exists;
int isopen;
int devn;
char name[16];
struct input_handle handle;
/* for touchpads */
unsigned int pkt_count;
int old_x[4], old_y[4];
int frac_dx, frac_dy;
unsigned long touch;
};
struct l4evdev *pcspkr;
/** all known devices */
static struct l4evdev l4evdev_devices[L4EVDEV_DEVICES];
/** global event list */
static struct l4evdev_event l4evdev_buffer[L4EVDEV_BUFFER];
static int l4evdev_buffer_head = 0;
static int l4evdev_buffer_tail = 0;
#define DEVS l4evdev_devices
#define BUFFER l4evdev_buffer
#define HEAD l4evdev_buffer_head /**< next event slot */
#define TAIL l4evdev_buffer_tail /**< first-in event if !(HEAD==TAIL) */
#define INC(x) (x) = ((x) + 1) % L4EVDEV_BUFFER
static pthread_mutex_t l4evdev_lock;
/*************************
*** TOUCHPAD HANDLING ***
*************************/
/* convert absolute to relative events for inappropriate devices like touchpads
* (taken from input/mousedev.c) */
static unsigned int tpad_touch(struct l4evdev *mousedev, int value)
{
if (!value) {
/* FIXME tap support */
#if 0
if (mousedev->touch &&
time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) {
/*
* Toggle left button to emulate tap.
* We rely on the fact that mousedev_mix always has 0
* motion packet so we won't mess current position.
*/
set_bit(0, &mousedev->packet.buttons);
set_bit(0, &mousedev_mix.packet.buttons);
mousedev_notify_readers(mousedev, &mousedev_mix.packet);
mousedev_notify_readers(&mousedev_mix, &mousedev_mix.packet);
clear_bit(0, &mousedev->packet.buttons);
clear_bit(0, &mousedev_mix.packet.buttons);
}
#endif
mousedev->touch = mousedev->pkt_count = 0;
mousedev->frac_dx = 0;
mousedev->frac_dy = 0;
}
else
if (!mousedev->touch)
mousedev->touch = jiffies;
/* FIXME tap support */
return KEY_RESERVED;
}
static unsigned int tpad_trans_key(unsigned int code)
{
unsigned int button;
switch (code) {
/* not translated */
case BTN_LEFT:
case BTN_RIGHT:
case BTN_MIDDLE:
case BTN_SIDE:
case BTN_EXTRA:
button = code;
break;
/* translations */
case BTN_TOUCH:
case BTN_0:
button = BTN_LEFT;
break;
case BTN_1:
button = BTN_RIGHT;
break;
case BTN_2:
button = BTN_MIDDLE;
break;
case BTN_3:
button = BTN_SIDE;
break;
case BTN_4:
button = BTN_EXTRA;
break;
/* abort per default */
default:
button = KEY_RESERVED;
}
return button;
}
#define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
#define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
/* check event and convert if appropriate
* returns 1 if event should be ignored */
static int tpad_event(struct input_dev *dev, struct l4evdev *mousedev,
unsigned int *type0, unsigned int *code0, int *value0)
{
int size, tmp;
unsigned int type = *type0;
unsigned int code = *code0;
int value = *value0;
enum { FRACTION_DENOM = 128 };
/* is it really a touchpad ? */
if (!test_bit(BTN_TOOL_FINGER, dev->keybit))
return 0;
switch (type) {
case EV_KEY:
/* from input/mousedev.c:315 */
/* ignore */
if (value == 2) return 1;
if (code == BTN_TOUCH)
code = tpad_touch(mousedev, value);
else
code = tpad_trans_key(code);
/* ignore some unhandled codes */
if (code == KEY_RESERVED) return 1;
break;
case EV_SYN:
/* from input/mousedev.c:324 */
if (code == SYN_REPORT) {
if (mousedev->touch) {
mousedev->pkt_count++;
/* Input system eats duplicate events, but we need all of them
* to do correct averaging so apply present one forward
*/
fx(0) = fx(1);
fy(0) = fy(1);
}
}
break;
case EV_ABS:
/* from input/mousedev.c:119 */
/* ignore if pad was not touched */
if (!mousedev->touch) return 1;
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
if (size == 0) size = 256 * 2;
switch (code) {
case ABS_X:
fx(0) = value;
/* ignore events with insufficient state */
if (mousedev->pkt_count < 2) return 1;
tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size;
tmp += mousedev->frac_dx;
value = tmp / FRACTION_DENOM;
mousedev->frac_dx = tmp - value * FRACTION_DENOM;
code = REL_X;
type = EV_REL;
break;
case ABS_Y:
fy(0) = value;
/* ignore events with insufficient state */
if (mousedev->pkt_count < 2) return 1;
tmp = ((value - fy(2)) * (256 * FRACTION_DENOM)) / size;
tmp += mousedev->frac_dy;
value = tmp / FRACTION_DENOM;
mousedev->frac_dy = tmp - value * FRACTION_DENOM;
code = REL_Y;
type = EV_REL;
break;
default: /* ignore this event */
return 1;
}
break;
default:
/* ignore unknown events */
#if DEBUG_L4EVDEV
printf("l4evdev.c: tpad ignored. D: %s, T: %d, C: %d, V: %d\n",
mousedev->handle.dev->phys, type, code, value);
#endif
return 1;
}
/* propagate handled events */
*type0 = type;
*code0 = code;
*value0 = value;
return 0;
}
/** SOME EVENTS ARE FILTERED OUT **/
static inline int filter_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
/* filter sound driver events */
if (test_bit(EV_SND, handle->dev->evbit))
return 1;
/* filter misc event: scancode -- no handlers yet */
if ((type == EV_MSC) && (code == MSC_SCAN))
return 1;
/* accepted */
return 0;
}
/** HANDLE INCOMING EVENTS (CALLBACK VARIANT) **/
static L4_CV void(*callback)(struct l4input *) = NULL;
static void l4evdev_event_cb(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
#if DEBUG_L4EVDEV
static unsigned long count = 0;
#endif
static struct l4input ev;
/* handle touchpads */
if (0)
if (tpad_event(handle->dev, (struct l4evdev *)handle->private,
&type, &code, &value))
return;
/* event filter */
if (filter_event(handle, type, code, value)) return;
ev.time = l4_kip_clock(l4re_kip());
ev.type = type;
ev.code = code;
ev.value = value;
ev.stream_id = (l4_umword_t)handle->dev;
/* call back */
callback(&ev);
#if DEBUG_L4EVDEV
printf("l4evdev.c: Ev[%ld]. D: %s, T: %d, C: %d, V: %d\n",
count++, handle->dev->phys, type, code, value);
#endif
}
/** HANDLE INCOMING EVENTS (BUFFER AND PULL VARIANT) **/
static void l4evdev_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
#if DEBUG_L4EVDEV
static unsigned long count = 0;
#endif
struct l4evdev *evdev = handle->private;
l4_kernel_clock_t clk = l4_kip_clock(l4re_kip());
/* handle touchpads */
if (0)
if (tpad_event(handle->dev, (struct l4evdev *)handle->private,
&type, &code, &value))
return;
/* event filter */
if (filter_event(handle, type, code, value)) return;
pthread_mutex_lock(&l4evdev_lock);
BUFFER[HEAD].evdev = evdev;
BUFFER[HEAD].l4event.time = clk;
BUFFER[HEAD].l4event.type = type;
BUFFER[HEAD].l4event.code = code;
BUFFER[HEAD].l4event.value = value;
BUFFER[HEAD].l4event.stream_id = (l4_umword_t)handle->dev;
INC(HEAD);
/* check head and tail */
if (HEAD == TAIL) {
/* clear oldest event struct in buffer */
//memset(&BUFFER[TAIL], 0, sizeof(struct l4evdev_event));
INC(TAIL);
}
pthread_mutex_unlock(&l4evdev_lock);
#if DEBUG_L4EVDEV
printf("l4evdev.c: Ev[%ld]. D: %s, T: %d, C: %d, V: %d\n",
count++, handle->dev->phys, type, code, value);
#endif
}
struct l4evdev *get_next_evdev(int *devnp)
{
int devn;
for (devn = 0; (devn < L4EVDEV_DEVICES) && (DEVS[devn].exists); devn++)
;
if (devn == L4EVDEV_DEVICES) {
printf("l4evdev.c: no more free l4evdev devices\n");
return NULL;
}
*devnp = devn;
return &DEVS[devn];
}
/* XXX had connect/disconnect to be locked? */
static struct input_handle * l4evdev_connect(struct input_handler *handler,
struct input_dev *dev,
struct input_device_id *id)
{
int devn;
struct l4evdev *evdev = get_next_evdev(&devn);
if (!evdev)
return NULL;
memset(evdev, 0, sizeof (struct l4evdev));
evdev->exists = 1;
evdev->devn = devn;
sprintf(evdev->name, "event%d", devn);
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
input_open_device(&evdev->handle);
printf("connect \"%s\", %s\n", dev->name, dev->phys);
if (test_bit(EV_SND, dev->evbit))
pcspkr = evdev;
return &evdev->handle;
}
static void l4evdev_disconnect(struct input_handle *handle)
{
struct l4evdev *evdev = handle->private;
evdev->exists = 0;
if (evdev->isopen) {
input_close_device(handle);
if (test_bit(EV_SND, handle->dev->evbit))
pcspkr = NULL;
} else /* XXX what about pending events? */
memset(&DEVS[evdev->devn], 0, sizeof(struct l4evdev));
}
static struct input_device_id l4evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
static struct input_handler l4evdev_handler = {
.event = NULL, /* fill it on init() */
.connect = l4evdev_connect,
.disconnect = l4evdev_disconnect,
.fops = NULL, /* not used */
.minor = 0, /* not used */
.name = "l4evdev",
.id_table = l4evdev_ids
};
/*****************************************************************************/
static int l4evdev_ispending(void)
{
return !(HEAD==TAIL);
}
static int l4evdev_flush(void *buf, int count)
{
int num = 0;
struct l4input *buffer = buf;
pthread_mutex_lock(&l4evdev_lock);
while ((TAIL!=HEAD) && count) {
/* flush event buffer */
*buffer = BUFFER[TAIL].l4event;
//memset(&BUFFER[TAIL], 0, sizeof(struct l4evdev_event));
num++; count--;
INC(TAIL);
buffer ++;
}
pthread_mutex_unlock(&l4evdev_lock);
return num;
}
static void l4_input_fill_info(struct input_dev *dev, l4re_event_stream_info_t *info)
{
memset(info, 0, sizeof(*info));
info->stream_id = (l4_umword_t)dev;
info->id.bustype = dev->id.bustype;
info->id.vendor = dev->id.vendor;
info->id.product = dev->id.product;
info->id.version = dev->id.version;
#define COPY_BITS(n) memcpy(info->n ##s, dev->n, min(sizeof(info->n ##s), sizeof(dev->n)))
COPY_BITS(evbit);
COPY_BITS(keybit);
COPY_BITS(relbit);
COPY_BITS(absbit);
#undef COPY_BITS
}
L4_CV int
l4evdev_stream_info_for_id(l4_umword_t id, l4re_event_stream_info_t *si)
{
unsigned devn;
for (devn = 0; devn < L4EVDEV_DEVICES; devn++) {
if (!DEVS[devn].exists)
continue;
if ((l4_umword_t)DEVS[devn].handle.dev == id) {
l4_input_fill_info(DEVS[devn].handle.dev, si);
return 0;
}
}
return -L4_EINVAL;
}
L4_CV int
l4evdev_absinfo(l4_umword_t id, unsigned naxes, unsigned const *axes,
l4re_event_absinfo_t *infos)
{
unsigned devn, idx;
struct input_dev *dev;
for (devn = 0; devn < L4EVDEV_DEVICES; devn++) {
if (!DEVS[devn].exists)
continue;
if ((l4_umword_t)DEVS[devn].handle.dev == id) {
break;
}
}
if (devn == L4EVDEV_DEVICES)
return -L4_EINVAL;
dev = DEVS[devn].handle.dev;
for (idx = 0; idx < naxes; idx++) {
unsigned a = axes[idx];
if (a > ABS_MAX)
return -L4_EINVAL;
infos[idx].value = 0;
infos[idx].min = dev->absmin[a];
infos[idx].max = dev->absmax[a];
infos[idx].fuzz = dev->absfuzz[a];
infos[idx].flat = dev->absflat[a];
infos[idx].resolution = 0;
}
return 0;
}
static int l4evdev_pcspkr(int tone)
{
if (!pcspkr)
return -L4_ENODEV;
input_event(pcspkr->handle.dev, EV_SND, SND_TONE, tone);
return 0;
}
static struct l4input_ops ops = {
l4evdev_ispending, l4evdev_flush, l4evdev_pcspkr
};
struct l4input_ops * l4input_internal_l4evdev_init(L4_CV void (*cb)(struct l4input *))
{
pthread_mutexattr_t a;
if (pthread_mutexattr_init(&a) != 0)
return NULL;
if (pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE) != 0)
return NULL;
if (pthread_mutex_init(&l4evdev_lock, &a) != 0)
return NULL;
pthread_mutexattr_destroy(&a);
if (cb) {
/* do callbacks */
callback = cb;
l4evdev_handler.event = l4evdev_event_cb;
} else {
/* buffer events in ring buffer */
l4evdev_handler.event = l4evdev_event;
}
input_register_handler(&l4evdev_handler);
return &ops;
}
void l4input_internal_register_ux(struct input_dev *dev)
{
int devn;
struct l4evdev *evdev = get_next_evdev(&devn);
if (!evdev)
return;
printf("EVDEV-NR: %d\n", devn);
evdev->exists = 1;
evdev->devn = devn;
sprintf(evdev->name, "event%d", devn);
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = 0;
evdev->handle.private = evdev;
}
void l4input_internal_l4evdev_exit(void)
{
input_unregister_handler(&l4evdev_handler);
}