diff -urNp orig-linux-2.6.19-beyond4/Documentation/input/acerhk/acertm.def linux-2.6.19-beyond4/Documentation/input/acerhk/acertm.def
--- orig-linux-2.6.19-beyond4/Documentation/input/acerhk/acertm.def 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/Documentation/input/acerhk/acertm.def 2004-01-31 23:00:30.000000000 +0200
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+ Konfiguration
+
+ Mute/Unmute
+ Lauter
+ Leiser
+
+ Terminal
+ XEmacs
+ Play/Pause
+ Play/Pause
+
+
+
+
diff -urNp orig-linux-2.6.19-beyond4/Documentation/input/acerhk/AUTHORS linux-2.6.19-beyond4/Documentation/input/acerhk/AUTHORS
--- orig-linux-2.6.19-beyond4/Documentation/input/acerhk/AUTHORS 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/Documentation/input/acerhk/AUTHORS 2002-05-08 22:01:52.000000000 +0300
@@ -0,0 +1 @@
+Olaf Tauber
\ No newline at end of file
diff -urNp orig-linux-2.6.19-beyond4/Documentation/input/acerhk/FAQ linux-2.6.19-beyond4/Documentation/input/acerhk/FAQ
--- orig-linux-2.6.19-beyond4/Documentation/input/acerhk/FAQ 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/Documentation/input/acerhk/FAQ 2005-11-10 20:52:20.000000000 +0200
@@ -0,0 +1,150 @@
+******************************************************************************
+
+Q: I have a (non Acer) notebook which is not recognized by your driver but I
+think it should. What information do you need?
+
+A: If it is a non-Acer laptop I would like to know, what makes you think that
+your laptop would work with my driver. A windows driver/utility package of the
+name "LaunchManager" or "EasyButton" is a good hint. If it is clear that your
+hardware is okay, I need the string with your model name from BIOS. Please
+don't ask me how to do it. To find out which functions of your laptop the
+driver will support, it is best, if you can tell me where to get this
+"LaunchManager" or "EasyButton" package.
+If it is a model from 2003 or newer then chances are good that it's a dritek
+type one. In this case try the option "force_series=6000" to at least enable
+the keys.
+
+******************************************************************************
+
+Q: Why can't I activate my wireless hardware on Acer TM 420/430/6000/8000 ...?
+
+A: On many newer laptops (Dritek type) I do not now how to do this,
+sorry. Someone would need to find out how windows does it. Volunteers?
+
+******************************************************************************
+
+Q: I know that wireless hardware is supported on my Aspire 1690/TravelMate4600,
+but I cannot get it to work, why?
+
+A: On these model (and similar) the wireless hardware is controlled on two
+different levels. One is controlled by acerhk's xxxled file, the other level
+is controlled by the key itself. To actually activate the hardware you need to
+write '1' to the xxled file and you need to press the corresponding button.
+Example:
+echo 1 > /proc/driver/acerhk/wirelessled
+
+
+******************************************************************************
+
+Q: Why do I always get 0x00 from /proc/driver/acerhk/key?
+
+A1: Check the type of your laptop if /proc/driver/acerhk/info. If it is
+"Dritek", you cannot read the keys from the driver. Starting from version
+0.5.17 you get "n/a" out of the key file in this case.
+A2: If you have polling enabled (default) you will almost always read 0x00
+from this file. This is because in every polling cycle it is checked for key
+events and if there is one, it is instantly translated and sent to the kernel
+input system. Starting with version 0.5.20 you get "n/a" out of the key file
+in this case.
+
+******************************************************************************
+
+Q: When I press a key, nothing happens. Why are they not working?
+
+A: Most keys won't do anything by themself. If nothing happens, that is
+because no programm knows what to do with the new keys. You need to assign
+actions to them, use the hotkey manager of your desktop to do that. If
+using Gnome, you find it under desktop settings - keyboard shortcuts.
+
+******************************************************************************
+
+Q: The driver works for my laptop, but not all of the keys are working. What
+can I do?
+
+A: There are some keys/key combinations which generate an ACPI event,
+e.g. Fn+F4 on some models or the lid button.
+If you have a different laptop than the one which got detected (or you used
+"force_series=xxx" anyway), then it is possible that the mapping acerhk uses
+to translate the codes from the buttons to key events is wrong.
+In this case, load the driver with "verbose=4" and press the buttons which do
+not work. Then look for messages from acerhk of the form "translated acer key
+code xxx to no key". Note these codes together with the button they belong to
+and send me this list along with the model name of your laptop.
+If you do not see a usable name in /proc/driver/acerhk/info please try the
+tools dmidecode/biosdecode/vpddecode to find this name.
+
+******************************************************************************
+Q: I press the wireless key but the hardware doesn't get activated, what's wrong?
+
+A: The driver is only on older models (non-Dritek type) able to read
+keypresses by itself and toggle the hardware/LED automatically (with option
+autowlan=1). On Dritek type models this must be done by writing the desired
+value to one of the wireless files in the proc filesystem. In most
+cases this would be /proc/drivers/acerhk/wirelessled to control wlan
+hardware (blueled for Bluetooth hardware):
+
+echo 1 > /proc/driver/acerhk/wirelessled
+
+But you could use a hotkey manager to do that automatically when you press the
+button. In this case be aware that on some models the button generates
+different key events according to the actual state of the wireless hardware.
+
+******************************************************************************
+
+Q: My keys do not work, I only get kernel messages of the form:
+atkbd.c: Unknown key pressed (translated set 2, code 0xf4 on isa0060/serio0).
+atkbd.c: Use 'setkeycodes e074 ' to make it known.
+
+A: Press each of the buttons and note the mentioned
+code for it (e074 in this example). You should get a list like this:
+P1 e074
+P2 e075
+...
+If you are finished with it, look into /usr/include/linux/input.h, using
+your favourite text viewer/editor. Search for "KEY_STOP". You should see
+the following line in the file:
+
+#define KEY_STOP 128
+
+After it many more lines with equal #defines should be visible. Look for
+key names which best match the names of your buttons, e.g. KEY_PROG1,
+KEY_WWW and so on. Note the numbers assigned to the names, for KEY_STOP
+this would be 128, for KEY_PROG1 148.
+Now you have a list with three items per entry, your button, a code
+from the kernel messages and a corresponding number from the file
+linux.h:
+P1 e074 148
+P2 e075 149
+...
+For each line in this list, issue the setkeycodes command as mentioned
+in the kernel message:
+setkeycodes e074 148
+setkeycodes e075 149
+...
+After doing that, the keys should be available for your hotkey manager.
+
+******************************************************************************
+
+Q: My WLAN hardware gets activated through /proc/driver/acerhk/wirelessled,
+but the LED is not working. Why not?
+
+A: Try if adding the option "led=1" to your wireless module helps. For the
+ipw2200 driver it works, as Didier CLERC found out:
+
+ I have to load the module "ipw2200" with the option "led=1", then
+ the wifi button blinks until a network is detected.
+
+******************************************************************************
+
+Q: I have an unsupported laptop, but the driver works when I force the series
+to a type like 610 or 2100. Only my buttons doesn't get recognized, why?
+
+A: On models of the type TM_new (like TravelMate 600, 2100 and many other, see
+acerhk.c, function setup_model_features for details) the buttons use different
+codes on different models. Therefore the driver needs to know these codes. You
+can get them if you load the driver with verbose=4, press the buttons and key
+combinations (Fn+xx) you are interested in and look for kernel messages of the
+form "acerhk: translated acer key code 0xnn to ...". Write down the key code
+for each button/key combination and send them to me so I can patch the
+driver. I also need your model string to make autodetection work, so include
+/proc/driver/acerk/info.
\ No newline at end of file
diff -urNp orig-linux-2.6.19-beyond4/Documentation/input/acerhk/INSTALL linux-2.6.19-beyond4/Documentation/input/acerhk/INSTALL
--- orig-linux-2.6.19-beyond4/Documentation/input/acerhk/INSTALL 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/Documentation/input/acerhk/INSTALL 2005-12-07 14:37:42.000000000 +0200
@@ -0,0 +1,109 @@
+Installation
+************
+
+1. You need the kernel sources (or kernel headers for your kernel)
+installed to compile the driver.
+
+2. Your kernel needs loadable module support with version information for
+modules enabled. Usage of procfs is highly recommended.
+If you want the driver to generate regular keyboard events using
+kernel version 2.4 you need the input system of the kernel enabled
+(Input core support AND keyboard support). In kernel version 2.6 all
+needed functionality should be available by default.
+
+In most cases you can skip the next step, the Makefile tries do determine
+the correct directory on its own. Change KERNELSRC only if the autodetection
+does not work for you. Otherwise proceed directly with step 4.
+
+3. Before you compile the driver, change KERNELSRC in the makefile to your
+path to the kernel build environment. If you are using a self compiled kernel,
+point it to the root of your sources. If you are using a packaged kernel of
+your distribution, install the package with kernel headers
+(Debian:kernel-headers) and point KERNELSRC to where the headers and config
+files are located. If you are using Debian, this
+would be "/lib/modules//build".
+
+4. Do:
+ make
+to compile the driver. If you run into problems because of the makefile not
+recognizing your kernel version correctly, try this:
+ make acerhk.o - kernel version 2.4
+ make acerhk.ko - kernel version 2.6
+
+5. Do:
+ make install
+to automatically copy the driver into the kernel module library. If you've
+done so, proceed directly with step 8. If you want to install the module
+binary yourself (because you want a different location), use steps 6 and 7
+instead.
+
+6. Copy the created file "acerhk.o" ("acerhk.ko" with version 2.6) to your
+kernel modules path. In Debian this could be
+"/lib/modules//kernel/drivers/extra/".
+
+7. Update module dependencies: depmod -a
+
+8. Try loading the module with:
+ insmod/modprobe acerhk
+If it succeeds - congratulations! If you have procfs enabled, you can try the
+following to test the driver:
+
+Non-Dritek models:
+Press one of the special keys and after that:
+ cat /proc/driver/acerhk/key
+to read the (hexadezimal) code of the key pressed. It should
+be different from 0x00.
+(Note: You mustn't have the polling feature enabled for this to work, so load
+ the module with poll=0)
+
+Dritek-models:
+Press one of the special keys and look for the generated key with "xev". If
+there is none, then you should see at least kernel messages about using
+setkeycodes.
+
+If your notebook has a mail led you can try this:
+ echo on > /proc/driver/acerhk/led
+This should sete the mail led to blinking mode.
+ echo off > /proc/driver/acerhk/led
+turns it off again.
+See README for further usage information.
+
+If the module didn't load look into your kernel messages what went wrong. If
+you see something like the following lines:
+ acerhk: could not find request handler
+ acerhk: can't find ROM area
+ acerhk: unloaded
+then your hardware is not recognized. See README for supported models. If it
+won't work on your notebook, please contact me and I will see if I can fix
+that.
+
+Integrating the driver into kernel tree version 2.6
+***************************************************
+
+If you want the driver to fully integrate into the kernel tree of version 2.6
+proceed as follows:
+1. Copy the acerhk directory into the source tree, for instance
+ /usr/src/linux/drivers/misc/acerhk
+2. Include the driver directory in the config files. Add to the Kconfig
+file of the parent directory(/usr/src/linux/drivers/misc/Kconfig):
+
+config ACERHK
+ tristate "Acerhk driver"
+ depends on EXPERIMENTAL
+ ---help---
+ This is an experimental acer keyboard driver for
+ acer laptops
+
+3. Include the acer directory in it's parents
+Makefile(/usr/src/linux/drivers/misc/Makefile):
+
+obj-$(CONFIG_ACERHK) += acerhk/
+
+4. In this case you also need to activate the misc drivers first
+(/usr/src/linux/drivers/Kconfig):
+
+source "drivers/misc/Kconfig"
+
+If that's done, you should be able to select the driver from the configuration
+programm and build the module.
+
diff -urNp orig-linux-2.6.19-beyond4/Documentation/input/acerhk/IOCTL linux-2.6.19-beyond4/Documentation/input/acerhk/IOCTL
--- orig-linux-2.6.19-beyond4/Documentation/input/acerhk/IOCTL 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/Documentation/input/acerhk/IOCTL 2004-01-31 23:00:30.000000000 +0200
@@ -0,0 +1,61 @@
+Documentation of possible IOCTLs used by acerhk
++++++++++++++++++++++++++++++++++++++++++++++++
+
+ACERHK_GET_KEYCOUNT
+ Read the number of unread key presses in queue
+ Parameter: pointer to char
+
+ACERHK_GET_KEYID
+ Read the code of the first key press (oldest) in queue
+ Parameter: pointer to char
+
+ACERHK_CONNECT
+ Don't know what it does, used in windows driver
+
+ACERHK_DISCONNECT
+ Don't know what it does, used in windows driver
+
+ACERHK_GET_THERMAL_EVENT
+ Don't know what it does, used in windows driver
+ Parameter: pointer to short
+
+ACERHK_MAIL_LED_OFF
+ Switch off the LED of the mail button(if available)
+
+ACERHK_MAIL_LED_ON
+ Switch on the LED of the mail button (if available)
+
+ACERHK_START_POLLING
+ Start polling (and translation to key events) in kernel
+
+ACERHK_STOP_POLLING
+ Stop polling in kernel
+
+ACERHK_GET_KEY_MAP
+ Get mapping of key names to key events
+ Parameter: pointer to t_map_name2event
+
+ACERHK_SET_KEY_MAP
+ Set mapping of key names to key events
+ Parameter: pointer to t_map_name2event
+
+IOCTLs used by windows driver
++++++++++++++++++++++++++++++
+device name:
+\DosDevices\HOTKEY
+\Device\HOTKEY
+
+
+630 series:
+0x222404
+ Get CMOS index
+0x222408
+ ACERHK_GET_KEYCOUNT
+0x22240C
+ ACERHK_GET_KEYID
+0x222410
+ ACERHK_MAIL_LED_OFF/ACERHK_MAIL_LED_ON
+0x222414
+ ACERHK_CONNECT/ACERHK_DISCONNECT
+0x222418
+ ACERHK_GET_THERMAL_EVENT
diff -urNp orig-linux-2.6.19-beyond4/Documentation/input/acerhk/keycodes linux-2.6.19-beyond4/Documentation/input/acerhk/keycodes
--- orig-linux-2.6.19-beyond4/Documentation/input/acerhk/keycodes 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/Documentation/input/acerhk/keycodes 2004-04-18 21:28:12.000000000 +0300
@@ -0,0 +1,26 @@
+standard X keycodes with older (acerhk controlled) hardware from Acer
+*********************************************************************
+help (Fn+F1) 226
+setup (Fn+F2) 129
+p1 153
+p2 144
+p3 171
+www 178
+mail 236
+wireless 147
+power (Fn+F3) 222
+mute (Fn+F8) 166
+volup (Fn+Up) 158
+voldn (Fn+Down) 165
+
+standard X keycodes with newer (acerhk activated) hardware from Dritek
+**********************************************************************
+help (Fn+F1) 226
+setup (Fn+F2) 129
+p1 153
+p2 144
+www 178
+mail 236
+volup (Fn+Up) 176
+voldn (Fn+Down) 174
+mute (Fn+F8) 160
diff -urNp orig-linux-2.6.19-beyond4/Documentation/input/acerhk/md95400.def linux-2.6.19-beyond4/Documentation/input/acerhk/md95400.def
--- orig-linux-2.6.19-beyond4/Documentation/input/acerhk/md95400.def 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/Documentation/input/acerhk/md95400.def 2005-08-15 14:43:08.000000000 +0300
@@ -0,0 +1,14 @@
+
+
+
+Mail
+Web
+Play/Pause
+Stop
+Previous
+Next
+Leiser
+Lauter
+
+
+
diff -urNp orig-linux-2.6.19-beyond4/Documentation/input/acerhk/NEWS linux-2.6.19-beyond4/Documentation/input/acerhk/NEWS
--- orig-linux-2.6.19-beyond4/Documentation/input/acerhk/NEWS 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/Documentation/input/acerhk/NEWS 2003-02-28 02:48:08.000000000 +0200
@@ -0,0 +1,16 @@
+2003-02-28 Version 0.5.0
+ completely changed key polling/translation
+ support for more series
+2002-07-06 Version 0.4.2
+ added debug functionality
+ bugfix with 520/210
+2002-06-24 Version 0.4.1
+ added support for TM 210 series
+ removed nvram dependency
+2002-06-15 Version 0.4
+ added model recognition
+ added kernel polling an key event generation
+ added support for 520 series
+
+2002-04-29 Version 0.3
+ Initial release
\ No newline at end of file
diff -urNp orig-linux-2.6.19-beyond4/Documentation/input/acerhk/README linux-2.6.19-beyond4/Documentation/input/acerhk/README
--- orig-linux-2.6.19-beyond4/Documentation/input/acerhk/README 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/Documentation/input/acerhk/README 2005-10-15 14:27:45.000000000 +0300
@@ -0,0 +1,215 @@
+
+What is this driver good for?
+*****************************
+
+This driver will give access to the special keys on notebooks of the
+Acer Travelmate series, which are not handled by the keyboard
+driver.
+It also works on notebooks from other manufacturers (some Medion,
+Fujitsu-Siemens, ...).
+
+It also has some other related functionality (depending on the model):
+- controlling LEDs (Mail, Wireless)
+- enable/disable wireless hardware
+
+Usage
+*****
+
+The driver provides a device /dev/misc/acerhk where you can access all
+functions through IOCTL's, look into acerhk.h for their definitions.
+I use devfs and don't care about the minor number. You may want to change
+ACERHK_MINOR in acerhk.h.
+If you don't use devfs you need to create the device node. The device uses
+major 10 (misc character devices), the minor is chosen by the kernel (if
+ACERHK_MINOR equals MISC_DYNAMIC_MINOR) or by yourself (any other value).
+Use something like
+ mknod /dev/acerhk c 10 123
+to create the device node.
+You can also use most functions through procfs. You will find some files in
+/proc/driver/acerhk:
+ led - write "on" or "off" to set the mail led state
+ key - read it to get a key press, only useful on non-dritek models
+ and only if polling is disabled
+ This file exists only on non-dritek models
+ info - some information about the driver, including the number of
+ pending keys
+ wirelessled - write "on" or "off" to set the led state and enable
+ wlan hardware
+ On some laptops you need to load the ipw2200 (others
+ too?) with option "led=1" to make the led work.
+ blueled - write "on" or "off" to set the led state and enable
+ bluetooth hardware
+ write a value n >= 50 to let the led blink every n jiffies
+ Do not use blinking mode where not only the led but also the
+ bluetooth hardware is controlled!
+
+Probably the best way to use the driver is to let it poll for the keys itself
+and generate real key events. this is the normal case when loading the module
+without an additional parameter :
+
+ modprobe acerhk
+
+or you can use the ioctl as specified in acerhk.h to control the polling. If
+your kernel has keyboard input support you will get real key events when
+pressing the special keys.
+You can then use whatever software you like to make use of those keys,
+i.e. 'hotkeys' or Linux Easy Access Keyboard 'lineakd'. I prefer hotkeys, just
+in case anyone is interested, you can find a sample configuration for this
+programm in doc/acertm.def.
+Gnome >= 2.4 has built in support for multimedia keys (acme), that's
+what I use now.
+
+Have a look at
+ http://mmkc.sourceforge.net/
+
+Note: If you have one of the newer models using dritek hardware you don't need
+polling (nor the file /proc/driver/acerhk/key) to use your buttons. On this
+hardware the driver only needs to send a special command to the keyboard
+controller to make them work. The actual key presses are then handled by the
+normal keyboard driver of the kernel. You can switch of polling by
+loading the module with poll=0. It saves you precious cpu time.
+
+
+Module parameters
+*****************
+name values meaning
+autowlan 0,1 disable(default)/enable automatic switching of wlan hardware
+ on wireless keypress, only useful for older (no
+ dritek-hardware) models, works only will poll=1
+poll 0,1 disable/enable(default) kernel polling of key events
+verbose 0-4 verbosity level, see below
+usedritek 0,1 disable/enable(default) use of dritek hardware on newer
+ series, needed to activate the keys on such models
+force_series set this to the laptop series number you want the driver to
+ assume, skip autodetection (look at function
+ setup_model_features() for possible values)
+
+Debugging
+*********
+
+You can make the the driver be more verbose. To do this, add the following
+parameter when loading the module:
+
+ verbose=
+
+n is an integer, 0 (default) means no additional information while increasing
+values provide an increasing amount of information. Currently 3 is maximum,
+bigger values will have the same effect as 3. The existing verbosity levels
+will generate information about:
+ Level Information
+ 1 state changes and variable initialization
+ 2 model probing
+ 3 key translation, only known keys
+ 4 key translation, also unknown keys
+ 5 misc. information (idle values)
+
+If you have rather serious problems you can activate debugging functionality
+in the driver. To do this, uncomment the '#define ACERDEBUG' in acerhk.c. You
+will find a new file in /proc/driver/acerhk, named 'debug'. You cannot read
+from this file, but you can write commands consisting of up to 4 digits. The
+first one specifies the action, while the latter can give additional
+parameters. Implemented are:
+ 'd' decrement module usage counter
+ 'i' increment module usage counter
+
+ 'p' call function pbutton_fct()
+ 't' call function get_thermal_event()
+ 'w1x' call function wbutton_fct_1() with parameter x (0-9)
+ 'w2x' call function wbutton_fct_2() with parameter x (0-9)
+
+ 'vx' change verbosity level to x (0-9)
+
+ 'mxyy' set mapping of key name x to key event yy (hex)
+ 'sxx' send key event xx (hex) to input system
+ 'Sxx' simulate acer key press with code xx (hex)
+
+ 'e0' stop kernel polling
+ 'e1' start kernel polling
+
+Example:
+
+1) echo d > /proc/driver/acerhk/debug
+
+will decrement the usage counter, very useful if a program using the driver
+segfaulted. This way you can still unload the driver.
+
+2) echo v4 > /proc/driver/acerhk/debug
+
+will set the verbosity level to maximum.
+
+Keycodes
+********
+
+see doc/keycodes
+Also see http://bernd-wurst.de/linux/tm800.php#mmkeys (only in german)
+
+If you have one of the newer models with the dritek hardware, use kernel 2.6
+and get (after enabling it) kernel messages of the form:
+
+ atkbd.c: Unknown key pressed (translated set 2, code 0xf4 on
+ isa0060/serio0).
+ atkbd.c: Use 'setkeycodes e074 ' to make it known.
+
+then you should do exactly what your told. In this case you could do
+
+ setkeycodes e074 158
+
+to map the button with scancode e074 (hex) to keycode 158 (decimal). To find
+out the scancodes of the buttons either look into the kernel log or into the
+file MMKEYBD.CFG of the windows launch manager package. There you should find
+lines like this:
+
+Key 1 = 1,E0,74,E0,F4,F500,P1
+ ** ** **
+
+The important information is marked in the example above. The numbers give the
+scancode produced by the button which's name is given last.
+The keycode you give as parameter to setkeycodes is one out of the header file
+linux/input.h, in the example above the one for KEY_BACK.
+Important note: I received mails from a number of people where setkeycodes
+rejected keycodes above a certain value. This is caused by an outdated version
+of setkeycodes, use a more recent one.
+To ease the setup of keymappings for the newer series I will try to include
+setup scripts for the different notebook series. If I have enough time to
+spare I will perhaps expand the driver itself do to that.
+
+How does it work?
+*****************
+
+The driver is based on the windows Me for series 610 driver and
+resembles its structure and functionality.
+Key presses are events, which are stored in a FIFO queue with 31
+entries. You can access the event count via CMOS nvram, but the access to
+the actual queue (and other functionality like switching the mail led) is
+done through calling a system ROM function.
+Upon loading the driver looks for this function, if it cannot find it,
+loading aborts.
+Newer Dritek Hardware:
+Button handling is done entirely by the EC (embbedded/environment
+controller) which behaves like an extended keyboard controller. My
+driver only enables/disables this extension.
+
+
+Credits
+*******
+
+Leif Jensen, whose driver inspired me to do the probing stuff
+http://www.math.columbia.edu/~jensen/linux/acertm/
+
+Thanks to all who contributed patches to this driver or who just tried it out
+on their laptops, without them it wouldn't support anything else but my
+TM 613.
+
+Contact
+*******
+
+If you have problems with the driver, please include the following information:
+1. name of your laptop (e.g. Acer TM 4001)
+2. content of /proc/driver/acerhk/info, if available
+3. kernel output after loading the module with verbose=2
+
+Email: Olaf Tauber
+
+The latest version can be found here:
+
+http://www.informatik.hu-berlin.de/~tauber/acerhk
diff -urNp orig-linux-2.6.19-beyond4/drivers/input/acerhk/acerhk.c linux-2.6.19-beyond4/drivers/input/acerhk/acerhk.c
--- orig-linux-2.6.19-beyond4/drivers/input/acerhk/acerhk.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/drivers/input/acerhk/acerhk.c 2006-12-27 21:26:58.000000000 +0200
@@ -0,0 +1,2990 @@
+/*********************************************************************
+ * Filename: acerhk.c
+ * Version: 0.5
+ *
+ * Copyright (C) 2002-2006, Olaf Tauber (olaf-tauber@versanet.de)
+ *
+ * Description: kernel driver for Acer Travelmate and similar
+ * laptops special keys
+ * Author: Olaf Tauber
+ * Created at: Mon Apr 29 22:16:42 2002
+ * Modified at: Thu Sep 21 19:44:03 2006
+ * Modified by: Olaf Tauber
+ * Modified at: Thu Nov 24 13:03:01 2005
+ * Modified by: Antonio Cuni
+ * Modified at: Wed Oct 27 19:47:11 CEST 2004
+ * Modified by: Joachim Fenkes
+ *
+ * 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
+ *
+ *
+ */
+#include
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#include
+#endif
+
+/* This driver is heavily dependent on the architecture, don't let
+ * anyone without an X86 machine use it. On laptops with AMD64
+ * architecture this driver is only useable in 32 bit mode.
+ */
+#ifdef CONFIG_X86
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#define KERNEL26
+#include
+#else
+#include
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
+#define STATIC_INPUT_DEV
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "acerhk.h"
+
+/* #define ACERDEBUG */
+/* #define DUMMYHW */
+
+#define ACERHK_VERSION "0.5.34"
+#define MODULE_NAME "acerhk"
+
+/* maximum number of polling loops, adjust it if needed to values between
+ * 1 and 32
+ */
+#define MAX_POLLING_LOOPS 16U
+
+/* maximum length for model string */
+#define ACERHK_MODEL_STRLEN 16
+/* size of mapped areas */
+#define AREA_SIZE 0xffff
+/* needed for colussi algorithm */
+#define XSIZE 20
+
+/* Module parameters */
+static int poll=1;
+static int autowlan;
+static int usedritek=1;
+static int wlan_state=-1;
+static int bluetooth_state=-1;
+static int verbose;
+static unsigned int force_series;
+#ifdef KERNEL26
+module_param(poll, int, 0444);
+module_param(autowlan, int, 0444);
+module_param(usedritek, int, 0444);
+module_param(verbose, int, 0444);
+module_param(wlan_state, int, 0444);
+module_param(bluetooth_state, int, 0444);
+module_param(force_series, uint, 0444);
+#else
+MODULE_PARM(poll, "i");
+MODULE_PARM(autowlan, "i");
+MODULE_PARM(wlan_state, "i");
+MODULE_PARM(bluetooth_state, "i");
+MODULE_PARM(usedritek, "i");
+MODULE_PARM(verbose, "i");
+MODULE_PARM(force_series, "i");
+#endif
+MODULE_PARM_DESC(poll, "start polling timer");
+MODULE_PARM_DESC(autowlan, "automatic switching of wlan hardware");
+MODULE_PARM_DESC(wlan_state, "(assumed) initial state of WLAN LED/hardware");
+MODULE_PARM_DESC(bluetooth_state, "(assumed) initial state of Bluetooth LED/hardware");
+MODULE_PARM_DESC(usedritek, "enable dritek keyboard extension");
+MODULE_PARM_DESC(verbose, "output additional information");
+MODULE_PARM_DESC(force_series, "force laptop series, skip autodetection");
+
+/* input device */
+static struct input_dev *acerhk_input_dev_ptr;
+#ifdef STATIC_INPUT_DEV
+static struct input_dev acerhk_input_dev;
+#endif
+
+/* mapped IO area from 0xf0000 */
+static void *reg1;
+/* mapped IO area from 0xe0000 */
+static void *reg2;
+/* Pointer to mapped area at 0x400 on 520 series */
+static void *preg400;
+/* location of IO routine in mapped area */
+static unsigned int bios_routine;
+/* index of CMOS port to get key event */
+static unsigned int cmos_index;
+/* function for bios call */
+static bios_call call_bios;
+/* address of model string */
+static char *acerhk_model_addr;
+/* copied string, maximum length 16 ('TravelMate xxx') */
+static char acerhk_model_string[ACERHK_MODEL_STRLEN];
+/* type of hardware access */
+static t_acer_type acerhk_type;
+/* travelmate series */
+static unsigned int acerhk_series;
+/* supported features for this model */
+static unsigned int acerhk_model_features;
+/* map of acer key codes to acer key names */
+static unsigned char acerhk_key2name[0xff];
+/* map of acer key names to key events */
+static t_map_name2event acerhk_name2event;
+/* timer for polling key presses */
+static struct timer_list acerhk_timer_poll;
+/* polling active */
+static int acerhk_polling_state;
+/* polling delay */
+static unsigned acerhk_polling_delay = HZ/5;
+/* wlan hardware toggle */
+static int acerhk_wlan_state;
+/* bluetooth hardware toggle */
+static int acerhk_bluetooth_state;
+
+/* bluetooth blinking state; added by Antonio Cuni
+ possible values:
+ -1: blinking disabled (default)
+ 0: blinking enabled, led currently off
+ 1: blinking enabled, led currently on
+*/
+static int acerhk_blueled_blinking = -1;
+/* delay between two changes of state, in jiffies */
+static unsigned acerhk_blueled_blinking_delay;
+/* timer for blinking */
+static struct timer_list acerhk_timer_blinking;
+
+/* function prototypes */
+static void start_polling(void);
+static void stop_polling(void);
+
+/* Added by Antonio Cuni */
+static void start_blinking(void);
+static void stop_blinking(void);
+
+/* {{{ Experimental use of dritek keyboard extension */
+
+#define EC_STATUS_REG 0x66 /* Status register of EC (R) */
+#define EC_CNTL_REG 0x66 /* Controller command register of EC (W) */
+#define EC_DATA_REG 0x62 /* EC data register (R/W) */
+
+#ifdef KERNEL26
+
+#include
+
+#define KBD_STATUS_REG 0x64 /* Status register (R) */
+#define KBD_CNTL_REG 0x64 /* Controller command register (W) */
+#define KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */
+
+#else
+
+#ifndef KEY_MEDIA
+#define KEY_MEDIA 226
+#endif
+
+#define preempt_disable() do { } while (0)
+#define preempt_enable_no_resched() do { } while (0)
+#define preempt_enable() do { } while (0)
+#define preempt_check_resched() do { } while (0)
+#include
+
+#endif
+
+static inline int my_i8042_read_status(void)
+{
+ return inb(KBD_STATUS_REG);
+}
+static int my_i8042_wait_write(void)
+{
+ int i = 0;
+ while ((my_i8042_read_status() & 0x02) && (i < 10000)) {
+ udelay(50);
+ i++;
+ }
+ return -(i == 10000);
+}
+static void send_kbd_cmd(unsigned char cmd, unsigned char val)
+{
+ if (usedritek) {
+ preempt_disable();
+ if (!my_i8042_wait_write())
+ outb(cmd, KBD_CNTL_REG);
+ if (!my_i8042_wait_write())
+ outb(val, KBD_DATA_REG);
+ preempt_enable_no_resched();
+ } else {
+ printk(KERN_INFO"acerhk: request for accessing EC ignored\n"
+ KERN_INFO"acerhk: Use of dritek keyboard extension not enabled, use module\n"
+ KERN_INFO"acerhk: parameter usedritek=1 to do that (possibly dangerous)\n");
+ }
+}
+#ifdef ACERDEBUG
+static inline int my_i8042_read_ecstatus(void)
+{
+ return inb(EC_STATUS_REG);
+}
+static int my_i8042_wait_ecwrite(void)
+{
+ int i = 0;
+ while ((my_i8042_read_ecstatus() & 0x02) && (i < 10000)) {
+ udelay(50);
+ i++;
+ }
+ return -(i == 10000);
+}
+static void send_ec_cmd(unsigned char cmd, unsigned char val)
+{
+ if (usedritek) {
+ preempt_disable();
+ if (!my_i8042_wait_ecwrite())
+ outb(cmd, EC_CNTL_REG);
+ if (!my_i8042_wait_ecwrite())
+ outb(val, EC_DATA_REG);
+ preempt_enable_no_resched();
+ } else {
+ printk(KERN_INFO"acerhk: request for accessing EC ignored\n"
+ KERN_INFO"acerhk: Use of dritek keyboard extension not enabled, use module\n"
+ KERN_INFO"acerhk: parameter usedritek=1 to do that (possibly dangerous)\n");
+ }
+}
+#endif
+#ifdef ACERDEBUG
+static void enable_mute_led_ec(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling mute led via EC\n");
+ send_kbd_cmd(0x59, 0x94);
+}
+static void disable_mute_led_ec(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: disabling mute led via EC\n");
+ send_kbd_cmd(0x59, 0x95);
+}
+static void enable_dmm_function(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling WLAN via EC variant 2\n");
+ send_kbd_cmd(0x45, 0xd3);
+}
+#endif
+static void enable_wlan_ec_1(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling WLAN via EC variant 1\n");
+ send_kbd_cmd(0xe7, 0x01);
+ acerhk_wlan_state = 1;
+}
+static void disable_wlan_ec_1(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: disabling WLAN via EC variant 1\n");
+ send_kbd_cmd(0xe7, 0x00);
+ acerhk_wlan_state = 0;
+}
+static void enable_bluetooth_ec_1(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling Bluetooth via EC variant 1\n");
+ send_kbd_cmd(0xe7, 0x03);
+ acerhk_bluetooth_state = 1;
+}
+static void disable_bluetooth_ec_1(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: disabling Bluetooth via EC variant 1\n");
+ send_kbd_cmd(0xe7, 0x02);
+ acerhk_bluetooth_state = 0;
+}
+static void enable_wlan_ec_2(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling WLAN via EC variant 2\n");
+ send_kbd_cmd(0x45, acerhk_bluetooth_state ? 0xa2 : 0xa0);
+ acerhk_wlan_state = 1;
+}
+static void disable_wlan_ec_2(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: disabling WLAN via EC variant 2\n");
+ send_kbd_cmd(0x45, acerhk_bluetooth_state ? 0xa1 : 0xa3);
+ acerhk_wlan_state = 0;
+}
+static void enable_bluetooth_ec_2(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling Bluetooth via EC variant 2\n");
+ send_kbd_cmd(0x45, acerhk_wlan_state ? 0xa2 : 0xa1);
+ acerhk_bluetooth_state = 1;
+}
+static void disable_bluetooth_ec_2(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: disabling Bluetooth via EC variant 2\n");
+ send_kbd_cmd(0x45, acerhk_wlan_state ? 0xa0 : 0xa3);
+ acerhk_bluetooth_state = 0;
+}
+#ifdef ACERDEBUG
+static void enable_wireless_ec(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling wireless hardware\n");
+ if (usedritek) {
+ preempt_disable();
+ if (!my_i8042_wait_ecwrite())
+ outb(0x4d, EC_CNTL_REG);
+ if (!my_i8042_wait_ecwrite())
+ outb(0xd2, EC_DATA_REG);
+ if (!my_i8042_wait_ecwrite())
+ outb(0x01, EC_DATA_REG);
+ preempt_enable_no_resched();
+ }
+ acerhk_wlan_state = 1;
+}
+static void disable_wireless_ec(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: disabling wireless hardware\n");
+ if (usedritek) {
+ preempt_disable();
+ if (!my_i8042_wait_ecwrite())
+ outb(0x4d, EC_CNTL_REG);
+ if (!my_i8042_wait_ecwrite())
+ outb(0xd2, EC_DATA_REG);
+ if (!my_i8042_wait_ecwrite())
+ outb(0x00, EC_DATA_REG);
+ preempt_enable_no_resched();
+ }
+ acerhk_wlan_state = 0;
+}
+#endif
+static void enable_dritek_keyboard(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling dritek keyboard extension\n");
+ send_kbd_cmd(0x59, 0x90);
+}
+static void disable_dritek_keyboard(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: disabling dritek keyboard extension\n");
+ send_kbd_cmd(0x59, 0x91);
+}
+static void enable_mail_led_ec_1(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling mail led via EC variant 1\n");
+ send_kbd_cmd(0xe8, 0x01);
+}
+static void disable_mail_led_ec_1(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: disabling mail led via EC variant 1\n");
+ send_kbd_cmd(0xe8, 0x00);
+}
+
+static void enable_mail_led_ec_2(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling mail led via EC variant 2\n");
+ send_kbd_cmd(0x59, 0x92);
+}
+static void disable_mail_led_ec_2(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: disabling mail led via EC variant 2\n");
+ send_kbd_cmd(0x59, 0x93);
+}
+static void enable_mail_led_ec_3(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: enabling mail led via EC variant 3\n");
+ if (usedritek) {
+ preempt_disable();
+ if (!my_i8042_wait_write())
+ outl(0x80008894, 0xCF8);
+ if (!my_i8042_wait_write())
+ outw(0xC061, 0xCFC);
+ preempt_enable_no_resched();
+ }
+}
+static void disable_mail_led_ec_3(void)
+{
+ if (verbose)
+ printk(KERN_INFO "acerhk: disabling mail led via EC variant 3\n");
+ if (usedritek) {
+ preempt_disable();
+ if (!my_i8042_wait_write())
+ outl(0x80008894, 0xCF8);
+ if (!my_i8042_wait_write())
+ outw(0xC060, 0xCFC);
+ preempt_enable_no_resched();
+ }
+}
+
+/* }}} */
+
+/* {{{ string search functions */
+
+/* This is the Colussi algorithm, the code is taken from
+ http://www-igm.univ-mlv.fr/~lecroq/string
+*/
+int preColussi(char *x, int m, int *h, int *next, int *shift)
+{
+ int i, k, nd, q, r, s;
+ int hmax[XSIZE], kmin[XSIZE], nhd0[XSIZE], rmin[XSIZE];
+ /* Computation of hmax */
+ i = k = 1;
+ do {
+ while (x[i] == x[i - k])
+ i++;
+ hmax[k] = i;
+ q = k + 1;
+ while (hmax[q - k] + k < i) {
+ hmax[q] = hmax[q - k] + k;
+ q++;
+ }
+ k = q;
+ if (k == i + 1)
+ i = k;
+ } while (k <= m); /* Computation of kmin */
+ memset(kmin, 0, m*sizeof(int));
+ r = 0;
+ for (i = m; i >= 1; --i)
+ if (hmax[i] < m)
+ kmin[hmax[i]] = i; /* Computation of rmin */
+ for (i = m - 1; i >= 0; --i) {
+ if (hmax[i + 1] == m)
+ r = i + 1;
+ if (kmin[i] == 0)
+ rmin[i] = r;
+ else
+ rmin[i] = 0;
+ } /* Computation of h */
+ s = -1;
+ r = m;
+ for (i = 0; i < m; ++i)
+ if (kmin[i] == 0)
+ h[--r] = i;
+ else
+ h[++s] = i;
+ nd = s; /* Computation of shift */
+ for (i = 0; i <= nd; ++i)
+ shift[i] = kmin[h[i]];
+ for (i = nd + 1; i < m; ++i)
+ shift[i] = rmin[h[i]];
+ shift[m] = rmin[0]; /* Computation of nhd0 */
+ s = 0;
+ for (i = 0; i < m; ++i) {
+ nhd0[i] = s;
+ if (kmin[i] > 0)
+ ++s;
+ } /* Computation of next */
+ for (i = 0; i <= nd; ++i)
+ next[i] = nhd0[h[i] - kmin[h[i]]];
+ for (i = nd + 1; i < m; ++i)
+ next[i] = nhd0[m - rmin[h[i]]];
+ next[m] = nhd0[m - rmin[h[m - 1]]]; return(nd);
+}
+
+int COLUSSI(char *x, int m, char *y, int n) {
+ int i, j, last, nd,
+ h[XSIZE], next[XSIZE], shift[XSIZE]; /* Processing */
+ int match_pos; /* position of first match */
+ nd = preColussi(x, m, h, next, shift); /* Searching */
+ i = j = 0;
+ last = -1;
+ match_pos = -1;
+ while ( (match_pos == -1)
+ && (j <= n - m) ) {
+ while (i < m && last < j + h[i] &&
+ x[h[i]] == y[j + h[i]])
+ i++;
+ if (i >= m || last >= j + h[i]) {
+ /* Match found, bail out */
+ match_pos = j;
+ i = m;
+ }
+ if (i > nd)
+ last = j + m - 1;
+ j += shift[i];
+ i = next[i];
+ }
+ return match_pos;
+}
+
+/* }}} */
+
+/* {{{ hardware access functions */
+
+/* call_bios_
+ *
+ * call request handler in mapped system rom
+ *
+ * the request is handed over via all 6 general purpose registers, results are
+ * taken from them and copied back to buf
+ */
+static asmlinkage void call_bios_6xx(struct register_buffer *buf)
+{
+ if (bios_routine) {
+ local_irq_disable();
+ __asm__ __volatile__(
+ "movl %1,%%edx\n\t"
+ "pusha\n\t"
+ "movl %%edx,%%ebp\n\t"
+ "movl (%%ebp),%%eax\n\t"
+ "movl 4(%%ebp),%%ebx\n\t"
+ "movl 8(%%ebp),%%ecx\n\t"
+ "movl 12(%%ebp),%%edx\n\t"
+ "movl 16(%%ebp),%%edi\n\t"
+ "movl 20(%%ebp),%%esi\n\t"
+ "pushl %%ebp\n\t"
+ "call *%0\n\t"
+ "popl %%ebp\n\t"
+ "movl %%eax, (%%ebp)\n\t"
+ "movl %%ebx, 4(%%ebp)\n\t"
+ "movl %%ecx, 8(%%ebp)\n\t"
+ "movl %%edx, 12(%%ebp)\n\t"
+ "movl %%edi, 16(%%ebp)\n\t"
+ "movl %%esi, 20(%%ebp)\n\t"
+ "popa\n\t"
+ :
+ :"m" (bios_routine), "m" (buf)
+ :"%eax", "%ebx", "%ecx", "%edx", "%edi", "%esi", "%ebp"
+ );
+ local_irq_enable();
+ }
+}
+
+static asmlinkage void call_bios_52x(struct register_buffer *buf)
+{
+ if (bios_routine) {
+ local_irq_disable();
+ __asm__ __volatile__(
+ "movl %2,%%edx\n\t"
+ "pusha\n\t"
+ "movl %%edx,%%ebp\n\t"
+ "movl (%%ebp),%%eax\n\t"
+ "movl 4(%%ebp),%%ebx\n\t"
+ "movl 8(%%ebp),%%ecx\n\t"
+ "movl 12(%%ebp),%%edx\n\t"
+ "movl 16(%%ebp),%%edi\n\t"
+ "movl 20(%%ebp),%%esi\n\t"
+ "pushl %%ebp\n\t"
+ "movl %1, %%ebp\n\t"
+ "call *%0\n\t"
+ "popl %%ebp\n\t"
+ "movl %%eax, (%%ebp)\n\t"
+ "movl %%ebx, 4(%%ebp)\n\t"
+ "movl %%ecx, 8(%%ebp)\n\t"
+ "movl %%edx, 12(%%ebp)\n\t"
+ "movl %%edi, 16(%%ebp)\n\t"
+ "movl %%esi, 20(%%ebp)\n\t"
+ "popa\n\t"
+ :
+ :"m" (bios_routine), "m" (preg400), "m" (buf)
+ :"%eax", "%ebx", "%ecx", "%edx", "%edi", "%esi", "%ebp"
+ );
+ local_irq_enable();
+ }
+}
+
+#define PRINT_BUFFER(x) \
+ printk(KERN_INFO"acerhk: eax=0x%x ebx=0x%x ecx=0x%x edx=0x%x\n" \
+ "acerhk: edi=0x%x esi=0x%x ebp=0x%x\n", \
+ x.eax, x.ebx, x.ecx, x.edx, x.edi, x.esi, x.ebp);
+
+/* get_fnkey_event
+ *
+ * gets the first (oldest) key id from the queue of events
+ *
+ * return value: id of key
+ */
+static int get_fnkey_event(void)
+{
+ struct register_buffer regs;
+ regs.eax = 0x9610;
+ regs.ebx = 0x61C;
+ /* clear other registers, some models need this */
+ regs.ecx = 0;
+ regs.edx = 0;
+ preempt_disable();
+ call_bios(®s);
+ preempt_enable_no_resched();
+ return regs.eax & 0xffff;
+}
+
+/* get_thermal_event
+ *
+ * does what?
+ *
+ * return value: event ?
+ */
+static int get_thermal_event(void)
+{
+ struct register_buffer regs;
+ if (acerhk_model_features & TM_F_THERMAL) {
+ regs.eax = 0x9612;
+ regs.ebx = 0x12e;
+ preempt_disable();
+ call_bios(®s);
+ preempt_enable_no_resched();
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: thermal event = 0x%x\n", regs.eax);
+ } else {
+ regs.eax = 0x00;
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: thermal event not supported\n");
+ }
+ return regs.eax & 0xffff;
+}
+
+#ifdef ACERDEBUG
+/* pbutton_fct
+ *
+ * does what?
+ *
+ * return value: ?
+ */
+static int pbutton_fct(void)
+{
+ struct register_buffer regs;
+ if (acerhk_model_features & TM_F_PBUTTON) {
+ regs.eax = 0x9612;
+ regs.ebx = 0x10b;
+ regs.ecx = 0x2;
+ preempt_disable();
+ call_bios(®s);
+ preempt_enable_no_resched();
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: pbutton = 0x%x\n", regs.eax);
+ } else {
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: pbutton function not supported\n");
+ regs.eax = 0x00;
+ }
+ return regs.eax & 0xffff;
+}
+#endif
+
+/* wbutton_fct_1
+ *
+ * turn on installed Bluetooth hardware together with the corresponding LED
+ *
+ * val: 0 turns off the LED
+ * 1 turns the LED to green/blue
+ *
+ * return value: ?
+ */
+static int wbutton_fct_1(int val)
+{
+ struct register_buffer regs;
+ if (acerhk_model_features & TM_F_WBUTTON) {
+ acerhk_bluetooth_state = val;
+ regs.eax = 0x9610;
+ regs.ebx = ((val & 0xff) << 8) | 0x34;
+ preempt_disable();
+ call_bios(®s);
+ preempt_enable_no_resched();
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: wbutton1 = 0x%x\n", regs.eax);
+ } else {
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: wbutton function 1 not supported\n");
+ regs.eax = 0x00;
+ }
+ return regs.eax & 0xffff;
+}
+
+/* wbutton_fct_2
+ *
+ * turn on installed WLAN hardware together with the corresponding LED
+ *
+ * val: 0 turns off the LED
+ * 1 turns the LED to orange
+ *
+ * return value: ?
+ */
+static int wbutton_fct_2(int val)
+{
+ struct register_buffer regs;
+ if (acerhk_model_features & TM_F_WBUTTON) {
+ acerhk_wlan_state = val;
+ regs.eax = 0x9610;
+ regs.ebx = ((val & 0xff) << 8) | 0x35;
+ preempt_disable();
+ call_bios(®s);
+ preempt_enable_no_resched();
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: wbutton2 = 0x%x\n", regs.eax);
+ } else {
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: wbutton function 2 not supported\n");
+ regs.eax = 0x00;
+ }
+ return regs.eax & 0xffff;
+}
+
+/* get_cmos_index
+ *
+ * gets index of CMOS port from ROM. The number of events is monitored
+ * in this port.
+ *
+ * return value: index of CMOS port
+ */
+static int get_cmos_index(void)
+{
+ struct register_buffer regs;
+ regs.eax = 0x9610;
+ regs.ebx = 0x51C;
+ preempt_disable();
+ call_bios(®s);
+ preempt_enable_no_resched();
+ cmos_index = regs.ecx & 0xff;
+ if (verbose)
+ printk(KERN_INFO"acerhk: cmos index set to 0x%x\n", cmos_index);
+ return cmos_index;
+}
+
+/* get_nr_events
+ *
+ * gets the number of cached events (keys pressed) in queue. Up to 31 events
+ * are cached.
+ *
+ * return value: number of events in queue
+ */
+static int get_nr_events(void)
+{
+ unsigned long flags;
+ unsigned char c = 0;
+
+ spin_lock_irqsave (&rtc_lock, flags);
+#ifndef DUMMYHW
+ if (cmos_index)
+ c = CMOS_READ(cmos_index);
+ else if (verbose > 3)
+ printk(KERN_INFO"acerhk: get_nr_events - no valid cmos index set\n");
+#endif
+ spin_unlock_irqrestore (&rtc_lock, flags);
+ return c;
+}
+
+/* set_mail_led
+ *
+ * change state of mail led
+ *
+ * val: 0 - switch led off
+ * 1 - switch led on (blinking)
+ *
+ * return value: 1 - action succesfull (val valid)
+ * 0 - no action taken (val invalid)
+ */
+static int set_mail_led(int val)
+{
+ struct register_buffer regs;
+ if (acerhk_model_features & TM_F_MAIL_LED) {
+ regs.eax = 0x9610;
+ regs.ebx = ((val & 0xff) << 8) | 0x31;
+ preempt_disable();
+ call_bios(®s);
+ preempt_enable_no_resched();
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: mail led set to = 0x%x\n", val);
+ } else if (acerhk_model_features & TM_F_MAIL_LED_EC) {
+ if (val == 1)
+ enable_mail_led_ec_1();
+ else if (val == 0)
+ disable_mail_led_ec_1();
+ } else if (acerhk_model_features & TM_F_MAIL_LED_EC2) {
+ if (val == 1)
+ enable_mail_led_ec_2();
+ else if (val == 0)
+ disable_mail_led_ec_2();
+ } else if (acerhk_model_features & TM_F_MAIL_LED_EC3) {
+ if (val == 1)
+ enable_mail_led_ec_3();
+ else if (val == 0)
+ disable_mail_led_ec_3();
+ } else {
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: mail led not supported\n");
+ regs.eax = 0x00;
+ }
+ return regs.eax & 0xffff;
+}
+
+/* launch_connect
+ *
+ * does what?
+ * val: 1 - only known value from windows driver
+ */
+static int launch_connect(int val)
+{
+ struct register_buffer regs;
+ if (acerhk_model_features & TM_F_CONNECT) {
+ regs.eax = 0x9610;
+ regs.ebx = ((val & 0xff) << 8) | 0x2e;
+ preempt_disable();
+ call_bios(®s);
+ preempt_enable_no_resched();
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: connect(%d) = 0x%x\n", val, regs.eax);
+ } else {
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: connect not supported\n");
+ regs.eax = 0x00;
+ }
+ return regs.eax & 0xffff;
+}
+
+/* }}} */
+
+/* {{{ hardware probing */
+
+static struct proc_dir_entry *proc_acer_dir;
+
+static unsigned int __init find_hk_area(void)
+{
+ int offset, sig;
+ unsigned int fkt;
+ fkt = 0;
+ sig = -1; /* offset to signature in io area */
+ /* Look for signature, start at 0xf0000, search until 0xffff0 */
+ for (offset = 0;offset < 0xfffd; offset += 16) {
+ if (readl(reg1 + offset) == 0x30552142) {
+ sig = offset;
+ offset = 0xffff;
+ }
+ }
+ if (sig < 0)
+ printk(KERN_WARNING"acerhk: could not find request handler, possibly not all functions available\n");
+ else {
+ /* compute location of bios routine */
+ fkt = readl(reg1 + sig + 5);
+ /* adjust fkt to address of mapped IO area */
+ if (fkt >= 0xf0000)
+ fkt = (unsigned int)reg1 + fkt - 0xf0000;
+ else if (fkt >= 0xe0000)
+ fkt = (unsigned int)reg1 + fkt - 0xe0000;
+ else
+ fkt = 0;
+ }
+ return fkt;
+}
+
+static void print_features(void)
+{
+ int i;
+ printk(KERN_INFO"acerhk: supported keys:");
+ for (i = 0; i < 255; i++) {
+ switch (acerhk_key2name[i]) {
+ case k_help: printk(" help"); break;
+ case k_setup: printk(" setup"); break;
+ case k_p1: printk(" p1"); break;
+ case k_p2: printk(" p2"); break;
+ case k_p3: printk(" p3"); break;
+ case k_www: printk(" www"); break;
+ case k_mail: printk(" mail"); break;
+ case k_wireless: printk(" wireless"); break;
+ case k_power: printk(" power"); break;
+ case k_mute: printk(" mute"); break;
+ case k_volup: printk(" volup"); break;
+ case k_voldn: printk(" voldn"); break;
+ case k_res: printk(" res"); break;
+ case k_close: printk(" close"); break;
+ case k_open: printk(" open"); break;
+ case k_wireless2: printk(" wireless2"); break;
+ case k_play: printk(" play"); break;
+ case k_stop: printk(" stop"); break;
+ case k_prev: printk(" prev"); break;
+ case k_next: printk(" next"); break;
+ case k_display: printk(" display"); break;
+ default: break;
+ }
+ }
+ printk("\n");
+ if (acerhk_model_features & TM_F_MUTE_LED_EC)
+ printk(KERN_INFO"acerhk: mute led is supported\n");
+ if (acerhk_model_features & TM_F_MAIL_LED)
+ printk(KERN_INFO"acerhk: mail led is supported\n");
+ else if (acerhk_model_features & TM_F_MAIL_LED_EC)
+ printk(KERN_INFO"acerhk: mail led (EC) is supported\n");
+ else if (acerhk_model_features & TM_F_MAIL_LED_EC2)
+ printk(KERN_INFO"acerhk: mail led (EC2) is supported\n");
+ else if (acerhk_model_features & TM_F_MAIL_LED_EC3)
+ printk(KERN_INFO"acerhk: mail led (EC3) is supported\n");
+ printk(KERN_INFO"acerhk: supported functions:");
+ if (acerhk_model_features & TM_F_CONNECT)
+ printk(" connect");
+ if (acerhk_model_features & TM_F_THERMAL)
+ printk(" thermal");
+ if (acerhk_model_features & TM_F_PBUTTON)
+ printk(" pbutton");
+ if (acerhk_model_features & TM_F_WBUTTON)
+ printk(" wbutton");
+ printk("\n");
+}
+
+static void __init setup_keymap_model(unsigned int series)
+{
+ /* clear mapping keycode -> keyname, */
+ memset(&acerhk_key2name[0], k_none, sizeof(acerhk_key2name));
+ /* first set the common keys, namely FnF1 and FnF2, */
+ acerhk_key2name[1] = k_help;
+ acerhk_key2name[2] = k_setup;
+ /* then set known keycodes according to model */
+ switch (series) {
+ case 110:
+ acerhk_key2name[48] = k_wireless;
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[3] = k_power;
+ acerhk_key2name[8] = k_mute;
+ acerhk_key2name[32] = k_volup;
+ acerhk_key2name[33] = k_voldn;
+ /* C110 generates 2 extra codes when opening/closing the lid */
+ acerhk_key2name[74] = k_close;
+ acerhk_key2name[75] = k_open;
+ break;
+ case 300: /* treat C300 like C100 with Bluetooth button */
+ acerhk_key2name[68] = k_wireless2;
+ case 100:
+ acerhk_key2name[48] = k_wireless;
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[49] = k_www;
+ acerhk_key2name[54] = k_mail;
+ acerhk_key2name[3] = k_power;
+ acerhk_key2name[8] = k_mute;
+ acerhk_key2name[32] = k_volup;
+ acerhk_key2name[33] = k_voldn;
+ break;
+ default:
+ /* only the two common keys are supported */
+ break;
+ case 210:
+ acerhk_key2name[19] = k_p1;
+ acerhk_key2name[20] = k_p2;
+ acerhk_key2name[17] = k_www;
+ acerhk_key2name[18] = k_mail;
+ break;
+ case 220:
+ case 260: /* 260 with same keys? */
+ acerhk_key2name[49] = k_p1;
+ acerhk_key2name[19] = k_p2;
+ acerhk_key2name[18] = k_www;
+ acerhk_key2name[17] = k_mail;
+ break;
+ case 230:
+ case 280: /* 280 with same keys? */
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[54] = k_www;
+ break;
+ case 1500:
+ acerhk_key2name[0x49] = k_setup;
+ acerhk_key2name[0x36] = k_www;
+ acerhk_key2name[0x31] = k_mail;
+ acerhk_key2name[0x11] = k_p1;
+ acerhk_key2name[0x12] = k_p2;
+ acerhk_key2name[0x30] = k_wireless;
+ acerhk_key2name[0x44] = k_wireless2;
+ acerhk_key2name[0x03] = k_power;
+ break;
+ case 240:
+ acerhk_key2name[0x31] = k_www;
+ acerhk_key2name[0x36] = k_mail;
+ acerhk_key2name[0x11] = k_p1;
+ acerhk_key2name[0x12] = k_p2;
+ acerhk_key2name[0x44] = k_wireless;
+ acerhk_key2name[0x30] = k_wireless2;
+ acerhk_key2name[0x03] = k_power;
+ acerhk_key2name[0x08] = k_mute;
+ // acerhk_key2name[] = k_volup;
+ // acerhk_key2name[] = k_voldn;
+ break;
+ case 2900:
+ acerhk_key2name[0x31] = k_mail; /* with led */
+ acerhk_key2name[0x36] = k_www;
+ acerhk_key2name[0x11] = k_p1;
+ acerhk_key2name[0x12] = k_p2;
+ acerhk_key2name[0x30] = k_wireless; /* wireless, with led, related with autowlan=1 */
+ break;
+ case 250: /* enriqueg@altern.org */
+ /* TravelMate 254LMi_DT manual common for 240/250 series, but key order
+ differ from 240 already present on acerhk driver */
+ /* TravelMate 254LMi_DT: 6 buttons: left to right: mail, www, p1, p2, bluetooth, wireless */
+ acerhk_key2name[0x31] = k_mail; /* with led */
+ acerhk_key2name[0x36] = k_www;
+ acerhk_key2name[0x11] = k_p1;
+ acerhk_key2name[0x12] = k_p2;
+ acerhk_key2name[0x44] = k_wireless2; /* bluetooth, hw optional */
+ acerhk_key2name[0x30] = k_wireless; /* wireless, with led, related with autowlan=1 */
+ acerhk_key2name[0x03] = k_power; /* Fn+F3 */
+ acerhk_key2name[0x08] = k_mute; /* Fn+F8 */
+ break;
+ case 380:
+ /* TM 380 has same codes as TM 370, with an additional one */
+ acerhk_key2name[0x03] = k_power;
+ case 370:
+ acerhk_key2name[0x30] = k_wireless;
+ acerhk_key2name[0x11] = k_p1;
+ acerhk_key2name[0x12] = k_p2;
+ acerhk_key2name[0x13] = k_p3;
+ acerhk_key2name[0x36] = k_www;
+ acerhk_key2name[0x31] = k_mail;
+ break;
+ case 360:
+ /* 360 series has the same layout as 350, with an
+ additional wireless key */
+ acerhk_key2name[64] = k_wireless;
+ case 350:
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[20] = k_p3;
+ acerhk_key2name[21] = k_www;
+ acerhk_key2name[19] = k_mail;
+ break;
+ case 520:
+ acerhk_key2name[19] = k_p1;
+ acerhk_key2name[20] = k_p2;
+ acerhk_key2name[17] = k_www;
+ acerhk_key2name[18] = k_mail;
+ break;
+ case 610:
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[19] = k_p3;
+ acerhk_key2name[21] = k_www;
+ acerhk_key2name[20] = k_mail;
+ acerhk_key2name[64] = k_wireless;
+ break;
+ case 630:
+ /* 630 has all keys of 620 plus one */
+ acerhk_key2name[8] = k_mute;
+ case 620:
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[19] = k_p3;
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[48] = k_wireless;
+ acerhk_key2name[3] = k_power;
+ acerhk_key2name[32] = k_volup;
+ acerhk_key2name[33] = k_voldn;
+ break;
+ case 290:
+ case 420:
+ case 430:
+ case 530:
+ case 540:
+ case 650:
+ case 660:
+ case 800:
+ case 1450:
+ case 2300:
+ case 2350:
+ case 4000:
+ case 4050:
+ case 6000:
+ case 8000:
+ case 4100:
+ case 4150:
+ case 4500:
+ case 4600:
+ case 4650:
+ case 1680:
+ case 1690:
+ /* keys are handled by dritek EC */
+ acerhk_key2name[1] = k_none;
+ acerhk_key2name[2] = k_none;
+ break;
+ case 1300:
+ case 1310:
+ case 1350:
+ case 1360:
+ case 1400:
+ case 1700:
+ case 1800:
+ case 2000:
+ case 2010:
+ case 2020:
+ /* Aspire 13xx series laptops use dritek hardware, no
+ acerhk-mapping needed
+ VolUp and VolDown are managed as normal keys
+ 1300/1310 series should have P1, P2, Mail, WWW, Mute buttons
+ 1353 has bluetooth, wifi, p1, p2, www, mail, help, setup, power
+ and mute
+ Aspire 1400/1450/Ferrari use dritek EC, too
+ 1450 should have bluetooth, wifi, p1, p2, www, mail, help,
+ setup, power and mute
+ Aspire 1700 uses dritek EC, too
+ 1700 should have bluetooth, wifi, p1, p2, www, mail, help,
+ setup, power and mute
+ need the MM-buttons Activation? (forward, shuffle, ...)
+ 2000 hast lots of MM buttons
+ 2010 should have bluetooth, wifi, p1, p2, www, mail, help,
+ setup, power and mute
+ */
+ acerhk_key2name[1] = k_none;
+ acerhk_key2name[2] = k_none;
+ break;
+ case 1600:
+ /* Aspire 1600 has acer keycode 0x49 for FnF2 */
+ acerhk_key2name[73] = k_setup;
+ acerhk_key2name[48] = k_wireless;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[19] = k_p3;
+ acerhk_key2name[3] = k_power;
+ acerhk_key2name[8] = k_mute;
+ /* VolUp and VolDown keys doesn't seem to be managed as special keys
+ but as normal keys ! */
+ break;
+ case 5020: /* Aspire 5020 has 0x6a for Fn+F2 */
+ acerhk_key2name[2] = k_none;
+ acerhk_key2name[106] = k_setup;
+ acerhk_key2name[3] = k_power;
+ acerhk_key2name[5] = k_display;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[48] = k_wireless;
+ acerhk_key2name[68] = k_wireless2;
+ break;
+ case 2410: /* TM 2410 is very similar to Aspire 5020, but has 0x6s for Fn-F3 */
+ acerhk_key2name[2] = k_none;
+ acerhk_key2name[106] = k_setup;
+ acerhk_key2name[109] = k_power;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[48] = k_wireless;
+ acerhk_key2name[68] = k_wireless2;
+ break;
+ case 40100:
+ /* Medion MD40100, 4 keys */
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[48] = k_wireless;
+ acerhk_key2name[55] = k_res;
+ break;
+ case 96500:
+ case 95400:
+ /* Medion MD95400, many keys */
+ acerhk_key2name[49] = k_mail; /* 1 */
+ acerhk_key2name[54] = k_www; /* 2 */
+ acerhk_key2name[48] = k_wireless; /* 3 */
+ acerhk_key2name[68] = k_wireless2; /* 4 (Bluetooth) */
+
+ acerhk_key2name[17] = k_p1; /* 5 */
+ acerhk_key2name[18] = k_p2; /* 6 */
+ acerhk_key2name[36] = k_play; /* 7 */
+ acerhk_key2name[37] = k_stop; /* 8 */
+ acerhk_key2name[34] = k_prev; /* 9 */
+ acerhk_key2name[35] = k_next; /* 10 */
+ acerhk_key2name[33] = k_voldn; /* 11 */
+ acerhk_key2name[32] = k_volup; /* 12 */
+ acerhk_key2name[38] = k_p3; /* 13 */
+ acerhk_key2name[8] = k_mute; /* 14 */
+
+ acerhk_key2name[1] = k_help; /* FN+F1 (Help) */
+ acerhk_key2name[5] = k_display; /* FN+F3 (Display switch) */
+ acerhk_key2name[6] = k_res; /* FN+F4 (Display ein/ausschalten) */
+ break;
+ case 42200:
+ /* Medion MD42200, 7 keys, no setup */
+ acerhk_key2name[2] = k_none;
+ acerhk_key2name[5] = k_display;
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[48] = k_wireless;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ break;
+ case 9783:
+ /* Medion MD9783, 6 keys + info, no setup */
+ acerhk_key2name[2] = k_none;
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[19] = k_p3;
+ acerhk_key2name[8] = k_mute;
+ break;
+ case 7400:
+ /* Amilo Pro V2000 does not have Help and Setup key (?)
+ Amilo M 7400 has Help key, disabling only setup
+ */
+ acerhk_key2name[2] = k_none;
+ acerhk_key2name[48] = k_wireless;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ break;
+ case 1559:
+ acerhk_key2name[6] = k_display; /* FN+F4 (Display ein/ausschalten) */
+ case 1555:
+ /* AOpen (Ahtec Signal 1555M) is similar to FS Amilo M */
+ acerhk_key2name[2] = k_none;
+ acerhk_key2name[48] = k_wireless;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[34] = k_prev;
+ acerhk_key2name[35] = k_next;
+ acerhk_key2name[36] = k_play;
+ acerhk_key2name[37] = k_stop;
+ break;
+ case 6800:
+ case 7820:
+ /* Amilo D does not have Setup key */
+ acerhk_key2name[2] = k_none;
+ acerhk_key2name[49] = k_mail;
+ acerhk_key2name[54] = k_www;
+ acerhk_key2name[17] = k_p1;
+ acerhk_key2name[18] = k_p2;
+ acerhk_key2name[19] = k_p3;
+ acerhk_key2name[8] = k_mute;
+ break;
+ }
+}
+
+static void __init setup_model_features(unsigned int series)
+{
+ switch (series) {
+ case 200:
+ case 210:
+ case 520:
+ /* nothing special */
+ acerhk_model_features = 0;
+ acerhk_type = TM_old;
+ break;
+ case 220:
+ case 230:
+ case 260:
+ case 280:
+ case 360:
+ case 40100: /* Medion MD40100 */
+ case 95400: /* Medion MD95400 */
+ case 96500: /* Medion MD96500 */
+ /* all special functions, no mail led */
+ acerhk_model_features = 0x00f00000;
+ acerhk_type = TM_new;
+ break;
+ case 42200: /* Medion MD42200 */
+ /* has WLAN button, should call connect() */
+ acerhk_model_features = TM_F_WBUTTON | TM_F_CONNECT;
+ acerhk_type = TM_old;
+ break;
+ case 9783: /* Medion MD9783 */
+ /* only email led */
+ acerhk_model_features = TM_F_MAIL_LED;
+ acerhk_type = TM_new;
+ break;
+ case 1600:
+ acerhk_type = TM_new;
+ /* Do Aspire 1600 series have special functions or not ? I enable
+ them, perhaps it helps with problems Francois Valenduc has */
+ acerhk_model_features = 0x00f00000;
+ break;
+ case 300:
+ case 100:
+ case 110:
+ case 240:
+ case 350:
+ case 610:
+ case 620:
+ case 630:
+ /* all special functions, mail led */
+ acerhk_model_features = TM_F_MAIL_LED | 0x00f00000;
+ acerhk_type = TM_new;
+ break;
+ case 370:
+ case 380:
+ case 2410:
+ case 2900: /* Medion MD2900 */
+ case 2100: /* TM 2100 uses same driver as 5020 */
+ case 5020: /* Aspire 5020 is still old hardware */
+ acerhk_model_features = TM_F_MAIL_LED | TM_F_CONNECT| TM_F_WBUTTON;
+ acerhk_type = TM_new;
+ break;
+ case 7400:
+ case 1555:
+ case 1559:
+ /* all special functions for Fujitsu-Siemens Amilo M7400, Pro V2000; AOpen */
+ acerhk_model_features = 0x00f00000;
+ acerhk_type = TM_new;
+ break;
+ case 6800:
+ case 7820:
+ /* mail led and all special functions for FS Amilo D */
+ acerhk_model_features = TM_F_MAIL_LED | 0x00f00000;
+ acerhk_type = TM_new;
+ break;
+ case 2350:
+ case 4050:
+ acerhk_wlan_state = 1; // Default state is on
+ case 290:
+ /* no special functions, wireless hardware controlled by EC */
+ acerhk_model_features = TM_F_WLAN_EC2 | TM_F_BLUE_EC2;
+ acerhk_type = TM_dritek;
+ break;
+ case 650:
+ case 1300:
+ case 1310:
+ case 1400:
+ case 1700:
+ /* all special functions, wireless hardware can be controlled */
+ acerhk_model_features = 0x00f00000;
+ acerhk_type = TM_dritek;
+ break;
+ case 4100:
+ case 4600:
+ case 1680:
+ case 1690: /* Aspire 1680/1690 should be similar to TM 4100/4600 */
+ /* mail led, wireless and bluetooth controlled the old way, but keys are
+ controlled by normal keyboard controller, so mark as dritek and
+ deactivate dritek use */
+ acerhk_model_features = TM_F_MAIL_LED | TM_F_WBUTTON;
+ acerhk_type = TM_dritek;
+ usedritek=0;
+ break;
+ case 660:
+ case 800:
+ /* all special functions, mail led */
+ acerhk_model_features = TM_F_MAIL_LED | 0x00f00000;
+ acerhk_type = TM_dritek;
+ break;
+ case 1350:
+ case 1360:
+ /* mail led, handled by EC, wireless HW is not (yet) controllable ? */
+ acerhk_model_features = TM_F_MAIL_LED_EC|TM_F_WLAN_EC1;
+ acerhk_type = TM_dritek;
+ break;
+ case 1450:
+ /* Bluetooth/Wlan led, Mail led handled by EC (variant 3) */
+ acerhk_model_features = TM_F_MAIL_LED_EC3|TM_F_WBUTTON;
+ acerhk_type = TM_dritek;
+ break;
+ case 1500:
+ /* Bluetooth/Wlan led */
+ acerhk_model_features = TM_F_WBUTTON;
+ acerhk_type = TM_new;
+ break;
+ case 420:
+ case 430:
+ /* all functions and dritek EC, mail LED is handled by EC, second
+ variant. An additional led is available, mute. (really?)
+ */
+ acerhk_type = TM_dritek;
+ acerhk_model_features = TM_F_MUTE_LED_EC|TM_F_MAIL_LED_EC2;
+ break;
+ case 2300:
+ case 4000:
+ case 4500:
+ /* wireless hardware, hopefully under control of my driver */
+ acerhk_type = TM_dritek;
+ acerhk_model_features = TM_F_BLUE_EC1|TM_F_WLAN_EC1;
+ break;
+ case 3200:
+ /* test, if this model uses old style wlan control */
+ acerhk_model_features = TM_F_WBUTTON;
+ acerhk_type = TM_dritek;
+ break;
+ case 6000:
+ case 8000:
+ /* 6000 and 8000 have wireless hardware, but I don't know how to handle,
+ so I choose no features */
+ acerhk_type = TM_dritek;
+ break;
+ case 530:
+ case 540:
+ case 2000:
+ /* No features (?) dritek EC, mail LED is handled by EC but
+ different from other Aspire series */
+ acerhk_type = TM_dritek;
+ acerhk_model_features = TM_F_MAIL_LED_EC2;
+ break;
+ case 4150:
+ case 4650:
+ /* Dritek EC, bluetooth, wifi, mail */
+ /* According to Andreas Stumpfl his TM 4652LMi does also work as series
+ 3200, which might mean that the BIOS function accesses the EC */
+ acerhk_type = TM_dritek;
+ acerhk_model_features = TM_F_MAIL_LED_EC2 | TM_F_WLAN_EC2 | TM_F_BLUE_EC2;
+ break;
+ case 1800:
+ case 2010:
+ case 2020:
+ /* Dritek EC, bluetooth, wifi, mail */
+ acerhk_type = TM_dritek;
+ acerhk_model_features = TM_F_MAIL_LED_EC2 | TM_F_WLAN_EC2 | TM_F_BLUE_EC2;
+ acerhk_wlan_state = 1; // Default state is on
+ break;
+ case 250: /* enriqueg@altern.org */
+ /* TravelMate254LMi_DT : mail led, bluetooth (button present, hw optional), wifi (with led) */
+ acerhk_model_features = TM_F_MAIL_LED|
+ TM_F_WBUTTON ;
+ acerhk_type = TM_new;
+ acerhk_wlan_state = 0; //Initial state is off on 254LMi_DT
+ break;
+ default:
+ /* nothing special */
+ acerhk_model_features = 0;
+ acerhk_type = TM_unknown;
+ break;
+ }
+ /* set the correct bios call function according to type */
+ if ((acerhk_type == TM_new) || (acerhk_type == TM_dritek)) {
+ call_bios = call_bios_6xx;
+ if (verbose > 2)
+ printk(KERN_INFO"acerhk: using call_bios_6xx mode\n");
+ } else {
+ call_bios = call_bios_52x;
+ if (verbose > 2)
+ printk(KERN_INFO"acerhk: using call_bios_52x mode\n");
+ }
+ /* remove key file on dritek hardware */
+ if (acerhk_type == TM_dritek) {
+ remove_proc_entry("key", proc_acer_dir);
+ }
+ /* setup available keys */
+ setup_keymap_model(acerhk_series);
+ if (verbose > 1)
+ print_features();
+}
+
+static unsigned int __init determine_laptop_series(char * str)
+{
+ /* 0 means unknown series, handled like TM 200 */
+ unsigned int series = 0;
+ if (strncmp(str, "TravelMate ", 11) == 0) {
+ switch (str[11]) {
+ case 'C':
+ if (str[12] == '1') {
+ if (str[13] == '0') {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates TM C100 series\n");
+ series = 100;
+ } else if (str[13] == '1') {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates TM C110 series\n");
+ series = 110;
+ }
+ } else if (str[12] == '3') {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates TM C300 series\n");
+ series = 300;
+ }
+ break;
+ case 'F':
+ if (str[12] == '4') {
+ series = 230;
+ }
+ break;
+ case '2':
+ if (str[14] == '0') {
+ /* newer Travelmate 2xxx series */
+ switch (str[12]) {
+ case '0':
+ case '5':
+ series = 2000; // 2000 and 2500 are the same
+ break;
+ case '1':
+ if (str[13] == '0')
+ series = 2100;
+ break;
+ case '2':
+ case '7':
+ series = 2200; // 2200 and 2700 are the same
+ break;
+ case '3':
+ if (str[13] == '0')
+ series = 4000; // 2300 is the same as 4000
+ else if (str[13] == '5')
+ series = 4050; // 2350 is the same as 4050
+ break;
+ case '4':
+ if (str[13] == '1')
+ series = 2410;
+ break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM 2xxx series\n");
+ break;
+ }
+ } else {
+ /* older Travelmate 2xx series */
+ switch (str[12]) {
+ case '0': series = 200; break;
+ case '1': series = 210; break;
+ case '2': series = 220; break;
+ case '4': series = 240; break;
+ case '5': series = 250; break; /* enriqueg@altern.org */
+ case '6': series = 260; break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM 2xx series\n");
+ break;
+ }
+ }
+ break;
+ case '3':
+ switch (str[12]) {
+ case '0': series = 3200; break; /* TM 3000 works like TM 3200 */
+ /* Travelmate 3xx series */
+ case '5': series = 350; break;
+ case '6': series = 360; break;
+ case '7': series = 370; break;
+ case '8': series = 380; break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM 3xx series\n");
+ break;
+ }
+ break;
+ case '4':
+ if ( (strnlen(str, ACERHK_MODEL_STRLEN-1) == 15) &&
+ (str[14] == '0') ) { /* Travelmate 4xxx series */
+ switch (str[12]) {
+ case '0': /* 4000 and 4500 are the same */
+ case '5':
+ series = 4000;
+ break;
+ case '1':
+ case '6': /* 4100 and 4600 are the same */
+ series = 4100;
+ break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM 4xxx series\n");
+ break;
+ }
+ } else { /* Travelmate 4xx series */
+ switch (str[12]) {
+ case '2': series = 420; break;
+ case '3': series = 430; break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM 4xx series\n");
+ break;
+ }
+ }
+ break;
+ case '5': /* Travelmate 5xx series */
+ if (str[12] == '2')
+ series = 520;
+ else if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM 5xx series\n");
+ break;
+ case '6': /* older Travelmate 6xx series */
+ switch (str[12]) {
+ case '1': series = 610; break;
+ case '2': series = 620; break;
+ case '3': series = 630; break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM 6xx series\n");
+ break;
+ }
+ break;
+ default:
+ printk(KERN_INFO"acerhk: model string indicates unknown TM xxx series\n");
+ break;
+ }
+ if (series && verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates TM %d series\n", series);
+ }
+ /* newer Travelmate series do not have a space after 'TravelMate' */
+ else if (strncmp(str, "TravelMate", 10) == 0) {
+ switch (str[10]) {
+ case '2':
+ if (str[11] == '9') {
+ series = 290;
+ } else {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM2xx series\n");
+ }
+ break;
+ case '3':
+ if (str[11] == '2' && str[14] == '3') {
+ // TM 3200 uses "TravelMate32003"
+ series = 3200;
+ } else {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM3xxx series\n");
+ }
+ break;
+ case '4':
+ switch (str[11]) {
+ case '3': series = 430; break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM4xx series\n");
+ break;
+ }
+ break;
+ case '5':
+ switch (str[11]) {
+ case '3': series = 530; break;
+ case '4': series = 540; break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM5xx series\n");
+ break;
+ }
+ break;
+ case '6':
+ switch (str[11]) {
+ case '5': series = 650; break;
+ case '6': series = 660; break;
+ case '0':
+ if (strncmp(str, "TravelMate60003", 15) == 0) {
+ series = 6000; break;
+ }
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM6xx series\n");
+ break;
+ }
+ break;
+ case '8':
+ if (strncmp(str, "TravelMate80003", 15) == 0) {
+ series = 8000;
+ } else if (str[11] == '0') {
+ series = 800;
+ } else {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown TM8xx series\n");
+ }
+ break;
+ default:
+ printk(KERN_INFO"acerhk: model string indicates unknown TMxxx series\n");
+ break;
+ }
+ if (series && verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates TM%d series\n", series);
+ }
+ else if (strncmp(str, "Aspire ", 7) == 0) {
+ switch(str[7]) {
+ case '1': /* Aspire 1xxx series */
+ switch(str[8]) {
+ case '3': /* Aspire 13xx series */
+ switch (str[9]) {
+ case '0': series = 1300; break;
+ case '1': series = 1310; break;
+ case '5': series = 1350; break;
+ case '6': series = 1360; break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 13xx series\n");
+ break;
+ }
+ break;
+ case '4': /* Aspire 14xx series */
+ switch (str[9]) {
+ case '0': series = 1400; break;
+ case '5': series = 1450; break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 14xx series\n");
+ break;
+ }
+ break;
+ case '5': series = 1500; break;
+ case '6': /* Aspire 14xx series */
+ switch (str[9]) {
+ case '0': series = 1600; break;
+ case '8':
+ case '9': series = 1680; break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 16xx series\n");
+ break;
+ }
+ break;
+ case '7': series = 1700; break;
+ case '8': series = 1800; break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 1xxx series\n");
+ break;
+ }
+ break;
+ case '2': /* Aspire 2xxx series */
+ if (str[8] == '0') {
+ switch (str[9]) {
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 20xx series\n");
+ break;
+ case '0': series = 2000; break;
+ case '1': series = 2010; break;
+ case '2': series = 2020; break;
+ }
+ } else {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 2xxx series\n");
+ }
+ break;
+ case '3': /* Aspire 3xxx series */
+ if (str[8] == '0') {
+ switch (str[9]) {
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 30xx series\n");
+ break;
+ case '2': series = 5020; break; /* Aspire 3020/5020 are identical */
+ }
+ } else {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 3xxx series\n");
+ }
+ break;
+ case '5': /* Aspire 5xxx series */
+ if (str[8] == '0') {
+ switch (str[9]) {
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 50xx series\n");
+ break;
+ case '2': series = 5020; break;
+ }
+ } else {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire 5xxx series\n");
+ }
+ break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Aspire series\n");
+ break;
+ }
+ if (series && verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates Aspire %d series\n", series);
+ }
+ else if (strncmp(str, "Extensa ", 8) == 0) {
+ /* Extensa series */
+ switch (str[8]) {
+ case '3':
+ switch (str[9]) {
+ case '0':
+ series = 3000; break;
+ default: break;
+ }
+ break;
+ default: break;
+ }
+ if (series && verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates Extensa %d series\n", series);
+ else if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown Extensa series\n");
+ }
+ else if (strncmp(str, "Amilo ", 6) == 0) {
+ switch (str[6]) {
+ case 'D': /* complete string is "Amilo D-Series", there seems to be no model number */
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates FS Amilo D series\n");
+ /* this is the model number of my Amilo */
+ series = 7820;
+ break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown FS Amilo XX series\n");
+ series = 7820;
+ }
+ }
+ else if (strncmp(str, "AMILO ", 6) == 0) {
+ switch (str[6]) {
+ case 'D': /* AMILO D 6800 P4-2000 */
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates FS AMILO D series\n");
+ series = 6800;
+ break;
+ case 'M':
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates FS AMILO M(7400) series\n");
+ series = 7400;
+ break;
+ case 'P':
+ /* it is assumed, that 'AMILO P' appears only on Amilo Pro Series */
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates FS AMILO Pro (V2000) series\n");
+ series = 7400;
+ break;
+ default:
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates unknown FS AMILO XX series\n");
+ series = 6800;
+ }
+ }
+ else if (strncmp(str, "MEDIONPC", 8) == 0) {
+ uint medionmodel;
+ if ((medionmodel = COLUSSI("WIM 2040", 4, reg1, AREA_SIZE)) >= 0) {
+ printk(KERN_INFO"acerhk: found Medion model string:'%s'\n", (char*)reg1+medionmodel);
+ series = 96500;
+ } else {
+ if ((medionmodel = COLUSSI("MD 9", 4, reg1, AREA_SIZE)) >= 0) {
+ printk(KERN_INFO"acerhk: found Medion model string:'%s'\n", (char*)reg1+medionmodel);
+ }
+ series = 95400;
+ }
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates a medion MD %d\n", series);
+ }
+ else if (strncmp(str, "MEDIONNB", 8) == 0) {
+ /* Search for the Product string of the MD9783. */
+ if (COLUSSI("MD 42200", 8, reg1, AREA_SIZE) >= 0) {
+ if (verbose>1)
+ printk(KERN_INFO"acerhk: model string indicates a Medion MD 42200\n");
+ series = 42200;
+ } else if (COLUSSI("MD 9783", 7, reg1, AREA_SIZE) >= 0){
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates a medion MD 9783\n");
+ series = 9783;
+ } else if (COLUSSI("WIM 2000", 7, reg1, AREA_SIZE) >= 0){
+ if (verbose>1)
+ printk(KERN_INFO"acerhk: model string indicates a Medion MD 2900\n");
+ series = 2900;
+ } else {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates a medion MD40100\n");
+ series = 40100;
+ }
+ } else if (strncmp(str, "AOpen", 5) == 0) {
+ if (strncmp(str, "AOpen*EzRestore", 15) == 0) {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates a AOpen 1559\n");
+ series = 1559;
+ } else {
+ /* Unless I know of other models no further differentiation,
+ although there is a second part of the model string */
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates a AOpen\n");
+ series = 1555;
+ }
+ } else if (strncmp(str, "CL56", 4) == 0) {
+ /* Unless I know of other models no further differentiation,
+ although there are strings with more numbers ("CL561" on a Compal
+ CL56/Zepto 4200, reported by Stian B. Barmen)
+ It has the same functions as Acer Aspire 2010
+ */
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates a Compal CL56 (or similar)\n");
+ series = 2010;
+ } else if (strncmp(str, "Geneva2", 7) == 0) {
+ /* This might be an Aspire 9110 which is very similar to 4650 */
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates an Aspire 9110\n");
+ series = 4650;
+ } else {
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: model string indicates no supported hardware\n");
+ }
+ return (series);
+}
+
+static void __init probe_model(void) {
+ int offset; /* offset from beginning of reg1 to Model string */
+ if (verbose)
+ printk(KERN_INFO"acerhk: start search for model string at %p\n", reg1);
+ /* first we look for Travelmate, if it isn't one we try to identify other
+ laptops, such as Medion or Aspire */
+ offset = COLUSSI("Travel", 6, reg1, AREA_SIZE);
+ /* Try to detect Aspire laptops */
+ if (offset < 0)
+ offset = COLUSSI("Aspire", 6, reg1, AREA_SIZE);
+ /* Try to detect Extensa laptops */
+ if (offset < 0)
+ offset = COLUSSI("Extensa", 7, reg1, AREA_SIZE);
+ /* Try to detect Medion laptops */
+ if (offset < 0)
+ offset = COLUSSI("MEDION", 6, reg1, AREA_SIZE);
+ /* Try to detect AOpen laptops */
+ if (offset < 0)
+ offset = COLUSSI("AOpen", 5, reg1, AREA_SIZE);
+ /* Try to detect Fujitsu Siemens Amilo laptops */
+ if (offset < 0)
+ offset = COLUSSI("Amilo", 5, reg1, AREA_SIZE);
+ if (offset < 0)
+ offset = COLUSSI("AMILO", 5, reg1, AREA_SIZE);
+ /* Try to detect Compal */
+ if (offset < 0)
+ offset = COLUSSI("CL56", 4, reg1, AREA_SIZE);
+ /* That might be an Aspire 9110 */
+ if (offset < 0)
+ offset = COLUSSI("Geneva2", 7, reg1, AREA_SIZE);
+ if (offset >= 0) {
+ acerhk_model_addr = reg1 + offset;
+ /* copy the string, but not more than 15 characters */
+ strncpy(acerhk_model_string, acerhk_model_addr, ACERHK_MODEL_STRLEN-1);
+ if (verbose)
+ printk(KERN_INFO"acerhk: found model string '%s' at %p\n",
+ acerhk_model_string, acerhk_model_addr);
+ if (bios_routine && verbose > 2)
+ printk(KERN_INFO"acerhk: offset from model string to function address: 0x%lx\n",
+ bios_routine - (unsigned long)acerhk_model_addr);
+ acerhk_series = determine_laptop_series(acerhk_model_string);
+ } else {
+ printk(KERN_WARNING"acerhk: Could not find model string, will assume type 200 series\n");
+ acerhk_series = 200;
+ }
+}
+
+/* }}} */
+
+/* {{{ key polling and translation */
+
+static void print_mapping(void)
+{
+ printk(KERN_INFO"acerhk: key mapping:\n");
+ printk("acerhk: help 0x%x\n", acerhk_name2event[k_help]);
+ printk("acerhk: setup 0x%x\n", acerhk_name2event[k_setup]);
+ printk("acerhk: p1 0x%x\n", acerhk_name2event[k_p1]);
+ printk("acerhk: p2 0x%x\n", acerhk_name2event[k_p2]);
+ printk("acerhk: p3 0x%x\n", acerhk_name2event[k_p3]);
+ printk("acerhk: www 0x%x\n", acerhk_name2event[k_www]);
+ printk("acerhk: mail 0x%x\n", acerhk_name2event[k_mail]);
+ printk("acerhk: wireless 0x%x\n", acerhk_name2event[k_wireless]);
+ printk("acerhk: power 0x%x\n", acerhk_name2event[k_power]);
+ printk("acerhk: mute 0x%x\n", acerhk_name2event[k_mute]);
+ printk("acerhk: volup 0x%x\n", acerhk_name2event[k_volup]);
+ printk("acerhk: voldn 0x%x\n", acerhk_name2event[k_voldn]);
+ printk("acerhk: res 0x%x\n", acerhk_name2event[k_res]);
+ printk("acerhk: close 0x%x\n", acerhk_name2event[k_close]);
+ printk("acerhk: open 0x%x\n", acerhk_name2event[k_open]);
+ printk("acerhk: wireless2 0x%x\n", acerhk_name2event[k_wireless2]);
+ printk("acerhk: play 0x%x\n", acerhk_name2event[k_play]);
+ printk("acerhk: stop 0x%x\n", acerhk_name2event[k_stop]);
+ printk("acerhk: prev 0x%x\n", acerhk_name2event[k_prev]);
+ printk("acerhk: next 0x%x\n", acerhk_name2event[k_next]);
+ printk("acerhk: display 0x%x\n", acerhk_name2event[k_display]);
+}
+
+static void set_keymap_name(t_key_names name, unsigned int key)
+{
+ acerhk_name2event[name] = key;
+}
+
+static void init_keymap_input(void)
+{
+ /* these values for input keys are chosen to match the key names on the
+ actual Acer laptop */
+ set_keymap_name(k_none, KEY_RESERVED);
+ set_keymap_name(k_help, KEY_HELP);
+ set_keymap_name(k_setup, KEY_CONFIG);
+ set_keymap_name(k_p1, KEY_PROG1);
+ set_keymap_name(k_p2, KEY_PROG2);
+ set_keymap_name(k_p3, KEY_PROG3);
+ set_keymap_name(k_www, KEY_WWW);
+ set_keymap_name(k_mail, KEY_MAIL);
+ set_keymap_name(k_wireless, KEY_XFER);
+ set_keymap_name(k_power, KEY_POWER);
+ set_keymap_name(k_mute, KEY_MUTE);
+ set_keymap_name(k_volup, KEY_VOLUMEUP);
+ set_keymap_name(k_voldn, KEY_VOLUMEDOWN);
+ set_keymap_name(k_res, KEY_CONFIG);
+ set_keymap_name(k_close, KEY_CLOSE);
+ set_keymap_name(k_open, KEY_OPEN);
+ /* I am not really happy with the selections for wireless and wireless2,
+ but coffee looks good. Michal Veselenyi proposed this value */
+ set_keymap_name(k_wireless2, KEY_COFFEE);
+ set_keymap_name(k_play, KEY_PLAYPAUSE);
+ set_keymap_name(k_stop, KEY_STOPCD);
+ set_keymap_name(k_prev, KEY_PREVIOUSSONG);
+ set_keymap_name(k_next, KEY_NEXTSONG);
+ set_keymap_name(k_display, KEY_MEDIA); /* also not happy with this */
+ if (verbose > 1)
+ print_mapping();
+}
+
+static int filter_idle_value(int keycode)
+{
+ int validkey = 0;
+ if (keycode != 0x0 &&
+ keycode != 0x9610 &&
+ keycode != 0xc100 && /* Francois Valenduc, Aspire 1601 LC */
+ keycode != 0x8610 &&
+ keycode != 0x861 &&
+ keycode != 0x8650 &&
+ keycode != 0x865)
+ validkey = keycode;
+ if (verbose > 4 && !validkey)
+ printk(KERN_INFO"acerhk: throw away idle value 0x%x\n", keycode);
+ return validkey;
+}
+
+static void send_key_event(t_key_names key)
+{
+ unsigned int input_key;
+ if (key != k_none) {
+ /* convert key name to kernel keycode */
+ input_key = acerhk_name2event[key];
+ if (verbose > 2)
+ printk(KERN_INFO"acerhk: translated acer key name 0x%x to input key 0x%x\n",
+ key, input_key);
+ /* send press and release together, as there is no such event from acer as 'release' */
+ input_report_key(acerhk_input_dev_ptr, input_key, 1);
+ input_report_key(acerhk_input_dev_ptr, input_key, 0);
+ }
+}
+
+static t_key_names transl8_key_code(int keycode)
+{
+ t_key_names keyname = k_none;
+ /* first filter out idle values */
+ if ( (keycode = filter_idle_value(keycode)) ) {
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: received key code 0x%x\n", keycode);
+ /* translate keycode to key name */
+ if (keycode >= 0 && keycode <= 255)
+ keyname = acerhk_key2name[keycode];
+ else {
+ if (verbose > 3)
+ printk(KERN_INFO"acerhk: keycode 0x%x too big, will use only 8 bits\n", keycode);
+ /* use only lower 8 bits of value to distinguish keys */
+ keyname = acerhk_key2name[keycode&0xff];
+ }
+ /* produce some log information for higher verbosity levels */
+ if (keyname != k_none && verbose > 2)
+ printk(KERN_INFO"acerhk: translated acer key code 0x%x to key name 0x%x\n",
+ keycode, keyname);
+ else if (keyname == k_none && verbose > 3)
+ printk(KERN_INFO"acerhk: translated acer key code 0x%x to no key\n",
+ keycode);
+ if (autowlan) {
+ /* if automatic switching of wlan hardware is enabled, do it here
+ on wireless key press */
+ if (keyname == k_wireless2) {
+ if (acerhk_bluetooth_state)
+ wbutton_fct_1(0);
+ else
+ wbutton_fct_1(1);
+ }
+ if (keyname == k_wireless) {
+ if (acerhk_wlan_state)
+ wbutton_fct_2(0);
+ else
+ wbutton_fct_2(1);
+ }
+ }
+ }
+ return keyname;
+}
+
+/* polling timer handler */
+static void acerhk_poll_event(unsigned long save_size)
+{
+#ifndef DUMMYHW
+ unsigned int max = MAX_POLLING_LOOPS;
+ /* make sure not to loop more then 32 times */
+ if (!max || max > 32)
+ max = 32;
+ if (acerhk_type != TM_dritek) {
+ while (get_nr_events() && max--) {
+ send_key_event(transl8_key_code(get_fnkey_event()));
+ }
+ } else {
+ send_key_event(transl8_key_code(get_fnkey_event()));
+ }
+#endif
+ acerhk_timer_poll.expires = jiffies + acerhk_polling_delay;
+ add_timer(&acerhk_timer_poll);
+}
+
+/* blinking timer handler; added by Antonio Cuni */
+static void acerhk_blink_event(unsigned long not_used)
+{
+ if (acerhk_blueled_blinking != -1) {
+ acerhk_blueled_blinking = !acerhk_blueled_blinking;
+#ifndef DUMMYHW
+ wbutton_fct_1(acerhk_blueled_blinking);
+#endif
+ acerhk_timer_blinking.expires = jiffies + acerhk_blueled_blinking_delay;
+ add_timer(&acerhk_timer_blinking);
+ }
+ else
+ printk(KERN_WARNING "acerhk: blinking event called, but blinking not active\n");
+}
+
+static void init_input(void)
+{
+ int i;
+
+#ifndef KERNEL26
+ /* request keyboard input module */
+ request_module("keybdev");
+ if (verbose > 3)
+ printk(KERN_INFO"requested keyboard input driver\n");
+#endif
+
+#ifndef STATIC_INPUT_DEV
+ /* allocate acerhk input device */
+ acerhk_input_dev_ptr=input_allocate_device();
+ /* enter some name */
+ acerhk_input_dev_ptr->name = "Acer hotkey driver";
+#else
+ acerhk_input_dev_ptr=&acerhk_input_dev;
+#endif
+
+ /* some laptops have a mail led, should I announce it here? */
+ acerhk_input_dev_ptr->evbit[0] = BIT(EV_KEY);
+ /* announce keys to input system
+ * the generated keys can be changed on runtime,
+ * but to publish those changes the device needs to
+ * get reconnected (I dont't know any other way)
+ * Therefore I enable all possible keys */
+ for (i = KEY_RESERVED; i < BTN_MISC; i++)
+ set_bit(i, acerhk_input_dev_ptr->keybit);
+ /* set mapping keyname -> input event */
+ init_keymap_input();
+ if (verbose)
+ printk(KERN_INFO"acerhk: registered input device\n");
+ input_register_device(acerhk_input_dev_ptr);
+ init_timer(&acerhk_timer_poll);
+ acerhk_polling_state = 0;
+}
+
+static void stop_polling(void)
+{
+ if (acerhk_polling_state == 1) {
+ del_timer(&acerhk_timer_poll);
+ if (verbose)
+ printk(KERN_INFO"acerhk: key polling stopped\n");
+ acerhk_polling_state = 0;
+ } else
+ if (verbose)
+ printk(KERN_INFO"acerhk: key polling not active\n");
+}
+
+static void start_polling(void)
+{
+ if (acerhk_polling_state != 1) {
+ acerhk_timer_poll.function = acerhk_poll_event;
+ acerhk_timer_poll.expires = jiffies + acerhk_polling_delay;
+ acerhk_timer_poll.data = get_nr_events();
+ add_timer(&acerhk_timer_poll);
+ acerhk_polling_state = 1;
+ if (acerhk_type == TM_dritek) {
+ printk(KERN_INFO"acerhk: Your hardware does not need polling enabled for hotkeys to work, "
+ "you can safely disable polling by using the module parameter poll=0 (unless you "
+ "want to play around with the driver and see if there are buttons which need polling).\n");
+ }
+ if (verbose)
+ printk(KERN_INFO"acerhk: starting key polling, every %d ms\n", acerhk_polling_delay);
+ } else
+ if (verbose)
+ printk(KERN_INFO"acerhk: key polling already active\n");
+}
+
+/* addedd by Antonio Cuni */
+static void start_blinking(void)
+{
+ if (acerhk_blueled_blinking == -1) {
+ // blinking was disabled... enable it!
+ acerhk_timer_blinking.function = acerhk_blink_event;
+ acerhk_timer_blinking.expires = jiffies + acerhk_blueled_blinking_delay;
+ acerhk_timer_blinking.data = 0; // not used
+ add_timer(&acerhk_timer_blinking);
+ acerhk_blueled_blinking = 0;
+ if (verbose)
+ printk(KERN_INFO "acerhk: starting blueled blinking\n");
+ } else
+ if (verbose)
+ printk(KERN_INFO "acerhk: blueled already blinking\n");
+}
+
+/* Added by Antonio Cuni */
+static void stop_blinking(void)
+{
+ if (acerhk_blueled_blinking != -1) {
+ del_timer(&acerhk_timer_blinking);
+ if (verbose)
+ printk(KERN_INFO "acerhk: blueled blinking stopped\n");
+ acerhk_blueled_blinking = -1;
+ }
+}
+
+static void release_input(void)
+{
+ stop_polling();
+ input_unregister_device(acerhk_input_dev_ptr);
+}
+
+/* }}} */
+
+/* {{{ procfs functions */
+
+#ifndef CONFIG_PROC_FS
+
+static int acerhk_proc_init(void)
+{
+ return 1;
+}
+#else
+
+/* This macro frees the machine specific function from bounds checking and
+ * things like that... */
+#define PRINT_PROC(fmt,args...) \
+ do { \
+ *len += sprintf( buffer+*len, fmt, ##args ); \
+ if (*begin + *len > offset + size) \
+ return( 0 ); \
+ if (*begin + *len < offset) { \
+ *begin += *len; \
+ *len = 0; \
+ } \
+ } while(0)
+
+static int pc_proc_infos( char *buffer, int *len,
+ off_t *begin, off_t offset, int size )
+{
+ PRINT_PROC( "Acer hotkeys version %s\n", ACERHK_VERSION);
+ PRINT_PROC( "Model(Type)\t: %s(", acerhk_model_string);
+ switch(acerhk_type) {
+ default:
+ PRINT_PROC( "unknown)\n");
+ break;
+ case TM_old:
+ PRINT_PROC( "old)\n");
+ break;
+ case TM_new:
+ PRINT_PROC( "new)\n");
+ break;
+ case TM_dritek:
+ PRINT_PROC( "Dritek)\n");
+ break;
+ }
+ if (bios_routine != 0) {
+ PRINT_PROC( "request handler\t: 0x%x\n", bios_routine);
+ if (cmos_index) {
+ PRINT_PROC( "CMOS index\t: 0x%x\n", cmos_index);
+ PRINT_PROC( "events pending\t: %u\n", get_nr_events());
+ } else {
+ PRINT_PROC( "CMOS index\t: not available\n");
+ }
+ if (acerhk_polling_state == 1)
+ PRINT_PROC( "kernel polling\t: active\n");
+ else
+ PRINT_PROC( "kernel polling\t: inactive\n");
+ PRINT_PROC( "autoswitch wlan\t: ");
+ if (autowlan == 1)
+ PRINT_PROC( "enabled\n");
+ else
+ PRINT_PROC( "disabled\n");
+ } else {
+ PRINT_PROC( "request handler\t: not found\n");
+ PRINT_PROC( "kernel polling\t: not possible\n");
+ }
+ /* model specific infos */
+ if (acerhk_type == TM_dritek) {
+ PRINT_PROC( "use of Dritek EC: ");
+ if (usedritek)
+ PRINT_PROC( "enabled\n");
+ else
+ PRINT_PROC( "disabled\n");
+ }
+ if (acerhk_type == TM_old)
+ PRINT_PROC( "preg400\t\t: 0x%p\n", preg400);
+ return (1);
+}
+
+static int acerhk_proc_info( char *buffer, char **start, off_t offset,
+ int size, int *eof, void *data )
+{
+ int len = 0;
+ off_t begin = 0;
+
+ *eof = pc_proc_infos( buffer, &len, &begin, offset, size );
+
+ if (offset >= begin + len)
+ return( 0 );
+ *start = buffer + (offset - begin);
+ return( size < begin + len - offset ? size : begin + len - offset );
+
+}
+
+static int acerhk_proc_key( char *buffer, char **start, off_t offset,
+ int size, int *eof, void *data )
+{
+ if (size >= 5 && offset == 0) {
+ if (acerhk_type == TM_dritek || acerhk_polling_state == 1) {
+ snprintf(buffer+offset, size, "n/a\n");
+ } else {
+ snprintf(buffer+offset, size, "0x%02x\n", filter_idle_value(get_fnkey_event()));
+ }
+ *eof = 1;
+ return 5;
+ }
+ *eof = 1;
+ return 0;
+}
+
+static int acerhk_proc_led(struct file* file, const char* buffer,
+ unsigned long count, void* data)
+{
+ char str[4];
+ int len;
+ if (count > 4)
+ len = 4;
+ else
+ len = count;
+ if (copy_from_user(str, buffer, len))
+ return -EFAULT;
+ str[3] = '\0';
+ if ( ( (len >= 2) && (!strncmp(str, "on", 2) || !strncmp(str, "an", 2)) )
+ || str[0] == '1')
+ set_mail_led(1);
+ else
+ set_mail_led(0);
+ return len;
+}
+
+static int acerhk_proc_wirelessled(struct file* file, const char* buffer,
+ unsigned long count, void* data)
+{
+ char str[4];
+ int len;
+ if (count > 4)
+ len = 4;
+ else
+ len = count;
+ if (copy_from_user(str, buffer, len))
+ return -EFAULT;
+ str[3] = '\0';
+ if ( ( (len >= 2) && (!strncmp(str, "on", 2) || !strncmp(str, "an", 2)) )
+ || str[0] == '1') {
+ if (acerhk_model_features & TM_F_WLAN_EC1)
+ enable_wlan_ec_1();
+ else if (acerhk_model_features & TM_F_WLAN_EC2)
+ enable_wlan_ec_2();
+ else
+ wbutton_fct_2(1);
+ }
+ else {
+ if (acerhk_model_features & TM_F_WLAN_EC1)
+ disable_wlan_ec_1();
+ else if (acerhk_model_features & TM_F_WLAN_EC2)
+ disable_wlan_ec_2();
+ else
+ wbutton_fct_2(0);
+ }
+ return len;
+}
+
+
+/* Modified by Antonio Cuni: added support for blinking
+ possible values:
+ - off, 0: led always off
+ - on, an, 1: led alway on
+ - n (a number): led blinking; n is the delay between
+ two changes of state, in jiffies; n must
+ be > 50, to prevent the user from overloading
+ the kernel.
+
+ */
+static int acerhk_proc_blueled(struct file* file, const char* buffer,
+ unsigned long count, void* data)
+{
+ const int MAXLEN=11;
+ char str[MAXLEN];
+ int len;
+ int isNumber;
+
+ if (count > MAXLEN)
+ len = MAXLEN;
+ else
+ len = count;
+ if (copy_from_user(str, buffer, len))
+ return -EFAULT;
+ str[MAXLEN - 1] = '\0';
+
+ /* try to parse a number */
+ isNumber = sscanf(str, "%u", &acerhk_blueled_blinking_delay);
+ /* if the delay is 0, turn off the led */
+ if (isNumber && acerhk_blueled_blinking_delay != 0 && acerhk_blueled_blinking_delay != 1) {
+ if (acerhk_blueled_blinking_delay < 50)
+ printk(KERN_INFO"acerhk: blinking request rejected. The delay must be > 50.\n");
+ else {
+ if (verbose)
+ printk(KERN_INFO"acerhk: blinking delay set to %u.\n", acerhk_blueled_blinking_delay);
+ start_blinking();
+ }
+ } else if (acerhk_blueled_blinking_delay == 1 || !strncmp(str, "on", 2) || !strncmp(str, "an", 2)) {
+ stop_blinking();
+ if (acerhk_model_features & TM_F_BLUE_EC1)
+ enable_bluetooth_ec_1();
+ else if (acerhk_model_features & TM_F_BLUE_EC2)
+ enable_bluetooth_ec_2();
+ else
+ wbutton_fct_1(1);
+ } else {
+ /* it's 0 or everything else */
+ stop_blinking();
+ if (acerhk_model_features & TM_F_BLUE_EC1)
+ disable_bluetooth_ec_1();
+ else if (acerhk_model_features & TM_F_BLUE_EC2)
+ disable_bluetooth_ec_2();
+ else
+ wbutton_fct_1(0);
+ }
+ return len;
+}
+
+#ifdef ACERDEBUG
+static void do_debug(const char* buffer, unsigned long len)
+{
+ unsigned int h, i;
+ switch (buffer[0]) {
+ case 'b':
+ /* test WLAN on TM 4001 */
+ switch (buffer[1]) {
+ case '0':
+ disable_wlan_ec_1();
+ break;
+ case '1':
+ default:
+ enable_wlan_ec_1();
+ }
+ break;
+ case 'B':
+ /* test BLUETOOTH on TM 4001 */
+ switch (buffer[1]) {
+ case '0':
+ disable_bluetooth_ec_1();
+ break;
+ case '1':
+ default:
+ enable_bluetooth_ec_1();
+ }
+ break;
+ case 'D':
+ /* test "DMM Function Enabled" entry of TM 4150/4650 */
+ enable_dmm_function();
+ break;
+ case 'i':
+ case '1':
+#ifndef KERNEL26
+ MOD_INC_USE_COUNT;
+#endif
+ break;
+ case 'e':
+ switch (buffer[1]) {
+ case '1':
+ start_polling();
+ break;
+ default:
+ stop_polling();
+ }
+ break;
+ case 'k':
+ for (i = 0; i <= 255;i++) {
+ input_report_key(acerhk_input_dev_ptr, i, 1);
+ input_report_key(acerhk_input_dev_ptr, i, 0);
+ }
+ break;
+ case 'm':
+ /* set mapping key names -> input events */
+ sscanf(&buffer[2],"%x", &i);
+ h = buffer[1] - '0' + 1;
+ printk("acerhk: key name %x maps to %x\n", h, i);
+ acerhk_name2event[h] = i;
+ break;
+ case 'M':
+ /* test mute LED on dritek hardware */
+ switch (buffer[1]) {
+ case '0':
+ disable_mute_led_ec();
+ break;
+ case '1':
+ default:
+ enable_mute_led_ec();
+ }
+ break;
+ case 'p':
+ printk("acerhk: pbutton = 0x%x\n", pbutton_fct());
+ break;
+ case 's':
+ /* send key event to test the key translation in input system */
+ sscanf(&buffer[1],"%x", &h);
+ printk("acerhk: sending key event 0x%x\n", h);
+ input_report_key(acerhk_input_dev_ptr, h, 1);
+ input_report_key(acerhk_input_dev_ptr, h, 0);
+ break;
+ case 'S':
+ /* simulate key codes to test the key translation in acerhk */
+ sscanf(&buffer[1],"%x", &h);
+ send_key_event(transl8_key_code(h));
+ break;
+ case 't':
+ printk("acerhk: thermal event = 0x%x\n", get_thermal_event());
+ break;
+ case 'w':
+ /* test the wbutton functions, someone really needs to have another look
+ at the windows driver */
+ switch (buffer[1]) {
+ case '2':
+ printk("acerhk: wbutton_2(%d) = 0x%x\n", buffer[2]-'0', wbutton_fct_2(buffer[2]-'0'));
+ break;
+ case '1':
+ default:
+ printk("acerhk: wbutton_1(%d) = 0x%x\n", buffer[2]-'0', wbutton_fct_1(buffer[2]-'0'));
+ }
+ break;
+ case 'W':
+ /* test wireless HW/LED on some models using dritek hardware */
+ switch (buffer[1]) {
+ case '0':
+ disable_wireless_ec();
+ break;
+ case '1':
+ default:
+ enable_wireless_ec();
+ }
+ break;
+ case 'v':
+ verbose = buffer[1]-'0';
+ printk("acerhk: verbosity level changed to %d\n", verbose);
+ break;
+ case 'd':
+ case '0':
+ default:
+#ifndef KERNEL26
+ MOD_DEC_USE_COUNT;
+#endif
+ break;
+ }
+}
+
+static int acerhk_proc_debug(struct file* file, const char* buffer,
+ unsigned long count, void* data)
+{
+ char str[5];
+ int len;
+ if (count > 5)
+ len = 5;
+ else
+ len = count;
+ if (copy_from_user(str, buffer, len))
+ return -EFAULT;
+ str[4] = '\0';
+ do_debug(str, len);
+ return len;
+}
+#endif
+
+static int acerhk_proc_init(void)
+{
+ int retval;
+ struct proc_dir_entry *entry;
+ /* create own directory */
+ proc_acer_dir = proc_mkdir("driver/acerhk", NULL);
+ if (proc_acer_dir == NULL) {
+ retval = 0;
+ printk(KERN_INFO"acerhk: could not create /proc/driver/acerhk\n");
+ }
+ else {
+ proc_acer_dir->owner = THIS_MODULE;
+ /* now create several files, first general info ... */
+ entry = create_proc_read_entry("info",
+ 0444, proc_acer_dir, acerhk_proc_info, NULL);
+ if (entry == NULL) {
+ printk(KERN_INFO"acerhk: cannot create info file\n");
+ remove_proc_entry("driver/acerhk", NULL);
+ retval = 0;
+ } else {
+ entry->owner = THIS_MODULE;
+ /* ... last pressed key ... */
+ entry = create_proc_read_entry("key",
+ 0444, proc_acer_dir, acerhk_proc_key, NULL);
+ if (entry == NULL) {
+ printk(KERN_INFO"acerhk: cannot create key file\n");
+ remove_proc_entry("info", proc_acer_dir);
+ remove_proc_entry("driver/acerhk", NULL);
+ retval = 0;
+ } else {
+ entry->owner = THIS_MODULE;
+ /* ... and led control file */
+ entry = create_proc_entry("led", 0222, proc_acer_dir);
+ if (entry == NULL) {
+ printk(KERN_INFO"acerhk: cannot create LED file\n");
+ remove_proc_entry("info", proc_acer_dir);
+ remove_proc_entry("key", proc_acer_dir);
+ remove_proc_entry("driver/acerhk", NULL);
+ retval = 0;
+ }
+ else {
+ entry->write_proc = acerhk_proc_led;
+ entry->owner = THIS_MODULE;
+ /* ... and wireless led controll file */
+ entry = create_proc_entry("wirelessled", 0222, proc_acer_dir);
+ if (entry == NULL) {
+ printk(KERN_INFO"acerhk: cannot create wirelessled file\n");
+ remove_proc_entry("info", proc_acer_dir);
+ remove_proc_entry("key", proc_acer_dir);
+ remove_proc_entry("led", proc_acer_dir);
+ remove_proc_entry("driver/acerhk", NULL);
+ retval = 0;
+ }
+ else {
+ entry->write_proc = acerhk_proc_wirelessled;
+ entry->owner = THIS_MODULE;
+ /* ... and bluetooth led controll file */
+ entry = create_proc_entry("blueled", 0222, proc_acer_dir);
+ if (entry == NULL) {
+ printk(KERN_INFO"acerhk: cannot create blueled file\n");
+ remove_proc_entry("info", proc_acer_dir);
+ remove_proc_entry("key", proc_acer_dir);
+ remove_proc_entry("led", proc_acer_dir);
+ remove_proc_entry("wirelessled", proc_acer_dir);
+ remove_proc_entry("driver/acerhk", NULL);
+ retval = 0;
+ } else {
+ entry->write_proc = acerhk_proc_blueled;
+ entry->owner = THIS_MODULE;
+ retval = 1;
+#ifdef ACERDEBUG
+ /* add extra file for debugging purposes */
+ entry = create_proc_entry("debug", 0222, proc_acer_dir);
+ if (entry == NULL) {
+ printk(KERN_INFO"acerhk: cannot create debug file\n");
+ remove_proc_entry("info", proc_acer_dir);
+ remove_proc_entry("key", proc_acer_dir);
+ remove_proc_entry("led", proc_acer_dir);
+ remove_proc_entry("wirelessled", proc_acer_dir);
+ remove_proc_entry("blueled", proc_acer_dir);
+ remove_proc_entry("driver/acerhk", NULL);
+ retval = 0;
+ }
+ else {
+ entry->write_proc = acerhk_proc_debug;
+ entry->owner = THIS_MODULE;
+ retval = 1;
+ }
+#endif
+ }
+ }
+ }
+ }
+ }
+ }
+ return retval;
+}
+
+static void acerhk_proc_cleanup(void)
+{
+ if (proc_acer_dir) {
+ remove_proc_entry("info", proc_acer_dir);
+ /* On dritek type hardware key file is already removed */
+ if (acerhk_type != TM_dritek)
+ remove_proc_entry("key", proc_acer_dir);
+ remove_proc_entry("led", proc_acer_dir);
+ remove_proc_entry("wirelessled", proc_acer_dir);
+ remove_proc_entry("blueled", proc_acer_dir);
+#ifdef ACERDEBUG
+ remove_proc_entry("debug", proc_acer_dir);
+#endif
+ remove_proc_entry("driver/acerhk", NULL);
+ proc_acer_dir = NULL;
+ }
+}
+
+#endif /* CONFIG_PROC_FS */
+
+/* }}} */
+
+/* {{{ file operations */
+
+static int acerhk_ioctl( struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg )
+{
+ int retval;
+ switch( cmd ) {
+ case ACERHK_GET_KEYCOUNT:
+ {
+ char nr;
+ nr = get_nr_events();
+ put_user(nr, (char*)arg);
+ retval = 0;
+ break;
+ }
+ case ACERHK_GET_KEYID:
+ {
+ char id;
+ id = get_fnkey_event();
+ put_user(id, (char*)arg);
+ retval = 0;
+ break;
+ }
+ case ACERHK_CONNECT:
+ launch_connect(1);
+ retval = 0;
+ break;
+ case ACERHK_START_POLLING:
+ start_polling();
+ retval = 0;
+ break;
+ case ACERHK_STOP_POLLING:
+ stop_polling();
+ retval = 0;
+ break;
+ case ACERHK_DISCONNECT:
+ launch_connect(0);
+ retval = 0;
+ break;
+ case ACERHK_GET_THERMAL_EVENT:
+ {
+ short event;
+ event = get_thermal_event();
+ put_user(event, (short*)arg);
+ retval = 0;
+ break;
+ }
+ case ACERHK_MAIL_LED_OFF:
+ set_mail_led(0);
+ retval = 0;
+ break;
+ case ACERHK_MAIL_LED_ON:
+ set_mail_led(1);
+ retval = 0;
+ break;
+ case ACERHK_GET_KEY_MAP:
+ if (copy_to_user((t_map_name2event*)arg, &acerhk_name2event, sizeof(acerhk_name2event)))
+ retval = -EFAULT;
+ else
+ retval = 0;
+ break;
+ case ACERHK_SET_KEY_MAP:
+ if (copy_from_user(&acerhk_name2event, (t_map_name2event*)arg, sizeof(acerhk_name2event)))
+ retval = -EFAULT;
+ else {
+ if (verbose) {
+ printk(KERN_INFO"acerhk: changed key mapping\n");
+ print_mapping();
+ }
+ retval = 0;
+ }
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+#ifdef ACERDEBUG
+static ssize_t acerhk_write (struct file* file, const char* buffer, size_t length, loff_t* offset)
+{
+ if (length)
+ do_debug(buffer, length);
+ return length;
+}
+#endif
+
+static int acerhk_open( struct inode *inode, struct file *file )
+{
+ return 0;
+}
+
+static int acerhk_release( struct inode *inode, struct file *file )
+{
+ return 0;
+}
+
+static struct file_operations acerhk_fops = {
+ owner: THIS_MODULE,
+ ioctl: acerhk_ioctl,
+ open: acerhk_open,
+#ifdef ACERDEBUG
+ write: acerhk_write,
+#endif
+ release: acerhk_release,
+};
+
+static struct miscdevice acerhk_dev = {
+ MISC_DYNAMIC_MINOR,
+ "acerhk",
+ &acerhk_fops
+};
+
+/* }}} */
+
+static void __init model_init(void)
+{
+ /* set callroutine, features and keymap for model */
+ setup_model_features(acerhk_series);
+ /* override initial state of wireless hardware if specified by module options */
+ if (wlan_state >= 0) acerhk_wlan_state = wlan_state;
+ if (bluetooth_state >= 0) acerhk_bluetooth_state = bluetooth_state;
+ /* Launch connect only if available */
+ if (acerhk_model_features & TM_F_CONNECT) {
+ if (verbose)
+ printk(KERN_INFO"acerhk: Model type %d, calling launch_connect(1)\n",
+ acerhk_type);
+ launch_connect(1);
+ }
+ if ( acerhk_type != TM_dritek ) {
+ get_cmos_index();
+ }
+ if ( acerhk_type == TM_dritek ) {
+ enable_dritek_keyboard();
+ }
+ /* added by Antonio Cuni */
+ init_timer(&acerhk_timer_blinking);
+}
+
+
+static void __exit acerhk_cleanup_module (void);
+static int __init acerhk_init(void)
+{
+ int ret;
+
+ ret = misc_register( &acerhk_dev );
+ if (ret) {
+ printk(KERN_ERR "acerhk: can't misc_register on minor=%d\n", ACERHK_MINOR);
+ ret = -EAGAIN;
+ }
+ else if (!acerhk_proc_init()) {
+ printk(KERN_ERR "acerhk: can't create procfs entries\n");
+ ret = -ENOMEM;
+ misc_deregister( &acerhk_dev );
+ }
+ else {
+ reg1 = ioremap(0xf0000, 0xffff);
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: area from 0xf000 to 0xffff mapped to %p\n", reg1);
+ reg2 = ioremap(0xe0000, 0xffff);
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: area from 0xe000 to 0xffff mapped to %p\n", reg2);
+ /* the area 0x400 is used as data area by earlier (520) series */
+ preg400 = ioremap(0x400, 0xfff);
+ if (verbose > 1)
+ printk(KERN_INFO"acerhk: area from 0x400 to 0x13ff mapped to %p\n", preg400);
+ /* attach to input system */
+ init_input();
+ memset(acerhk_model_string, 0x00, ACERHK_MODEL_STRLEN);
+#ifdef DUMMYHW
+ acerhk_model_addr = (void*)0x12345678;
+ /* copy the string, but not more than 15 characters */
+ strncpy(acerhk_model_string, "TravelmateDummy", ACERHK_MODEL_STRLEN-1);
+ /* set callroutine for model */
+ if (force_series)
+ acerhk_series = force_series;
+ else
+ acerhk_series = 2000;
+ setup_model_features(acerhk_series);
+ printk(KERN_INFO "Acer Travelmate hotkey driver v" ACERHK_VERSION " dummy\n");
+ if ( acerhk_type == TM_dritek )
+ enable_dritek_keyboard();
+ if (poll)
+ start_polling();
+ init_timer(&acerhk_timer_blinking);
+#else
+ bios_routine = find_hk_area();
+ if (!force_series)
+ probe_model();
+ else {
+ if (verbose)
+ printk(KERN_INFO"acerhk: forced laptop series to %d\n", force_series);
+ acerhk_series = force_series;
+ }
+ /* do model specific initialization */
+ model_init();
+ /* Without a bios routine we cannot do anything except on dritek
+ type HW, unload on other types */
+ if (bios_routine || (acerhk_type == TM_dritek)) {
+ ret = 0;
+ if (verbose && bios_routine)
+ printk(KERN_INFO"acerhk: bios routine found at 0x%x\n", bios_routine);
+ printk(KERN_INFO "Acer Travelmate hotkey driver v" ACERHK_VERSION "\n");
+ /* If automatic switching of wlan is wanted but polling is disabled,
+ automatically enable it */
+ if (!poll && autowlan) {
+ printk(KERN_INFO "Automatic switching of wireless hardware needs polling, enabling it\n");
+ poll = 1;
+ }
+ /* start automatic polling of key presses if wanted and bios routine found */
+ if (poll && bios_routine)
+ start_polling();
+ } else {
+ printk(KERN_ERR "acerhk: can't find bios routine, cannot do anything for you, sorry!\n");
+ ret = -ENOMEM;
+ acerhk_cleanup_module();
+ }
+#endif
+ }
+ return ret;
+}
+
+static void __exit acerhk_cleanup_module (void)
+{
+ acerhk_proc_cleanup();
+ stop_blinking();
+ if (reg1)
+ iounmap(reg1);
+ if (reg2)
+ iounmap(reg2);
+ if (preg400)
+ iounmap(preg400);
+ release_input();
+ misc_deregister( &acerhk_dev );
+ if ( acerhk_type == TM_dritek ) {
+ disable_dritek_keyboard();
+ }
+ if (verbose > 2)
+ printk(KERN_INFO "acerhk: unloaded\n");
+}
+
+module_init(acerhk_init);
+module_exit(acerhk_cleanup_module);
+
+MODULE_AUTHOR("Olaf Tauber");
+MODULE_DESCRIPTION("AcerHotkeys extra buttons keyboard driver");
+MODULE_LICENSE("GPL");
+
+#ifndef KERNEL26
+EXPORT_NO_SYMBOLS;
+#endif
+
+#else
+#error This driver is only available for X86 architecture
+#endif
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
+
diff -urNp orig-linux-2.6.19-beyond4/drivers/input/acerhk/acerhk.h linux-2.6.19-beyond4/drivers/input/acerhk/acerhk.h
--- orig-linux-2.6.19-beyond4/drivers/input/acerhk/acerhk.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/drivers/input/acerhk/acerhk.h 2005-06-17 00:10:12.000000000 +0300
@@ -0,0 +1,91 @@
+#ifndef __ACERHK_H__
+#define __ACERHK_H__
+
+#include
+
+#define ACERHK_MINOR MISC_DYNAMIC_MINOR
+
+#define ACERHK_GET_KEYCOUNT _IOR('p', 0x01, char) /* Get number of cached key presses */
+#define ACERHK_GET_KEYID _IOR('p', 0x02, char) /* Get first key in queue */
+#define ACERHK_CONNECT _IO('p', 0x03) /* ? */
+#define ACERHK_DISCONNECT _IO('p', 0x04) /* ? */
+#define ACERHK_GET_THERMAL_EVENT _IOR('p', 0x05, short) /* ? */
+#define ACERHK_MAIL_LED_OFF _IO('p', 0x10) /* switch mail LED off */
+#define ACERHK_MAIL_LED_ON _IO('p', 0x11) /* switch mail LED on (blinking) */
+#define ACERHK_START_POLLING _IO('p', 0x12) /* poll keys in kernel, send real key events */
+#define ACERHK_STOP_POLLING _IO('p', 0x13) /* stop key polling in kernel */
+#define ACERHK_GET_KEY_MAP _IOR('p', 0x20, int) /* Get mapping of key names to key events, */
+#define ACERHK_SET_KEY_MAP _IOW('p', 0x21, int) /* Set mapping of key names to key events */
+
+/* all possible keys (known to me) */
+typedef enum e_key_names {
+ k_none = 0,
+ k_help = 1, /* Fn+F1 */
+ k_setup = 2, /* Fn+F2 */
+ k_p1 = 3,
+ k_p2 = 4,
+ k_p3 = 5,
+ k_www = 6,
+ k_mail = 7,
+ k_wireless = 8,
+ k_power = 9, /* Fn+F3 */
+ k_mute = 10, /* Fn+F8 */
+ k_volup = 11, /* Fn+Up */
+ k_voldn = 12, /* Fn+Down */
+ k_res = 13, /* resolution change on Medion MD 40100 */
+ k_close = 14, /* if lid is closed in tablet mode */
+ k_open = 15, /* if lid is opend in tablet mode */
+ k_wireless2 = 16, /* second wireless button on TM 243LC */
+ k_play = 17, /* Play/Pause found on AOpen */
+ k_stop = 18, /* Stop/Eject found on AOpen */
+ k_prev = 19, /* Prev found on AOpen */
+ k_next = 20, /* Next found on AOpen */
+ k_display = 21 /* Change internal/external display on MD 42200 */
+} t_key_names;
+#define NR_KEY_NAMES 22
+typedef unsigned int t_map_name2event[NR_KEY_NAMES];
+
+#ifdef __KERNEL__
+
+/* available features */
+#define TM_F_WLAN_EC1 0x00000010
+#define TM_F_BLUE_EC1 0x00000020
+#define TM_F_WLAN_EC2 0x00000040
+#define TM_F_BLUE_EC2 0x00000080
+#define TM_F_MUTE_LED_EC 0x00001000
+#define TM_F_MAIL_LED 0x00010000
+#define TM_F_MAIL_LED_EC 0x00020000
+#define TM_F_MAIL_LED_EC2 0x00040000
+#define TM_F_MAIL_LED_EC3 0x00080000
+
+#define TM_F_CONNECT 0x00100000
+#define TM_F_THERMAL 0x00200000
+#define TM_F_PBUTTON 0x00400000
+#define TM_F_WBUTTON 0x00800000
+
+typedef enum acer_type {
+ TM_unknown,
+ /* 200, 210, 520, 600 and 730 series, Medion MD42200 */
+ TM_old,
+ /* C100, C110, 220, 230, 240, 260, 350, 360, 610, 620, 630, 740 series
+ Medion MD40100, Aspire 1600, FS Amilo */
+ TM_new,
+ /* Aspire 13xx, 14xx, 1700, TM 290, 650, 660, 800 */
+ TM_dritek
+} t_acer_type;
+
+struct register_buffer {
+ unsigned int eax;
+ unsigned int ebx;
+ unsigned int ecx;
+ unsigned int edx;
+ unsigned int edi;
+ unsigned int esi;
+ unsigned int ebp;
+};
+
+typedef asmlinkage void (*bios_call) (struct register_buffer *);
+
+#endif
+
+#endif
diff -urNp orig-linux-2.6.19-beyond4/drivers/input/acerhk/Makefile linux-2.6.19-beyond4/drivers/input/acerhk/Makefile
--- orig-linux-2.6.19-beyond4/drivers/input/acerhk/Makefile 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.19-beyond4/drivers/input/acerhk/Makefile 2007-02-16 16:14:42.000000000 +0200
@@ -0,0 +1 @@
+obj-$(CONFIG_ACERHK) += acerhk.o
diff -urNp orig-linux-2.6.19-beyond4/drivers/input/Kconfig linux-2.6.19-beyond4/drivers/input/Kconfig
--- orig-linux-2.6.19-beyond4/drivers/input/Kconfig 2006-11-29 23:57:37.000000000 +0200
+++ linux-2.6.19-beyond4/drivers/input/Kconfig 2007-02-16 16:12:56.000000000 +0200
@@ -24,6 +24,13 @@ config INPUT
if INPUT
+config ACERHK
+ tristate "Acerhk driver"
+ depends on EXPERIMENTAL && PROC_FS
+ ---help---
+ This is an experimental acer keyboard driver for
+ acer laptops
+
config INPUT_FF_MEMLESS
tristate "Support for memoryless force-feedback devices"
default n
diff -urNp orig-linux-2.6.19-beyond4/drivers/input/Makefile linux-2.6.19-beyond4/drivers/input/Makefile
--- orig-linux-2.6.19-beyond4/drivers/input/Makefile 2006-11-29 23:57:37.000000000 +0200
+++ linux-2.6.19-beyond4/drivers/input/Makefile 2007-02-16 16:06:29.000000000 +0200
@@ -21,3 +21,4 @@ obj-$(CONFIG_INPUT_MOUSE) += mouse/
obj-$(CONFIG_INPUT_JOYSTICK) += joystick/
obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
+obj-$(CONFIG_ACERHK) += acerhk/