GIT 11ea3173d5f2de71d037ef58ac43395795fed2bc git+ssh://master.kernel.org/pub/scm/linux/kernel/git/dtor/input.git commit 11ea3173d5f2de71d037ef58ac43395795fed2bc Author: Rick Koch Date: Sat Aug 5 00:32:30 2006 -0400 Input: add driver for Touchwin serial touchscreens Signed-off-by: Rick Koch Signed-off-by: Dmitry Torokhov commit 4003dff41e65ad338a60dde90019bffcb5531fb6 Author: Rick Koch Date: Sat Aug 5 00:32:24 2006 -0400 Input: add driver for Touchright serial touchscreens Signed-off-by: Rick Koch Signed-off-by: Dmitry Torokhov commit ee4799997950e81437ef9055a4b104099e3272c4 Author: Rick Koch Date: Sat Aug 5 00:32:18 2006 -0400 Input: add driver for Penmount serial touchscreens Signed-off-by: Rick Koch Signed-off-by: Dmitry Torokhov commit fae3006e4b42eafbed4af714e93cf6c2b92ff793 Author: Shaun Jackman Date: Sat Aug 5 00:29:49 2006 -0400 Input: elo - add support for non-pressure-sensitive touchscreens - Use the touch status bit rather than the pressure bits to distinguish a BTN_TOUCH event. Non-pressure-sensitive touchscreens always report full pressure - Report ABS_PRESSURE information only if the touchscreen supports it Signed-off-by: Shaun Jackman Signed-off-by: Dmitry Torokhov commit 1ce316efb55a1497d07d518853e60a4356abceb6 Author: Shaun Jackman Date: Sat Aug 5 00:27:59 2006 -0400 Input: elo - fix checksum calculation Fix 10-byte protocol checksum calculation and do not discard packet early unless it is missing lead in byte. Signed-off-by: Shaun Jackman Signed-off-by: Dmitry Torokhov commit 6b50d8b862284929314e9ff09e5b1cce2c43d32b Author: Dmitry Torokhov Date: Sat Aug 5 00:27:00 2006 -0400 Input: elo - handle input_register_device() failures Signed-off-by: Dmitry Torokhov commit f287caee8094097e3901d9982b6780873b36944f Author: Adrian Bunk Date: Fri Aug 4 23:00:02 2006 -0400 Input: hid - #if 0 the no longer used hid_find_field_by_usage() Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov commit 8b8277a17477de38d8df6783e8221aed55bab300 Author: Anssi Hannula Date: Wed Jul 19 01:44:22 2006 -0400 Input: update the force feedback documentation Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov commit bb3caf7f438a67452f5cf4773ca1bf82260bbbad Author: Anssi Hannula Date: Wed Jul 19 01:44:17 2006 -0400 Input: add force feedback driver for PSX-style Zeroplus devices Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov commit 1f734cb461e1f029d751deb15c8d9f8137fb2ca7 Author: Anssi Hannula Date: Wed Jul 19 01:44:08 2006 -0400 Input: drop remnants of the old force-feedback interface Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov commit ff462551235d8d7d843a005950bc90924fcedede Author: Anssi Hannula Date: Wed Jul 19 01:41:09 2006 -0400 Input: uinput - switch to the new FF interface The userspace interface of the force feedback part is changed and documentation in uinput.h is updated accordingly. MODULE_VERSION is also incremented to reflect the revision. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov commit dc76c912145febae8b62746d6f93e5edae342c9d Author: Anssi Hannula Date: Wed Jul 19 01:40:55 2006 -0400 Input: use new FF interface in the HID force feedback drivers Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov commit 224ee88fe39564358ec99b46bf3ee6e6999ae17d Author: Anssi Hannula Date: Wed Jul 19 01:40:47 2006 -0400 Input: add force feedback driver for PID devices This replaces the older PID driver which was never completed. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov commit f6a01c85965c9e6fa8fb893c1fa5db16130d0ccb Author: Anssi Hannula Date: Wed Jul 19 01:40:39 2006 -0400 Input: iforce - switch to the new FF interface Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov commit 7d928a2b14eede1f333db7b7b684c57f7fa7f456 Author: Anssi Hannula Date: Wed Jul 19 01:40:30 2006 -0400 Input: unified force feedback support for memoryless devices Consolidate core implementing memoryless devices in one module; added support for gain and envelopes and periodic => rumble conversion. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov commit 509ca1a9383601fdc5612d3d3ba5b981f6eb6c8b Author: Anssi Hannula Date: Wed Jul 19 01:40:22 2006 -0400 Input: implement new force feedback interface Implement a new force feedback interface, in which all non-driver-specific operations are separated to a common module. This includes handling effect type validations, locking, etc. The effects are now file descriptor specific instead of the previous strange half-process half-fd specific behaviour. The effect memory of devices is not emptied if the root user opens and closes the device while another user is using effects. This is a minor change and most likely no force feedback aware programs are affected by this negatively. Otherwise the userspace interface is left unaltered. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov commit 806d41b756fecc1b13584e2b806b76d8934b1679 Author: Anssi Hannula Date: Wed Jul 19 01:40:14 2006 -0400 Input: move fixp-arith.h to drivers/input Move fixp-arith.h from drivers/usb/input to drivers/input, as the part of force feedback support that requires trigonometric functions is being moved there. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov commit 57e6b724c08697d8332a63b60d3a60ee5e74e219 Author: Dmitry Torokhov Date: Wed Jul 19 01:39:56 2006 -0400 Input: rename input.ko into input-core.ko This will allow building input core module from several files which is needed for the reworked force feedback support. Signed-off-by: Dmitry Torokhov commit 73b59a3b1c09e4bfc678400b77b96134dcfbf112 Author: Randy Dunlap Date: Wed Jul 19 01:14:55 2006 -0400 Input: serio/gameport - check whether driver core calls succeeded Signed-off-by: Randy Dunlap Signed-off-by: Dmitry Torokhov commit 2c1dd69d28cba5a51e838897e0335f82a292f366 Author: Nick Martin Date: Wed Jul 19 01:14:44 2006 -0400 Input: spaceball - make 4000FLX Lefty work Although the Spaceball 4000FLX Lefty is already supported by the spaceball driver, it does not register properly due to SPACEBALL_MAX_ID being set too low. Increment SPACEBALL_MAX_ID such that the 4000FLX Lefty is properly recognized. Signed-off-by: Nick Martin Signed-off-by: Dmitry Torokhov commit 896cdc7b802c972d625351eb47d4add22d1fccb6 Author: Dmitry Torokhov Date: Wed Jul 19 01:14:25 2006 -0400 Input: keyboard - simplify emulate_raw() implementation Signed-off-by: Dmitry Torokhov commit 2b192908b8e74ce0753c7dff92d244735d1eeed9 Author: Dmitry Torokhov Date: Wed Jul 19 01:13:26 2006 -0400 Input: keyboard - remove static variable and clean up initialization Signed-off-by: Dmitry Torokhov commit 826d598242d9200ddee63fce96f03793fddee4fc Author: Dmitry Torokhov Date: Wed Jul 19 01:09:10 2006 -0400 Input: hiddev - use standard list implementation Fixes Coverity #id 303 Signed-off-by: Dmitry Torokhov commit b6d786dbe6fc19b51edd6cf6de84fd65b702c800 Author: Dmitry Torokhov Date: Wed Jul 19 01:08:51 2006 -0400 Input: add missing handler->start() call The start() method need to be called every time we create a new handle. This includes not only registering new devices but also when registering new handlers. Signed-off-by: Dmitry Torokhov commit cb78623517ca08a035a3f69c0ca6d68a0123e649 Author: Dmitry Torokhov Date: Sat Jul 15 01:17:54 2006 -0400 Input: HID - fix potential out-of-bound array access Fixes Coverity #id 978 Signed-off-by: Dmitry Torokhov commit a2b2ed2ce20ae6c335a27ae5bf8580f385014e70 Author: Andrew Morton Date: Sat Jul 15 01:17:38 2006 -0400 Input: fix list iteration in input_release_device() Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov commit b53174fb5a0e47a19e10b8ef74ab0c12a19f92dc Author: Przemek Iskra Date: Sat Jul 15 01:16:21 2006 -0400 Input: iforce - add Trust Force Feedback Race Master support Signed-off-by: Vojtech Pavlik Signed-off-by: Dmitry Torokhov commit 8d64d3722c6abbb43bccd518ececc5559e1962b4 Author: Dmitry Torokhov Date: Thu Jul 6 23:55:00 2006 -0400 Input: iforce - check array bounds before accessing elements Fixes Coverity #id 864 Signed-off-by: Dmitry Torokhov commit 95349fe8144b7d18f04bdca1c2d3fb85789de4fb Author: Dmitry Torokhov Date: Thu Jul 6 23:54:48 2006 -0400 Input: libps2 - warn instead of oopsing when passed bad arguments This is more user-friendly and also fixes Coverity #id 249 Signed-off-by: Dmitry Torokhov commit 699756199d65700e8deed59ae250439ca8684686 Author: Dmitry Torokhov Date: Thu Jul 6 23:54:30 2006 -0400 Input: fm801-gp - fix use after free Fixes Coverity #id 916 Signed-off-by: Dmitry Torokhov commit c7948989f84ee6e9c68cc643f8c6a635eb7a904b Author: Andrew Morton Date: Thu Jul 6 00:23:38 2006 -0400 Input: wistron - fix section reference mismatches Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov commit 0e739d28762e78c746cad8edd33223550a1c6a3f Author: Dmitry Torokhov Date: Thu Jul 6 00:22:43 2006 -0400 Input: introduce input_inject_event() function Create input_inject_event() function which is to be used by input handlers as opposed to input_event() which is reserved for drivers implementing input devices. The difference is that if device is "grabbed" by some process input_inject_event() will ignore events unless sent from the handle that is currently owns the device. Signed-off-by: Dmitry Torokhov commit c7e8dc6ee6d59bf72f5478fa6355a27750e6c7d2 Author: Dmitry Torokhov Date: Thu Jul 6 00:21:03 2006 -0400 Input: add start() method to input handlers The new start() method is called immediately after connect() and also when "grabbed" device is released by its owner. This will allow input handlers to re-synchronize state of once-grabbed device with the rest of devices. Signed-off-by: Dmitry Torokhov commit e9c8862f19958846dd0c7b39d0f6216aad6c7bee Author: Dmitry Torokhov Date: Thu Jul 6 00:20:47 2006 -0400 Input: remove accept method from input_dev This method used to enforce exclusive access to iforce devices, but presenlty there are no known users of this method. Signed-off-by: Dmitry Torokhov --- diff --git a/Documentation/input/ff.txt b/Documentation/input/ff.txt index c7e10ea..c53b1c1 100644 --- a/Documentation/input/ff.txt +++ b/Documentation/input/ff.txt @@ -1,67 +1,37 @@ Force feedback for Linux. By Johann Deneux on 2001/04/22. +Updated by Anssi Hannula on 2006/04/09. You may redistribute this file. Please remember to include shape.fig and interactive.fig as well. ---------------------------------------------------------------------------- -0. Introduction +1. Introduction ~~~~~~~~~~~~~~~ This document describes how to use force feedback devices under Linux. The goal is not to support these devices as if they were simple input-only devices (as it is already the case), but to really enable the rendering of force effects. -At the moment, only I-Force devices are supported, and not officially. That -means I had to find out how the protocol works on my own. Of course, the -information I managed to grasp is far from being complete, and I can not -guarranty that this driver will work for you. -This document only describes the force feedback part of the driver for I-Force -devices. Please read joystick.txt before reading further this document. +This document only describes the force feedback part of the Linux input +interface. Please read joystick.txt and input.txt before reading further this +document. 2. Instructions to the user ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Here are instructions on how to compile and use the driver. In fact, this -driver is the normal iforce, input and evdev drivers written by Vojtech -Pavlik, plus additions to support force feedback. +To enable force feedback, you have to: + +1. have your kernel configured with evdev and a driver that supports your + device. +2. make sure evdev module is loaded and /dev/input/event* device files are + created. Before you start, let me WARN you that some devices shake violently during the initialisation phase. This happens for example with my "AVB Top Shot Pegasus". To stop this annoying behaviour, move you joystick to its limits. Anyway, you -should keep a hand on your device, in order to avoid it to brake down if +should keep a hand on your device, in order to avoid it to break down if something goes wrong. -At the kernel's compilation: - - Enable IForce/Serial - - Enable Event interface - -Compile the modules, install them. - -You also need inputattach. - -You then need to insert the modules into the following order: -% modprobe joydev -% modprobe serport # Only for serial -% modprobe iforce -% modprobe evdev -% ./inputattach -ifor $2 & # Only for serial -If you are using USB, you don't need the inputattach step. - -Please check that you have all the /dev/input entries needed: -cd /dev -rm js* -mkdir input -mknod input/js0 c 13 0 -mknod input/js1 c 13 1 -mknod input/js2 c 13 2 -mknod input/js3 c 13 3 -ln -s input/js0 js0 -ln -s input/js1 js1 -ln -s input/js2 js2 -ln -s input/js3 js3 - -mknod input/event0 c 13 64 -mknod input/event1 c 13 65 -mknod input/event2 c 13 66 -mknod input/event3 c 13 67 +If you have a serial iforce device, you need to start inputattach. See +joystick.txt for details. 2.1 Does it work ? ~~~~~~~~~~~~~~~~~~ @@ -70,9 +40,9 @@ There is an utility called fftest that w 3. Instructions to the developper ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - All interactions are done using the event API. That is, you can use ioctl() +All interactions are done using the event API. That is, you can use ioctl() and write() on /dev/input/eventXX. - This information is subject to change. +This information is subject to change. 3.1 Querying device capabilities ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -86,18 +56,29 @@ int ioctl(int file_descriptor, int reque Returns the features supported by the device. features is a bitfield with the following bits: -- FF_X has an X axis (usually joysticks) -- FF_Y has an Y axis (usually joysticks) -- FF_WHEEL has a wheel (usually sterring wheels) - FF_CONSTANT can render constant force effects -- FF_PERIODIC can render periodic effects (sine, triangle, square...) +- FF_PERIODIC can render periodic effects with the following waveforms: + - FF_SQUARE square waveform + - FF_TRIANGLE triangle waveform + - FF_SINE sine waveform + - FF_SAW_UP sawtooth up waveform + - FF_SAW_DOWN sawtooth down waveform + - FF_CUSTOM custom waveform - FF_RAMP can render ramp effects - FF_SPRING can simulate the presence of a spring -- FF_FRICTION can simulate friction +- FF_FRICTION can simulate friction - FF_DAMPER can simulate damper effects -- FF_RUMBLE rumble effects (normally the only effect supported by rumble - pads) +- FF_RUMBLE rumble effects - FF_INERTIA can simulate inertia +- FF_GAIN gain is adjustable +- FF_AUTOCENTER autocenter is adjustable + +Note: In most cases you should use FF_PERIODIC instead of FF_RUMBLE. All + devices that support FF_RUMBLE support FF_PERIODIC (square, triangle, + sine) and the other way around. + +Note: The exact syntax FF_CUSTOM is undefined for the time being as no driver + supports it yet. int ioctl(int fd, EVIOCGEFFECTS, int *n); @@ -108,7 +89,7 @@ Returns the number of effects the device ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include #include - + int ioctl(int file_descriptor, int request, struct ff_effect *effect); "request" must be EVIOCSFF. @@ -120,6 +101,9 @@ to the unique id assigned by the driver. some operations (removing an effect, controlling the playback). This if field must be set to -1 by the user in order to tell the driver to allocate a new effect. + +Effects are file descriptor specific. + See for a description of the ff_effect struct. You should also find help in a few sketches, contained in files shape.fig and interactive.fig. You need xfig to visualize these files. @@ -128,8 +112,8 @@ You need xfig to visualize these files. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ int ioctl(int fd, EVIOCRMFF, effect.id); -This makes room for new effects in the device's memory. Please note this won't -stop the effect if it was playing. +This makes room for new effects in the device's memory. Note that this also +stops the effect if it was playing. 3.4 Controlling the playback of effects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -149,22 +133,21 @@ #include play.type = EV_FF; play.code = effect.id; play.value = 3; - + write(fd, (const void*) &play, sizeof(play)); ... /* Stop an effect */ stop.type = EV_FF; stop.code = effect.id; stop.value = 0; - + write(fd, (const void*) &play, sizeof(stop)); 3.5 Setting the gain ~~~~~~~~~~~~~~~~~~~~ Not all devices have the same strength. Therefore, users should set a gain factor depending on how strong they want effects to be. This setting is -persistent across access to the driver, so you should not care about it if -you are writing games, as another utility probably already set this for you. +persistent across access to the driver. /* Set the gain of the device int gain; /* between 0 and 100 */ @@ -204,11 +187,14 @@ type of device, not all parameters can b the direction of an effect cannot be updated with iforce devices. In this case, the driver stops the effect, up-load it, and restart it. +Therefore it is recommended to dynamically change direction while the effect +is playing only when it is ok to restart the effect with a replay count of 1. 3.8 Information about the status of effects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Every time the status of an effect is changed, an event is sent. The values and meanings of the fields of the event are as follows: + struct input_event { /* When the status of the effect changed */ struct timeval time; @@ -225,3 +211,9 @@ struct input_event { FF_STATUS_STOPPED The effect stopped playing FF_STATUS_PLAYING The effect started to play + +NOTE: Status feedback is only supported by iforce driver. If you have + a really good reason to use this, please contact + linux-joystick@atrey.karlin.mff.cuni.cz or anssi.hannula@gmail.com + so that support for it can be added to the rest of the drivers. + diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 056ebe8..30a7454 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -107,7 +107,6 @@ const int NR_TYPES = ARRAY_SIZE(max_vals struct kbd_struct kbd_table[MAX_NR_CONSOLES]; static struct kbd_struct *kbd = kbd_table; -static struct kbd_struct kbd0; int spawnpid, spawnsig; @@ -223,13 +222,13 @@ static void kd_nosound(unsigned long ign { struct list_head *node; - list_for_each(node,&kbd_handler.h_list) { + list_for_each(node, &kbd_handler.h_list) { struct input_handle *handle = to_handle_h(node); if (test_bit(EV_SND, handle->dev->evbit)) { if (test_bit(SND_TONE, handle->dev->sndbit)) - input_event(handle->dev, EV_SND, SND_TONE, 0); + input_inject_event(handle, EV_SND, SND_TONE, 0); if (test_bit(SND_BELL, handle->dev->sndbit)) - input_event(handle->dev, EV_SND, SND_BELL, 0); + input_inject_event(handle, EV_SND, SND_BELL, 0); } } } @@ -247,11 +246,11 @@ void kd_mksound(unsigned int hz, unsigne struct input_handle *handle = to_handle_h(node); if (test_bit(EV_SND, handle->dev->evbit)) { if (test_bit(SND_TONE, handle->dev->sndbit)) { - input_event(handle->dev, EV_SND, SND_TONE, hz); + input_inject_event(handle, EV_SND, SND_TONE, hz); break; } if (test_bit(SND_BELL, handle->dev->sndbit)) { - input_event(handle->dev, EV_SND, SND_BELL, 1); + input_inject_event(handle, EV_SND, SND_BELL, 1); break; } } @@ -272,15 +271,15 @@ int kbd_rate(struct kbd_repeat *rep) unsigned int d = 0; unsigned int p = 0; - list_for_each(node,&kbd_handler.h_list) { + list_for_each(node, &kbd_handler.h_list) { struct input_handle *handle = to_handle_h(node); struct input_dev *dev = handle->dev; if (test_bit(EV_REP, dev->evbit)) { if (rep->delay > 0) - input_event(dev, EV_REP, REP_DELAY, rep->delay); + input_inject_event(handle, EV_REP, REP_DELAY, rep->delay); if (rep->period > 0) - input_event(dev, EV_REP, REP_PERIOD, rep->period); + input_inject_event(handle, EV_REP, REP_PERIOD, rep->period); d = dev->rep[REP_DELAY]; p = dev->rep[REP_PERIOD]; } @@ -988,7 +987,7 @@ static inline unsigned char getleds(void * interrupt routines for this thing allows us to easily mask * this when we don't want any of the above to happen. * This allows for easy and efficient race-condition prevention - * for kbd_refresh_leds => input_event(dev, EV_LED, ...) => ... + * for kbd_start => input_inject_event(dev, EV_LED, ...) => ... */ static void kbd_bh(unsigned long dummy) @@ -998,11 +997,11 @@ static void kbd_bh(unsigned long dummy) if (leds != ledstate) { list_for_each(node, &kbd_handler.h_list) { - struct input_handle * handle = to_handle_h(node); - input_event(handle->dev, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_event(handle->dev, EV_LED, LED_NUML, !!(leds & 0x02)); - input_event(handle->dev, EV_LED, LED_CAPSL, !!(leds & 0x04)); - input_sync(handle->dev); + struct input_handle *handle = to_handle_h(node); + input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); + input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); + input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); + input_inject_event(handle, EV_SYN, SYN_REPORT, 0); } } @@ -1011,23 +1010,6 @@ static void kbd_bh(unsigned long dummy) DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); -/* - * This allows a newly plugged keyboard to pick the LED state. - */ -static void kbd_refresh_leds(struct input_handle *handle) -{ - unsigned char leds = ledstate; - - tasklet_disable(&keyboard_tasklet); - if (leds != 0xff) { - input_event(handle->dev, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_event(handle->dev, EV_LED, LED_NUML, !!(leds & 0x02)); - input_event(handle->dev, EV_LED, LED_CAPSL, !!(leds & 0x04)); - input_sync(handle->dev); - } - tasklet_enable(&keyboard_tasklet); -} - #if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(CONFIG_ALPHA) ||\ defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) ||\ defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\ @@ -1043,7 +1025,7 @@ static const unsigned short x86_keycodes 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,118, 86, 87, 88,115,120,119,121,112,123, 92, - 284,285,309,298,312, 91,327,328,329,331,333,335,336,337,338,339, + 284,285,309, 0,312, 91,327,328,329,331,333,335,336,337,338,339, 367,288,302,304,350, 89,334,326,267,126,268,269,125,347,348,349, 360,261,262,263,268,376,100,101,321,316,373,286,289,102,351,355, 103,104,105,275,287,279,306,106,274,107,294,364,358,363,362,361, @@ -1065,38 +1047,55 @@ #endif static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char up_flag) { - if (keycode > 255 || !x86_keycodes[keycode]) - return -1; + int code; switch (keycode) { case KEY_PAUSE: put_queue(vc, 0xe1); put_queue(vc, 0x1d | up_flag); put_queue(vc, 0x45 | up_flag); - return 0; + break; + case KEY_HANGEUL: if (!up_flag) put_queue(vc, 0xf2); - return 0; + break; + case KEY_HANJA: if (!up_flag) put_queue(vc, 0xf1); - return 0; - } + break; - if (keycode == KEY_SYSRQ && sysrq_alt) { - put_queue(vc, 0x54 | up_flag); - return 0; - } + case KEY_SYSRQ: + /* + * Real AT keyboards (that's what we're trying + * to emulate here emit 0xe0 0x2a 0xe0 0x37 when + * pressing PrtSc/SysRq alone, but simply 0x54 + * when pressing Alt+PrtSc/SysRq. + */ + if (sysrq_alt) { + put_queue(vc, 0x54 | up_flag); + } else { + put_queue(vc, 0xe0); + put_queue(vc, 0x2a | up_flag); + put_queue(vc, 0xe0); + put_queue(vc, 0x37 | up_flag); + } + break; + + default: + if (keycode > 255) + return -1; - if (x86_keycodes[keycode] & 0x100) - put_queue(vc, 0xe0); + code = x86_keycodes[keycode]; + if (!code) + return -1; - put_queue(vc, (x86_keycodes[keycode] & 0x7f) | up_flag); + if (code & 0x100) + put_queue(vc, 0xe0); + put_queue(vc, (code & 0x7f) | up_flag); - if (keycode == KEY_SYSRQ) { - put_queue(vc, 0xe0); - put_queue(vc, 0x37 | up_flag); + break; } return 0; @@ -1307,7 +1306,6 @@ static struct input_handle *kbd_connect( handle->name = "kbd"; input_open_device(handle); - kbd_refresh_leds(handle); return handle; } @@ -1318,6 +1316,24 @@ static void kbd_disconnect(struct input_ kfree(handle); } +/* + * Start keyboard handler on the new keyboard by refreshing LED state to + * match the rest of the system. + */ +static void kbd_start(struct input_handle *handle) +{ + unsigned char leds = ledstate; + + tasklet_disable(&keyboard_tasklet); + if (leds != 0xff) { + input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); + input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); + input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); + input_inject_event(handle, EV_SYN, SYN_REPORT, 0); + } + tasklet_enable(&keyboard_tasklet); +} + static struct input_device_id kbd_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT, @@ -1338,6 +1354,7 @@ static struct input_handler kbd_handler .event = kbd_event, .connect = kbd_connect, .disconnect = kbd_disconnect, + .start = kbd_start, .name = "kbd", .id_table = kbd_ids, }; @@ -1346,15 +1363,15 @@ int __init kbd_init(void) { int i; - kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS; - kbd0.ledmode = LED_SHOW_FLAGS; - kbd0.lockstate = KBD_DEFLOCK; - kbd0.slockstate = 0; - kbd0.modeflags = KBD_DEFMODE; - kbd0.kbdmode = VC_XLATE; - - for (i = 0 ; i < MAX_NR_CONSOLES ; i++) - kbd_table[i] = kbd0; + for (i = 0; i < MAX_NR_CONSOLES; i++) { + kbd_table[i].ledflagstate = KBD_DEFLEDS; + kbd_table[i].default_ledflagstate = KBD_DEFLEDS; + kbd_table[i].ledmode = LED_SHOW_FLAGS; + kbd_table[i].lockstate = KBD_DEFLOCK; + kbd_table[i].slockstate = 0; + kbd_table[i].modeflags = KBD_DEFMODE; + kbd_table[i].kbdmode = VC_XLATE; + } input_register_handler(&kbd_handler); diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 58223b5..9623231 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -24,6 +24,20 @@ config INPUT if INPUT +config INPUT_FF_MEMLESS + tristate "Support for memoryless force-feedback devices" + default n + ---help--- + Say Y here if you have memoryless force-feedback input device + such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual + Power 2, or similar. You will also need to enable hardware-specific + driver. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ff-memless. + comment "Userland interfaces" config INPUT_MOUSEDEV diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 1a6ff49..a005b1d 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -4,7 +4,11 @@ # # Each configuration option enables a list of files. -obj-$(CONFIG_INPUT) += input.o +obj-$(CONFIG_INPUT) += input-core.o +input-core-objs := input.o ff-core.o + +obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o + obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index a29d5ce..12c7ab8 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -127,14 +127,10 @@ static int evdev_open(struct inode * ino { struct evdev_list *list; int i = iminor(inode) - EVDEV_MINOR_BASE; - int accept_err; if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist) return -ENODEV; - if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file))) - return accept_err; - if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL))) return -ENOMEM; @@ -260,7 +256,7 @@ static ssize_t evdev_write(struct file * if (evdev_event_from_user(buffer + retval, &event)) return -EFAULT; - input_event(list->evdev->handle.dev, event.type, event.code, event.value); + input_inject_event(&list->evdev->handle, event.type, event.code, event.value); retval += evdev_event_size(); } @@ -395,8 +391,10 @@ static long evdev_ioctl_handler(struct f struct evdev *evdev = list->evdev; struct input_dev *dev = evdev->handle.dev; struct input_absinfo abs; + struct ff_effect effect; int __user *ip = (int __user *)p; int i, t, u, v; + int error; if (!evdev->exist) return -ENODEV; @@ -428,8 +426,8 @@ static long evdev_ioctl_handler(struct f if (get_user(v, ip + 1)) return -EFAULT; - input_event(dev, EV_REP, REP_DELAY, u); - input_event(dev, EV_REP, REP_PERIOD, v); + input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u); + input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v); return 0; @@ -464,27 +462,22 @@ static long evdev_ioctl_handler(struct f return 0; case EVIOCSFF: - if (dev->upload_effect) { - struct ff_effect effect; - int err; - - if (copy_from_user(&effect, p, sizeof(effect))) - return -EFAULT; - err = dev->upload_effect(dev, &effect); - if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) - return -EFAULT; - return err; - } else - return -ENOSYS; + if (copy_from_user(&effect, p, sizeof(effect))) + return -EFAULT; - case EVIOCRMFF: - if (!dev->erase_effect) - return -ENOSYS; + error = input_ff_upload(dev, &effect, file); - return dev->erase_effect(dev, (int)(unsigned long) p); + if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) + return -EFAULT; + + return error; + + case EVIOCRMFF: + return input_ff_erase(dev, (int)(unsigned long) p, file); case EVIOCGEFFECTS: - if (put_user(dev->ff_effects_max, ip)) + i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0; + if (put_user(i, ip)) return -EFAULT; return 0; @@ -673,6 +666,7 @@ static void evdev_disconnect(struct inpu evdev->exist = 0; if (evdev->open) { + input_flush_device(handle, NULL); input_close_device(handle); wake_up_interruptible(&evdev->wait); list_for_each_entry(list, &evdev->list, node) diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c new file mode 100644 index 0000000..35656ca --- /dev/null +++ b/drivers/input/ff-core.c @@ -0,0 +1,367 @@ +/* + * Force feedback support for Linux input subsystem + * + * Copyright (c) 2006 Anssi Hannula + * Copyright (c) 2006 Dmitry Torokhov + */ + +/* + * 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 + */ + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("ff-core: " format "\n", ## arg) + +#include +#include +#include + +/* + * Check that the effect_id is a valid effect and whether the user + * is the owner + */ +static int check_effect_access(struct ff_device *ff, int effect_id, + struct file *file) +{ + if (effect_id < 0 || effect_id >= ff->max_effects || + !ff->effect_owners[effect_id]) + return -EINVAL; + + if (file && ff->effect_owners[effect_id] != file) + return -EACCES; + + return 0; +} + +/* + * Checks whether 2 effects can be combined together + */ +static inline int check_effects_compatible(struct ff_effect *e1, + struct ff_effect *e2) +{ + return e1->type == e2->type && + (e1->type != FF_PERIODIC || + e1->u.periodic.waveform == e2->u.periodic.waveform); +} + +/* + * Convert an effect into compatible one + */ +static int compat_effect(struct ff_device *ff, struct ff_effect *effect) +{ + int magnitude; + + switch (effect->type) { + case FF_RUMBLE: + if (!test_bit(FF_PERIODIC, ff->ffbit)) + return -EINVAL; + + /* + * calculate manginude of sine wave as average of rumble's + * 2/3 of strong magnitude and 1/3 of weak magnitude + */ + magnitude = effect->u.rumble.strong_magnitude / 3 + + effect->u.rumble.weak_magnitude / 6; + + effect->type = FF_PERIODIC; + effect->u.periodic.waveform = FF_SINE; + effect->u.periodic.period = 50; + effect->u.periodic.magnitude = max(magnitude, 0x7fff); + effect->u.periodic.offset = 0; + effect->u.periodic.phase = 0; + effect->u.periodic.envelope.attack_length = 0; + effect->u.periodic.envelope.attack_level = 0; + effect->u.periodic.envelope.fade_length = 0; + effect->u.periodic.envelope.fade_level = 0; + + return 0; + + default: + /* Let driver handle conversion */ + return 0; + } +} + +/** + * input_ff_upload() - upload effect into force-feedback device + * @dev: input device + * @effect: effect to be uploaded + * @file: owner of the effect + */ +int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, + struct file *file) +{ + struct ff_device *ff = dev->ff; + struct ff_effect *old; + int ret = 0; + int id; + + if (!test_bit(EV_FF, dev->evbit)) + return -ENOSYS; + + if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX || + !test_bit(effect->type, dev->ffbit)) { + debug("invalid or not supported effect type in upload"); + return -EINVAL; + } + + if (effect->type == FF_PERIODIC && + (effect->u.periodic.waveform < FF_WAVEFORM_MIN || + effect->u.periodic.waveform > FF_WAVEFORM_MAX || + !test_bit(effect->u.periodic.waveform, dev->ffbit))) { + debug("invalid or not supported wave form in upload"); + return -EINVAL; + } + + if (!test_bit(effect->type, ff->ffbit)) { + ret = compat_effect(ff, effect); + if (ret) + return ret; + } + + mutex_lock(&ff->mutex); + + if (effect->id == -1) { + for (id = 0; id < ff->max_effects; id++) + if (!ff->effect_owners[id]) + break; + + if (id >= ff->max_effects) { + ret = -ENOSPC; + goto out; + } + + effect->id = id; + old = NULL; + + } else { + id = effect->id; + + ret = check_effect_access(ff, id, file); + if (ret) + goto out; + + old = &ff->effects[id]; + + if (!check_effects_compatible(effect, old)) { + ret = -EINVAL; + goto out; + } + } + + ret = ff->upload(dev, effect, old); + if (ret) + goto out; + + ff->effects[id] = *effect; + ff->effect_owners[id] = file; + + out: + mutex_unlock(&ff->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(input_ff_upload); + +/* + * Erases the effect if the requester is also the effect owner. The mutex + * should already be locked before calling this function. + */ +static int erase_effect(struct input_dev *dev, int effect_id, + struct file *file) +{ + struct ff_device *ff = dev->ff; + int error; + + error = check_effect_access(ff, effect_id, file); + if (error) + return error; + + ff->playback(dev, effect_id, 0); + + if (ff->erase) { + error = ff->erase(dev, effect_id); + if (error) + return error; + } + + ff->effect_owners[effect_id] = NULL; + + return 0; +} + +/** + * input_ff_erase - erase an effect from device + * @dev: input device to erase effect from + * @effect_id: id of the ffect to be erased + * @file: purported owner of the request + * + * This function erases a force-feedback effect from specified device. + * The effect will only be erased if it was uploaded through the same + * file handle that is requesting erase. + */ +int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file) +{ + struct ff_device *ff = dev->ff; + int ret; + + if (!test_bit(EV_FF, dev->evbit)) + return -ENOSYS; + + mutex_lock(&ff->mutex); + ret = erase_effect(dev, effect_id, file); + mutex_unlock(&ff->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(input_ff_erase); + +/* + * flush_effects - erase all effects owned by a file handle + */ +static int flush_effects(struct input_dev *dev, struct file *file) +{ + struct ff_device *ff = dev->ff; + int i; + + debug("flushing now"); + + mutex_lock(&ff->mutex); + + for (i = 0; i < ff->max_effects; i++) + erase_effect(dev, i, file); + + mutex_unlock(&ff->mutex); + + return 0; +} + +/** + * input_ff_event() - generic handler for force-feedback events + * @dev: input device to send the effect to + * @type: event type (anything but EV_FF is ignored) + * @code: event code + * @value: event value + */ +int input_ff_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct ff_device *ff = dev->ff; + + if (type != EV_FF) + return 0; + + mutex_lock(&ff->mutex); + + switch (code) { + case FF_GAIN: + if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffff) + break; + + ff->set_gain(dev, value); + break; + + case FF_AUTOCENTER: + if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffff) + break; + + ff->set_autocenter(dev, value); + break; + + default: + ff->playback(dev, code, value); + break; + } + + mutex_unlock(&ff->mutex); + return 0; +} +EXPORT_SYMBOL_GPL(input_ff_event); + +/** + * input_ff_create() - create force-feedback device + * @dev: input device supporting force-feedback + * @max_effects: maximum number of effects supported by the device + * + * This function allocates all necessary memory for a force feedback + * portion of an input device and installs all default handlers. + * @dev->ffbit should be already set up before calling this function. + * Once ff device is created you need to setup its upload, erase, + * playback and other handlers before registering input device + */ +int input_ff_create(struct input_dev *dev, int max_effects) +{ + struct ff_device *ff; + int i; + + if (!max_effects) { + printk(KERN_ERR + "ff-core: cannot allocate device without any effects\n"); + return -EINVAL; + } + + ff = kzalloc(sizeof(struct ff_device) + + max_effects * sizeof(struct file *), GFP_KERNEL); + if (!ff) + return -ENOMEM; + + ff->effects = kcalloc(max_effects, sizeof(struct ff_effect), + GFP_KERNEL); + if (!ff->effects) { + kfree(ff); + return -ENOMEM; + } + + ff->max_effects = max_effects; + mutex_init(&ff->mutex); + + dev->ff = ff; + dev->flush = flush_effects; + dev->event = input_ff_event; + set_bit(EV_FF, dev->evbit); + + /* Copy "true" bits into ff device bitmap */ + for (i = 0; i <= FF_MAX; i++) + if (test_bit(i, dev->ffbit)) + set_bit(i, ff->ffbit); + + /* we can emulate RUMBLE with periodic effects */ + if (test_bit(FF_PERIODIC, ff->ffbit)) + set_bit(FF_RUMBLE, dev->ffbit); + + return 0; +} +EXPORT_SYMBOL_GPL(input_ff_create); + +/** + * input_ff_free() - frees force feedback portion of input device + * @dev: input device supporintg force feedback + * + * This function is only needed in error path as input core will + * automatically free force feedback structures when device is + * destroyed. + */ +void input_ff_destroy(struct input_dev *dev) +{ + clear_bit(EV_FF, dev->evbit); + if (dev->ff) { + if (dev->ff->destroy) + dev->ff->destroy(dev->ff); + kfree(dev->ff->private); + kfree(dev->ff); + dev->ff = NULL; + } +} +EXPORT_SYMBOL_GPL(input_ff_destroy); diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c new file mode 100644 index 0000000..cd8b729 --- /dev/null +++ b/drivers/input/ff-memless.c @@ -0,0 +1,515 @@ +/* + * Force feedback support for memoryless devices + * + * Copyright (c) 2006 Anssi Hannula + * Copyright (c) 2006 Dmitry Torokhov + */ + +/* + * 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 + */ + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("ff-memless: " format "\n", ## arg) + +#include +#include +#include +#include +#include + +#include "fixp-arith.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anssi Hannula "); +MODULE_DESCRIPTION("Force feedback support for memoryless devices"); + +/* Number of effects handled with memoryless devices */ +#define FF_MEMLESS_EFFECTS 16 + +/* Envelope update interval in ms */ +#define FF_ENVELOPE_INTERVAL 50 + +#define FF_EFFECT_STARTED 0 +#define FF_EFFECT_PLAYING 1 +#define FF_EFFECT_ABORTING 2 + +struct ml_effect_state { + struct ff_effect *effect; + unsigned long flags; /* effect state (STARTED, PLAYING, etc) */ + int count; /* loop count of the effect */ + unsigned long play_at; /* start time */ + unsigned long stop_at; /* stop time */ + unsigned long adj_at; /* last time the effect was sent */ +}; + +struct ml_device { + void *private; + struct ml_effect_state states[FF_MEMLESS_EFFECTS]; + int gain; + struct timer_list timer; + spinlock_t timer_lock; + struct input_dev *dev; + + int (*play_effect)(struct input_dev *dev, void *data, + struct ff_effect *effect); +}; + +static const struct ff_envelope *get_envelope(const struct ff_effect *effect) +{ + static const struct ff_envelope empty_envelope; + + switch (effect->type) { + case FF_PERIODIC: + return &effect->u.periodic.envelope; + case FF_CONSTANT: + return &effect->u.constant.envelope; + default: + return &empty_envelope; + } +} + +/* + * Check for the next time envelope requires an update on memoryless devices + */ +static unsigned long calculate_next_time(struct ml_effect_state *state) +{ + const struct ff_envelope *envelope = get_envelope(state->effect); + unsigned long attack_stop, fade_start, next_fade; + + if (envelope->attack_length) { + attack_stop = state->play_at + + msecs_to_jiffies(envelope->attack_length); + if (time_before(state->adj_at, attack_stop)) + return state->adj_at + + msecs_to_jiffies(FF_ENVELOPE_INTERVAL); + } + + if (state->effect->replay.length) { + if (envelope->fade_length) { + /* check when fading should start */ + fade_start = state->stop_at - + msecs_to_jiffies(envelope->fade_length); + + if (time_before(state->adj_at, fade_start)) + return fade_start; + + /* already fading, advance to next checkpoint */ + next_fade = state->adj_at + + msecs_to_jiffies(FF_ENVELOPE_INTERVAL); + if (time_before(next_fade, state->stop_at)) + return next_fade; + } + + return state->stop_at; + } + + return state->play_at; +} + +static void ml_schedule_timer(struct ml_device *ml) +{ + struct ml_effect_state *state; + unsigned long now = jiffies; + unsigned long earliest = 0; + unsigned long next_at; + int events = 0; + int i; + + debug("calculating next timer"); + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) { + + state = &ml->states[i]; + + if (!test_bit(FF_EFFECT_STARTED, &state->flags)) + continue; + + if (test_bit(FF_EFFECT_PLAYING, &state->flags)) + next_at = calculate_next_time(state); + else + next_at = state->play_at; + + if (time_before_eq(now, next_at) && + (++events == 1 || time_before(next_at, earliest))) + earliest = next_at; + } + + if (!events) { + debug("no actions"); + del_timer(&ml->timer); + } else { + debug("timer set"); + mod_timer(&ml->timer, earliest); + } +} + +/* + * Apply an envelope to a value + */ +static int apply_envelope(struct ml_effect_state *state, int value, + struct ff_envelope *envelope) +{ + struct ff_effect *effect = state->effect; + unsigned long now = jiffies; + int time_from_level; + int time_of_envelope; + int envelope_level; + int difference; + + if (envelope->attack_length && + time_before(now, + state->play_at + msecs_to_jiffies(envelope->attack_length))) { + debug("value = 0x%x, attack_level = 0x%x", value, + envelope->attack_level); + time_from_level = jiffies_to_msecs(now - state->play_at); + time_of_envelope = envelope->attack_length; + envelope_level = min_t(__s16, envelope->attack_level, 0x7fff); + + } else if (envelope->fade_length && effect->replay.length && + time_after(now, + state->stop_at - msecs_to_jiffies(envelope->fade_length)) && + time_before(now, state->stop_at)) { + time_from_level = jiffies_to_msecs(state->stop_at - now); + time_of_envelope = envelope->fade_length; + envelope_level = min_t(__s16, envelope->fade_level, 0x7fff); + } else + return value; + + difference = abs(value) - envelope_level; + + debug("difference = %d", difference); + debug("time_from_level = 0x%x", time_from_level); + debug("time_of_envelope = 0x%x", time_of_envelope); + + difference = difference * time_from_level / time_of_envelope; + + debug("difference = %d", difference); + + return value < 0 ? + -(difference + envelope_level) : (difference + envelope_level); +} + +/* + * Return the type the effect has to be converted into (memless devices) + */ +static int get_compatible_type(struct ff_device *ff, int effect_type) +{ + + if (test_bit(effect_type, ff->ffbit)) + return effect_type; + + if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit)) + return FF_RUMBLE; + + printk(KERN_ERR + "ff-memless: invalid type in get_compatible_type()\n"); + + return 0; +} + +/* + * Combine two effects and apply gain. + */ +static void ml_combine_effects(struct ff_effect *effect, + struct ml_effect_state *state, + int gain) +{ + struct ff_effect *new = state->effect; + unsigned int strong, weak, i; + int x, y; + fixp_t level; + + switch (new->type) { + case FF_CONSTANT: + i = new->direction * 360 / 0xffff; + level = fixp_new16(apply_envelope(state, + new->u.constant.level, + &new->u.constant.envelope)); + x = fixp_mult(fixp_sin(i), level) * gain / 0xffff; + y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff; + /* + * here we abuse ff_ramp to hold x and y of constant force + * If in future any driver wants something else than x and y + * in s8, this should be changed to something more generic + */ + effect->u.ramp.start_level = + max(min(effect->u.ramp.start_level + x, 0x7f), -0x80); + effect->u.ramp.end_level = + max(min(effect->u.ramp.end_level + y, 0x7f), -0x80); + break; + + case FF_RUMBLE: + strong = new->u.rumble.strong_magnitude * gain / 0xffff; + weak = new->u.rumble.weak_magnitude * gain / 0xffff; + effect->u.rumble.strong_magnitude = + min(strong + effect->u.rumble.strong_magnitude, + 0xffffU); + effect->u.rumble.weak_magnitude = + min(weak + effect->u.rumble.weak_magnitude, 0xffffU); + break; + + case FF_PERIODIC: + i = apply_envelope(state, abs(new->u.periodic.magnitude), + &new->u.periodic.envelope); + + /* here we also scale it 0x7fff => 0xffff */ + i = i * gain / 0x7fff; + + effect->u.rumble.strong_magnitude = + min(i + effect->u.rumble.strong_magnitude, 0xffffU); + effect->u.rumble.weak_magnitude = + min(i + effect->u.rumble.weak_magnitude, 0xffffU); + break; + + default: + printk(KERN_ERR "ff-memless: invalid type in ml_combine_effects()\n"); + break; + } + +} + + +/* + * Because memoryless devices have only one effect per effect type active + * at one time we have to combine multiple effects into one + */ +static int ml_get_combo_effect(struct ml_device *ml, + unsigned long *effect_handled, + struct ff_effect *combo_effect) +{ + struct ff_effect *effect; + struct ml_effect_state *state; + int effect_type; + int i; + + memset(combo_effect, 0, sizeof(struct ff_effect)); + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) { + if (__test_and_set_bit(i, effect_handled)) + continue; + + state = &ml->states[i]; + effect = state->effect; + + if (!test_bit(FF_EFFECT_STARTED, &state->flags)) + continue; + + if (time_before(jiffies, state->play_at)) + continue; + + /* + * here we have started effects that are either + * currently playing (and may need be aborted) + * or need to start playing. + */ + effect_type = get_compatible_type(ml->dev->ff, effect->type); + if (combo_effect->type != effect_type) { + if (combo_effect->type != 0) { + __clear_bit(i, effect_handled); + continue; + } + combo_effect->type = effect_type; + } + + if (__test_and_clear_bit(FF_EFFECT_ABORTING, &state->flags)) { + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + __clear_bit(FF_EFFECT_STARTED, &state->flags); + } else if (effect->replay.length && + time_after_eq(jiffies, state->stop_at)) { + + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + + if (--state->count <= 0) { + __clear_bit(FF_EFFECT_STARTED, &state->flags); + } else { + state->play_at = jiffies + + msecs_to_jiffies(effect->replay.delay); + state->stop_at = state->play_at + + msecs_to_jiffies(effect->replay.length); + } + } else { + __set_bit(FF_EFFECT_PLAYING, &state->flags); + state->adj_at = jiffies; + ml_combine_effects(combo_effect, state, ml->gain); + } + } + + return combo_effect->type != 0; +} + +static void ml_play_effects(struct ml_device *ml) +{ + struct ff_effect effect; + DECLARE_BITMAP(handled_bm, FF_MEMLESS_EFFECTS); + + memset(handled_bm, 0, sizeof(handled_bm)); + + while (ml_get_combo_effect(ml, handled_bm, &effect)) + ml->play_effect(ml->dev, ml->private, &effect); + + ml_schedule_timer(ml); +} + +static void ml_effect_timer(unsigned long timer_data) +{ + struct input_dev *dev = (struct input_dev *)timer_data; + struct ml_device *ml = dev->ff->private; + + debug("timer: updating effects"); + + spin_lock(&ml->timer_lock); + ml_play_effects(ml); + spin_unlock(&ml->timer_lock); +} + +static void ml_ff_set_gain(struct input_dev *dev, u16 gain) +{ + struct ml_device *ml = dev->ff->private; + int i; + + spin_lock_bh(&ml->timer_lock); + + ml->gain = gain; + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) + __clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags); + + ml_play_effects(ml); + + spin_unlock_bh(&ml->timer_lock); +} + +static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) +{ + struct ml_device *ml = dev->ff->private; + struct ml_effect_state *state = &ml->states[effect_id]; + + spin_lock_bh(&ml->timer_lock); + + if (value > 0) { + debug("initiated play"); + + __set_bit(FF_EFFECT_STARTED, &state->flags); + state->count = value; + state->play_at = jiffies + + msecs_to_jiffies(state->effect->replay.delay); + state->stop_at = state->play_at + + msecs_to_jiffies(state->effect->replay.length); + state->adj_at = state->play_at; + + ml_schedule_timer(ml); + + } else { + debug("initiated stop"); + + if (test_bit(FF_EFFECT_PLAYING, &state->flags)) + __set_bit(FF_EFFECT_ABORTING, &state->flags); + else + __clear_bit(FF_EFFECT_STARTED, &state->flags); + + ml_play_effects(ml); + } + + spin_unlock_bh(&ml->timer_lock); + + return 0; +} + +static int ml_ff_upload(struct input_dev *dev, + struct ff_effect *effect, struct ff_effect *old) +{ + struct ml_device *ml = dev->ff->private; + struct ml_effect_state *state = &ml->states[effect->id]; + + spin_lock_bh(&ml->timer_lock); + + if (test_bit(FF_EFFECT_STARTED, &state->flags)) { + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + state->play_at = jiffies + + msecs_to_jiffies(state->effect->replay.delay); + state->stop_at = state->play_at + + msecs_to_jiffies(state->effect->replay.length); + state->adj_at = state->play_at; + ml_schedule_timer(ml); + } + + spin_unlock_bh(&ml->timer_lock); + + return 0; +} + +static void ml_ff_destroy(struct ff_device *ff) +{ + struct ml_device *ml = ff->private; + + kfree(ml->private); +} + +/** + * input_ff_create_memless() - create memoryless FF device + * @dev: input device supporting force-feedback + * @data: driver-specific data to be passed into @play_effect + * @play_effect: driver-specific method for playing FF effect + */ +int input_ff_create_memless(struct input_dev *dev, void *data, + int (*play_effect)(struct input_dev *, void *, struct ff_effect *)) +{ + struct ml_device *ml; + struct ff_device *ff; + int error; + int i; + + ml = kzalloc(sizeof(struct ml_device), GFP_KERNEL); + if (!ml) + return -ENOMEM; + + ml->dev = dev; + ml->private = data; + ml->play_effect = play_effect; + ml->gain = 0xffff; + spin_lock_init(&ml->timer_lock); + setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev); + + set_bit(FF_GAIN, dev->ffbit); + + error = input_ff_create(dev, FF_MEMLESS_EFFECTS); + if (error) { + kfree(ml); + return error; + } + + ff = dev->ff; + ff->private = ml; + ff->upload = ml_ff_upload; + ff->playback = ml_ff_playback; + ff->set_gain = ml_ff_set_gain; + ff->destroy = ml_ff_destroy; + + /* we can emulate periodic effects with RUMBLE */ + if (test_bit(FF_RUMBLE, ff->ffbit)) { + set_bit(FF_PERIODIC, dev->ffbit); + set_bit(FF_SINE, dev->ffbit); + set_bit(FF_TRIANGLE, dev->ffbit); + set_bit(FF_SQUARE, dev->ffbit); + } + + for (i = 0; i < FF_MEMLESS_EFFECTS; i++) + ml->states[i].effect = &ff->effects[i]; + + return 0; +} +EXPORT_SYMBOL_GPL(input_ff_create_memless); diff --git a/drivers/input/fixp-arith.h b/drivers/input/fixp-arith.h new file mode 100644 index 0000000..ed3d2da --- /dev/null +++ b/drivers/input/fixp-arith.h @@ -0,0 +1,87 @@ +#ifndef _FIXP_ARITH_H +#define _FIXP_ARITH_H + +/* + * Simplistic fixed-point arithmetics. + * Hmm, I'm probably duplicating some code :( + * + * Copyright (c) 2002 Johann Deneux + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so by + * e-mail - mail your message to + */ + +#include + +/* The type representing fixed-point values */ +typedef s16 fixp_t; + +#define FRAC_N 8 +#define FRAC_MASK ((1< 123.0 */ +static inline fixp_t fixp_new(s16 a) +{ + return a< -1.0 + 0x8000 -> 1.0 + 0x0000 -> 0.0 +*/ +static inline fixp_t fixp_new16(s16 a) +{ + return ((s32)a)>>(16-FRAC_N); +} + +static inline fixp_t fixp_cos(unsigned int degrees) +{ + int quadrant = (degrees / 90) & 3; + unsigned int i = degrees % 90; + + if (quadrant == 1 || quadrant == 3) + i = 90 - i; + + i >>= 1; + + return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i]; +} + +static inline fixp_t fixp_sin(unsigned int degrees) +{ + return -fixp_cos(degrees + 90); +} + +static inline fixp_t fixp_mult(fixp_t a, fixp_t b) +{ + return ((s32)(a*b))>>FRAC_N; +} + +#endif diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c index 47e93da..90de5af 100644 --- a/drivers/input/gameport/fm801-gp.c +++ b/drivers/input/gameport/fm801-gp.c @@ -106,10 +106,10 @@ #endif gp->gameport = port; gp->res_port = request_region(port->io, 0x10, "FM801 GP"); if (!gp->res_port) { - kfree(gp); - gameport_free_port(port); printk(KERN_DEBUG "fm801-gp: unable to grab region 0x%x-0x%x\n", port->io, port->io + 0x0f); + gameport_free_port(port); + kfree(gp); return -EBUSY; } diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 36644bf..3f47ae5 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -53,6 +53,7 @@ static LIST_HEAD(gameport_list); static struct bus_type gameport_bus; +static void gameport_add_driver(struct gameport_driver *drv); static void gameport_add_port(struct gameport *gameport); static void gameport_destroy_port(struct gameport *gameport); static void gameport_reconnect_port(struct gameport *gameport); @@ -211,8 +212,14 @@ static void gameport_release_driver(stru static void gameport_find_driver(struct gameport *gameport) { + int error; + down_write(&gameport_bus.subsys.rwsem); - device_attach(&gameport->dev); + error = device_attach(&gameport->dev); + if (error < 0) + printk(KERN_WARNING + "gameport: device_attach() failed for %s (%s), error: %d\n", + gameport->phys, gameport->name, error); up_write(&gameport_bus.subsys.rwsem); } @@ -316,7 +323,6 @@ static void gameport_remove_duplicate_ev spin_unlock_irqrestore(&gameport_event_lock, flags); } - static struct gameport_event *gameport_get_event(void) { struct gameport_event *event; @@ -342,7 +348,6 @@ static struct gameport_event *gameport_g static void gameport_handle_event(void) { struct gameport_event *event; - struct gameport_driver *gameport_drv; mutex_lock(&gameport_mutex); @@ -369,8 +374,7 @@ static void gameport_handle_event(void) break; case GAMEPORT_REGISTER_DRIVER: - gameport_drv = event->object; - driver_register(&gameport_drv->driver); + gameport_add_driver(event->object); break; default: @@ -532,6 +536,7 @@ static void gameport_init_port(struct ga if (gameport->parent) gameport->dev.parent = &gameport->parent->dev; + INIT_LIST_HEAD(&gameport->node); spin_lock_init(&gameport->timer_lock); init_timer(&gameport->poll_timer); gameport->poll_timer.function = gameport_run_poll_handler; @@ -544,6 +549,8 @@ static void gameport_init_port(struct ga */ static void gameport_add_port(struct gameport *gameport) { + int error; + if (gameport->parent) gameport->parent->child = gameport; @@ -558,8 +565,13 @@ static void gameport_add_port(struct gam printk(KERN_INFO "gameport: %s is %s, speed %dkHz\n", gameport->name, gameport->phys, gameport->speed); - device_add(&gameport->dev); - gameport->registered = 1; + error = device_add(&gameport->dev); + if (error) + printk(KERN_ERR + "gameport: device_add() failed for %s (%s), error: %d\n", + gameport->phys, gameport->name, error); + else + gameport->registered = 1; } /* @@ -583,10 +595,11 @@ static void gameport_destroy_port(struct if (gameport->registered) { device_del(&gameport->dev); - list_del_init(&gameport->node); gameport->registered = 0; } + list_del_init(&gameport->node); + gameport_remove_pending_events(gameport); put_device(&gameport->dev); } @@ -704,11 +717,22 @@ static int gameport_driver_remove(struct } static struct bus_type gameport_bus = { - .name = "gameport", - .probe = gameport_driver_probe, - .remove = gameport_driver_remove, + .name = "gameport", + .probe = gameport_driver_probe, + .remove = gameport_driver_remove, }; +static void gameport_add_driver(struct gameport_driver *drv) +{ + int error; + + error = driver_register(&drv->driver); + if (error) + printk(KERN_ERR + "gameport: driver_register() failed for %s, error: %d\n", + drv->driver.name, error); +} + void __gameport_register_driver(struct gameport_driver *drv, struct module *owner) { drv->driver.bus = &gameport_bus; @@ -778,16 +802,24 @@ void gameport_close(struct gameport *gam static int __init gameport_init(void) { - gameport_task = kthread_run(gameport_thread, NULL, "kgameportd"); - if (IS_ERR(gameport_task)) { - printk(KERN_ERR "gameport: Failed to start kgameportd\n"); - return PTR_ERR(gameport_task); - } + int error; gameport_bus.dev_attrs = gameport_device_attrs; gameport_bus.drv_attrs = gameport_driver_attrs; gameport_bus.match = gameport_bus_match; - bus_register(&gameport_bus); + error = bus_register(&gameport_bus); + if (error) { + printk(KERN_ERR "gameport: failed to register gameport bus, error: %d\n", error); + return error; + } + + gameport_task = kthread_run(gameport_thread, NULL, "kgameportd"); + if (IS_ERR(gameport_task)) { + bus_unregister(&gameport_bus); + error = PTR_ERR(gameport_task); + printk(KERN_ERR "gameport: Failed to start kgameportd, error: %d\n", error); + return error; + } return 0; } diff --git a/drivers/input/input.c b/drivers/input/input.c index a90486f..1fc0517 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -35,6 +35,16 @@ static LIST_HEAD(input_handler_list); static struct input_handler *input_table[8]; +/** + * input_event() - report new input event + * @handle: device that generated the event + * @type: type of the event + * @code: event code + * @value: value of the event + * + * This function should be used by drivers implementing various input devices + * See also input_inject_event() + */ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct input_handle *handle; @@ -166,6 +176,10 @@ void input_event(struct input_dev *dev, break; case EV_FF: + + if (value < 0) + return; + if (dev->event) dev->event(dev, type, code, value); break; @@ -183,6 +197,23 @@ void input_event(struct input_dev *dev, } EXPORT_SYMBOL(input_event); +/** + * input_inject_event() - send input event from input handler + * @handle: input handle to send event through + * @type: type of the event + * @code: event code + * @value: value of the event + * + * Similar to input_event() but will ignore event if device is "grabbed" and handle + * injecting event is not the one that owns the device. + */ +void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) +{ + if (!handle->dev->grab || handle->dev->grab == handle) + input_event(handle->dev, type, code, value); +} +EXPORT_SYMBOL(input_inject_event); + static void input_repeat_key(unsigned long data) { struct input_dev *dev = (void *) data; @@ -197,15 +228,6 @@ static void input_repeat_key(unsigned lo mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD])); } -int input_accept_process(struct input_handle *handle, struct file *file) -{ - if (handle->dev->accept) - return handle->dev->accept(handle->dev, file); - - return 0; -} -EXPORT_SYMBOL(input_accept_process); - int input_grab_device(struct input_handle *handle) { if (handle->dev->grab) @@ -218,8 +240,15 @@ EXPORT_SYMBOL(input_grab_device); void input_release_device(struct input_handle *handle) { - if (handle->dev->grab == handle) - handle->dev->grab = NULL; + struct input_dev *dev = handle->dev; + + if (dev->grab == handle) { + dev->grab = NULL; + + list_for_each_entry(handle, &dev->h_list, d_node) + if (handle->handler->start) + handle->handler->start(handle); + } } EXPORT_SYMBOL(input_release_device); @@ -737,7 +766,9 @@ static void input_dev_release(struct cla { struct input_dev *dev = to_input_dev(class_dev); + input_ff_destroy(dev); kfree(dev); + module_put(THIS_MODULE); } @@ -963,8 +994,11 @@ int input_register_device(struct input_d list_for_each_entry(handler, &input_handler_list, node) if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) if ((id = input_match_device(handler->id_table, dev))) - if ((handle = handler->connect(handler, dev, id))) + if ((handle = handler->connect(handler, dev, id))) { input_link_handle(handle); + if (handler->start) + handler->start(handle); + } input_wakeup_procfs_readers(); @@ -1028,8 +1062,11 @@ void input_register_handler(struct input list_for_each_entry(dev, &input_dev_list, node) if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) if ((id = input_match_device(handler->id_table, dev))) - if ((handle = handler->connect(handler, dev, id))) + if ((handle = handler->connect(handler, dev, id))) { input_link_handle(handle); + if (handler->start) + handler->start(handle); + } input_wakeup_procfs_readers(); } diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c index 50c9076..8fb0c19 100644 --- a/drivers/input/joystick/iforce/iforce-ff.c +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -165,19 +165,19 @@ static int make_condition_modifier(struc data[0] = LO(mod_chunk->start); data[1] = HI(mod_chunk->start); - data[2] = (100*rk)>>15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */ - data[3] = (100*lk)>>15; /* This code is incorrect on cpus lacking arith shift */ + data[2] = (100 * rk) >> 15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */ + data[3] = (100 * lk) >> 15; /* This code is incorrect on cpus lacking arith shift */ - center = (500*center)>>15; + center = (500 * center) >> 15; data[4] = LO(center); data[5] = HI(center); - db = (1000*db)>>16; + db = (1000 * db) >> 16; data[6] = LO(db); data[7] = HI(db); - data[8] = (100*rsat)>>16; - data[9] = (100*lsat)>>16; + data[8] = (100 * rsat) >> 16; + data[9] = (100 * lsat) >> 16; iforce_send_packet(iforce, FF_CMD_CONDITION, data); iforce_dump_packet("condition", FF_CMD_CONDITION, data); @@ -188,6 +188,7 @@ static int make_condition_modifier(struc static unsigned char find_button(struct iforce *iforce, signed short button) { int i; + for (i = 1; iforce->type->btn[i] >= 0; i++) if (iforce->type->btn[i] == button) return i + 1; @@ -198,19 +199,17 @@ static unsigned char find_button(struct * Analyse the changes in an effect, and tell if we need to send an condition * parameter packet */ -static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new) +static int need_condition_modifier(struct ff_effect *old, struct ff_effect *new) { - int id = new->id; - struct ff_effect* old = &iforce->core_effects[id].effect; - int ret=0; + int ret = 0; int i; if (new->type != FF_SPRING && new->type != FF_FRICTION) { printk(KERN_WARNING "iforce.c: bad effect type in need_condition_modifier\n"); - return FALSE; + return 0; } - for(i=0; i<2; i++) { + for (i = 0; i < 2; i++) { ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation || old->u.condition[i].left_saturation != new->u.condition[i].left_saturation || old->u.condition[i].right_coeff != new->u.condition[i].right_coeff @@ -225,35 +224,29 @@ static int need_condition_modifier(struc * Analyse the changes in an effect, and tell if we need to send a magnitude * parameter packet */ -static int need_magnitude_modifier(struct iforce* iforce, struct ff_effect* effect) +static int need_magnitude_modifier(struct ff_effect *old, struct ff_effect *effect) { - int id = effect->id; - struct ff_effect* old = &iforce->core_effects[id].effect; - if (effect->type != FF_CONSTANT) { printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n"); - return FALSE; + return 0; } - return (old->u.constant.level != effect->u.constant.level); + return old->u.constant.level != effect->u.constant.level; } /* * Analyse the changes in an effect, and tell if we need to send an envelope * parameter packet */ -static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effect) +static int need_envelope_modifier(struct ff_effect *old, struct ff_effect *effect) { - int id = effect->id; - struct ff_effect* old = &iforce->core_effects[id].effect; - switch (effect->type) { case FF_CONSTANT: if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length || old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level || old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length || old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level) - return TRUE; + return 1; break; case FF_PERIODIC: @@ -261,30 +254,26 @@ static int need_envelope_modifier(struct || old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level || old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length || old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level) - return TRUE; + return 1; break; default: printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n"); } - return FALSE; + return 0; } /* * Analyse the changes in an effect, and tell if we need to send a periodic * parameter effect */ -static int need_period_modifier(struct iforce* iforce, struct ff_effect* new) +static int need_period_modifier(struct ff_effect *old, struct ff_effect *new) { - int id = new->id; - struct ff_effect* old = &iforce->core_effects[id].effect; - if (new->type != FF_PERIODIC) { - printk(KERN_WARNING "iforce.c: bad effect type in need_periodic_modifier\n"); - return FALSE; + printk(KERN_WARNING "iforce.c: bad effect type in need_period_modifier\n"); + return 0; } - return (old->u.periodic.period != new->u.periodic.period || old->u.periodic.magnitude != new->u.periodic.magnitude || old->u.periodic.offset != new->u.periodic.offset @@ -295,19 +284,16 @@ static int need_period_modifier(struct i * Analyse the changes in an effect, and tell if we need to send an effect * packet */ -static int need_core(struct iforce* iforce, struct ff_effect* new) +static int need_core(struct ff_effect *old, struct ff_effect *new) { - int id = new->id; - struct ff_effect* old = &iforce->core_effects[id].effect; - if (old->direction != new->direction || old->trigger.button != new->trigger.button || old->trigger.interval != new->trigger.interval || old->replay.length != new->replay.length || old->replay.delay != new->replay.delay) - return TRUE; + return 1; - return FALSE; + return 0; } /* * Send the part common to all effects to the device @@ -360,7 +346,7 @@ static int make_core(struct iforce* ifor * Upload a periodic effect to the device * See also iforce_upload_constant. */ -int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int is_update) +int iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) { u8 wave_code; int core_id = effect->id; @@ -371,23 +357,25 @@ int iforce_upload_periodic(struct iforce int param2_err = 1; int core_err = 0; - if (!is_update || need_period_modifier(iforce, effect)) { + if (!old || need_period_modifier(old, effect)) { param1_err = make_period_modifier(iforce, mod1_chunk, - is_update, + old != NULL, effect->u.periodic.magnitude, effect->u.periodic.offset, effect->u.periodic.period, effect->u.periodic.phase); - if (param1_err) return param1_err; + if (param1_err) + return param1_err; set_bit(FF_MOD1_IS_USED, core_effect->flags); } - if (!is_update || need_envelope_modifier(iforce, effect)) { + if (!old || need_envelope_modifier(old, effect)) { param2_err = make_envelope_modifier(iforce, mod2_chunk, - is_update, + old !=NULL, effect->u.periodic.envelope.attack_length, effect->u.periodic.envelope.attack_level, effect->u.periodic.envelope.fade_length, effect->u.periodic.envelope.fade_level); - if (param2_err) return param2_err; + if (param2_err) + return param2_err; set_bit(FF_MOD2_IS_USED, core_effect->flags); } @@ -400,7 +388,7 @@ int iforce_upload_periodic(struct iforce default: wave_code = 0x20; break; } - if (!is_update || need_core(iforce, effect)) { + if (!old || need_core(old, effect)) { core_err = make_core(iforce, effect->id, mod1_chunk->start, mod2_chunk->start, @@ -429,7 +417,7 @@ int iforce_upload_periodic(struct iforce * 0 Ok, effect created or updated * 1 effect did not change since last upload, and no packet was therefore sent */ -int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int is_update) +int iforce_upload_constant(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) { int core_id = effect->id; struct iforce_core_effect* core_effect = iforce->core_effects + core_id; @@ -439,26 +427,28 @@ int iforce_upload_constant(struct iforce int param2_err = 1; int core_err = 0; - if (!is_update || need_magnitude_modifier(iforce, effect)) { + if (!old || need_magnitude_modifier(old, effect)) { param1_err = make_magnitude_modifier(iforce, mod1_chunk, - is_update, + old != NULL, effect->u.constant.level); - if (param1_err) return param1_err; + if (param1_err) + return param1_err; set_bit(FF_MOD1_IS_USED, core_effect->flags); } - if (!is_update || need_envelope_modifier(iforce, effect)) { + if (!old || need_envelope_modifier(old, effect)) { param2_err = make_envelope_modifier(iforce, mod2_chunk, - is_update, + old != NULL, effect->u.constant.envelope.attack_length, effect->u.constant.envelope.attack_level, effect->u.constant.envelope.fade_length, effect->u.constant.envelope.fade_level); - if (param2_err) return param2_err; + if (param2_err) + return param2_err; set_bit(FF_MOD2_IS_USED, core_effect->flags); } - if (!is_update || need_core(iforce, effect)) { + if (!old || need_core(old, effect)) { core_err = make_core(iforce, effect->id, mod1_chunk->start, mod2_chunk->start, @@ -483,7 +473,7 @@ int iforce_upload_constant(struct iforce /* * Upload an condition effect. Those are for example friction, inertia, springs... */ -int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int is_update) +int iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old) { int core_id = effect->id; struct iforce_core_effect* core_effect = iforce->core_effects + core_id; @@ -494,37 +484,39 @@ int iforce_upload_condition(struct iforc int core_err = 0; switch (effect->type) { - case FF_SPRING: type = 0x40; break; - case FF_DAMPER: type = 0x41; break; + case FF_SPRING: type = 0x40; break; + case FF_DAMPER: type = 0x41; break; default: return -1; } - if (!is_update || need_condition_modifier(iforce, effect)) { + if (!old || need_condition_modifier(old, effect)) { param_err = make_condition_modifier(iforce, mod1_chunk, - is_update, + old != NULL, effect->u.condition[0].right_saturation, effect->u.condition[0].left_saturation, effect->u.condition[0].right_coeff, effect->u.condition[0].left_coeff, effect->u.condition[0].deadband, effect->u.condition[0].center); - if (param_err) return param_err; + if (param_err) + return param_err; set_bit(FF_MOD1_IS_USED, core_effect->flags); param_err = make_condition_modifier(iforce, mod2_chunk, - is_update, + old != NULL, effect->u.condition[1].right_saturation, effect->u.condition[1].left_saturation, effect->u.condition[1].right_coeff, effect->u.condition[1].left_coeff, effect->u.condition[1].deadband, effect->u.condition[1].center); - if (param_err) return param_err; + if (param_err) + return param_err; set_bit(FF_MOD2_IS_USED, core_effect->flags); } - if (!is_update || need_core(iforce, effect)) { + if (!old || need_core(old, effect)) { core_err = make_core(iforce, effect->id, mod1_chunk->start, mod2_chunk->start, type, 0xc0, diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index 6d99e3c..24c684b 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -79,106 +79,61 @@ static struct iforce_device iforce_devic { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //? { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //? { 0x06f8, 0x0004, "Gullemot Jet Leader 3D", btn_joystick, abs_joystick, ff_iforce }, //? + { 0x06d6, 0x29bc, "Trust Force Feedback Race Master", btn_wheel, abs_wheel, ff_iforce }, { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce } }; - - -static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +static int iforce_playback(struct input_dev *dev, int effect_id, int value) { struct iforce* iforce = dev->private; - unsigned char data[3]; - - if (type != EV_FF) - return -1; - - switch (code) { - - case FF_GAIN: - - data[0] = value >> 9; - iforce_send_packet(iforce, FF_CMD_GAIN, data); - - return 0; - - case FF_AUTOCENTER: + struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id]; - data[0] = 0x03; - data[1] = value >> 9; - iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); + if (value > 0) + set_bit(FF_CORE_SHOULD_PLAY, core_effect->flags); + else + clear_bit(FF_CORE_SHOULD_PLAY, core_effect->flags); - data[0] = 0x04; - data[1] = 0x01; - iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); + iforce_control_playback(iforce, effect_id, value); + return 0; +} - return 0; +static void iforce_set_gain(struct input_dev *dev, u16 gain) +{ + struct iforce* iforce = dev->private; + unsigned char data[3]; - default: /* Play or stop an effect */ + data[0] = gain >> 9; + iforce_send_packet(iforce, FF_CMD_GAIN, data); +} - if (!CHECK_OWNERSHIP(code, iforce)) { - return -1; - } - if (value > 0) { - set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags); - } - else { - clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags); - } +static void iforce_set_autocenter(struct input_dev *dev, u16 magnitude) +{ + struct iforce* iforce = dev->private; + unsigned char data[3]; - iforce_control_playback(iforce, code, value); - return 0; - } + data[0] = 0x03; + data[1] = magnitude >> 9; + iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); - return -1; + data[0] = 0x04; + data[1] = 0x01; + iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data); } /* * Function called when an ioctl is performed on the event dev entry. * It uploads an effect to the device */ -static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect) +static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { struct iforce* iforce = dev->private; - int id; + struct iforce_core_effect *core_effect = &iforce->core_effects[effect->id]; int ret; - int is_update; - -/* Check this effect type is supported by this device */ - if (!test_bit(effect->type, iforce->dev->ffbit)) - return -EINVAL; - -/* - * If we want to create a new effect, get a free id - */ - if (effect->id == -1) { - - for (id = 0; id < FF_EFFECTS_MAX; ++id) - if (!test_and_set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) - break; - - if (id == FF_EFFECTS_MAX || id >= iforce->dev->ff_effects_max) - return -ENOMEM; - - effect->id = id; - iforce->core_effects[id].owner = current->pid; - iforce->core_effects[id].flags[0] = (1 << FF_CORE_IS_USED); /* Only IS_USED bit must be set */ - - is_update = FALSE; - } - else { - /* We want to update an effect */ - if (!CHECK_OWNERSHIP(effect->id, iforce)) - return -EACCES; - - /* Parameter type cannot be updated */ - if (effect->type != iforce->core_effects[effect->id].effect.type) - return -EINVAL; + if (__test_and_set_bit(FF_CORE_IS_USED, core_effect->flags)) { /* Check the effect is not already being updated */ - if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags)) + if (test_bit(FF_CORE_UPDATE, core_effect->flags)) return -EAGAIN; - - is_update = TRUE; } /* @@ -187,28 +142,28 @@ static int iforce_upload_effect(struct i switch (effect->type) { case FF_PERIODIC: - ret = iforce_upload_periodic(iforce, effect, is_update); + ret = iforce_upload_periodic(iforce, effect, old); break; case FF_CONSTANT: - ret = iforce_upload_constant(iforce, effect, is_update); + ret = iforce_upload_constant(iforce, effect, old); break; case FF_SPRING: case FF_DAMPER: - ret = iforce_upload_condition(iforce, effect, is_update); + ret = iforce_upload_condition(iforce, effect, old); break; default: return -EINVAL; } + if (ret == 0) { /* A packet was sent, forbid new updates until we are notified * that the packet was updated */ - set_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags); + set_bit(FF_CORE_UPDATE, core_effect->flags); } - iforce->core_effects[effect->id].effect = *effect; return ret; } @@ -218,28 +173,17 @@ static int iforce_upload_effect(struct i */ static int iforce_erase_effect(struct input_dev *dev, int effect_id) { - struct iforce* iforce = dev->private; + struct iforce *iforce = dev->private; + struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id]; int err = 0; - struct iforce_core_effect* core_effect; - - /* Check who is trying to erase this effect */ - if (iforce->core_effects[effect_id].owner != current->pid) { - printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, iforce->core_effects[effect_id].owner); - return -EACCES; - } - - if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX) - return -EINVAL; - - core_effect = iforce->core_effects + effect_id; if (test_bit(FF_MOD1_IS_USED, core_effect->flags)) - err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk)); + err = release_resource(&core_effect->mod1_chunk); if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags)) - err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk)); + err = release_resource(&core_effect->mod2_chunk); - /*TODO: remember to change that if more FF_MOD* bits are added */ + /* TODO: remember to change that if more FF_MOD* bits are added */ core_effect->flags[0] = 0; return err; @@ -259,52 +203,31 @@ #ifdef CONFIG_JOYSTICK_IFORCE_USB #endif } - /* Enable force feedback */ - iforce_send_packet(iforce, FF_CMD_ENABLE, "\004"); + if (test_bit(EV_FF, dev->evbit)) { + /* Enable force feedback */ + iforce_send_packet(iforce, FF_CMD_ENABLE, "\004"); + } return 0; } -static int iforce_flush(struct input_dev *dev, struct file *file) +static void iforce_release(struct input_dev *dev) { struct iforce *iforce = dev->private; int i; - /* Erase all effects this process owns */ - for (i=0; iff_effects_max; ++i) { - - if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) && - current->pid == iforce->core_effects[i].owner) { - - /* Stop effect */ - input_report_ff(dev, i, 0); - - /* Free ressources assigned to effect */ - if (iforce_erase_effect(dev, i)) { - printk(KERN_WARNING "iforce_flush: erase effect %d failed\n", i); + if (test_bit(EV_FF, dev->evbit)) { + /* Check: no effects should be present in memory */ + for (i = 0; i < dev->ff->max_effects; i++) { + if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) { + printk(KERN_WARNING "iforce_release: Device still owns effects\n"); + break; } } + /* Disable force feedback playback */ + iforce_send_packet(iforce, FF_CMD_ENABLE, "\001"); } - return 0; -} - -static void iforce_release(struct input_dev *dev) -{ - struct iforce *iforce = dev->private; - int i; - - /* Check: no effect should be present in memory */ - for (i=0; iff_effects_max; ++i) { - if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) - break; - } - if (iff_effects_max) { - printk(KERN_WARNING "iforce_release: Device still owns effects\n"); - } - - /* Disable force feedback playback */ - iforce_send_packet(iforce, FF_CMD_ENABLE, "\001"); switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_USB @@ -341,8 +264,10 @@ #endif int iforce_init_device(struct iforce *iforce) { struct input_dev *input_dev; + struct ff_device *ff; unsigned char c[] = "CEOV"; - int i; + int i, error; + int ff_effects = 0; input_dev = input_allocate_device(); if (!input_dev) @@ -377,11 +302,6 @@ #endif input_dev->name = "Unknown I-Force device"; input_dev->open = iforce_open; input_dev->close = iforce_release; - input_dev->flush = iforce_flush; - input_dev->event = iforce_input_event; - input_dev->upload_effect = iforce_upload_effect; - input_dev->erase_effect = iforce_erase_effect; - input_dev->ff_effects_max = 10; /* * On-device memory allocation. @@ -429,15 +349,15 @@ #endif printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n"); if (!iforce_get_id_packet(iforce, "N")) - iforce->dev->ff_effects_max = iforce->edata[1]; + ff_effects = iforce->edata[1]; else printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n"); /* Check if the device can store more effects than the driver can really handle */ - if (iforce->dev->ff_effects_max > FF_EFFECTS_MAX) { - printk(KERN_WARNING "input??: Device can handle %d effects, but N_EFFECTS_MAX is set to %d in iforce.h\n", - iforce->dev->ff_effects_max, FF_EFFECTS_MAX); - iforce->dev->ff_effects_max = FF_EFFECTS_MAX; + if (ff_effects > IFORCE_EFFECTS_MAX) { + printk(KERN_WARNING "iforce: Limiting number of effects to %d (device reports %d)\n", + IFORCE_EFFECTS_MAX, ff_effects); + ff_effects = IFORCE_EFFECTS_MAX; } /* @@ -471,12 +391,10 @@ #endif * Set input device bitfields and ranges. */ - input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS); + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF_STATUS); - for (i = 0; iforce->type->btn[i] >= 0; i++) { - signed short t = iforce->type->btn[i]; - set_bit(t, input_dev->keybit); - } + for (i = 0; iforce->type->btn[i] >= 0; i++) + set_bit(iforce->type->btn[i], input_dev->keybit); set_bit(BTN_DEAD, input_dev->keybit); for (i = 0; iforce->type->abs[i] >= 0; i++) { @@ -515,9 +433,24 @@ #endif } } - for (i = 0; iforce->type->ff[i] >= 0; i++) - set_bit(iforce->type->ff[i], input_dev->ffbit); + if (ff_effects) { + for (i = 0; iforce->type->ff[i] >= 0; i++) + set_bit(iforce->type->ff[i], input_dev->ffbit); + + error = input_ff_create(input_dev, ff_effects); + if (error) { + input_free_device(input_dev); + return error; + } + + ff = input_dev->ff; + ff->upload = iforce_upload_effect; + ff->erase = iforce_erase_effect; + ff->set_gain = iforce_set_gain; + ff->set_autocenter = iforce_set_autocenter; + ff->playback = iforce_playback; + } /* * Register input device. */ diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index 76cb1f8..8632d47 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -140,7 +140,10 @@ static int mark_core_as_ready(struct ifo { int i; - for (i = 0; i < iforce->dev->ff_effects_max; ++i) { + if (!iforce->dev->ff) + return 0; + + for (i = 0; i < iforce->dev->ff->max_effects; ++i) { if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) && (iforce->core_effects[i].mod1_chunk.start == addr || iforce->core_effects[i].mod2_chunk.start == addr)) { @@ -229,19 +232,17 @@ #endif i = data[1] & 0x7f; if (data[1] & 0x80) { if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { - /* Report play event */ - input_report_ff_status(dev, i, FF_STATUS_PLAYING); + /* Report play event */ + input_report_ff_status(dev, i, FF_STATUS_PLAYING); } - } - else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { + } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) { /* Report stop event */ input_report_ff_status(dev, i, FF_STATUS_STOPPED); } if (LO(cmd) > 3) { int j; - for (j=3; j= 0 && \ - test_bit(FF_CORE_IS_USED, (iforce)->core_effects[(i)].flags) && \ - (current->pid == 0 || \ - (iforce)->core_effects[(i)].owner == current->pid)) - struct iforce_core_effect { /* Information about where modifiers are stored in the device's memory */ struct resource mod1_chunk; struct resource mod2_chunk; unsigned long flags[NBITS(FF_MODCORE_MAX)]; - pid_t owner; - /* Used to keep track of parameters of an effect. They are needed - * to know what parts of an effect changed in an update operation. - * We try to send only parameter packets if possible, as sending - * effect parameter requires the effect to be stoped and restarted - */ - struct ff_effect effect; }; #define FF_CMD_EFFECT 0x010e @@ -145,7 +129,7 @@ #endif /* Force Feedback */ wait_queue_head_t wait; struct resource device_memory; - struct iforce_core_effect core_effects[FF_EFFECTS_MAX]; + struct iforce_core_effect core_effects[IFORCE_EFFECTS_MAX]; struct mutex mem_mutex; }; @@ -182,9 +166,9 @@ void iforce_dump_packet(char *msg, u16 c int iforce_get_id_packet(struct iforce *iforce, char *packet); /* iforce-ff.c */ -int iforce_upload_periodic(struct iforce*, struct ff_effect*, int is_update); -int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update); -int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update); +int iforce_upload_periodic(struct iforce *, struct ff_effect *, struct ff_effect *); +int iforce_upload_constant(struct iforce *, struct ff_effect *, struct ff_effect *); +int iforce_upload_condition(struct iforce *, struct ff_effect *, struct ff_effect *); /* Public variables */ extern struct serio_driver iforce_serio_drv; diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c index 75eb5ca..7a19ee0 100644 --- a/drivers/input/joystick/spaceball.c +++ b/drivers/input/joystick/spaceball.c @@ -50,7 +50,7 @@ MODULE_LICENSE("GPL"); */ #define SPACEBALL_MAX_LENGTH 128 -#define SPACEBALL_MAX_ID 8 +#define SPACEBALL_MAX_ID 9 #define SPACEBALL_1003 1 #define SPACEBALL_2003B 3 diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index d723e9a..9516439 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -20,6 +20,9 @@ * Author: Aristeu Sergio Rozanski Filho * * Changes/Revisions: + * 0.3 09/04/2006 (Anssi Hannula ) + * - updated ff support for the changes in kernel interface + * - added MODULE_VERSION * 0.2 16/10/2004 (Micah Dowty ) * - added force feedback support * - added UI_SET_PHYS @@ -107,18 +110,31 @@ static int uinput_request_submit(struct return request->retval; } -static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect) +static void uinput_dev_set_gain(struct input_dev *dev, u16 gain) +{ + uinput_dev_event(dev, EV_FF, FF_GAIN, gain); +} + +static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude) +{ + uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude); +} + +static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value) +{ + return uinput_dev_event(dev, EV_FF, effect_id, value); +} + +static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { struct uinput_request request; int retval; - if (!test_bit(EV_FF, dev->evbit)) - return -ENOSYS; - request.id = -1; init_completion(&request.done); request.code = UI_FF_UPLOAD; - request.u.effect = effect; + request.u.upload.effect = effect; + request.u.upload.old = old; retval = uinput_request_reserve_slot(dev->private, &request); if (!retval) @@ -168,6 +184,7 @@ static void uinput_destroy_device(struct static int uinput_create_device(struct uinput_device *udev) { + struct input_dev *dev = udev->dev; int error; if (udev->state != UIST_SETUP_COMPLETE) { @@ -175,15 +192,29 @@ static int uinput_create_device(struct u return -EINVAL; } - error = input_register_device(udev->dev); - if (error) { - uinput_destroy_device(udev); - return error; + if (udev->ff_effects_max) { + error = input_ff_create(dev, udev->ff_effects_max); + if (error) + goto fail1; + + dev->ff->upload = uinput_dev_upload_effect; + dev->ff->erase = uinput_dev_erase_effect; + dev->ff->playback = uinput_dev_playback; + dev->ff->set_gain = uinput_dev_set_gain; + dev->ff->set_autocenter = uinput_dev_set_autocenter; } + error = input_register_device(udev->dev); + if (error) + goto fail2; + udev->state = UIST_CREATED; return 0; + + fail2: input_ff_destroy(dev); + fail1: uinput_destroy_device(udev); + return error; } static int uinput_open(struct inode *inode, struct file *file) @@ -243,8 +274,6 @@ static int uinput_allocate_device(struct return -ENOMEM; udev->dev->event = uinput_dev_event; - udev->dev->upload_effect = uinput_dev_upload_effect; - udev->dev->erase_effect = uinput_dev_erase_effect; udev->dev->private = udev; return 0; @@ -278,6 +307,8 @@ static int uinput_setup_device(struct ui goto exit; } + udev->ff_effects_max = user_dev->ff_effects_max; + size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1; if (!size) { retval = -EINVAL; @@ -296,7 +327,6 @@ static int uinput_setup_device(struct ui dev->id.vendor = user_dev->id.vendor; dev->id.product = user_dev->id.product; dev->id.version = user_dev->id.version; - dev->ff_effects_max = user_dev->ff_effects_max; size = sizeof(int) * (ABS_MAX + 1); memcpy(dev->absmax, user_dev->absmax, size); @@ -525,12 +555,17 @@ static long uinput_ioctl(struct file *fi break; } req = uinput_request_find(udev, ff_up.request_id); - if (!(req && req->code == UI_FF_UPLOAD && req->u.effect)) { + if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) { retval = -EINVAL; break; } ff_up.retval = 0; - memcpy(&ff_up.effect, req->u.effect, sizeof(struct ff_effect)); + memcpy(&ff_up.effect, req->u.upload.effect, sizeof(struct ff_effect)); + if (req->u.upload.old) + memcpy(&ff_up.old, req->u.upload.old, sizeof(struct ff_effect)); + else + memset(&ff_up.old, 0, sizeof(struct ff_effect)); + if (copy_to_user(p, &ff_up, sizeof(ff_up))) { retval = -EFAULT; break; @@ -561,12 +596,11 @@ static long uinput_ioctl(struct file *fi break; } req = uinput_request_find(udev, ff_up.request_id); - if (!(req && req->code == UI_FF_UPLOAD && req->u.effect)) { + if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) { retval = -EINVAL; break; } req->retval = ff_up.retval; - memcpy(req->u.effect, &ff_up.effect, sizeof(struct ff_effect)); uinput_request_done(udev, req); break; @@ -622,6 +656,7 @@ static void __exit uinput_exit(void) MODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); MODULE_DESCRIPTION("User level driver support for input subsystem"); MODULE_LICENSE("GPL"); +MODULE_VERSION("0.3"); module_init(uinput_init); module_exit(uinput_exit); diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index ccf0fae..a8efc1a 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -94,7 +94,7 @@ static void call_bios(struct regs *regs) static ssize_t __init locate_wistron_bios(void __iomem *base) { - static const unsigned char __initdata signature[] = + static unsigned char __initdata signature[] = { 0x42, 0x21, 0x55, 0x30 }; ssize_t offset; @@ -259,11 +259,11 @@ static int __init dmi_matched(struct dmi return 1; } -static struct key_entry keymap_empty[] = { +static struct key_entry keymap_empty[] __initdata = { { KE_END, 0 } }; -static struct key_entry keymap_fs_amilo_pro_v2000[] = { +static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = { { KE_KEY, 0x01, KEY_HELP }, { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, @@ -273,7 +273,7 @@ static struct key_entry keymap_fs_amilo_ { KE_END, 0 } }; -static struct key_entry keymap_fujitsu_n3510[] = { +static struct key_entry keymap_fujitsu_n3510[] __initdata = { { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, { KE_KEY, 0x36, KEY_WWW }, @@ -285,7 +285,7 @@ static struct key_entry keymap_fujitsu_n { KE_END, 0 } }; -static struct key_entry keymap_wistron_ms2111[] = { +static struct key_entry keymap_wistron_ms2111[] __initdata = { { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, { KE_KEY, 0x13, KEY_PROG3 }, @@ -294,7 +294,7 @@ static struct key_entry keymap_wistron_m { KE_END, 0 } }; -static struct key_entry keymap_wistron_ms2141[] = { +static struct key_entry keymap_wistron_ms2141[] __initdata = { { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, { KE_WIFI, 0x30, 0 }, @@ -307,7 +307,7 @@ static struct key_entry keymap_wistron_m { KE_END, 0 } }; -static struct key_entry keymap_acer_aspire_1500[] = { +static struct key_entry keymap_acer_aspire_1500[] __initdata = { { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, { KE_WIFI, 0x30, 0 }, @@ -317,7 +317,7 @@ static struct key_entry keymap_acer_aspi { KE_END, 0 } }; -static struct key_entry keymap_acer_travelmate_240[] = { +static struct key_entry keymap_acer_travelmate_240[] __initdata = { { KE_KEY, 0x31, KEY_MAIL }, { KE_KEY, 0x36, KEY_WWW }, { KE_KEY, 0x11, KEY_PROG1 }, @@ -327,7 +327,7 @@ static struct key_entry keymap_acer_trav { KE_END, 0 } }; -static struct key_entry keymap_aopen_1559as[] = { +static struct key_entry keymap_aopen_1559as[] __initdata = { { KE_KEY, 0x01, KEY_HELP }, { KE_KEY, 0x06, KEY_PROG3 }, { KE_KEY, 0x11, KEY_PROG1 }, @@ -343,7 +343,7 @@ static struct key_entry keymap_aopen_155 * a list of buttons and their key codes (reported when loading this module * with force=1) and the output of dmidecode to $MODULE_AUTHOR. */ -static struct dmi_system_id dmi_ids[] = { +static struct dmi_system_id dmi_ids[] __initdata = { { .callback = dmi_matched, .ident = "Fujitsu-Siemens Amilo Pro V2000", diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 61a6f97..ed202f2 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -177,6 +177,11 @@ int ps2_command(struct ps2dev *ps2dev, u return -1; } + if (send && !param) { + WARN_ON(1); + return -1; + } + mutex_lock_nested(&ps2dev->cmd_mutex, SINGLE_DEPTH_NESTING); serio_pause_rx(ps2dev->serio); diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 6521034..3e76ad7 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -62,6 +62,7 @@ static LIST_HEAD(serio_list); static struct bus_type serio_bus; +static void serio_add_driver(struct serio_driver *drv); static void serio_add_port(struct serio *serio); static void serio_destroy_port(struct serio *serio); static void serio_reconnect_port(struct serio *serio); @@ -140,8 +141,14 @@ static void serio_release_driver(struct static void serio_find_driver(struct serio *serio) { + int error; + down_write(&serio_bus.subsys.rwsem); - device_attach(&serio->dev); + error = device_attach(&serio->dev); + if (error < 0) + printk(KERN_WARNING + "serio: device_attach() failed for %s (%s), error: %d\n", + serio->phys, serio->name, error); up_write(&serio_bus.subsys.rwsem); } @@ -272,7 +279,6 @@ static struct serio_event *serio_get_eve static void serio_handle_event(void) { struct serio_event *event; - struct serio_driver *serio_drv; mutex_lock(&serio_mutex); @@ -304,8 +310,7 @@ static void serio_handle_event(void) break; case SERIO_REGISTER_DRIVER: - serio_drv = event->object; - driver_register(&serio_drv->driver); + serio_add_driver(event->object); break; default: @@ -525,6 +530,7 @@ static void serio_init_port(struct serio __module_get(THIS_MODULE); + INIT_LIST_HEAD(&serio->node); spin_lock_init(&serio->lock); mutex_init(&serio->drv_mutex); device_initialize(&serio->dev); @@ -542,6 +548,8 @@ static void serio_init_port(struct serio */ static void serio_add_port(struct serio *serio) { + int error; + if (serio->parent) { serio_pause_rx(serio->parent); serio->parent->child = serio; @@ -551,9 +559,19 @@ static void serio_add_port(struct serio list_add_tail(&serio->node, &serio_list); if (serio->start) serio->start(serio); - device_add(&serio->dev); - sysfs_create_group(&serio->dev.kobj, &serio_id_attr_group); - serio->registered = 1; + error = device_add(&serio->dev); + if (error) + printk(KERN_ERR + "serio: device_add() failed for %s (%s), error: %d\n", + serio->phys, serio->name, error); + else { + serio->registered = 1; + error = sysfs_create_group(&serio->dev.kobj, &serio_id_attr_group); + if (error) + printk(KERN_ERR + "serio: sysfs_create_group() failed for %s (%s), error: %d\n", + serio->phys, serio->name, error); + } } /* @@ -583,10 +601,10 @@ static void serio_destroy_port(struct se if (serio->registered) { sysfs_remove_group(&serio->dev.kobj, &serio_id_attr_group); device_del(&serio->dev); - list_del_init(&serio->node); serio->registered = 0; } + list_del_init(&serio->node); serio_remove_pending_events(serio); put_device(&serio->dev); } @@ -756,6 +774,17 @@ static struct bus_type serio_bus = { .remove = serio_driver_remove, }; +static void serio_add_driver(struct serio_driver *drv) +{ + int error; + + error = driver_register(&drv->driver); + if (error) + printk(KERN_ERR + "serio: driver_register() failed for %s, error: %d\n", + drv->driver.name, error); +} + void __serio_register_driver(struct serio_driver *drv, struct module *owner) { drv->driver.bus = &serio_bus; @@ -903,18 +932,26 @@ irqreturn_t serio_interrupt(struct serio static int __init serio_init(void) { - serio_task = kthread_run(serio_thread, NULL, "kseriod"); - if (IS_ERR(serio_task)) { - printk(KERN_ERR "serio: Failed to start kseriod\n"); - return PTR_ERR(serio_task); - } + int error; serio_bus.dev_attrs = serio_device_attrs; serio_bus.drv_attrs = serio_driver_attrs; serio_bus.match = serio_bus_match; serio_bus.uevent = serio_uevent; serio_bus.resume = serio_resume; - bus_register(&serio_bus); + error = bus_register(&serio_bus); + if (error) { + printk(KERN_ERR "serio: failed to register serio bus, error: %d\n", error); + return error; + } + + serio_task = kthread_run(serio_thread, NULL, "kseriod"); + if (IS_ERR(serio_task)) { + bus_unregister(&serio_bus); + error = PTR_ERR(serio_task); + printk(KERN_ERR "serio: Failed to start kseriod, error: %d\n", error); + return error; + } return 0; } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b1b14f8..9418bbe 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -108,4 +108,40 @@ config TOUCHSCREEN_HP600 To compile this driver as a module, choose M here: the module will be called hp680_ts_input. +config TOUCHSCREEN_PENMOUNT + tristate "Penmount serial touchscreen" + select SERIO + help + Say Y here if you have a Penmount serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called penmount. + +config TOUCHSCREEN_TOUCHRIGHT + tristate "Touchright serial touchscreen" + select SERIO + help + Say Y here if you have a Touchright serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchright. + +config TOUCHSCREEN_TOUCHWIN + tristate "Touchwin serial touchscreen" + select SERIO + help + Say Y here if you have a Touchwin serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchwin. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 5e5557c..1abb8f1 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,3 +12,6 @@ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index c86a2eb..ab56533 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -23,6 +23,7 @@ #include #include #include #include +#include #define DRIVER_DESC "Elo serial touchscreen driver" @@ -34,7 +35,19 @@ MODULE_LICENSE("GPL"); * Definitions & global arrays. */ -#define ELO_MAX_LENGTH 10 +#define ELO_MAX_LENGTH 10 + +#define ELO10_PACKET_LEN 8 +#define ELO10_TOUCH 0x03 +#define ELO10_PRESSURE 0x80 + +#define ELO10_LEAD_BYTE 'U' + +#define ELO10_ID_CMD 'i' + +#define ELO10_TOUCH_PACKET 'T' +#define ELO10_ACK_PACKET 'A' +#define ELI10_ID_PACKET 'I' /* * Per-touchscreen data. @@ -43,51 +56,67 @@ #define ELO_MAX_LENGTH 10 struct elo { struct input_dev *dev; struct serio *serio; + struct mutex cmd_mutex; + struct completion cmd_done; int id; int idx; + unsigned char expected_packet; unsigned char csum; unsigned char data[ELO_MAX_LENGTH]; + unsigned char response[ELO10_PACKET_LEN]; char phys[32]; }; -static void elo_process_data_10(struct elo* elo, unsigned char data, struct pt_regs *regs) +static void elo_process_data_10(struct elo *elo, unsigned char data, struct pt_regs *regs) { struct input_dev *dev = elo->dev; - elo->csum += elo->data[elo->idx] = data; - + elo->data[elo->idx] = data; switch (elo->idx++) { - case 0: - if (data != 'U') { + elo->csum = 0xaa; + if (data != ELO10_LEAD_BYTE) { + pr_debug("elo: unsynchronized data: 0x%02x\n", data); elo->idx = 0; - elo->csum = 0; - } - break; - - case 1: - if (data != 'T') { - elo->idx = 0; - elo->csum = 0; } break; case 9: - if (elo->csum) { + elo->idx = 0; + if (data != elo->csum) { + pr_debug("elo: bad checksum: 0x%02x, expected 0x%02x\n", + data, elo->csum); + break; + } + if (elo->data[1] != elo->expected_packet) { + if (elo->data[1] != ELO10_TOUCH_PACKET) + pr_debug("elo: unexpected packet: 0x%02x\n", + elo->data[1]); + break; + } + if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) { input_regs(dev, regs); input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]); input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]); - input_report_abs(dev, ABS_PRESSURE, (elo->data[8] << 8) | elo->data[7]); - input_report_key(dev, BTN_TOUCH, elo->data[8] || elo->data[7]); + if (elo->data[2] & ELO10_PRESSURE) + input_report_abs(dev, ABS_PRESSURE, + (elo->data[8] << 8) | elo->data[7]); + input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH); input_sync(dev); + } else if (elo->data[1] == ELO10_ACK_PACKET) { + if (elo->data[2] == '0') + elo->expected_packet = ELO10_TOUCH_PACKET; + complete(&elo->cmd_done); + } else { + memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN); + elo->expected_packet = ELO10_ACK_PACKET; } - elo->idx = 0; - elo->csum = 0; break; } + elo->csum += data; } -static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_regs *regs) +static void elo_process_data_6(struct elo *elo, unsigned char data, struct pt_regs *regs) { struct input_dev *dev = elo->dev; @@ -135,7 +164,7 @@ static void elo_process_data_6(struct el } } -static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_regs *regs) +static void elo_process_data_3(struct elo *elo, unsigned char data, struct pt_regs *regs) { struct input_dev *dev = elo->dev; @@ -161,7 +190,7 @@ static void elo_process_data_3(struct el static irqreturn_t elo_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs) { - struct elo* elo = serio_get_drvdata(serio); + struct elo *elo = serio_get_drvdata(serio); switch(elo->id) { case 0: @@ -181,17 +210,81 @@ static irqreturn_t elo_interrupt(struct return IRQ_HANDLED; } +static int elo_command_10(struct elo *elo, unsigned char *packet) +{ + int rc = -1; + int i; + unsigned char csum = 0xaa + ELO10_LEAD_BYTE; + + mutex_lock(&elo->cmd_mutex); + + serio_pause_rx(elo->serio); + elo->expected_packet = toupper(packet[0]); + init_completion(&elo->cmd_done); + serio_continue_rx(elo->serio); + + if (serio_write(elo->serio, ELO10_LEAD_BYTE)) + goto out; + + for (i = 0; i < ELO10_PACKET_LEN; i++) { + csum += packet[i]; + if (serio_write(elo->serio, packet[i])) + goto out; + } + + if (serio_write(elo->serio, csum)) + goto out; + + wait_for_completion_timeout(&elo->cmd_done, HZ); + + if (elo->expected_packet == ELO10_TOUCH_PACKET) { + /* We are back in reporting mode, the command was ACKed */ + memcpy(packet, elo->response, ELO10_PACKET_LEN); + rc = 0; + } + + out: + mutex_unlock(&elo->cmd_mutex); + return rc; +} + +static int elo_setup_10(struct elo *elo) +{ + static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" }; + struct input_dev *dev = elo->dev; + unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD }; + + if (elo_command_10(elo, packet)) + return -1; + + dev->id.version = (packet[5] << 8) | packet[4]; + + input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0); + input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0); + if (packet[3] & ELO10_PRESSURE) + input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + + printk(KERN_INFO "elo: %sTouch touchscreen, fw: %02x.%02x, " + "features: %x02x, controller: 0x%02x\n", + elo_types[(packet[1] -'0') & 0x03], + packet[5], packet[4], packet[3], packet[7]); + + return 0; +} + /* * elo_disconnect() is the opposite of elo_connect() */ static void elo_disconnect(struct serio *serio) { - struct elo* elo = serio_get_drvdata(serio); + struct elo *elo = serio_get_drvdata(serio); + input_get_device(elo->dev); input_unregister_device(elo->dev); serio_close(serio); serio_set_drvdata(serio, NULL); + input_put_device(elo->dev); kfree(elo); } @@ -211,12 +304,15 @@ static int elo_connect(struct serio *ser input_dev = input_allocate_device(); if (!elo || !input_dev) { err = -ENOMEM; - goto fail; + goto fail1; } elo->serio = serio; elo->id = serio->id.id; elo->dev = input_dev; + elo->expected_packet = ELO10_TOUCH_PACKET; + mutex_init(&elo->cmd_mutex); + init_completion(&elo->cmd_done); snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys); input_dev->private = elo; @@ -231,12 +327,17 @@ static int elo_connect(struct serio *ser input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + serio_set_drvdata(serio, elo); + err = serio_open(serio, drv); + if (err) + goto fail2; + switch (elo->id) { case 0: /* 10-byte protocol */ - input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + if (elo_setup_10(elo)) + goto fail3; + break; case 1: /* 6-byte protocol */ @@ -253,17 +354,15 @@ static int elo_connect(struct serio *ser break; } - serio_set_drvdata(serio, elo); - - err = serio_open(serio, drv); + err = input_register_device(elo->dev); if (err) - goto fail; + goto fail3; - input_register_device(elo->dev); return 0; - fail: serio_set_drvdata(serio, NULL); - input_free_device(input_dev); + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); kfree(elo); return err; } diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c new file mode 100644 index 0000000..f737010 --- /dev/null +++ b/drivers/input/touchscreen/penmount.c @@ -0,0 +1,185 @@ +/* + * Penmount serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch + * + * Based on ELO driver (drivers/input/touchscreen/elo.c) + * Copyright (c) 2004 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Penmount serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define PM_MAX_LENGTH 5 + +/* + * Per-touchscreen data. + */ + +struct pm { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[PM_MAX_LENGTH]; + char phys[32]; +}; + +static irqreturn_t pm_interrupt(struct serio *serio, + unsigned char data, unsigned int flags, struct pt_regs *regs) +{ + struct pm *pm = serio_get_drvdata(serio); + struct input_dev *dev = pm->dev; + + pm->data[pm->idx] = data; + + if (pm->data[0] & 0x80) { + if (PM_MAX_LENGTH == ++pm->idx) { + input_regs(dev, regs); + input_report_abs(dev, ABS_X, pm->data[2] * 128 + pm->data[1]); + input_report_abs(dev, ABS_Y, pm->data[4] * 128 + pm->data[3]); + input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40)); + input_sync(dev); + pm->idx = 0; + } + } + + return IRQ_HANDLED; +} + +/* + * pm_disconnect() is the opposite of pm_connect() + */ + +static void pm_disconnect(struct serio *serio) +{ + struct pm *pm = serio_get_drvdata(serio); + + input_get_device(pm->dev); + input_unregister_device(pm->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(pm->dev); + kfree(pm); +} + +/* + * pm_connect() is the routine that is called when someone adds a + * new serio device that supports Gunze protocol and registers it as + * an input device. + */ + +static int pm_connect(struct serio *serio, struct serio_driver *drv) +{ + struct pm *pm; + struct input_dev *input_dev; + int err; + + pm = kzalloc(sizeof(struct pm), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pm || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + pm->serio = serio; + pm->dev = input_dev; + snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys); + + input_dev->private = pm; + input_dev->name = "Penmount Serial TouchScreen"; + input_dev->phys = pm->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_PENMOUNT; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->cdev.dev = &serio->dev; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(pm->dev, ABS_X, 0, 0x3ff, 0, 0); + input_set_abs_params(pm->dev, ABS_Y, 0, 0x3ff, 0, 0); + + serio_set_drvdata(serio, pm); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(pm->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(pm); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id pm_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_PENMOUNT, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, pm_serio_ids); + +static struct serio_driver pm_drv = { + .driver = { + .name = "penmountlpc", + }, + .description = DRIVER_DESC, + .id_table = pm_serio_ids, + .interrupt = pm_interrupt, + .connect = pm_connect, + .disconnect = pm_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init pm_init(void) +{ + serio_register_driver(&pm_drv); + return 0; +} + +static void __exit pm_exit(void) +{ + serio_unregister_driver(&pm_drv); +} + +module_init(pm_init); +module_exit(pm_exit); diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c new file mode 100644 index 0000000..1c89fa5 --- /dev/null +++ b/drivers/input/touchscreen/touchright.c @@ -0,0 +1,196 @@ +/* + * Touchright serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch + * + * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c) + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Touchright serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define TR_FORMAT_TOUCH_BIT 0x01 +#define TR_FORMAT_STATUS_BYTE 0x40 +#define TR_FORMAT_STATUS_MASK ~TR_FORMAT_TOUCH_BIT + +#define TR_LENGTH 5 + +#define TR_MIN_XC 0 +#define TR_MAX_XC 0x1ff +#define TR_MIN_YC 0 +#define TR_MAX_YC 0x1ff + +/* + * Per-touchscreen data. + */ + +struct tr { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[TR_LENGTH]; + char phys[32]; +}; + +static irqreturn_t tr_interrupt(struct serio *serio, + unsigned char data, unsigned int flags, struct pt_regs *regs) +{ + struct tr *tr = serio_get_drvdata(serio); + struct input_dev *dev = tr->dev; + + tr->data[tr->idx] = data; + + if ((tr->data[0] & TR_FORMAT_STATUS_MASK) == TR_FORMAT_STATUS_BYTE) { + if (++tr->idx == TR_LENGTH) { + input_regs(dev, regs); + input_report_abs(dev, ABS_X, + (tr->data[1] << 5) | (tr->data[2] >> 1)); + input_report_abs(dev, ABS_Y, + (tr->data[3] << 5) | (tr->data[4] >> 1)); + input_report_key(dev, BTN_TOUCH, + tr->data[0] & TR_FORMAT_TOUCH_BIT); + input_sync(dev); + tr->idx = 0; + } + } + + return IRQ_HANDLED; +} + +/* + * tr_disconnect() is the opposite of tr_connect() + */ + +static void tr_disconnect(struct serio *serio) +{ + struct tr *tr = serio_get_drvdata(serio); + + input_get_device(tr->dev); + input_unregister_device(tr->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(tr->dev); + kfree(tr); +} + +/* + * tr_connect() is the routine that is called when someone adds a + * new serio device that supports the Touchright protocol and registers it as + * an input device. + */ + +static int tr_connect(struct serio *serio, struct serio_driver *drv) +{ + struct tr *tr; + struct input_dev *input_dev; + int err; + + tr = kzalloc(sizeof(struct tr), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!tr || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + tr->serio = serio; + tr->dev = input_dev; + snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys); + + input_dev->private = tr; + input_dev->name = "Touchright Serial TouchScreen"; + input_dev->phys = tr->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TOUCHRIGHT; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0); + input_set_abs_params(tr->dev, ABS_Y, TR_MIN_YC, TR_MAX_YC, 0, 0); + + serio_set_drvdata(serio, tr); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(tr->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(tr); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id tr_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TOUCHRIGHT, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, tr_serio_ids); + +static struct serio_driver tr_drv = { + .driver = { + .name = "touchright", + }, + .description = DRIVER_DESC, + .id_table = tr_serio_ids, + .interrupt = tr_interrupt, + .connect = tr_connect, + .disconnect = tr_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init tr_init(void) +{ + serio_register_driver(&tr_drv); + return 0; +} + +static void __exit tr_exit(void) +{ + serio_unregister_driver(&tr_drv); +} + +module_init(tr_init); +module_exit(tr_exit); diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c new file mode 100644 index 0000000..a7b4c75 --- /dev/null +++ b/drivers/input/touchscreen/touchwin.c @@ -0,0 +1,203 @@ +/* + * Touchwindow serial touchscreen driver + * + * Copyright (c) 2006 Rick Koch + * + * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c) + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +/* + * 2005/02/19 Rick Koch: + * The Touchwindow I used is made by Edmark Corp. and + * constantly outputs a stream of 0's unless it is touched. + * It then outputs 3 bytes: X, Y, and a copy of Y. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Touchwindow serial touchscreen driver" + +MODULE_AUTHOR("Rick Koch "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define TW_LENGTH 3 + +#define TW_MIN_XC 0 +#define TW_MAX_XC 0xff +#define TW_MIN_YC 0 +#define TW_MAX_YC 0xff + +/* + * Per-touchscreen data. + */ + +struct tw { + struct input_dev *dev; + struct serio *serio; + int idx; + int touched; + unsigned char data[TW_LENGTH]; + char phys[32]; +}; + +static irqreturn_t tw_interrupt(struct serio *serio, + unsigned char data, unsigned int flags, struct pt_regs *regs) +{ + struct tw *tw = serio_get_drvdata(serio); + struct input_dev *dev = tw->dev; + + if (data) { /* touch */ + tw->touched = 1; + tw->data[tw->idx++] = data; + /* verify length and that the two Y's are the same */ + if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) { + input_regs(dev, regs); + input_report_abs(dev, ABS_X, tw->data[0]); + input_report_abs(dev, ABS_Y, tw->data[1]); + input_report_key(dev, BTN_TOUCH, 1); + input_sync(dev); + tw->idx = 0; + } + } else if (tw->touched) { /* untouch */ + input_report_key(dev, BTN_TOUCH, 0); + input_sync(dev); + tw->idx = 0; + tw->touched = 0; + } + + return IRQ_HANDLED; +} + +/* + * tw_disconnect() is the opposite of tw_connect() + */ + +static void tw_disconnect(struct serio *serio) +{ + struct tw *tw = serio_get_drvdata(serio); + + input_get_device(tw->dev); + input_unregister_device(tw->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(tw->dev); + kfree(tw); +} + +/* + * tw_connect() is the routine that is called when someone adds a + * new serio device that supports the Touchwin protocol and registers it as + * an input device. + */ + +static int tw_connect(struct serio *serio, struct serio_driver *drv) +{ + struct tw *tw; + struct input_dev *input_dev; + int err; + + tw = kzalloc(sizeof(struct tw), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!tw || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + tw->serio = serio; + tw->dev = input_dev; + snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys); + + input_dev->private = tw; + input_dev->name = "Touchwindow Serial TouchScreen"; + input_dev->phys = tw->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TOUCHWIN; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0); + input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0); + + serio_set_drvdata(serio, tw); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(tw->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(tw); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id tw_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TOUCHWIN, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, tw_serio_ids); + +static struct serio_driver tw_drv = { + .driver = { + .name = "touchwin", + }, + .description = DRIVER_DESC, + .id_table = tw_serio_ids, + .interrupt = tw_interrupt, + .connect = tw_connect, + .disconnect = tw_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init tw_init(void) +{ + serio_register_driver(&tw_drv); + return 0; +} + +static void __exit tw_exit(void) +{ + serio_unregister_driver(&tw_drv); +} + +module_init(tw_init); +module_exit(tw_exit); diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index 650103b..7ec35c9 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -60,16 +60,17 @@ config HID_FF If unsure, say N. config HID_PID - bool "PID Devices (Microsoft Sidewinder Force Feedback 2)" + bool "PID device support" depends on HID_FF help - Say Y here if you have a PID-compliant joystick and wish to enable force - feedback for it. The Microsoft Sidewinder Force Feedback 2 is one such - device. + Say Y here if you have a PID-compliant device and wish to enable force + feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such + devices. config LOGITECH_FF bool "Logitech WingMan *3D support" depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID help Say Y here if you have one of these devices: - Logitech WingMan Cordless RumblePad @@ -81,12 +82,21 @@ config LOGITECH_FF config THRUSTMASTER_FF bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)" depends on HID_FF && EXPERIMENTAL + select INPUT_FF_MEMLESS if USB_HID help Say Y here if you have a THRUSTMASTER FireStore Dual Power 2, and want to enable force feedback support for it. Note: if you say N here, this device will still be supported, but without force feedback. +config ZEROPLUS_FF + bool "Zeroplus based game controller support" + depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have a Zeroplus based game controller and want to + enable force feedback for it. + config USB_HIDDEV bool "/dev/hiddev raw HID device support" depends on USB_HID diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile index 7641145..affb6a3 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile @@ -14,7 +14,7 @@ ifeq ($(CONFIG_USB_HIDINPUT),y) usbhid-objs += hid-input.o endif ifeq ($(CONFIG_HID_PID),y) - usbhid-objs += pid.o + usbhid-objs += hid-pidff.o endif ifeq ($(CONFIG_LOGITECH_FF),y) usbhid-objs += hid-lgff.o @@ -22,6 +22,9 @@ endif ifeq ($(CONFIG_THRUSTMASTER_FF),y) usbhid-objs += hid-tmff.o endif +ifeq ($(CONFIG_ZEROPLUS_FF),y) + usbhid-objs += hid-zpff.o +endif ifeq ($(CONFIG_HID_FF),y) usbhid-objs += hid-ff.o endif diff --git a/drivers/usb/input/fixp-arith.h b/drivers/usb/input/fixp-arith.h deleted file mode 100644 index ed3d2da..0000000 --- a/drivers/usb/input/fixp-arith.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef _FIXP_ARITH_H -#define _FIXP_ARITH_H - -/* - * Simplistic fixed-point arithmetics. - * Hmm, I'm probably duplicating some code :( - * - * Copyright (c) 2002 Johann Deneux - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to - */ - -#include - -/* The type representing fixed-point values */ -typedef s16 fixp_t; - -#define FRAC_N 8 -#define FRAC_MASK ((1< 123.0 */ -static inline fixp_t fixp_new(s16 a) -{ - return a< -1.0 - 0x8000 -> 1.0 - 0x0000 -> 0.0 -*/ -static inline fixp_t fixp_new16(s16 a) -{ - return ((s32)a)>>(16-FRAC_N); -} - -static inline fixp_t fixp_cos(unsigned int degrees) -{ - int quadrant = (degrees / 90) & 3; - unsigned int i = degrees % 90; - - if (quadrant == 1 || quadrant == 3) - i = 90 - i; - - i >>= 1; - - return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i]; -} - -static inline fixp_t fixp_sin(unsigned int degrees) -{ - return -fixp_cos(degrees + 90); -} - -static inline fixp_t fixp_mult(fixp_t a, fixp_t b) -{ - return ((s32)(a*b))>>FRAC_N; -} - -#endif diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index b9fb968..e3cd032 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -543,8 +543,6 @@ static void hid_free_device(struct hid_d { unsigned i,j; - hid_ff_exit(device); - for (i = 0; i < HID_REPORT_TYPES; i++) { struct hid_report_enum *report_enum = device->report_enum + i; @@ -1108,7 +1106,7 @@ int hid_set_field(struct hid_field *fiel /* * Find a report field with a specified HID usage. */ - +#if 0 struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type) { struct hid_report *report; @@ -1120,6 +1118,7 @@ struct hid_field *hid_find_field_by_usag return report->field[i]; return NULL; } +#endif /* 0 */ static int hid_submit_out(struct hid_device *hid) { diff --git a/drivers/usb/input/hid-ff.c b/drivers/usb/input/hid-ff.c index d5c91ee..a8fc46c 100644 --- a/drivers/usb/input/hid-ff.c +++ b/drivers/usb/input/hid-ff.c @@ -44,45 +44,38 @@ struct hid_ff_initializer { int (*init)(struct hid_device*); }; +/* + * We try pidff when no other driver is found because PID is the + * standards compliant way of implementing force feedback in HID. + * pidff_init() will quickly abort if the device doesn't appear to + * be a PID device + */ static struct hid_ff_initializer inits[] = { #ifdef CONFIG_LOGITECH_FF - {0x46d, 0xc211, hid_lgff_init}, // Logitech Cordless rumble pad - {0x46d, 0xc283, hid_lgff_init}, // Logitech Wingman Force 3d - {0x46d, 0xc295, hid_lgff_init}, // Logitech MOMO force wheel - {0x46d, 0xc219, hid_lgff_init}, // Logitech Cordless rumble pad 2 -#endif -#ifdef CONFIG_HID_PID - {0x45e, 0x001b, hid_pid_init}, + { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */ + { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */ + { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */ + { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */ #endif #ifdef CONFIG_THRUSTMASTER_FF - {0x44f, 0xb304, hid_tmff_init}, + { 0x44f, 0xb304, hid_tmff_init }, #endif - {0, 0, NULL} /* Terminating entry */ +#ifdef CONFIG_ZEROPLUS_FF + { 0xc12, 0x0005, hid_zpff_init }, + { 0xc12, 0x0030, hid_zpff_init }, +#endif + { 0, 0, hid_pidff_init} /* Matches anything */ }; -static struct hid_ff_initializer *hid_get_ff_init(__u16 idVendor, - __u16 idProduct) -{ - struct hid_ff_initializer *init; - for (init = inits; - init->idVendor - && !(init->idVendor == idVendor - && init->idProduct == idProduct); - init++); - - return init->idVendor? init : NULL; -} - int hid_ff_init(struct hid_device* hid) { struct hid_ff_initializer *init; + int vendor = le16_to_cpu(hid->dev->descriptor.idVendor); + int product = le16_to_cpu(hid->dev->descriptor.idProduct); - init = hid_get_ff_init(le16_to_cpu(hid->dev->descriptor.idVendor), - le16_to_cpu(hid->dev->descriptor.idProduct)); + for (init = inits; init->idVendor; init++) + if (init->idVendor == vendor && init->idProduct == product) + break; - if (!init) { - dbg("hid_ff_init could not find initializer"); - return -ENOSYS; - } return init->init(hid); } diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index 028e1ad..4c62afb 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -65,11 +65,9 @@ #define map_abs(c) do { usage->code = c; #define map_rel(c) do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0) #define map_key(c) do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0) #define map_led(c) do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0) -#define map_ff(c) do { usage->code = c; usage->type = EV_FF; bit = input->ffbit; max = FF_MAX; } while (0) #define map_abs_clear(c) do { map_abs(c); clear_bit(c, bit); } while (0) #define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0) -#define map_ff_effect(c) do { set_bit(c, input->ffbit); } while (0) #ifdef CONFIG_USB_HIDINPUT_POWERBOOK @@ -525,23 +523,7 @@ #endif case HID_UP_PID: - set_bit(EV_FF, input->evbit); switch(usage->hid & HID_USAGE) { - case 0x26: map_ff_effect(FF_CONSTANT); goto ignore; - case 0x27: map_ff_effect(FF_RAMP); goto ignore; - case 0x28: map_ff_effect(FF_CUSTOM); goto ignore; - case 0x30: map_ff_effect(FF_SQUARE); map_ff_effect(FF_PERIODIC); goto ignore; - case 0x31: map_ff_effect(FF_SINE); map_ff_effect(FF_PERIODIC); goto ignore; - case 0x32: map_ff_effect(FF_TRIANGLE); map_ff_effect(FF_PERIODIC); goto ignore; - case 0x33: map_ff_effect(FF_SAW_UP); map_ff_effect(FF_PERIODIC); goto ignore; - case 0x34: map_ff_effect(FF_SAW_DOWN); map_ff_effect(FF_PERIODIC); goto ignore; - case 0x40: map_ff_effect(FF_SPRING); goto ignore; - case 0x41: map_ff_effect(FF_DAMPER); goto ignore; - case 0x42: map_ff_effect(FF_INERTIA); goto ignore; - case 0x43: map_ff_effect(FF_FRICTION); goto ignore; - case 0x7e: map_ff(FF_GAIN); break; - case 0x83: input->ff_effects_max = field->value[0]; goto ignore; - case 0x98: map_ff(FF_AUTOCENTER); break; case 0xa4: map_key_clear(BTN_DEAD); break; default: goto ignore; } @@ -607,7 +589,8 @@ #endif } - if (usage->hat_min < usage->hat_max || usage->hat_dir) { + if (usage->type == EV_ABS && + (usage->hat_min < usage->hat_max || usage->hat_dir)) { int i; for (i = usage->code; i < usage->code + 2 && i <= max; i++) { input_set_abs_params(input, i, -1, 1, 0, 0); @@ -697,8 +680,7 @@ void hidinput_hid_event(struct hid_devic } if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */ - input->ff_effects_max = value; - dbg("Maximum Effects - %d",input->ff_effects_max); + dbg("Maximum Effects - %d",value); return; } @@ -747,7 +729,7 @@ static int hidinput_input_event(struct i int offset; if (type == EV_FF) - return hid_ff_event(hid, dev, type, code, value); + return input_ff_event(dev, type, code, value); if (type != EV_LED) return -1; diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c index f07d443..93da222 100644 --- a/drivers/usb/input/hid-lgff.c +++ b/drivers/usb/input/hid-lgff.c @@ -1,12 +1,11 @@ /* - * $$ - * * Force feedback support for hid-compliant for some of the devices from * Logitech, namely: * - WingMan Cordless RumblePad * - WingMan Force 3D * * Copyright (c) 2002-2004 Johann Deneux + * Copyright (c) 2006 Anssi Hannula */ /* @@ -29,495 +28,117 @@ */ #include -#include - -//#define DEBUG #include - -#include - #include "hid.h" -#include "fixp-arith.h" - - -/* Periodicity of the update */ -#define PERIOD (HZ/10) - -#define RUN_AT(t) (jiffies + (t)) - -/* Effect status */ -#define EFFECT_STARTED 0 /* Effect is going to play after some time - (ff_replay.delay) */ -#define EFFECT_PLAYING 1 /* Effect is being played */ -#define EFFECT_USED 2 - -// For lgff_device::flags -#define DEVICE_CLOSING 0 /* The driver is being unitialised */ - -/* Check that the current process can access an effect */ -#define CHECK_OWNERSHIP(effect) (current->pid == 0 \ - || effect.owner == current->pid) - -#define LGFF_CHECK_OWNERSHIP(i, l) \ - (i>=0 && ieffects[i].flags) \ - && CHECK_OWNERSHIP(l->effects[i])) - -#define LGFF_EFFECTS 8 struct device_type { u16 idVendor; u16 idProduct; - signed short *ff; -}; - -struct lgff_effect { - pid_t owner; - - struct ff_effect effect; - - unsigned long flags[1]; - unsigned int count; /* Number of times left to play */ - unsigned long started_at; /* When the effect started to play */ -}; - -struct lgff_device { - struct hid_device* hid; - - struct hid_report* constant; - struct hid_report* rumble; - struct hid_report* condition; - - struct lgff_effect effects[LGFF_EFFECTS]; - spinlock_t lock; /* device-level lock. Having locks on - a per-effect basis could be nice, but - isn't really necessary */ - - unsigned long flags[1]; /* Contains various information about the - state of the driver for this device */ - - struct timer_list timer; + const signed short *ff; }; -/* Callbacks */ -static void hid_lgff_exit(struct hid_device* hid); -static int hid_lgff_event(struct hid_device *hid, struct input_dev *input, - unsigned int type, unsigned int code, int value); -static int hid_lgff_flush(struct input_dev *input, struct file *file); -static int hid_lgff_upload_effect(struct input_dev *input, - struct ff_effect *effect); -static int hid_lgff_erase(struct input_dev *input, int id); - -/* Local functions */ -static void hid_lgff_input_init(struct hid_device* hid); -static void hid_lgff_timer(unsigned long timer_data); -static struct hid_report* hid_lgff_duplicate_report(struct hid_report*); -static void hid_lgff_delete_report(struct hid_report*); - -static signed short ff_rumble[] = { +static const signed short ff_rumble[] = { FF_RUMBLE, -1 }; -static signed short ff_joystick[] = { +static const signed short ff_joystick[] = { FF_CONSTANT, -1 }; -static struct device_type devices[] = { - {0x046d, 0xc211, ff_rumble}, - {0x046d, 0xc219, ff_rumble}, - {0x046d, 0xc283, ff_joystick}, - {0x0000, 0x0000, ff_joystick} +static const struct device_type devices[] = { + { 0x046d, 0xc211, ff_rumble }, + { 0x046d, 0xc219, ff_rumble }, + { 0x046d, 0xc283, ff_joystick }, + { 0x0000, 0x0000, ff_joystick } }; +static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + int x, y; + unsigned int left, right; + +#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff + + switch (effect->type) { + case FF_CONSTANT: + x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */ + y = effect->u.ramp.end_level + 0x7f; + CLAMP(x); + CLAMP(y); + report->field[0]->value[0] = 0x51; + report->field[0]->value[1] = 0x08; + report->field[0]->value[2] = x; + report->field[0]->value[3] = y; + dbg("(x, y)=(%04x, %04x)", x, y); + hid_submit_report(hid, report, USB_DIR_OUT); + break; + + case FF_RUMBLE: + right = effect->u.rumble.strong_magnitude; + left = effect->u.rumble.weak_magnitude; + right = right * 0xff / 0xffff; + left = left * 0xff / 0xffff; + CLAMP(left); + CLAMP(right); + report->field[0]->value[0] = 0x42; + report->field[0]->value[1] = 0x00; + report->field[0]->value[2] = left; + report->field[0]->value[3] = right; + dbg("(left, right)=(%04x, %04x)", left, right); + hid_submit_report(hid, report, USB_DIR_OUT); + break; + } + return 0; +} + int hid_lgff_init(struct hid_device* hid) { - struct lgff_device *private; - struct hid_report* report; - struct hid_field* field; + struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + struct hid_report *report; + struct hid_field *field; + int error; + int i, j; /* Find the report to use */ - if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) { + if (list_empty(report_list)) { err("No output report found"); return -1; } + /* Check that the report looks ok */ - report = (struct hid_report*)hid->report_enum[HID_OUTPUT_REPORT].report_list.next; + report = list_entry(report_list->next, struct hid_report, list); if (!report) { err("NULL output report"); return -1; } + field = report->field[0]; if (!field) { err("NULL field"); return -1; } - private = kzalloc(sizeof(struct lgff_device), GFP_KERNEL); - if (!private) - return -1; - hid->ff_private = private; - - /* Input init */ - hid_lgff_input_init(hid); - - - private->constant = hid_lgff_duplicate_report(report); - if (!private->constant) { - kfree(private); - return -1; - } - private->constant->field[0]->value[0] = 0x51; - private->constant->field[0]->value[1] = 0x08; - private->constant->field[0]->value[2] = 0x7f; - private->constant->field[0]->value[3] = 0x7f; - - private->rumble = hid_lgff_duplicate_report(report); - if (!private->rumble) { - hid_lgff_delete_report(private->constant); - kfree(private); - return -1; - } - private->rumble->field[0]->value[0] = 0x42; - - - private->condition = hid_lgff_duplicate_report(report); - if (!private->condition) { - hid_lgff_delete_report(private->rumble); - hid_lgff_delete_report(private->constant); - kfree(private); - return -1; - } - - private->hid = hid; - - spin_lock_init(&private->lock); - init_timer(&private->timer); - private->timer.data = (unsigned long)private; - private->timer.function = hid_lgff_timer; - - /* Event and exit callbacks */ - hid->ff_exit = hid_lgff_exit; - hid->ff_event = hid_lgff_event; - - /* Start the update task */ - private->timer.expires = RUN_AT(PERIOD); - add_timer(&private->timer); /*TODO: only run the timer when at least - one effect is playing */ - - printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux \n"); - - return 0; -} - -static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report) -{ - struct hid_report* ret; - - ret = kmalloc(sizeof(struct lgff_device), GFP_KERNEL); - if (!ret) - return NULL; - *ret = *report; - - ret->field[0] = kmalloc(sizeof(struct hid_field), GFP_KERNEL); - if (!ret->field[0]) { - kfree(ret); - return NULL; - } - *ret->field[0] = *report->field[0]; - - ret->field[0]->value = kzalloc(sizeof(s32[8]), GFP_KERNEL); - if (!ret->field[0]->value) { - kfree(ret->field[0]); - kfree(ret); - return NULL; - } - - return ret; -} - -static void hid_lgff_delete_report(struct hid_report* report) -{ - if (report) { - kfree(report->field[0]->value); - kfree(report->field[0]); - kfree(report); - } -} - -static void hid_lgff_input_init(struct hid_device* hid) -{ - struct device_type* dev = devices; - signed short* ff; - u16 idVendor = le16_to_cpu(hid->dev->descriptor.idVendor); - u16 idProduct = le16_to_cpu(hid->dev->descriptor.idProduct); - struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct input_dev *input_dev = hidinput->input; - - while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct)) - dev++; - - for (ff = dev->ff; *ff >= 0; ff++) - set_bit(*ff, input_dev->ffbit); - - input_dev->upload_effect = hid_lgff_upload_effect; - input_dev->flush = hid_lgff_flush; - - set_bit(EV_FF, input_dev->evbit); - input_dev->ff_effects_max = LGFF_EFFECTS; -} - -static void hid_lgff_exit(struct hid_device* hid) -{ - struct lgff_device *lgff = hid->ff_private; - - set_bit(DEVICE_CLOSING, lgff->flags); - del_timer_sync(&lgff->timer); - - hid_lgff_delete_report(lgff->condition); - hid_lgff_delete_report(lgff->rumble); - hid_lgff_delete_report(lgff->constant); - - kfree(lgff); -} - -static int hid_lgff_event(struct hid_device *hid, struct input_dev* input, - unsigned int type, unsigned int code, int value) -{ - struct lgff_device *lgff = hid->ff_private; - struct lgff_effect *effect = lgff->effects + code; - unsigned long flags; - - if (type != EV_FF) return -EINVAL; - if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES; - if (value < 0) return -EINVAL; - - spin_lock_irqsave(&lgff->lock, flags); - - if (value > 0) { - if (test_bit(EFFECT_STARTED, effect->flags)) { - spin_unlock_irqrestore(&lgff->lock, flags); - return -EBUSY; - } - if (test_bit(EFFECT_PLAYING, effect->flags)) { - spin_unlock_irqrestore(&lgff->lock, flags); - return -EBUSY; - } - - effect->count = value; - - if (effect->effect.replay.delay) { - set_bit(EFFECT_STARTED, effect->flags); - } else { - set_bit(EFFECT_PLAYING, effect->flags); - } - effect->started_at = jiffies; - } - else { /* value == 0 */ - clear_bit(EFFECT_STARTED, effect->flags); - clear_bit(EFFECT_PLAYING, effect->flags); - } - - spin_unlock_irqrestore(&lgff->lock, flags); - - return 0; - -} - -/* Erase all effects this process owns */ -static int hid_lgff_flush(struct input_dev *dev, struct file *file) -{ - struct hid_device *hid = dev->private; - struct lgff_device *lgff = hid->ff_private; - int i; - - for (i=0; iff_effects_max; ++i) { - - /*NOTE: no need to lock here. The only times EFFECT_USED is - modified is when effects are uploaded or when an effect is - erased. But a process cannot close its dev/input/eventX fd - and perform ioctls on the same fd all at the same time */ - if ( current->pid == lgff->effects[i].owner - && test_bit(EFFECT_USED, lgff->effects[i].flags)) { - - if (hid_lgff_erase(dev, i)) - warn("erase effect %d failed", i); - } - - } - - return 0; -} - -static int hid_lgff_erase(struct input_dev *dev, int id) -{ - struct hid_device *hid = dev->private; - struct lgff_device *lgff = hid->ff_private; - unsigned long flags; - - if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES; - - spin_lock_irqsave(&lgff->lock, flags); - lgff->effects[id].flags[0] = 0; - spin_unlock_irqrestore(&lgff->lock, flags); - - return 0; -} - -static int hid_lgff_upload_effect(struct input_dev* input, - struct ff_effect* effect) -{ - struct hid_device *hid = input->private; - struct lgff_device *lgff = hid->ff_private; - struct lgff_effect new; - int id; - unsigned long flags; - - dbg("ioctl rumble"); - - if (!test_bit(effect->type, input->ffbit)) return -EINVAL; - - spin_lock_irqsave(&lgff->lock, flags); - - if (effect->id == -1) { - int i; - - for (i=0; ieffects[i].flags); ++i); - if (i >= LGFF_EFFECTS) { - spin_unlock_irqrestore(&lgff->lock, flags); - return -ENOSPC; + for (i = 0; i < ARRAY_SIZE(devices); i++) { + if (dev->id.vendor == devices[i].idVendor && + dev->id.product == devices[i].idProduct) { + for (j = 0; devices[i].ff[j] >= 0; j++) + set_bit(devices[i].ff[j], dev->ffbit); + break; } - - effect->id = i; - lgff->effects[i].owner = current->pid; - lgff->effects[i].flags[0] = 0; - set_bit(EFFECT_USED, lgff->effects[i].flags); } - else if (!LGFF_CHECK_OWNERSHIP(effect->id, lgff)) { - spin_unlock_irqrestore(&lgff->lock, flags); - return -EACCES; - } - - id = effect->id; - new = lgff->effects[id]; - - new.effect = *effect; - - if (test_bit(EFFECT_STARTED, lgff->effects[id].flags) - || test_bit(EFFECT_STARTED, lgff->effects[id].flags)) { - - /* Changing replay parameters is not allowed (for the time - being) */ - if (new.effect.replay.delay != lgff->effects[id].effect.replay.delay - || new.effect.replay.length != lgff->effects[id].effect.replay.length) { - spin_unlock_irqrestore(&lgff->lock, flags); - return -ENOSYS; - } - lgff->effects[id] = new; + error = input_ff_create_memless(dev, NULL, hid_lgff_play); + if (error) + return error; - } else { - lgff->effects[id] = new; - } + printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux \n"); - spin_unlock_irqrestore(&lgff->lock, flags); return 0; } - -static void hid_lgff_timer(unsigned long timer_data) -{ - struct lgff_device *lgff = (struct lgff_device*)timer_data; - struct hid_device *hid = lgff->hid; - unsigned long flags; - int x = 0x7f, y = 0x7f; // Coordinates of constant effects - unsigned int left = 0, right = 0; // Rumbling - int i; - - spin_lock_irqsave(&lgff->lock, flags); - - for (i=0; ieffects +i; - - if (test_bit(EFFECT_PLAYING, effect->flags)) { - - switch (effect->effect.type) { - case FF_CONSTANT: { - //TODO: handle envelopes - int degrees = effect->effect.direction * 360 >> 16; - x += fixp_mult(fixp_sin(degrees), - fixp_new16(effect->effect.u.constant.level)); - y += fixp_mult(-fixp_cos(degrees), - fixp_new16(effect->effect.u.constant.level)); - } break; - case FF_RUMBLE: - right += effect->effect.u.rumble.strong_magnitude; - left += effect->effect.u.rumble.weak_magnitude; - break; - }; - - /* One run of the effect is finished playing */ - if (time_after(jiffies, - effect->started_at - + effect->effect.replay.delay*HZ/1000 - + effect->effect.replay.length*HZ/1000)) { - dbg("Finished playing once %d", i); - if (--effect->count <= 0) { - dbg("Stopped %d", i); - clear_bit(EFFECT_PLAYING, effect->flags); - } - else { - dbg("Start again %d", i); - if (effect->effect.replay.length != 0) { - clear_bit(EFFECT_PLAYING, effect->flags); - set_bit(EFFECT_STARTED, effect->flags); - } - effect->started_at = jiffies; - } - } - - } else if (test_bit(EFFECT_STARTED, lgff->effects[i].flags)) { - /* Check if we should start playing the effect */ - if (time_after(jiffies, - lgff->effects[i].started_at - + lgff->effects[i].effect.replay.delay*HZ/1000)) { - dbg("Now playing %d", i); - clear_bit(EFFECT_STARTED, lgff->effects[i].flags); - set_bit(EFFECT_PLAYING, lgff->effects[i].flags); - } - } - } - -#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff - - // Clamp values - CLAMP(x); - CLAMP(y); - CLAMP(left); - CLAMP(right); - -#undef CLAMP - - if (x != lgff->constant->field[0]->value[2] - || y != lgff->constant->field[0]->value[3]) { - lgff->constant->field[0]->value[2] = x; - lgff->constant->field[0]->value[3] = y; - dbg("(x,y)=(%04x, %04x)", x, y); - hid_submit_report(hid, lgff->constant, USB_DIR_OUT); - } - - if (left != lgff->rumble->field[0]->value[2] - || right != lgff->rumble->field[0]->value[3]) { - lgff->rumble->field[0]->value[2] = left; - lgff->rumble->field[0]->value[3] = right; - dbg("(left,right)=(%04x, %04x)", left, right); - hid_submit_report(hid, lgff->rumble, USB_DIR_OUT); - } - - if (!test_bit(DEVICE_CLOSING, lgff->flags)) { - lgff->timer.expires = RUN_AT(PERIOD); - add_timer(&lgff->timer); - } - - spin_unlock_irqrestore(&lgff->lock, flags); -} diff --git a/drivers/usb/input/hid-pidff.c b/drivers/usb/input/hid-pidff.c new file mode 100644 index 0000000..5420c13 --- /dev/null +++ b/drivers/usb/input/hid-pidff.c @@ -0,0 +1,1330 @@ +/* + * Force feedback driver for USB HID PID compliant devices + * + * Copyright (c) 2005, 2006 Anssi Hannula + */ + +/* + * 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 + */ + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("hid-pidff: " format "\n" , ## arg) + +#include +#include +#include + +#include "hid.h" + +#define PID_EFFECTS_MAX 64 + +/* Report usage table used to put reports into an array */ + +#define PID_SET_EFFECT 0 +#define PID_EFFECT_OPERATION 1 +#define PID_DEVICE_GAIN 2 +#define PID_POOL 3 +#define PID_BLOCK_LOAD 4 +#define PID_BLOCK_FREE 5 +#define PID_DEVICE_CONTROL 6 +#define PID_CREATE_NEW_EFFECT 7 + +#define PID_REQUIRED_REPORTS 7 + +#define PID_SET_ENVELOPE 8 +#define PID_SET_CONDITION 9 +#define PID_SET_PERIODIC 10 +#define PID_SET_CONSTANT 11 +#define PID_SET_RAMP 12 +static const u8 pidff_reports[] = { + 0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab, + 0x5a, 0x5f, 0x6e, 0x73, 0x74 +}; + +/* device_control is really 0x95, but 0x96 specified as it is the usage of +the only field in that report */ + +/* Value usage tables used to put fields and values into arrays */ + +#define PID_EFFECT_BLOCK_INDEX 0 + +#define PID_DURATION 1 +#define PID_GAIN 2 +#define PID_TRIGGER_BUTTON 3 +#define PID_TRIGGER_REPEAT_INT 4 +#define PID_DIRECTION_ENABLE 5 +#define PID_START_DELAY 6 +static const u8 pidff_set_effect[] = { + 0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7 +}; + +#define PID_ATTACK_LEVEL 1 +#define PID_ATTACK_TIME 2 +#define PID_FADE_LEVEL 3 +#define PID_FADE_TIME 4 +static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e }; + +#define PID_PARAM_BLOCK_OFFSET 1 +#define PID_CP_OFFSET 2 +#define PID_POS_COEFFICIENT 3 +#define PID_NEG_COEFFICIENT 4 +#define PID_POS_SATURATION 5 +#define PID_NEG_SATURATION 6 +#define PID_DEAD_BAND 7 +static const u8 pidff_set_condition[] = { + 0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65 +}; + +#define PID_MAGNITUDE 1 +#define PID_OFFSET 2 +#define PID_PHASE 3 +#define PID_PERIOD 4 +static const u8 pidff_set_periodic[] = { 0x22, 0x70, 0x6f, 0x71, 0x72 }; +static const u8 pidff_set_constant[] = { 0x22, 0x70 }; + +#define PID_RAMP_START 1 +#define PID_RAMP_END 2 +static const u8 pidff_set_ramp[] = { 0x22, 0x75, 0x76 }; + +#define PID_RAM_POOL_AVAILABLE 1 +static const u8 pidff_block_load[] = { 0x22, 0xac }; + +#define PID_LOOP_COUNT 1 +static const u8 pidff_effect_operation[] = { 0x22, 0x7c }; + +static const u8 pidff_block_free[] = { 0x22 }; + +#define PID_DEVICE_GAIN_FIELD 0 +static const u8 pidff_device_gain[] = { 0x7e }; + +#define PID_RAM_POOL_SIZE 0 +#define PID_SIMULTANEOUS_MAX 1 +#define PID_DEVICE_MANAGED_POOL 2 +static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 }; + +/* Special field key tables used to put special field keys into arrays */ + +#define PID_ENABLE_ACTUATORS 0 +#define PID_RESET 1 +static const u8 pidff_device_control[] = { 0x97, 0x9a }; + +#define PID_CONSTANT 0 +#define PID_RAMP 1 +#define PID_SQUARE 2 +#define PID_SINE 3 +#define PID_TRIANGLE 4 +#define PID_SAW_UP 5 +#define PID_SAW_DOWN 6 +#define PID_SPRING 7 +#define PID_DAMPER 8 +#define PID_INERTIA 9 +#define PID_FRICTION 10 +static const u8 pidff_effect_types[] = { + 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x40, 0x41, 0x42, 0x43 +}; + +#define PID_BLOCK_LOAD_SUCCESS 0 +#define PID_BLOCK_LOAD_FULL 1 +static const u8 pidff_block_load_status[] = { 0x8c, 0x8d }; + +#define PID_EFFECT_START 0 +#define PID_EFFECT_STOP 1 +static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b }; + +struct pidff_usage { + struct hid_field *field; + s32 *value; +}; + +struct pidff_device { + struct hid_device *hid; + + struct hid_report *reports[sizeof(pidff_reports)]; + + struct pidff_usage set_effect[sizeof(pidff_set_effect)]; + struct pidff_usage set_envelope[sizeof(pidff_set_envelope)]; + struct pidff_usage set_condition[sizeof(pidff_set_condition)]; + struct pidff_usage set_periodic[sizeof(pidff_set_periodic)]; + struct pidff_usage set_constant[sizeof(pidff_set_constant)]; + struct pidff_usage set_ramp[sizeof(pidff_set_ramp)]; + + struct pidff_usage device_gain[sizeof(pidff_device_gain)]; + struct pidff_usage block_load[sizeof(pidff_block_load)]; + struct pidff_usage pool[sizeof(pidff_pool)]; + struct pidff_usage effect_operation[sizeof(pidff_effect_operation)]; + struct pidff_usage block_free[sizeof(pidff_block_free)]; + + /* Special field is a field that is not composed of + usage<->value pairs that pidff_usage values are */ + + /* Special field in create_new_effect */ + struct hid_field *create_new_effect_type; + + /* Special fields in set_effect */ + struct hid_field *set_effect_type; + struct hid_field *effect_direction; + + /* Special field in device_control */ + struct hid_field *device_control; + + /* Special field in block_load */ + struct hid_field *block_load_status; + + /* Special field in effect_operation */ + struct hid_field *effect_operation_status; + + int control_id[sizeof(pidff_device_control)]; + int type_id[sizeof(pidff_effect_types)]; + int status_id[sizeof(pidff_block_load_status)]; + int operation_id[sizeof(pidff_effect_operation_status)]; + + int pid_id[PID_EFFECTS_MAX]; +}; + +/* + * Scale an unsigned value with range 0..max for the given field + */ +static int pidff_rescale(int i, int max, struct hid_field *field) +{ + return i * (field->logical_maximum - field->logical_minimum) / max + + field->logical_minimum; +} + +/* + * Scale a signed value in range -0x8000..0x7fff for the given field + */ +static int pidff_rescale_signed(int i, struct hid_field *field) +{ + return i == 0 ? 0 : i > + 0 ? i * field->logical_maximum / 0x7fff : i * + field->logical_minimum / -0x8000; +} + +static void pidff_set(struct pidff_usage *usage, u16 value) +{ + usage->value[0] = pidff_rescale(value, 0xffff, usage->field); + debug("calculated from %d to %d", value, usage->value[0]); +} + +static void pidff_set_signed(struct pidff_usage *usage, s16 value) +{ + if (usage->field->logical_minimum < 0) + usage->value[0] = pidff_rescale_signed(value, usage->field); + else { + if (value < 0) + usage->value[0] = + pidff_rescale(-value, 0x8000, usage->field); + else + usage->value[0] = + pidff_rescale(value, 0x7fff, usage->field); + } + debug("calculated from %d to %d", value, usage->value[0]); +} + +/* + * Send envelope report to the device + */ +static void pidff_set_envelope_report(struct pidff_device *pidff, + struct ff_envelope *envelope) +{ + pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + + pidff->set_envelope[PID_ATTACK_LEVEL].value[0] = + pidff_rescale(envelope->attack_level > + 0x7fff ? 0x7fff : envelope->attack_level, 0x7fff, + pidff->set_envelope[PID_ATTACK_LEVEL].field); + pidff->set_envelope[PID_FADE_LEVEL].value[0] = + pidff_rescale(envelope->fade_level > + 0x7fff ? 0x7fff : envelope->fade_level, 0x7fff, + pidff->set_envelope[PID_FADE_LEVEL].field); + + pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length; + pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length; + + debug("attack %u => %d", envelope->attack_level, + pidff->set_envelope[PID_ATTACK_LEVEL].value[0]); + + hid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE], + USB_DIR_OUT); +} + +/* + * Test if the new envelope differs from old one + */ +static int pidff_needs_set_envelope(struct ff_envelope *envelope, + struct ff_envelope *old) +{ + return envelope->attack_level != old->attack_level || + envelope->fade_level != old->fade_level || + envelope->attack_length != old->attack_length || + envelope->fade_length != old->fade_length; +} + +/* + * Send constant force report to the device + */ +static void pidff_set_constant_force_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_constant[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE], + effect->u.constant.level); + + hid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT], + USB_DIR_OUT); +} + +/* + * Test if the constant parameters have changed between effects + */ +static int pidff_needs_set_constant(struct ff_effect *effect, + struct ff_effect *old) +{ + return effect->u.constant.level != old->u.constant.level; +} + +/* + * Send set effect report to the device + */ +static void pidff_set_effect_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff->set_effect_type->value[0] = + pidff->create_new_effect_type->value[0]; + pidff->set_effect[PID_DURATION].value[0] = effect->replay.length; + pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button; + pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = + effect->trigger.interval; + pidff->set_effect[PID_GAIN].value[0] = + pidff->set_effect[PID_GAIN].field->logical_maximum; + pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; + pidff->effect_direction->value[0] = + pidff_rescale(effect->direction, 0xffff, + pidff->effect_direction); + pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay; + + hid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], + USB_DIR_OUT); +} + +/* + * Test if the values used in set_effect have changed + */ +static int pidff_needs_set_effect(struct ff_effect *effect, + struct ff_effect *old) +{ + return effect->replay.length != old->replay.length || + effect->trigger.interval != old->trigger.interval || + effect->trigger.button != old->trigger.button || + effect->direction != old->direction || + effect->replay.delay != old->replay.delay; +} + +/* + * Send periodic effect report to the device + */ +static void pidff_set_periodic_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_periodic[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff_set_signed(&pidff->set_periodic[PID_MAGNITUDE], + effect->u.periodic.magnitude); + pidff_set_signed(&pidff->set_periodic[PID_OFFSET], + effect->u.periodic.offset); + pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase); + pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period; + + hid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC], + USB_DIR_OUT); + +} + +/* + * Test if periodic effect parameters have changed + */ +static int pidff_needs_set_periodic(struct ff_effect *effect, + struct ff_effect *old) +{ + return effect->u.periodic.magnitude != old->u.periodic.magnitude || + effect->u.periodic.offset != old->u.periodic.offset || + effect->u.periodic.phase != old->u.periodic.phase || + effect->u.periodic.period != old->u.periodic.period; +} + +/* + * Send condition effect reports to the device + */ +static void pidff_set_condition_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + int i; + + pidff->set_condition[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + + for (i = 0; i < 2; i++) { + pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i; + pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET], + effect->u.condition[i].center); + pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT], + effect->u.condition[i].right_coeff); + pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT], + effect->u.condition[i].left_coeff); + pidff_set(&pidff->set_condition[PID_POS_SATURATION], + effect->u.condition[i].right_saturation); + pidff_set(&pidff->set_condition[PID_NEG_SATURATION], + effect->u.condition[i].left_saturation); + pidff_set(&pidff->set_condition[PID_DEAD_BAND], + effect->u.condition[i].deadband); + hid_wait_io(pidff->hid); + hid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], + USB_DIR_OUT); + } +} + +/* + * Test if condition effect parameters have changed + */ +static int pidff_needs_set_condition(struct ff_effect *effect, + struct ff_effect *old) +{ + int i; + int ret = 0; + + for (i = 0; i < 2; i++) { + struct ff_condition_effect *cond = &effect->u.condition[i]; + struct ff_condition_effect *old_cond = &old->u.condition[i]; + + ret |= cond->center != old_cond->center || + cond->right_coeff != old_cond->right_coeff || + cond->left_coeff != old_cond->left_coeff || + cond->right_saturation != old_cond->right_saturation || + cond->left_saturation != old_cond->left_saturation || + cond->deadband != old_cond->deadband; + } + + return ret; +} + +/* + * Send ramp force report to the device + */ +static void pidff_set_ramp_force_report(struct pidff_device *pidff, + struct ff_effect *effect) +{ + pidff->set_ramp[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + pidff_set_signed(&pidff->set_ramp[PID_RAMP_START], + effect->u.ramp.start_level); + pidff_set_signed(&pidff->set_ramp[PID_RAMP_END], + effect->u.ramp.end_level); + hid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP], + USB_DIR_OUT); +} + +/* + * Test if ramp force parameters have changed + */ +static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old) +{ + return effect->u.ramp.start_level != old->u.ramp.start_level || + effect->u.ramp.end_level != old->u.ramp.end_level; +} + +/* + * Send a request for effect upload to the device + * + * Returns 0 if device reported success, -ENOSPC if the device reported memory + * is full. Upon unknown response the function will retry for 60 times, if + * still unsuccessful -EIO is returned. + */ +static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum) +{ + int j; + + pidff->create_new_effect_type->value[0] = efnum; + hid_submit_report(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT], + USB_DIR_OUT); + debug("create_new_effect sent, type: %d", efnum); + + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; + pidff->block_load_status->value[0] = 0; + hid_wait_io(pidff->hid); + + for (j = 0; j < 60; j++) { + debug("pid_block_load requested"); + hid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_LOAD], + USB_DIR_IN); + hid_wait_io(pidff->hid); + if (pidff->block_load_status->value[0] == + pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) { + debug("device reported free memory: %d bytes", + pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? + pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); + return 0; + } + if (pidff->block_load_status->value[0] == + pidff->status_id[PID_BLOCK_LOAD_FULL]) { + debug("not enough memory free: %d bytes", + pidff->block_load[PID_RAM_POOL_AVAILABLE].value ? + pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1); + return -ENOSPC; + } + } + printk(KERN_ERR "hid-pidff: pid_block_load failed 60 times\n"); + return -EIO; +} + +/* + * Play the effect with PID id n times + */ +static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) +{ + pidff->effect_operation[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; + + if (n == 0) { + pidff->effect_operation_status->value[0] = + pidff->operation_id[PID_EFFECT_STOP]; + } else { + pidff->effect_operation_status->value[0] = + pidff->operation_id[PID_EFFECT_START]; + pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; + } + + hid_wait_io(pidff->hid); + hid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], + USB_DIR_OUT); +} + +/** + * Play the effect with effect id @effect_id for @value times + */ +static int pidff_playback(struct input_dev *dev, int effect_id, int value) +{ + struct pidff_device *pidff = dev->ff->private; + + pidff_playback_pid(pidff, pidff->pid_id[effect_id], value); + + return 0; +} + +/* + * Erase effect with PID id + */ +static void pidff_erase_pid(struct pidff_device *pidff, int pid_id) +{ + pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id; + hid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_FREE], + USB_DIR_OUT); +} + +/* + * Stop and erase effect with effect_id + */ +static int pidff_erase_effect(struct input_dev *dev, int effect_id) +{ + struct pidff_device *pidff = dev->ff->private; + int pid_id = pidff->pid_id[effect_id]; + + debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]); + pidff_playback_pid(pidff, pid_id, 0); + pidff_erase_pid(pidff, pid_id); + + return 0; +} + +/* + * Effect upload handler + */ +static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect, + struct ff_effect *old) +{ + struct pidff_device *pidff = dev->ff->private; + int type_id; + int error; + + switch (effect->type) { + case FF_CONSTANT: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_CONSTANT]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_constant(effect, old)) + pidff_set_constant_force_report(pidff, effect); + if (!old || + pidff_needs_set_envelope(&effect->u.constant.envelope, + &old->u.constant.envelope)) + pidff_set_envelope_report(pidff, + &effect->u.constant.envelope); + break; + + case FF_PERIODIC: + if (!old) { + switch (effect->u.periodic.waveform) { + case FF_SQUARE: + type_id = PID_SQUARE; + break; + case FF_TRIANGLE: + type_id = PID_TRIANGLE; + break; + case FF_SINE: + type_id = PID_SINE; + break; + case FF_SAW_UP: + type_id = PID_SAW_UP; + break; + case FF_SAW_DOWN: + type_id = PID_SAW_DOWN; + break; + default: + printk(KERN_ERR + "hid-pidff: invalid waveform\n"); + return -EINVAL; + } + + error = pidff_request_effect_upload(pidff, + pidff->type_id[type_id]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_periodic(effect, old)) + pidff_set_periodic_report(pidff, effect); + if (!old || + pidff_needs_set_envelope(&effect->u.periodic.envelope, + &old->u.periodic.envelope)) + pidff_set_envelope_report(pidff, + &effect->u.periodic.envelope); + break; + + case FF_RAMP: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_RAMP]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_ramp(effect, old)) + pidff_set_ramp_force_report(pidff, effect); + if (!old || + pidff_needs_set_envelope(&effect->u.ramp.envelope, + &old->u.ramp.envelope)) + pidff_set_envelope_report(pidff, + &effect->u.ramp.envelope); + break; + + case FF_SPRING: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_SPRING]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + case FF_FRICTION: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_FRICTION]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + case FF_DAMPER: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_DAMPER]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + case FF_INERTIA: + if (!old) { + error = pidff_request_effect_upload(pidff, + pidff->type_id[PID_INERTIA]); + if (error) + return error; + } + if (!old || pidff_needs_set_effect(effect, old)) + pidff_set_effect_report(pidff, effect); + if (!old || pidff_needs_set_condition(effect, old)) + pidff_set_condition_report(pidff, effect); + break; + + default: + printk(KERN_ERR "hid-pidff: invalid type\n"); + return -EINVAL; + } + + if (!old) + pidff->pid_id[effect->id] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; + + debug("uploaded"); + + return 0; +} + +/* + * set_gain() handler + */ +static void pidff_set_gain(struct input_dev *dev, u16 gain) +{ + struct pidff_device *pidff = dev->ff->private; + + pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain); + hid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], + USB_DIR_OUT); +} + +static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude) +{ + struct hid_field *field = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field; + + if (!magnitude) { + pidff_playback_pid(pidff, field->logical_minimum, 0); + return; + } + + pidff_playback_pid(pidff, field->logical_minimum, 1); + + pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum; + pidff->set_effect_type->value[0] = pidff->type_id[PID_SPRING]; + pidff->set_effect[PID_DURATION].value[0] = 0; + pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = 0; + pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0; + pidff_set(&pidff->set_effect[PID_GAIN], magnitude); + pidff->set_effect[PID_START_DELAY].value[0] = 0; + + hid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], + USB_DIR_OUT); +} + +/* + * pidff_set_autocenter() handler + */ +static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude) +{ + struct pidff_device *pidff = dev->ff->private; + + pidff_autocenter(pidff, magnitude); +} + +/* + * Find fields from a report and fill a pidff_usage + */ +static int pidff_find_fields(struct pidff_usage *usage, const u8 *table, + struct hid_report *report, int count, int strict) +{ + int i, j, k, found; + + for (k = 0; k < count; k++) { + found = 0; + for (i = 0; i < report->maxfield; i++) { + if (report->field[i]->maxusage != + report->field[i]->report_count) { + debug("maxusage and report_count do not match, " + "skipping"); + continue; + } + for (j = 0; j < report->field[i]->maxusage; j++) { + if (report->field[i]->usage[j].hid == + (HID_UP_PID | table[k])) { + debug("found %d at %d->%d", k, i, j); + usage[k].field = report->field[i]; + usage[k].value = + &report->field[i]->value[j]; + found = 1; + break; + } + } + if (found) + break; + } + if (!found && strict) { + debug("failed to locate %d", k); + return -1; + } + } + return 0; +} + +/* + * Return index into pidff_reports for the given usage + */ +static int pidff_check_usage(int usage) +{ + int i; + + for (i = 0; i < sizeof(pidff_reports); i++) + if (usage == (HID_UP_PID | pidff_reports[i])) + return i; + + return -1; +} + +/* + * Find the reports and fill pidff->reports[] + * report_type specifies either OUTPUT or FEATURE reports + */ +static void pidff_find_reports(struct hid_device *hid, int report_type, + struct pidff_device *pidff) +{ + struct hid_report *report; + int i, ret; + + list_for_each_entry(report, + &hid->report_enum[report_type].report_list, list) { + if (report->maxfield < 1) + continue; + ret = pidff_check_usage(report->field[0]->logical); + if (ret != -1) { + debug("found usage 0x%02x from field->logical", + pidff_reports[ret]); + pidff->reports[ret] = report; + continue; + } + + /* + * Sometimes logical collections are stacked to indicate + * different usages for the report and the field, in which + * case we want the usage of the parent. However, Linux HID + * implementation hides this fact, so we have to dig it up + * ourselves + */ + i = report->field[0]->usage[0].collection_index; + if (i <= 0 || + hid->collection[i - 1].type != HID_COLLECTION_LOGICAL) + continue; + ret = pidff_check_usage(hid->collection[i - 1].usage); + if (ret != -1 && !pidff->reports[ret]) { + debug("found usage 0x%02x from collection array", + pidff_reports[ret]); + pidff->reports[ret] = report; + } + } +} + +/* + * Test if the required reports have been found + */ +static int pidff_reports_ok(struct pidff_device *pidff) +{ + int i; + + for (i = 0; i <= PID_REQUIRED_REPORTS; i++) { + if (!pidff->reports[i]) { + debug("%d missing", i); + return 0; + } + } + + return 1; +} + +/* + * Find a field with a specific usage within a report + */ +static struct hid_field *pidff_find_special_field(struct hid_report *report, + int usage, int enforce_min) +{ + int i; + + for (i = 0; i < report->maxfield; i++) { + if (report->field[i]->logical == (HID_UP_PID | usage) && + report->field[i]->report_count > 0) { + if (!enforce_min || + report->field[i]->logical_minimum == 1) + return report->field[i]; + else { + printk(KERN_ERR "hid-pidff: logical_minimum " + "is not 1 as it should be\n"); + return NULL; + } + } + } + return NULL; +} + +/* + * Fill a pidff->*_id struct table + */ +static int pidff_find_special_keys(int *keys, struct hid_field *fld, + const u8 *usagetable, int count) +{ + + int i, j; + int found = 0; + + for (i = 0; i < count; i++) { + for (j = 0; j < fld->maxusage; j++) { + if (fld->usage[j].hid == (HID_UP_PID | usagetable[i])) { + keys[i] = j + 1; + found++; + break; + } + } + } + return found; +} + +#define PIDFF_FIND_SPECIAL_KEYS(keys, field, name) \ + pidff_find_special_keys(pidff->keys, pidff->field, pidff_ ## name, \ + sizeof(pidff_ ## name)) + +/* + * Find and check the special fields + */ +static int pidff_find_special_fields(struct pidff_device *pidff) +{ + debug("finding special fields"); + + pidff->create_new_effect_type = + pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT], + 0x25, 1); + pidff->set_effect_type = + pidff_find_special_field(pidff->reports[PID_SET_EFFECT], + 0x25, 1); + pidff->effect_direction = + pidff_find_special_field(pidff->reports[PID_SET_EFFECT], + 0x57, 0); + pidff->device_control = + pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL], + 0x96, 1); + pidff->block_load_status = + pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD], + 0x8b, 1); + pidff->effect_operation_status = + pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION], + 0x78, 1); + + debug("search done"); + + if (!pidff->create_new_effect_type || !pidff->set_effect_type) { + printk(KERN_ERR "hid-pidff: effect lists not found\n"); + return -1; + } + + if (!pidff->effect_direction) { + printk(KERN_ERR "hid-pidff: direction field not found\n"); + return -1; + } + + if (!pidff->device_control) { + printk(KERN_ERR "hid-pidff: device control field not found\n"); + return -1; + } + + if (!pidff->block_load_status) { + printk(KERN_ERR + "hid-pidff: block load status field not found\n"); + return -1; + } + + if (!pidff->effect_operation_status) { + printk(KERN_ERR + "hid-pidff: effect operation field not found\n"); + return -1; + } + + pidff_find_special_keys(pidff->control_id, pidff->device_control, + pidff_device_control, + sizeof(pidff_device_control)); + + PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control); + + if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type, + effect_types)) { + printk(KERN_ERR "hid-pidff: no effect types found\n"); + return -1; + } + + if (PIDFF_FIND_SPECIAL_KEYS(status_id, block_load_status, + block_load_status) != + sizeof(pidff_block_load_status)) { + printk(KERN_ERR + "hidpidff: block load status identifiers not found\n"); + return -1; + } + + if (PIDFF_FIND_SPECIAL_KEYS(operation_id, effect_operation_status, + effect_operation_status) != + sizeof(pidff_effect_operation_status)) { + printk(KERN_ERR + "hidpidff: effect operation identifiers not found\n"); + return -1; + } + + return 0; +} + +/** + * Find the implemented effect types + */ +static int pidff_find_effects(struct pidff_device *pidff, + struct input_dev *dev) +{ + int i; + + for (i = 0; i < sizeof(pidff_effect_types); i++) { + int pidff_type = pidff->type_id[i]; + if (pidff->set_effect_type->usage[pidff_type].hid != + pidff->create_new_effect_type->usage[pidff_type].hid) { + printk(KERN_ERR "hid-pidff: " + "effect type number %d is invalid\n", i); + return -1; + } + } + + if (pidff->type_id[PID_CONSTANT]) + set_bit(FF_CONSTANT, dev->ffbit); + if (pidff->type_id[PID_RAMP]) + set_bit(FF_RAMP, dev->ffbit); + if (pidff->type_id[PID_SQUARE]) { + set_bit(FF_SQUARE, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SINE]) { + set_bit(FF_SINE, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_TRIANGLE]) { + set_bit(FF_TRIANGLE, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SAW_UP]) { + set_bit(FF_SAW_UP, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SAW_DOWN]) { + set_bit(FF_SAW_DOWN, dev->ffbit); + set_bit(FF_PERIODIC, dev->ffbit); + } + if (pidff->type_id[PID_SPRING]) + set_bit(FF_SPRING, dev->ffbit); + if (pidff->type_id[PID_DAMPER]) + set_bit(FF_DAMPER, dev->ffbit); + if (pidff->type_id[PID_INERTIA]) + set_bit(FF_INERTIA, dev->ffbit); + if (pidff->type_id[PID_FRICTION]) + set_bit(FF_FRICTION, dev->ffbit); + + return 0; + +} + +#define PIDFF_FIND_FIELDS(name, report, strict) \ + pidff_find_fields(pidff->name, pidff_ ## name, \ + pidff->reports[report], \ + sizeof(pidff_ ## name), strict) + +/* + * Fill and check the pidff_usages + */ +static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev) +{ + int envelope_ok = 0; + + if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) { + printk(KERN_ERR + "hid-pidff: unknown set_effect report layout\n"); + return -ENODEV; + } + + PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0); + if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) { + printk(KERN_ERR + "hid-pidff: unknown pid_block_load report layout\n"); + return -ENODEV; + } + + if (PIDFF_FIND_FIELDS(effect_operation, PID_EFFECT_OPERATION, 1)) { + printk(KERN_ERR + "hid-pidff: unknown effect_operation report layout\n"); + return -ENODEV; + } + + if (PIDFF_FIND_FIELDS(block_free, PID_BLOCK_FREE, 1)) { + printk(KERN_ERR + "hid-pidff: unknown pid_block_free report layout\n"); + return -ENODEV; + } + + if (!PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1)) + envelope_ok = 1; + + if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev)) + return -ENODEV; + + if (!envelope_ok) { + if (test_and_clear_bit(FF_CONSTANT, dev->ffbit)) + printk(KERN_WARNING "hid-pidff: " + "has constant effect but no envelope\n"); + if (test_and_clear_bit(FF_RAMP, dev->ffbit)) + printk(KERN_WARNING "hid-pidff: " + "has ramp effect but no envelope\n"); + + if (test_and_clear_bit(FF_PERIODIC, dev->ffbit)) + printk(KERN_WARNING "hid-pidff: " + "has periodic effect but no envelope\n"); + } + + if (test_bit(FF_CONSTANT, dev->ffbit) && + PIDFF_FIND_FIELDS(set_constant, PID_SET_CONSTANT, 1)) { + printk(KERN_WARNING + "hid-pidff: unknown constant effect layout\n"); + clear_bit(FF_CONSTANT, dev->ffbit); + } + + if (test_bit(FF_RAMP, dev->ffbit) && + PIDFF_FIND_FIELDS(set_ramp, PID_SET_RAMP, 1)) { + printk(KERN_WARNING "hid-pidff: unknown ramp effect layout\n"); + clear_bit(FF_RAMP, dev->ffbit); + } + + if ((test_bit(FF_SPRING, dev->ffbit) || + test_bit(FF_DAMPER, dev->ffbit) || + test_bit(FF_FRICTION, dev->ffbit) || + test_bit(FF_INERTIA, dev->ffbit)) && + PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1)) { + printk(KERN_WARNING + "hid-pidff: unknown condition effect layout\n"); + clear_bit(FF_SPRING, dev->ffbit); + clear_bit(FF_DAMPER, dev->ffbit); + clear_bit(FF_FRICTION, dev->ffbit); + clear_bit(FF_INERTIA, dev->ffbit); + } + + if (test_bit(FF_PERIODIC, dev->ffbit) && + PIDFF_FIND_FIELDS(set_periodic, PID_SET_PERIODIC, 1)) { + printk(KERN_WARNING + "hid-pidff: unknown periodic effect layout\n"); + clear_bit(FF_PERIODIC, dev->ffbit); + } + + PIDFF_FIND_FIELDS(pool, PID_POOL, 0); + + if (!PIDFF_FIND_FIELDS(device_gain, PID_DEVICE_GAIN, 1)) + set_bit(FF_GAIN, dev->ffbit); + + return 0; +} + +/* + * Reset the device + */ +static void pidff_reset(struct pidff_device *pidff) +{ + struct hid_device *hid = pidff->hid; + int i = 0; + + pidff->device_control->value[0] = pidff->control_id[PID_RESET]; + /* We reset twice as sometimes hid_wait_io isn't waiting long enough */ + hid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + hid_wait_io(hid); + hid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + hid_wait_io(hid); + + pidff->device_control->value[0] = + pidff->control_id[PID_ENABLE_ACTUATORS]; + hid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT); + hid_wait_io(hid); + + /* pool report is sometimes messed up, refetch it */ + hid_submit_report(hid, pidff->reports[PID_POOL], USB_DIR_IN); + hid_wait_io(hid); + + if (pidff->pool[PID_SIMULTANEOUS_MAX].value) { + int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0]; + while (sim_effects < 2) { + if (i++ > 20) { + printk(KERN_WARNING "hid-pidff: device reports " + "%d simultaneous effects\n", + sim_effects); + break; + } + debug("pid_pool requested again"); + hid_submit_report(hid, pidff->reports[PID_POOL], + USB_DIR_IN); + hid_wait_io(hid); + } + } +} + +/* + * Test if autocenter modification is using the supported method + */ +static int pidff_check_autocenter(struct pidff_device *pidff, + struct input_dev *dev) +{ + int error; + + /* + * Let's find out if autocenter modification is supported + * Specification doesn't specify anything, so we request an + * effect upload and cancel it immediately. If the approved + * effect id was one above the minimum, then we assume the first + * effect id is a built-in spring type effect used for autocenter + */ + + error = pidff_request_effect_upload(pidff, 1); + if (error) { + printk(KERN_ERR "hid-pidff: upload request failed\n"); + return error; + } + + if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] == + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) { + pidff_autocenter(pidff, 0xffff); + set_bit(FF_AUTOCENTER, dev->ffbit); + } else { + printk(KERN_NOTICE "hid-pidff: " + "device has unknown autocenter control method\n"); + } + + pidff_erase_pid(pidff, + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]); + + return 0; + +} + +/* + * Check if the device is PID and initialize it + */ +int hid_pidff_init(struct hid_device *hid) +{ + struct pidff_device *pidff; + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); + struct input_dev *dev = hidinput->input; + struct ff_device *ff; + int max_effects; + int error; + + debug("starting pid init"); + + if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) { + debug("not a PID device, no output report"); + return -ENODEV; + } + + pidff = kzalloc(sizeof(*pidff), GFP_KERNEL); + if (!pidff) + return -ENOMEM; + + pidff->hid = hid; + + pidff_find_reports(hid, HID_OUTPUT_REPORT, pidff); + pidff_find_reports(hid, HID_FEATURE_REPORT, pidff); + + if (!pidff_reports_ok(pidff)) { + debug("reports not ok, aborting"); + error = -ENODEV; + goto fail; + } + + error = pidff_init_fields(pidff, dev); + if (error) + goto fail; + + pidff_reset(pidff); + + if (test_bit(FF_GAIN, dev->ffbit)) { + pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff); + hid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN], + USB_DIR_OUT); + } + + error = pidff_check_autocenter(pidff, dev); + if (error) + goto fail; + + max_effects = + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_maximum - + pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + + 1; + debug("max effects is %d", max_effects); + + if (max_effects > PID_EFFECTS_MAX) + max_effects = PID_EFFECTS_MAX; + + if (pidff->pool[PID_SIMULTANEOUS_MAX].value) + debug("max simultaneous effects is %d", + pidff->pool[PID_SIMULTANEOUS_MAX].value[0]); + + if (pidff->pool[PID_RAM_POOL_SIZE].value) + debug("device memory size is %d bytes", + pidff->pool[PID_RAM_POOL_SIZE].value[0]); + + if (pidff->pool[PID_DEVICE_MANAGED_POOL].value && + pidff->pool[PID_DEVICE_MANAGED_POOL].value[0] == 0) { + printk(KERN_NOTICE "hid-pidff: " + "device does not support device managed pool\n"); + goto fail; + } + + error = input_ff_create(dev, max_effects); + if (error) + goto fail; + + ff = dev->ff; + ff->private = pidff; + ff->upload = pidff_upload_effect; + ff->erase = pidff_erase_effect; + ff->set_gain = pidff_set_gain; + ff->set_autocenter = pidff_set_autocenter; + ff->playback = pidff_playback; + + printk(KERN_INFO "Force feedback for USB HID PID devices by " + "Anssi Hannula \n"); + + return 0; + + fail: + kfree(pidff); + return error; +} diff --git a/drivers/usb/input/hid-tmff.c b/drivers/usb/input/hid-tmff.c index 534425c..2d5be4c 100644 --- a/drivers/usb/input/hid-tmff.c +++ b/drivers/usb/input/hid-tmff.c @@ -28,97 +28,65 @@ */ #include -#include #undef DEBUG #include -#include - #include "hid.h" -#include "fixp-arith.h" /* Usages for thrustmaster devices I know about */ #define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb) -#define DELAY_CALC(t,delay) ((t) + (delay)*HZ/1000) - -/* Effect status */ -#define EFFECT_STARTED 0 /* Effect is going to play after some time */ -#define EFFECT_PLAYING 1 /* Effect is playing */ -#define EFFECT_USED 2 - -/* For tmff_device::flags */ -#define DEVICE_CLOSING 0 /* The driver is being unitialised */ - -/* Check that the current process can access an effect */ -#define CHECK_OWNERSHIP(effect) (current->pid == 0 \ - || effect.owner == current->pid) - -#define TMFF_CHECK_ID(id) ((id) >= 0 && (id) < TMFF_EFFECTS) -#define TMFF_CHECK_OWNERSHIP(i, l) \ - (test_bit(EFFECT_USED, l->effects[i].flags) \ - && CHECK_OWNERSHIP(l->effects[i])) - -#define TMFF_EFFECTS 8 - -struct tmff_effect { - pid_t owner; - - struct ff_effect effect; - - unsigned long flags[1]; - unsigned int count; /* Number of times left to play */ - - unsigned long play_at; /* When the effect starts to play */ - unsigned long stop_at; /* When the effect ends */ -}; struct tmff_device { - struct hid_device *hid; - struct hid_report *report; - struct hid_field *rumble; +}; - unsigned int effects_playing; - struct tmff_effect effects[TMFF_EFFECTS]; - spinlock_t lock; /* device-level lock. Having locks on - a per-effect basis could be nice, but - isn't really necessary */ +/* Changes values from 0 to 0xffff into values from minimum to maximum */ +static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) +{ + int ret; - unsigned long flags[1]; /* Contains various information about the - state of the driver for this device */ + ret = (in * (maximum - minimum) / 0xffff) + minimum; + if (ret < minimum) + return minimum; + if (ret > maximum) + return maximum; + return ret; +} - struct timer_list timer; -}; +static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct tmff_device *tmff = data; + int left, right; /* Rumbling */ -/* Callbacks */ -static void hid_tmff_exit(struct hid_device *hid); -static int hid_tmff_event(struct hid_device *hid, struct input_dev *input, - unsigned int type, unsigned int code, int value); -static int hid_tmff_flush(struct input_dev *input, struct file *file); -static int hid_tmff_upload_effect(struct input_dev *input, - struct ff_effect *effect); -static int hid_tmff_erase(struct input_dev *input, int id); + left = hid_tmff_scale(effect->u.rumble.weak_magnitude, + tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); + right = hid_tmff_scale(effect->u.rumble.strong_magnitude, + tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); -/* Local functions */ -static void hid_tmff_recalculate_timer(struct tmff_device *tmff); -static void hid_tmff_timer(unsigned long timer_data); + tmff->rumble->value[0] = left; + tmff->rumble->value[1] = right; + dbg("(left,right)=(%08x, %08x)", left, right); + hid_submit_report(hid, tmff->report, USB_DIR_OUT); + + return 0; +} int hid_tmff_init(struct hid_device *hid) { - struct tmff_device *private; + struct tmff_device *tmff; struct list_head *pos; struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); struct input_dev *input_dev = hidinput->input; + int error; - private = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); - if (!private) + tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); + if (!tmff) return -ENOMEM; - hid->ff_private = private; - /* Find the report to use */ __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) { struct hid_report *report = (struct hid_report *)pos; @@ -142,18 +110,18 @@ int hid_tmff_init(struct hid_device *hid continue; } - if (private->report && private->report != report) { + if (tmff->report && tmff->report != report) { warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report"); continue; } - if (private->rumble && private->rumble != field) { + if (tmff->rumble && tmff->rumble != field) { warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR"); continue; } - private->report = report; - private->rumble = field; + tmff->report = report; + tmff->rumble = field; set_bit(FF_RUMBLE, input_dev->ffbit); break; @@ -162,302 +130,17 @@ int hid_tmff_init(struct hid_device *hid warn("ignoring unknown output usage %08x", field->usage[0].hid); continue; } - - /* Fallthrough to here only when a valid usage is found */ - input_dev->upload_effect = hid_tmff_upload_effect; - input_dev->flush = hid_tmff_flush; - - set_bit(EV_FF, input_dev->evbit); - input_dev->ff_effects_max = TMFF_EFFECTS; } } - private->hid = hid; - - spin_lock_init(&private->lock); - init_timer(&private->timer); - private->timer.data = (unsigned long)private; - private->timer.function = hid_tmff_timer; - - /* Event and exit callbacks */ - hid->ff_exit = hid_tmff_exit; - hid->ff_event = hid_tmff_event; - - info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse "); - - return 0; -} - -static void hid_tmff_exit(struct hid_device *hid) -{ - struct tmff_device *tmff = hid->ff_private; - unsigned long flags; - - spin_lock_irqsave(&tmff->lock, flags); - - set_bit(DEVICE_CLOSING, tmff->flags); - del_timer_sync(&tmff->timer); - - spin_unlock_irqrestore(&tmff->lock, flags); - - kfree(tmff); -} - -static int hid_tmff_event(struct hid_device *hid, struct input_dev *input, - unsigned int type, unsigned int code, int value) -{ - struct tmff_device *tmff = hid->ff_private; - struct tmff_effect *effect = &tmff->effects[code]; - unsigned long flags; - - if (type != EV_FF) - return -EINVAL; - if (!TMFF_CHECK_ID(code)) - return -EINVAL; - if (!TMFF_CHECK_OWNERSHIP(code, tmff)) - return -EACCES; - if (value < 0) - return -EINVAL; - - spin_lock_irqsave(&tmff->lock, flags); - - if (value > 0) { - set_bit(EFFECT_STARTED, effect->flags); - clear_bit(EFFECT_PLAYING, effect->flags); - effect->count = value; - effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay); - } else { - clear_bit(EFFECT_STARTED, effect->flags); - clear_bit(EFFECT_PLAYING, effect->flags); - } - - hid_tmff_recalculate_timer(tmff); - - spin_unlock_irqrestore(&tmff->lock, flags); - - return 0; - -} - -/* Erase all effects this process owns */ - -static int hid_tmff_flush(struct input_dev *dev, struct file *file) -{ - struct hid_device *hid = dev->private; - struct tmff_device *tmff = hid->ff_private; - int i; - - for (i=0; iff_effects_max; ++i) - - /* NOTE: no need to lock here. The only times EFFECT_USED is - modified is when effects are uploaded or when an effect is - erased. But a process cannot close its dev/input/eventX fd - and perform ioctls on the same fd all at the same time */ - - if (current->pid == tmff->effects[i].owner - && test_bit(EFFECT_USED, tmff->effects[i].flags)) - if (hid_tmff_erase(dev, i)) - warn("erase effect %d failed", i); - - - return 0; -} - -static int hid_tmff_erase(struct input_dev *dev, int id) -{ - struct hid_device *hid = dev->private; - struct tmff_device *tmff = hid->ff_private; - unsigned long flags; - - if (!TMFF_CHECK_ID(id)) - return -EINVAL; - if (!TMFF_CHECK_OWNERSHIP(id, tmff)) - return -EACCES; - - spin_lock_irqsave(&tmff->lock, flags); - - tmff->effects[id].flags[0] = 0; - hid_tmff_recalculate_timer(tmff); - - spin_unlock_irqrestore(&tmff->lock, flags); - - return 0; -} - -static int hid_tmff_upload_effect(struct input_dev *input, - struct ff_effect *effect) -{ - struct hid_device *hid = input->private; - struct tmff_device *tmff = hid->ff_private; - int id; - unsigned long flags; - - if (!test_bit(effect->type, input->ffbit)) - return -EINVAL; - if (effect->id != -1 && !TMFF_CHECK_ID(effect->id)) - return -EINVAL; - - spin_lock_irqsave(&tmff->lock, flags); - - if (effect->id == -1) { - /* Find a free effect */ - for (id = 0; id < TMFF_EFFECTS && test_bit(EFFECT_USED, tmff->effects[id].flags); ++id); - - if (id >= TMFF_EFFECTS) { - spin_unlock_irqrestore(&tmff->lock, flags); - return -ENOSPC; - } - - effect->id = id; - tmff->effects[id].owner = current->pid; - tmff->effects[id].flags[0] = 0; - set_bit(EFFECT_USED, tmff->effects[id].flags); - - } else { - /* Re-uploading an owned effect, to change parameters */ - id = effect->id; - clear_bit(EFFECT_PLAYING, tmff->effects[id].flags); + error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); + if (error) { + kfree(tmff); + return error; } - tmff->effects[id].effect = *effect; - - hid_tmff_recalculate_timer(tmff); + info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse "); - spin_unlock_irqrestore(&tmff->lock, flags); return 0; } -/* Start the timer for the next start/stop/delay */ -/* Always call this while tmff->lock is locked */ - -static void hid_tmff_recalculate_timer(struct tmff_device *tmff) -{ - int i; - int events = 0; - unsigned long next_time; - - next_time = 0; /* Shut up compiler's incorrect warning */ - - /* Find the next change in an effect's status */ - for (i = 0; i < TMFF_EFFECTS; ++i) { - struct tmff_effect *effect = &tmff->effects[i]; - unsigned long play_time; - - if (!test_bit(EFFECT_STARTED, effect->flags)) - continue; - - effect->stop_at = DELAY_CALC(effect->play_at, effect->effect.replay.length); - - if (!test_bit(EFFECT_PLAYING, effect->flags)) - play_time = effect->play_at; - else - play_time = effect->stop_at; - - events++; - - if (time_after(jiffies, play_time)) - play_time = jiffies; - - if (events == 1) - next_time = play_time; - else { - if (time_after(next_time, play_time)) - next_time = play_time; - } - } - - if (!events && tmff->effects_playing) { - /* Treat all effects turning off as an event */ - events = 1; - next_time = jiffies; - } - - if (!events) { - /* No events, no time, no need for a timer. */ - del_timer_sync(&tmff->timer); - return; - } - - mod_timer(&tmff->timer, next_time); -} - -/* Changes values from 0 to 0xffff into values from minimum to maximum */ -static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) -{ - int ret; - - ret = (in * (maximum - minimum) / 0xffff) + minimum; - if (ret < minimum) - return minimum; - if (ret > maximum) - return maximum; - return ret; -} - -static void hid_tmff_timer(unsigned long timer_data) -{ - struct tmff_device *tmff = (struct tmff_device *) timer_data; - struct hid_device *hid = tmff->hid; - unsigned long flags; - int left = 0, right = 0; /* Rumbling */ - int i; - - spin_lock_irqsave(&tmff->lock, flags); - - tmff->effects_playing = 0; - - for (i = 0; i < TMFF_EFFECTS; ++i) { - struct tmff_effect *effect = &tmff->effects[i]; - - if (!test_bit(EFFECT_STARTED, effect->flags)) - continue; - - if (!time_after(jiffies, effect->play_at)) - continue; - - if (time_after(jiffies, effect->stop_at)) { - - dbg("Finished playing once %d", i); - clear_bit(EFFECT_PLAYING, effect->flags); - - if (--effect->count <= 0) { - dbg("Stopped %d", i); - clear_bit(EFFECT_STARTED, effect->flags); - continue; - } else { - dbg("Start again %d", i); - effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay); - continue; - } - } - - ++tmff->effects_playing; - - set_bit(EFFECT_PLAYING, effect->flags); - - switch (effect->effect.type) { - case FF_RUMBLE: - right += effect->effect.u.rumble.strong_magnitude; - left += effect->effect.u.rumble.weak_magnitude; - break; - default: - BUG(); - break; - } - } - - left = hid_tmff_scale(left, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); - right = hid_tmff_scale(right, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); - - if (left != tmff->rumble->value[0] || right != tmff->rumble->value[1]) { - tmff->rumble->value[0] = left; - tmff->rumble->value[1] = right; - dbg("(left,right)=(%08x, %08x)", left, right); - hid_submit_report(hid, tmff->report, USB_DIR_OUT); - } - - if (!test_bit(DEVICE_CLOSING, tmff->flags)) - hid_tmff_recalculate_timer(tmff); - - spin_unlock_irqrestore(&tmff->lock, flags); -} diff --git a/drivers/usb/input/hid-zpff.c b/drivers/usb/input/hid-zpff.c new file mode 100644 index 0000000..d2ce321 --- /dev/null +++ b/drivers/usb/input/hid-zpff.c @@ -0,0 +1,110 @@ +/* + * Force feedback support for Zeroplus based devices + * + * Copyright (c) 2005, 2006 Anssi Hannula + */ + +/* + * 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 + */ + + +/* #define DEBUG */ + +#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg) + +#include +#include +#include "hid.h" + +struct zpff_device { + struct hid_report *report; +}; + +static int hid_zpff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = dev->private; + struct zpff_device *zpff = data; + int left, right; + + /* + * The following is specified the other way around in the Zeroplus + * datasheet but the order below is correct for the XFX Executioner; + * however it is possible that the XFX Executioner is an exception + */ + + left = effect->u.rumble.strong_magnitude; + right = effect->u.rumble.weak_magnitude; + debug("called with 0x%04x 0x%04x", left, right); + + left = left * 0x7f / 0xffff; + right = right * 0x7f / 0xffff; + + zpff->report->field[2]->value[0] = left; + zpff->report->field[3]->value[0] = right; + debug("running with 0x%02x 0x%02x", left, right); + hid_submit_report(hid, zpff->report, USB_DIR_OUT); + + return 0; +} + +int hid_zpff_init(struct hid_device *hid) +{ + struct zpff_device *zpff; + struct hid_report *report; + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + int error; + + if (list_empty(report_list)) { + printk(KERN_ERR "hid-zpff: no output report found\n"); + return -ENODEV; + } + + report = list_entry(report_list->next, struct hid_report, list); + + if (report->maxfield < 4) { + printk(KERN_ERR "hid-zpff: not enough fields in report\n"); + return -ENODEV; + } + + zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL); + if (!zpff) + return -ENOMEM; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, zpff, hid_zpff_play); + if (error) { + kfree(zpff); + return error; + } + + zpff->report = report; + zpff->report->field[0]->value[0] = 0x00; + zpff->report->field[1]->value[0] = 0x02; + zpff->report->field[2]->value[0] = 0x00; + zpff->report->field[3]->value[0] = 0x00; + hid_submit_report(hid, zpff->report, USB_DIR_OUT); + + printk(KERN_INFO "Force feedback for Zeroplus based devices by " + "Anssi Hannula \n"); + + return 0; +} diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h index 778e575..b03fd9b 100644 --- a/drivers/usb/input/hid.h +++ b/drivers/usb/input/hid.h @@ -449,11 +449,6 @@ struct hid_device { /* device repo char phys[64]; /* Device physical location */ char uniq[64]; /* Device unique identifier (serial #) */ - void *ff_private; /* Private data for the force-feedback driver */ - void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */ - int (*ff_event)(struct hid_device *hid, struct input_dev *input, - unsigned int type, unsigned int code, int value); - #ifdef CONFIG_USB_HIDINPUT_POWERBOOK unsigned long pb_pressed_fn[NBITS(KEY_MAX)]; unsigned long pb_pressed_numlock[NBITS(KEY_MAX)]; @@ -521,29 +516,22 @@ void hid_close(struct hid_device *); int hid_set_field(struct hid_field *, unsigned, __s32); void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir); void hid_init_reports(struct hid_device *hid); -struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type); int hid_wait_io(struct hid_device* hid); #ifdef CONFIG_HID_FF int hid_ff_init(struct hid_device *hid); + +int hid_lgff_init(struct hid_device *hid); +int hid_tmff_init(struct hid_device *hid); +int hid_zpff_init(struct hid_device *hid); +#ifdef CONFIG_HID_PID +int hid_pidff_init(struct hid_device *hid); +#else +static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; } +#endif + #else static inline int hid_ff_init(struct hid_device *hid) { return -1; } #endif -static inline void hid_ff_exit(struct hid_device *hid) -{ - if (hid->ff_exit) - hid->ff_exit(hid); -} -static inline int hid_ff_event(struct hid_device *hid, struct input_dev *input, - unsigned int type, unsigned int code, int value) -{ - if (hid->ff_event) - return hid->ff_event(hid, input, type, code, value); - return -ENOSYS; -} - -int hid_lgff_init(struct hid_device* hid); -int hid_tmff_init(struct hid_device* hid); -int hid_pid_init(struct hid_device* hid); diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index 70477f0..f6b839c 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -49,7 +49,7 @@ struct hiddev { int open; wait_queue_head_t wait; struct hid_device *hid; - struct hiddev_list *list; + struct list_head list; }; struct hiddev_list { @@ -59,7 +59,7 @@ struct hiddev_list { unsigned flags; struct fasync_struct *fasync; struct hiddev *hiddev; - struct hiddev_list *next; + struct list_head node; }; static struct hiddev *hiddev_table[HIDDEV_MINORS]; @@ -73,12 +73,15 @@ static struct hiddev *hiddev_table[HIDDE static struct hid_report * hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo) { - unsigned flags = rinfo->report_id & ~HID_REPORT_ID_MASK; + unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK; + unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK; struct hid_report_enum *report_enum; + struct hid_report *report; struct list_head *list; if (rinfo->report_type < HID_REPORT_TYPE_MIN || - rinfo->report_type > HID_REPORT_TYPE_MAX) return NULL; + rinfo->report_type > HID_REPORT_TYPE_MAX) + return NULL; report_enum = hid->report_enum + (rinfo->report_type - HID_REPORT_TYPE_MIN); @@ -88,21 +91,25 @@ hiddev_lookup_report(struct hid_device * break; case HID_REPORT_ID_FIRST: - list = report_enum->report_list.next; - if (list == &report_enum->report_list) + if (list_empty(&report_enum->report_list)) return NULL; - rinfo->report_id = ((struct hid_report *) list)->id; + + list = report_enum->report_list.next; + report = list_entry(list, struct hid_report, list); + rinfo->report_id = report->id; break; case HID_REPORT_ID_NEXT: - list = (struct list_head *) - report_enum->report_id_hash[rinfo->report_id & HID_REPORT_ID_MASK]; - if (list == NULL) + report = report_enum->report_id_hash[rid]; + if (!report) return NULL; - list = list->next; + + list = report->list.next; if (list == &report_enum->report_list) return NULL; - rinfo->report_id = ((struct hid_report *) list)->id; + + report = list_entry(list, struct hid_report, list); + rinfo->report_id = report->id; break; default: @@ -125,12 +132,13 @@ hiddev_lookup_usage(struct hid_device *h struct hid_field *field; if (uref->report_type < HID_REPORT_TYPE_MIN || - uref->report_type > HID_REPORT_TYPE_MAX) return NULL; + uref->report_type > HID_REPORT_TYPE_MAX) + return NULL; report_enum = hid->report_enum + (uref->report_type - HID_REPORT_TYPE_MIN); - list_for_each_entry(report, &report_enum->report_list, list) + list_for_each_entry(report, &report_enum->report_list, list) { for (i = 0; i < report->maxfield; i++) { field = report->field[i]; for (j = 0; j < field->maxusage; j++) { @@ -142,6 +150,7 @@ hiddev_lookup_usage(struct hid_device *h } } } + } return NULL; } @@ -150,9 +159,9 @@ static void hiddev_send_event(struct hid struct hiddev_usage_ref *uref) { struct hiddev *hiddev = hid->hiddev; - struct hiddev_list *list = hiddev->list; + struct hiddev_list *list; - while (list) { + list_for_each_entry(list, &hiddev->list, node) { if (uref->field_index != HID_FIELD_INDEX_NONE || (list->flags & HIDDEV_FLAG_REPORT) != 0) { list->buffer[list->head] = *uref; @@ -160,8 +169,6 @@ static void hiddev_send_event(struct hid (HIDDEV_BUFFER_SIZE - 1); kill_fasync(&list->fasync, SIGIO, POLL_IN); } - - list = list->next; } wake_up_interruptible(&hiddev->wait); @@ -180,7 +187,7 @@ void hiddev_hid_event(struct hid_device uref.report_type = (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : - ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); + ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); uref.report_id = field->report->id; uref.field_index = field->index; uref.usage_index = (usage - field->usage); @@ -200,7 +207,7 @@ void hiddev_report_event(struct hid_devi uref.report_type = (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT : ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : - ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0)); + ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0)); uref.report_id = report->id; uref.field_index = HID_FIELD_INDEX_NONE; @@ -213,7 +220,9 @@ static int hiddev_fasync(int fd, struct { int retval; struct hiddev_list *list = file->private_data; + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; } @@ -224,14 +233,9 @@ static int hiddev_fasync(int fd, struct static int hiddev_release(struct inode * inode, struct file * file) { struct hiddev_list *list = file->private_data; - struct hiddev_list **listptr; - listptr = &list->hiddev->list; hiddev_fasync(-1, file, 0); - - while (*listptr && (*listptr != list)) - listptr = &((*listptr)->next); - *listptr = (*listptr)->next; + list_del(&list->node); if (!--list->hiddev->open) { if (list->hiddev->exist) @@ -248,7 +252,8 @@ static int hiddev_release(struct inode * /* * open file op */ -static int hiddev_open(struct inode * inode, struct file * file) { +static int hiddev_open(struct inode *inode, struct file *file) +{ struct hiddev_list *list; int i = iminor(inode) - HIDDEV_MINOR_BASE; @@ -260,9 +265,7 @@ static int hiddev_open(struct inode * in return -ENOMEM; list->hiddev = hiddev_table[i]; - list->next = hiddev_table[i]->list; - hiddev_table[i]->list = list; - + list_add_tail(&list->node, &hiddev_table[i]->list); file->private_data = list; if (!list->hiddev->open++) @@ -362,6 +365,7 @@ static ssize_t hiddev_read(struct file * static unsigned int hiddev_poll(struct file *file, poll_table *wait) { struct hiddev_list *list = file->private_data; + poll_wait(file, &list->hiddev->wait, wait); if (list->head != list->tail) return POLLIN | POLLRDNORM; @@ -382,7 +386,7 @@ static int hiddev_ioctl(struct inode *in struct hiddev_collection_info cinfo; struct hiddev_report_info rinfo; struct hiddev_field_info finfo; - struct hiddev_usage_ref_multi *uref_multi=NULL; + struct hiddev_usage_ref_multi *uref_multi = NULL; struct hiddev_usage_ref *uref; struct hiddev_devinfo dinfo; struct hid_report *report; @@ -764,15 +768,15 @@ int hiddev_connect(struct hid_device *hi } init_waitqueue_head(&hiddev->wait); - - hiddev_table[hid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; - + INIT_LIST_HEAD(&hiddev->list); hiddev->hid = hid; hiddev->exist = 1; hid->minor = hid->intf->minor; hid->hiddev = hiddev; + hiddev_table[hid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; + return 0; } diff --git a/drivers/usb/input/pid.c b/drivers/usb/input/pid.c deleted file mode 100644 index d9d9f65..0000000 --- a/drivers/usb/input/pid.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * PID Force feedback support for hid devices. - * - * Copyright (c) 2002 Rodrigo Damazio. - * Portions by Johann Deneux and Bjorn Augustson - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "hid.h" -#include "pid.h" - -#define CHECK_OWNERSHIP(i, hid_pid) \ - ((i) < FF_EFFECTS_MAX && i >= 0 && \ - test_bit(FF_PID_FLAGS_USED, &hid_pid->effects[(i)].flags) && \ - (current->pid == 0 || \ - (hid_pid)->effects[(i)].owner == current->pid)) - -/* Called when a transfer is completed */ -static void hid_pid_ctrl_out(struct urb *u, struct pt_regs *regs) -{ - dev_dbg(&u->dev->dev, "hid_pid_ctrl_out - Transfer Completed\n"); -} - -static void hid_pid_exit(struct hid_device *hid) -{ - struct hid_ff_pid *private = hid->ff_private; - - if (private->urbffout) { - usb_kill_urb(private->urbffout); - usb_free_urb(private->urbffout); - } -} - -static int pid_upload_periodic(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update) -{ - dev_info(&pid->hid->dev->dev, "requested periodic force upload\n"); - return 0; -} - -static int pid_upload_constant(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update) -{ - dev_info(&pid->hid->dev->dev, "requested constant force upload\n"); - return 0; -} - -static int pid_upload_condition(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update) -{ - dev_info(&pid->hid->dev->dev, "requested Condition force upload\n"); - return 0; -} - -static int pid_upload_ramp(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update) -{ - dev_info(&pid->hid->dev->dev, "request ramp force upload\n"); - return 0; -} - -static int hid_pid_event(struct hid_device *hid, struct input_dev *input, - unsigned int type, unsigned int code, int value) -{ - dev_dbg(&hid->dev->dev, "PID event received: type=%d,code=%d,value=%d.\n", type, code, value); - - if (type != EV_FF) - return -1; - - return 0; -} - -/* Lock must be held by caller */ -static void hid_pid_ctrl_playback(struct hid_device *hid, struct hid_pid_effect *effect, int play) -{ - if (play) - set_bit(FF_PID_FLAGS_PLAYING, &effect->flags); - else - clear_bit(FF_PID_FLAGS_PLAYING, &effect->flags); -} - -static int hid_pid_erase(struct input_dev *dev, int id) -{ - struct hid_device *hid = dev->private; - struct hid_ff_pid *pid = hid->ff_private; - struct hid_field *field; - unsigned long flags; - int ret; - - if (!CHECK_OWNERSHIP(id, pid)) - return -EACCES; - - /* Find report */ - field = hid_find_field_by_usage(hid, HID_UP_PID | FF_PID_USAGE_BLOCK_FREE, - HID_OUTPUT_REPORT); - if (!field) { - dev_err(&hid->dev->dev, "couldn't find report\n"); - return -EIO; - } - - ret = hid_set_field(field, 0, pid->effects[id].device_id); - if (ret) { - dev_err(&hid->dev->dev, "couldn't set field\n"); - return ret; - } - - hid_submit_report(hid, field->report, USB_DIR_OUT); - - spin_lock_irqsave(&pid->lock, flags); - hid_pid_ctrl_playback(hid, pid->effects + id, 0); - pid->effects[id].flags = 0; - spin_unlock_irqrestore(&pid->lock, flags); - - return 0; -} - -/* Erase all effects this process owns */ -static int hid_pid_flush(struct input_dev *dev, struct file *file) -{ - struct hid_device *hid = dev->private; - struct hid_ff_pid *pid = hid->ff_private; - int i; - - /*NOTE: no need to lock here. The only times EFFECT_USED is - modified is when effects are uploaded or when an effect is - erased. But a process cannot close its dev/input/eventX fd - and perform ioctls on the same fd all at the same time */ - /*FIXME: multiple threads, anyone? */ - for (i = 0; i < dev->ff_effects_max; ++i) - if (current->pid == pid->effects[i].owner - && test_bit(FF_PID_FLAGS_USED, &pid->effects[i].flags)) - if (hid_pid_erase(dev, i)) - dev_warn(&hid->dev->dev, "erase effect %d failed", i); - - return 0; -} - -static int hid_pid_upload_effect(struct input_dev *dev, - struct ff_effect *effect) -{ - struct hid_ff_pid *pid_private = (struct hid_ff_pid *)(dev->private); - int ret; - int is_update; - unsigned long flags; - - dev_dbg(&pid_private->hid->dev->dev, "upload effect called: effect_type=%x\n", effect->type); - /* Check this effect type is supported by this device */ - if (!test_bit(effect->type, dev->ffbit)) { - dev_dbg(&pid_private->hid->dev->dev, - "invalid kind of effect requested.\n"); - return -EINVAL; - } - - /* - * If we want to create a new effect, get a free id - */ - if (effect->id == -1) { - int id = 0; - - // Spinlock so we don`t get a race condition when choosing IDs - spin_lock_irqsave(&pid_private->lock, flags); - - while (id < FF_EFFECTS_MAX) - if (!test_and_set_bit(FF_PID_FLAGS_USED, &pid_private->effects[id++].flags)) - break; - - if (id == FF_EFFECTS_MAX) { - spin_unlock_irqrestore(&pid_private->lock, flags); -// TEMP - We need to get ff_effects_max correctly first: || id >= dev->ff_effects_max) { - dev_dbg(&pid_private->hid->dev->dev, "Not enough device memory\n"); - return -ENOMEM; - } - - effect->id = id; - dev_dbg(&pid_private->hid->dev->dev, "effect ID is %d.\n", id); - pid_private->effects[id].owner = current->pid; - pid_private->effects[id].flags = (1 << FF_PID_FLAGS_USED); - spin_unlock_irqrestore(&pid_private->lock, flags); - - is_update = FF_PID_FALSE; - } else { - /* We want to update an effect */ - if (!CHECK_OWNERSHIP(effect->id, pid_private)) - return -EACCES; - - /* Parameter type cannot be updated */ - if (effect->type != pid_private->effects[effect->id].effect.type) - return -EINVAL; - - /* Check the effect is not already being updated */ - if (test_bit(FF_PID_FLAGS_UPDATING, &pid_private->effects[effect->id].flags)) - return -EAGAIN; - - is_update = FF_PID_TRUE; - } - - /* - * Upload the effect - */ - switch (effect->type) { - case FF_PERIODIC: - ret = pid_upload_periodic(pid_private, effect, is_update); - break; - - case FF_CONSTANT: - ret = pid_upload_constant(pid_private, effect, is_update); - break; - - case FF_SPRING: - case FF_FRICTION: - case FF_DAMPER: - case FF_INERTIA: - ret = pid_upload_condition(pid_private, effect, is_update); - break; - - case FF_RAMP: - ret = pid_upload_ramp(pid_private, effect, is_update); - break; - - default: - dev_dbg(&pid_private->hid->dev->dev, - "invalid type of effect requested - %x.\n", - effect->type); - return -EINVAL; - } - /* If a packet was sent, forbid new updates until we are notified - * that the packet was updated - */ - if (ret == 0) - set_bit(FF_PID_FLAGS_UPDATING, &pid_private->effects[effect->id].flags); - pid_private->effects[effect->id].effect = *effect; - return ret; -} - -int hid_pid_init(struct hid_device *hid) -{ - struct hid_ff_pid *private; - struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); - struct input_dev *input_dev = hidinput->input; - - private = hid->ff_private = kzalloc(sizeof(struct hid_ff_pid), GFP_KERNEL); - if (!private) - return -ENOMEM; - - private->hid = hid; - - hid->ff_exit = hid_pid_exit; - hid->ff_event = hid_pid_event; - - /* Open output URB */ - if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) { - kfree(private); - return -1; - } - - usb_fill_control_urb(private->urbffout, hid->dev, 0, - (void *)&private->ffcr, private->ctrl_buffer, 8, - hid_pid_ctrl_out, hid); - - input_dev->upload_effect = hid_pid_upload_effect; - input_dev->flush = hid_pid_flush; - input_dev->ff_effects_max = 8; // A random default - set_bit(EV_FF, input_dev->evbit); - set_bit(EV_FF_STATUS, input_dev->evbit); - - spin_lock_init(&private->lock); - - printk(KERN_INFO "Force feedback driver for PID devices by Rodrigo Damazio .\n"); - - return 0; -} diff --git a/drivers/usb/input/pid.h b/drivers/usb/input/pid.h deleted file mode 100644 index a2cb962..0000000 --- a/drivers/usb/input/pid.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * PID Force feedback support for hid devices. - * - * Copyright (c) 2002 Rodrigo Damazio. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to - */ - -#define FF_EFFECTS_MAX 64 - -#define FF_PID_FLAGS_USED 1 /* If the effect exists */ -#define FF_PID_FLAGS_UPDATING 2 /* If the effect is being updated */ -#define FF_PID_FLAGS_PLAYING 3 /* If the effect is currently being played */ - -#define FF_PID_FALSE 0 -#define FF_PID_TRUE 1 - -struct hid_pid_effect { - unsigned long flags; - pid_t owner; - unsigned int device_id; /* The device-assigned ID */ - struct ff_effect effect; -}; - -struct hid_ff_pid { - struct hid_device *hid; - unsigned long gain; - - struct urb *urbffout; - struct usb_ctrlrequest ffcr; - spinlock_t lock; - - unsigned char ctrl_buffer[8]; - - struct hid_pid_effect effects[FF_EFFECTS_MAX]; -}; - -/* - * Constants from the PID usage table (still far from complete) - */ - -#define FF_PID_USAGE_BLOCK_LOAD 0x89UL -#define FF_PID_USAGE_BLOCK_FREE 0x90UL -#define FF_PID_USAGE_NEW_EFFECT 0xABUL -#define FF_PID_USAGE_POOL_REPORT 0x7FUL diff --git a/include/linux/input.h b/include/linux/input.h index 56f1e0e..a7a1f55 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -667,98 +667,167 @@ #define FF_STATUS_MAX 0x01 /* * Structures used in ioctls to upload effects to a device - * The first structures are not passed directly by using ioctls. - * They are sub-structures of the actually sent structure (called ff_effect) + * They are pieces of a bigger structure (called ff_effect) */ +/* + * All duration values are expressed in ms. Values above 32767 ms (0x7fff) + * should not be used and have unspecified results. + */ + +/** + * struct ff_replay - defines scheduling of the effect + * @length: duration of the effect + * @delay: delay before effect should start playing + */ struct ff_replay { - __u16 length; /* Duration of an effect in ms. All other times are also expressed in ms */ - __u16 delay; /* Time to wait before to start playing an effect */ + __u16 length; + __u16 delay; }; +/** + * struct ff_trigger - defines what triggers the effect + * @button: number of the button triggering the effect + * @interval: controls how soon the effect can be re-triggered + */ struct ff_trigger { - __u16 button; /* Number of button triggering an effect */ - __u16 interval; /* Time to wait before an effect can be re-triggered (ms) */ + __u16 button; + __u16 interval; }; +/** + * struct ff_envelope - generic effect envelope + * @attack_length: duration of the attack (ms) + * @attack_level: level at the beginning of the attack + * @fade_length: duration of fade (ms) + * @fade_level: level at the end of fade + * + * The @attack_level and @fade_level are absolute values; when applying + * envelope force-feedback core will convert to positive/negative + * value based on polarity of the default level of the effect. + * Valid range for the attack and fade levels is 0x0000 - 0x7fff + */ struct ff_envelope { - __u16 attack_length; /* Duration of attack (ms) */ - __u16 attack_level; /* Level at beginning of attack */ - __u16 fade_length; /* Duration of fade (ms) */ - __u16 fade_level; /* Level at end of fade */ + __u16 attack_length; + __u16 attack_level; + __u16 fade_length; + __u16 fade_level; }; -/* FF_CONSTANT */ +/** + * struct ff_constant_effect - defines parameters of a constant effect + * @level: strength of the effect; may be negative + * @envelope: envelope data + */ struct ff_constant_effect { - __s16 level; /* Strength of effect. Negative values are OK */ + __s16 level; struct ff_envelope envelope; }; -/* FF_RAMP */ +/** + * struct ff_ramp_effect - defines parameters of a ramp effect + * @start_level: beginning strength of the effect; may be negative + * @end_level: final strength of the effect; may be negative + * @envelope: envelope data + */ struct ff_ramp_effect { __s16 start_level; __s16 end_level; struct ff_envelope envelope; }; -/* FF_SPRING of FF_FRICTION */ +/** + * struct ff_condition_effect - defines a spring or friction effect + * @right_saturation: maximum level when joystick moved all way to the right + * @left_saturation: same for the left side + * @right_coeff: controls how fast the force grows when the joystick moves + * to the right + * @left_coeff: same for the left side + * @deadband: size of the dead zone, where no force is produced + * @center: position of the dead zone + */ struct ff_condition_effect { - __u16 right_saturation; /* Max level when joystick is on the right */ - __u16 left_saturation; /* Max level when joystick in on the left */ - - __s16 right_coeff; /* Indicates how fast the force grows when the - joystick moves to the right */ - __s16 left_coeff; /* Same for left side */ + __u16 right_saturation; + __u16 left_saturation; - __u16 deadband; /* Size of area where no force is produced */ - __s16 center; /* Position of dead zone */ + __s16 right_coeff; + __s16 left_coeff; + __u16 deadband; + __s16 center; }; -/* FF_PERIODIC */ +/** + * struct ff_periodic_effect - defines parameters of a periodic effect + * @waveform: kind of the effect (wave) + * @period: period of the wave (ms) + * @magnitude: peak value + * @offset: mean value of the wave (roughly) + * @phase: 'horizontal' shift + * @envelope: envelope data + * @custom_len: number of samples (FF_CUSTOM only) + * @custom_data: buffer of samples (FF_CUSTOM only) + * + * Known waveforms - FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, + * FF_SAW_DOWN, FF_CUSTOM. The exact syntax FF_CUSTOM is undefined + * for the time being as no driver supports it yet. + * + * Note: the data pointed by custom_data is copied by the driver. + * You can therefore dispose of the memory after the upload/update. + */ struct ff_periodic_effect { - __u16 waveform; /* Kind of wave (sine, square...) */ - __u16 period; /* in ms */ - __s16 magnitude; /* Peak value */ - __s16 offset; /* Mean value of wave (roughly) */ - __u16 phase; /* 'Horizontal' shift */ + __u16 waveform; + __u16 period; + __s16 magnitude; + __s16 offset; + __u16 phase; struct ff_envelope envelope; -/* Only used if waveform == FF_CUSTOM */ - __u32 custom_len; /* Number of samples */ - __s16 *custom_data; /* Buffer of samples */ -/* Note: the data pointed by custom_data is copied by the driver. You can - * therefore dispose of the memory after the upload/update */ + __u32 custom_len; + __s16 *custom_data; }; -/* FF_RUMBLE */ -/* Some rumble pads have two motors of different weight. - strong_magnitude represents the magnitude of the vibration generated - by the heavy motor. -*/ +/** + * struct ff_rumble_effect - defines parameters of a periodic effect + * @strong_magnitude: magnitude of the heavy motor + * @weak_magnitude: magnitude of the light one + * + * Some rumble pads have two motors of different weight. Strong_magnitude + * represents the magnitude of the vibration generated by the heavy one. + */ struct ff_rumble_effect { - __u16 strong_magnitude; /* Magnitude of the heavy motor */ - __u16 weak_magnitude; /* Magnitude of the light one */ + __u16 strong_magnitude; + __u16 weak_magnitude; }; -/* - * Structure sent through ioctl from the application to the driver +/** + * struct ff_effect - defines force feedback effect + * @type: type of the effect (FF_CONSTANT, FF_PERIODIC, FF_RAMP, FF_SPRING, + * FF_FRICTION, FF_DAMPER, FF_RUMBLE, FF_INERTIA, or FF_CUSTOM) + * @id: an unique id assigned to an effect + * @direction: direction of the effect + * @trigger: trigger conditions (struct ff_trigger) + * @replay: scheduling of the effect (struct ff_replay) + * @u: effect-specific structure (one of ff_constant_effect, ff_ramp_effect, + * ff_periodic_effect, ff_condition_effect, ff_rumble_effect) further + * defining effect parameters + * + * This structure is sent through ioctl from the application to the driver. + * To create a new effect aplication should set its @id to -1; the kernel + * will return assigned @id which can later be used to update or delete + * this effect. + * + * Direction of the effect is encoded as follows: + * 0 deg -> 0x0000 (down) + * 90 deg -> 0x4000 (left) + * 180 deg -> 0x8000 (up) + * 270 deg -> 0xC000 (right) */ struct ff_effect { __u16 type; -/* Following field denotes the unique id assigned to an effect. - * If user sets if to -1, a new effect is created, and its id is returned in the same field - * Else, the user sets it to the effect id it wants to update. - */ __s16 id; - - __u16 direction; /* Direction. 0 deg -> 0x0000 (down) - 90 deg -> 0x4000 (left) - 180 deg -> 0x8000 (up) - 270 deg -> 0xC000 (right) - */ - + __u16 direction; struct ff_trigger trigger; struct ff_replay replay; @@ -784,6 +853,9 @@ #define FF_DAMPER 0x55 #define FF_INERTIA 0x56 #define FF_RAMP 0x57 +#define FF_EFFECT_MIN FF_RUMBLE +#define FF_EFFECT_MAX FF_RAMP + /* * Force feedback periodic effect types */ @@ -795,6 +867,9 @@ #define FF_SAW_UP 0x5b #define FF_SAW_DOWN 0x5c #define FF_CUSTOM 0x5d +#define FF_WAVEFORM_MIN FF_SQUARE +#define FF_WAVEFORM_MAX FF_CUSTOM + /* * Set ff device properties */ @@ -864,12 +939,13 @@ struct input_dev { unsigned long sndbit[NBITS(SND_MAX)]; unsigned long ffbit[NBITS(FF_MAX)]; unsigned long swbit[NBITS(SW_MAX)]; - int ff_effects_max; unsigned int keycodemax; unsigned int keycodesize; void *keycode; + struct ff_device *ff; + unsigned int repeat_key; struct timer_list timer; @@ -893,11 +969,8 @@ struct input_dev { int (*open)(struct input_dev *dev); void (*close)(struct input_dev *dev); - int (*accept)(struct input_dev *dev, struct file *file); int (*flush)(struct input_dev *dev, struct file *file); int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); - int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect); - int (*erase_effect)(struct input_dev *dev, int effect_id); struct input_handle *grab; @@ -961,6 +1034,26 @@ #define INPUT_DEVICE_ID_MATCH_DEVICE_AND struct input_handle; +/** + * struct input_handler - implements one of interfaces for input devices + * @private: driver-specific data + * @event: event handler + * @connect: called when attaching a handler to an input device + * @disconnect: disconnects a handler from input device + * @start: starts handler for given handle. This function is called by + * input core right after connect() method and also when a process + * that "grabbed" a device releases it + * @fops: file operations this driver implements + * @minor: beginning of range of 32 minors for devices this driver + * can provide + * @name: name of the handler, to be shown in /proc/bus/input/handlers + * @id_table: pointer to a table of input_device_ids this driver can + * handle + * @blacklist: prointer to a table of input_device_ids this driver should + * ignore even if they match @id_table + * @h_list: list of input handles associated with the handler + * @node: for placing the driver onto input_handler_list + */ struct input_handler { void *private; @@ -968,6 +1061,7 @@ struct input_handler { void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id); void (*disconnect)(struct input_handle *handle); + void (*start)(struct input_handle *handle); const struct file_operations *fops; int minor; @@ -1030,10 +1124,10 @@ void input_release_device(struct input_h int input_open_device(struct input_handle *); void input_close_device(struct input_handle *); -int input_accept_process(struct input_handle *handle, struct file *file); int input_flush_device(struct input_handle* handle, struct file* file); void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); +void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value); static inline void input_report_key(struct input_dev *dev, unsigned int code, int value) { @@ -1050,11 +1144,6 @@ static inline void input_report_abs(stru input_event(dev, EV_ABS, code, value); } -static inline void input_report_ff(struct input_dev *dev, unsigned int code, int value) -{ - input_event(dev, EV_FF, code, value); -} - static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value) { input_event(dev, EV_FF_STATUS, code, value); @@ -1088,5 +1177,61 @@ static inline void input_set_abs_params( extern struct class input_class; +/** + * struct ff_device - force-feedback part of an input device + * @upload: Called to upload an new effect into device + * @erase: Called to erase an effect from device + * @playback: Called to request device to start playing specified effect + * @set_gain: Called to set specified gain + * @set_autocenter: Called to auto-center device + * @destroy: called by input core when parent input device is being + * destroyed + * @private: driver-specific data, will be freed automatically + * @ffbit: bitmap of force feedback capabilities truly supported by + * device (not emulated like ones in input_dev->ffbit) + * @mutex: mutex for serializing access to the device + * @max_effects: maximum number of effects supported by device + * @effects: pointer to an array of effects currently loaded into device + * @effect_owners: array of effect owners; when file handle owning + * an effect gets closed the effcet is automatically erased + * + * Every force-feedback device must implement upload() and playback() + * methods; erase() is optional. set_gain() and set_autocenter() need + * only be implemented if driver sets up FF_GAIN and FF_AUTOCENTER + * bits. + */ +struct ff_device { + int (*upload)(struct input_dev *dev, struct ff_effect *effect, + struct ff_effect *old); + int (*erase)(struct input_dev *dev, int effect_id); + + int (*playback)(struct input_dev *dev, int effect_id, int value); + void (*set_gain)(struct input_dev *dev, u16 gain); + void (*set_autocenter)(struct input_dev *dev, u16 magnitude); + + void (*destroy)(struct ff_device *); + + void *private; + + unsigned long ffbit[NBITS(FF_MAX)]; + + struct mutex mutex; + + int max_effects; + struct ff_effect *effects; + struct file *effect_owners[]; +}; + +int input_ff_create(struct input_dev *dev, int max_effects); +void input_ff_destroy(struct input_dev *dev); + +int input_ff_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); + +int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, struct file *file); +int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file); + +int input_ff_create_memless(struct input_dev *dev, void *data, + int (*play_effect)(struct input_dev *, void *, struct ff_effect *)); + #endif #endif diff --git a/include/linux/serio.h b/include/linux/serio.h index 6348e83..c906931 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -217,5 +217,8 @@ #define SERIO_SEMTECH 0x27 #define SERIO_LKKBD 0x28 #define SERIO_ELO 0x29 #define SERIO_MICROTOUCH 0x30 +#define SERIO_PENMOUNT 0x31 +#define SERIO_TOUCHRIGHT 0x32 +#define SERIO_TOUCHWIN 0x33 #endif diff --git a/include/linux/uinput.h b/include/linux/uinput.h index 7168302..1fd61ee 100644 --- a/include/linux/uinput.h +++ b/include/linux/uinput.h @@ -22,12 +22,18 @@ #define __UINPUT_H_ * Author: Aristeu Sergio Rozanski Filho * * Changes/Revisions: + * 0.3 24/05/2006 (Anssi Hannula ) + * - update ff support for the changes in kernel interface + * - add UINPUT_VERSION * 0.2 16/10/2004 (Micah Dowty ) * - added force feedback support * - added UI_SET_PHYS * 0.1 20/06/2002 * - first public version */ + +#define UINPUT_VERSION 3 + #ifdef __KERNEL__ #define UINPUT_MINOR 223 #define UINPUT_NAME "uinput" @@ -45,7 +51,10 @@ struct uinput_request { union { int effect_id; - struct ff_effect* effect; + struct { + struct ff_effect *effect; + struct ff_effect *old; + } upload; } u; }; @@ -58,6 +67,7 @@ struct uinput_device { unsigned char head; unsigned char tail; struct input_event buff[UINPUT_BUFFER_SIZE]; + int ff_effects_max; struct uinput_request *requests[UINPUT_NUM_REQUESTS]; wait_queue_head_t requests_waitq; @@ -69,6 +79,7 @@ struct uinput_ff_upload { int request_id; int retval; struct ff_effect effect; + struct ff_effect old; }; struct uinput_ff_erase { @@ -98,33 +109,33 @@ #define UI_END_FF_UPLOAD _IOW(UINPUT_IOC #define UI_BEGIN_FF_ERASE _IOWR(UINPUT_IOCTL_BASE, 202, struct uinput_ff_erase) #define UI_END_FF_ERASE _IOW(UINPUT_IOCTL_BASE, 203, struct uinput_ff_erase) -/* To write a force-feedback-capable driver, the upload_effect +/* + * To write a force-feedback-capable driver, the upload_effect * and erase_effect callbacks in input_dev must be implemented. * The uinput driver will generate a fake input event when one of * these callbacks are invoked. The userspace code then uses * ioctls to retrieve additional parameters and send the return code. * The callback blocks until this return code is sent. * - * The described callback mechanism is only used if EV_FF is set. - * Otherwise, default implementations of upload_effect and erase_effect - * are used. + * The described callback mechanism is only used if ff_effects_max + * is set. * * To implement upload_effect(): - * 1. Wait for an event with type==EV_UINPUT and code==UI_FF_UPLOAD. + * 1. Wait for an event with type == EV_UINPUT and code == UI_FF_UPLOAD. * A request ID will be given in 'value'. * 2. Allocate a uinput_ff_upload struct, fill in request_id with * the 'value' from the EV_UINPUT event. * 3. Issue a UI_BEGIN_FF_UPLOAD ioctl, giving it the * uinput_ff_upload struct. It will be filled in with the - * ff_effect passed to upload_effect(). - * 4. Perform the effect upload, and place the modified ff_effect - * and a return code back into the uinput_ff_upload struct. + * ff_effects passed to upload_effect(). + * 4. Perform the effect upload, and place a return code back into + the uinput_ff_upload struct. * 5. Issue a UI_END_FF_UPLOAD ioctl, also giving it the * uinput_ff_upload_effect struct. This will complete execution * of our upload_effect() handler. * * To implement erase_effect(): - * 1. Wait for an event with type==EV_UINPUT and code==UI_FF_ERASE. + * 1. Wait for an event with type == EV_UINPUT and code == UI_FF_ERASE. * A request ID will be given in 'value'. * 2. Allocate a uinput_ff_erase struct, fill in request_id with * the 'value' from the EV_UINPUT event. @@ -133,13 +144,13 @@ #define UI_END_FF_ERASE _IOW(UINPUT_IOC * effect ID passed to erase_effect(). * 4. Perform the effect erasure, and place a return code back * into the uinput_ff_erase struct. - * and a return code back into the uinput_ff_erase struct. * 5. Issue a UI_END_FF_ERASE ioctl, also giving it the * uinput_ff_erase_effect struct. This will complete execution * of our erase_effect() handler. */ -/* This is the new event type, used only by uinput. +/* + * This is the new event type, used only by uinput. * 'code' is UI_FF_UPLOAD or UI_FF_ERASE, and 'value' * is the unique request ID. This number was picked * arbitrarily, above EV_MAX (since the input system