Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
Linus Torvalds [Wed, 15 Oct 2008 15:07:35 +0000 (08:07 -0700)]
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (158 commits)
  powerpc: Fix CHRP PCI config access for indirect_pci
  powerpc/chrp: Fix detection of Python PCI host bridge on IBM CHRPs
  powerpc: Fix 32-bit SMP boot on CHRP
  powerpc: Fix link errors on 32-bit machines using legacy DMA
  powerpc/pci: Improve detection of unassigned bridge resources
  hvc_console: Fix free_irq in spinlocked section
  powerpc: Get USE_STRICT_MM_TYPECHECKS working again
  powerpc: Reflect the used arguments in machine_init() prototype
  powerpc: Fix DMA offset for non-coherent DMA
  powerpc: fix fsl_upm nand driver modular build
  powerpc/83xx: add NAND support for the MPC8360E-RDK boards
  powerpc: FPGA support for GE Fanuc SBC610
  i2c: MPC8349E-mITX Power Management and GPIO expander driver
  powerpc: reserve two DMA channels for audio in MPC8610 HPCD device tree
  powerpc: document the "fsl,ssi-dma-channel" compatible property
  powerpc: disable CHRP and PMAC support in various defconfigs
  OF: add fsl,mcu-mpc8349emitx to the exception list
  powerpc/83xx: add DS1374 RTC support for the MPC837xE-MDS boards
  powerpc: remove support for bootmem-allocated memory for the DIU driver
  powerpc: remove non-dependent load fsl_booke PTE_64BIT
  ...

109 files changed:
Documentation/feature-removal-schedule.txt
Documentation/filesystems/ocfs2.txt
Documentation/i2c/busses/i2c-viapro
Documentation/i2c/dev-interface
Documentation/i2c/smbus-protocol
Documentation/i2c/writing-clients
arch/arm/mach-omap1/board-h3.c
arch/arm/mach-omap2/board-h4.c
arch/x86/kernel/rtc.c
drivers/acpi/glue.c
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-a4tech.c [new file with mode: 0644]
drivers/hid/hid-apple.c [new file with mode: 0644]
drivers/hid/hid-belkin.c [new file with mode: 0644]
drivers/hid/hid-bright.c [new file with mode: 0644]
drivers/hid/hid-cherry.c [new file with mode: 0644]
drivers/hid/hid-chicony.c [new file with mode: 0644]
drivers/hid/hid-core.c
drivers/hid/hid-cypress.c [new file with mode: 0644]
drivers/hid/hid-dell.c [new file with mode: 0644]
drivers/hid/hid-dummy.c [new file with mode: 0644]
drivers/hid/hid-ezkey.c [new file with mode: 0644]
drivers/hid/hid-gyration.c [new file with mode: 0644]
drivers/hid/hid-ids.h [new file with mode: 0644]
drivers/hid/hid-input-quirks.c [deleted file]
drivers/hid/hid-input.c
drivers/hid/hid-lg.c [new file with mode: 0644]
drivers/hid/hid-lg.h [new file with mode: 0644]
drivers/hid/hid-lg2ff.c [moved from drivers/hid/usbhid/hid-lg2ff.c with 90% similarity]
drivers/hid/hid-lgff.c [moved from drivers/hid/usbhid/hid-lgff.c with 81% similarity]
drivers/hid/hid-microsoft.c [new file with mode: 0644]
drivers/hid/hid-monterey.c [new file with mode: 0644]
drivers/hid/hid-petalynx.c [new file with mode: 0644]
drivers/hid/hid-pl.c [moved from drivers/hid/usbhid/hid-plff.c with 68% similarity]
drivers/hid/hid-samsung.c [new file with mode: 0644]
drivers/hid/hid-sony.c [new file with mode: 0644]
drivers/hid/hid-sunplus.c [new file with mode: 0644]
drivers/hid/hid-tmff.c [moved from drivers/hid/usbhid/hid-tmff.c with 60% similarity]
drivers/hid/hid-zpff.c [moved from drivers/hid/usbhid/hid-zpff.c with 67% similarity]
drivers/hid/hidraw.c
drivers/hid/usbhid/Kconfig
drivers/hid/usbhid/Makefile
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-ff.c [deleted file]
drivers/hid/usbhid/hid-pidff.c
drivers/hid/usbhid/hid-quirks.c
drivers/hid/usbhid/hiddev.c
drivers/hid/usbhid/usbhid.h
drivers/hid/usbhid/usbkbd.c
drivers/hid/usbhid/usbmouse.c
drivers/hwmon/dme1737.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-highlander.c [new file with mode: 0644]
drivers/i2c/busses/i2c-parport-light.c
drivers/i2c/busses/i2c-pca-isa.c
drivers/i2c/busses/i2c-viapro.c
drivers/i2c/chips/isp1301_omap.c
drivers/i2c/chips/tps65010.c
drivers/i2c/i2c-core.c
drivers/rtc/rtc-cmos.c
fs/Kconfig
fs/ocfs2/Makefile
fs/ocfs2/alloc.c
fs/ocfs2/alloc.h
fs/ocfs2/aops.c
fs/ocfs2/buffer_head_io.c
fs/ocfs2/buffer_head_io.h
fs/ocfs2/cluster/masklog.c
fs/ocfs2/cluster/masklog.h
fs/ocfs2/dir.c
fs/ocfs2/dlmglue.c
fs/ocfs2/extent_map.c
fs/ocfs2/extent_map.h
fs/ocfs2/file.c
fs/ocfs2/file.h
fs/ocfs2/inode.c
fs/ocfs2/inode.h
fs/ocfs2/ioctl.c
fs/ocfs2/journal.c
fs/ocfs2/journal.h
fs/ocfs2/localalloc.c
fs/ocfs2/localalloc.h
fs/ocfs2/locks.c
fs/ocfs2/locks.h
fs/ocfs2/namei.c
fs/ocfs2/ocfs2.h
fs/ocfs2/ocfs2_fs.h
fs/ocfs2/ocfs2_jbd_compat.h [new file with mode: 0644]
fs/ocfs2/resize.c
fs/ocfs2/slot_map.c
fs/ocfs2/stack_user.c
fs/ocfs2/stackglue.c
fs/ocfs2/stackglue.h
fs/ocfs2/suballoc.c
fs/ocfs2/suballoc.h
fs/ocfs2/super.c
fs/ocfs2/symlink.c
fs/ocfs2/uptodate.c
fs/ocfs2/uptodate.h
fs/ocfs2/xattr.c [new file with mode: 0644]
fs/ocfs2/xattr.h [new file with mode: 0644]
include/linux/hid.h
include/linux/hiddev.h
include/linux/mod_devicetable.h
net/bluetooth/hidp/core.c
net/bluetooth/hidp/hidp.h
scripts/mod/file2alias.c

index cc8093c..4d2566a 100644 (file)
@@ -287,6 +287,13 @@ Who:       Glauber Costa <gcosta@redhat.com>
 
 ---------------------------
 
+What:  remove HID compat support
+When:  2.6.29
+Why:   needed only as a temporary solution until distros fix themselves up
+Who:   Jiri Slaby <jirislaby@gmail.com>
+
+---------------------------
+
 What:  /sys/o2cb symlink
 When:  January 2010
 Why:   /sys/fs/o2cb is the proper location for this information - /sys/o2cb
index c318a8b..4340cc8 100644 (file)
@@ -76,3 +76,9 @@ localalloc=8(*)               Allows custom localalloc size in MB. If the value is too
                        large, the fs will silently revert it to the default.
                        Localalloc is not enabled for local mounts.
 localflocks            This disables cluster aware flock.
+inode64                        Indicates that Ocfs2 is allowed to create inodes at
+                       any location in the filesystem, including those which
+                       will result in inode numbers occupying more than 32
+                       bits of significance.
+user_xattr     (*)     Enables Extended User Attributes.
+nouser_xattr           Disables Extended User Attributes.
index 1405fb6..22efedf 100644 (file)
@@ -16,6 +16,9 @@ Supported adapters:
   * VIA Technologies, Inc. CX700
     Datasheet: available on request and under NDA from VIA
 
+  * VIA Technologies, Inc. VX800/VX820
+    Datasheet: available on http://linux.via.com.tw
+
 Authors:
        Kyösti Mälkki <kmalkki@cc.hut.fi>,
        Mark D. Studebaker <mdsxyz123@yahoo.com>,
@@ -49,6 +52,7 @@ Your lspci -n listing must show one of these :
  device 1106:3372   (VT8237S)
  device 1106:3287   (VT8251)
  device 1106:8324   (CX700)
+ device 1106:8353   (VX800/VX820)
 
 If none of these show up, you should look in the BIOS for settings like
 enable ACPI / SMBus or even USB.
@@ -57,5 +61,5 @@ Except for the oldest chips (VT82C596A/B, VT82C686A and most probably
 VT8231), this driver supports I2C block transactions. Such transactions
 are mainly useful to read from and write to EEPROMs.
 
-The CX700 additionally appears to support SMBus PEC, although this driver
-doesn't implement it yet.
+The CX700/VX800/VX820 additionally appears to support SMBus PEC, although
+this driver doesn't implement it yet.
index 9dd7912..3e742ba 100644 (file)
@@ -4,6 +4,10 @@ the /dev interface. You need to load module i2c-dev for this.
 
 Each registered i2c adapter gets a number, counting from 0. You can
 examine /sys/class/i2c-dev/ to see what number corresponds to which adapter.
+Alternatively, you can run "i2cdetect -l" to obtain a formated list of all
+i2c adapters present on your system at a given time. i2cdetect is part of
+the i2c-tools package.
+
 I2C device files are character device files with major device number 89
 and a minor device number corresponding to the number assigned as 
 explained above. They should be called "i2c-%d" (i2c-0, i2c-1, ..., 
@@ -17,30 +21,34 @@ So let's say you want to access an i2c adapter from a C program. The
 first thing to do is "#include <linux/i2c-dev.h>". Please note that
 there are two files named "i2c-dev.h" out there, one is distributed
 with the Linux kernel and is meant to be included from kernel
-driver code, the other one is distributed with lm_sensors and is
+driver code, the other one is distributed with i2c-tools and is
 meant to be included from user-space programs. You obviously want
 the second one here.
 
 Now, you have to decide which adapter you want to access. You should
-inspect /sys/class/i2c-dev/ to decide this. Adapter numbers are assigned
-somewhat dynamically, so you can not even assume /dev/i2c-0 is the
-first adapter.
+inspect /sys/class/i2c-dev/ or run "i2cdetect -l" to decide this.
+Adapter numbers are assigned somewhat dynamically, so you can not
+assume much about them. They can even change from one boot to the next.
 
 Next thing, open the device file, as follows:
+
   int file;
   int adapter_nr = 2; /* probably dynamically determined */
   char filename[20];
   
-  sprintf(filename,"/dev/i2c-%d",adapter_nr);
-  if ((file = open(filename,O_RDWR)) < 0) {
+  snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
+  file = open(filename, O_RDWR);
+  if (file < 0) {
     /* ERROR HANDLING; you can check errno to see what went wrong */
     exit(1);
   }
 
 When you have opened the device, you must specify with what device
 address you want to communicate:
+
   int addr = 0x40; /* The I2C address */
-  if (ioctl(file,I2C_SLAVE,addr) < 0) {
+
+  if (ioctl(file, I2C_SLAVE, addr) < 0) {
     /* ERROR HANDLING; you can check errno to see what went wrong */
     exit(1);
   }
@@ -48,31 +56,41 @@ address you want to communicate:
 Well, you are all set up now. You can now use SMBus commands or plain
 I2C to communicate with your device. SMBus commands are preferred if
 the device supports them. Both are illustrated below.
+
   __u8 register = 0x10; /* Device register to access */
   __s32 res;
   char buf[10];
+
   /* Using SMBus commands */
-  res = i2c_smbus_read_word_data(file,register);
+  res = i2c_smbus_read_word_data(file, register);
   if (res < 0) {
     /* ERROR HANDLING: i2c transaction failed */
   } else {
     /* res contains the read word */
   }
+
   /* Using I2C Write, equivalent of 
-           i2c_smbus_write_word_data(file,register,0x6543) */
+     i2c_smbus_write_word_data(file, register, 0x6543) */
   buf[0] = register;
   buf[1] = 0x43;
   buf[2] = 0x65;
-  if ( write(file,buf,3) != 3) {
+  if (write(file, buf, 3) ! =3) {
     /* ERROR HANDLING: i2c transaction failed */
   }
+
   /* Using I2C Read, equivalent of i2c_smbus_read_byte(file) */
-  if (read(file,buf,1) != 1) {
+  if (read(file, buf, 1) != 1) {
     /* ERROR HANDLING: i2c transaction failed */
   } else {
     /* buf[0] contains the read byte */
   }
 
+Note that only a subset of the I2C and SMBus protocols can be achieved by
+the means of read() and write() calls. In particular, so-called combined
+transactions (mixing read and write messages in the same transaction)
+aren't supported. For this reason, this interface is almost never used by
+user-space programs.
+
 IMPORTANT: because of the use of inline functions, you *have* to use
 '-O' or some variation when you compile your program!
 
@@ -80,31 +98,29 @@ IMPORTANT: because of the use of inline functions, you *have* to use
 Full interface description
 ==========================
 
-The following IOCTLs are defined and fully supported 
-(see also i2c-dev.h):
+The following IOCTLs are defined:
 
-ioctl(file,I2C_SLAVE,long addr)
+ioctl(file, I2C_SLAVE, long addr)
   Change slave address. The address is passed in the 7 lower bits of the
   argument (except for 10 bit addresses, passed in the 10 lower bits in this
   case).
 
-ioctl(file,I2C_TENBIT,long select)
+ioctl(file, I2C_TENBIT, long select)
   Selects ten bit addresses if select not equals 0, selects normal 7 bit
   addresses if select equals 0. Default 0.  This request is only valid
   if the adapter has I2C_FUNC_10BIT_ADDR.
 
-ioctl(file,I2C_PEC,long select)
+ioctl(file, I2C_PEC, long select)
   Selects SMBus PEC (packet error checking) generation and verification
   if select not equals 0, disables if select equals 0. Default 0.
   Used only for SMBus transactions.  This request only has an effect if the
   the adapter has I2C_FUNC_SMBUS_PEC; it is still safe if not, it just
   doesn't have any effect.
 
-ioctl(file,I2C_FUNCS,unsigned long *funcs)
+ioctl(file, I2C_FUNCS, unsigned long *funcs)
   Gets the adapter functionality and puts it in *funcs.
 
-ioctl(file,I2C_RDWR,struct i2c_rdwr_ioctl_data *msgset)
-
+ioctl(file, I2C_RDWR, struct i2c_rdwr_ioctl_data *msgset)
   Do combined read/write transaction without stop in between.
   Only valid if the adapter has I2C_FUNC_I2C.  The argument is
   a pointer to a
@@ -120,10 +136,9 @@ ioctl(file,I2C_RDWR,struct i2c_rdwr_ioctl_data *msgset)
   The slave address and whether to use ten bit address mode has to be
   set in each message, overriding the values set with the above ioctl's.
 
-
-Other values are NOT supported at this moment, except for I2C_SMBUS,
-which you should never directly call; instead, use the access functions
-below.
+ioctl(file, I2C_SMBUS, struct i2c_smbus_ioctl_data *args)
+  Not meant to be called  directly; instead, use the access functions
+  below.
 
 You can do plain i2c transactions by using read(2) and write(2) calls.
 You do not need to pass the address byte; instead, set it through
@@ -148,7 +163,52 @@ what happened. The 'write' transactions return 0 on success; the
 returns the number of values read. The block buffers need not be longer
 than 32 bytes.
 
-The above functions are all macros, that resolve to calls to the
-i2c_smbus_access function, that on its turn calls a specific ioctl
+The above functions are all inline functions, that resolve to calls to
+the i2c_smbus_access function, that on its turn calls a specific ioctl
 with the data in a specific format. Read the source code if you
 want to know what happens behind the screens.
+
+
+Implementation details
+======================
+
+For the interested, here's the code flow which happens inside the kernel
+when you use the /dev interface to I2C:
+
+1* Your program opens /dev/i2c-N and calls ioctl() on it, as described in
+section "C example" above.
+
+2* These open() and ioctl() calls are handled by the i2c-dev kernel
+driver: see i2c-dev.c:i2cdev_open() and i2c-dev.c:i2cdev_ioctl(),
+respectively. You can think of i2c-dev as a generic I2C chip driver
+that can be programmed from user-space.
+
+3* Some ioctl() calls are for administrative tasks and are handled by
+i2c-dev directly. Examples include I2C_SLAVE (set the address of the
+device you want to access) and I2C_PEC (enable or disable SMBus error
+checking on future transactions.)
+
+4* Other ioctl() calls are converted to in-kernel function calls by
+i2c-dev. Examples include I2C_FUNCS, which queries the I2C adapter
+functionality using i2c.h:i2c_get_functionality(), and I2C_SMBUS, which
+performs an SMBus transaction using i2c-core.c:i2c_smbus_xfer().
+
+The i2c-dev driver is responsible for checking all the parameters that
+come from user-space for validity. After this point, there is no
+difference between these calls that came from user-space through i2c-dev
+and calls that would have been performed by kernel I2C chip drivers
+directly. This means that I2C bus drivers don't need to implement
+anything special to support access from user-space.
+
+5* These i2c-core.c/i2c.h functions are wrappers to the actual
+implementation of your I2C bus driver. Each adapter must declare
+callback functions implementing these standard calls.
+i2c.h:i2c_get_functionality() calls i2c_adapter.algo->functionality(),
+while i2c-core.c:i2c_smbus_xfer() calls either
+adapter.algo->smbus_xfer() if it is implemented, or if not,
+i2c-core.c:i2c_smbus_xfer_emulated() which in turn calls
+i2c_adapter.algo->master_xfer().
+
+After your I2C bus driver has processed these requests, execution runs
+up the call chain, with almost no processing done, except by i2c-dev to
+package the returned data, if any, in suitable format for the ioctl.
index 24bfb65..9df4744 100644 (file)
@@ -109,8 +109,8 @@ specified through the Comm byte.
 S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P
 
 
-SMBus Process Call
-==================
+SMBus Process Call:  i2c_smbus_process_call()
+=============================================
 
 This command selects a device register (through the Comm byte), sends
 16 bits of data to it, and reads 16 bits of data in return.
index 6b61b3a..d73ee11 100644 (file)
@@ -606,6 +606,8 @@ SMBus communication
   extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command);
   extern s32 i2c_smbus_write_word_data(struct i2c_client * client,
                                        u8 command, u16 value);
+  extern s32 i2c_smbus_process_call(struct i2c_client *client,
+                                    u8 command, u16 value);
   extern s32 i2c_smbus_read_block_data(struct i2c_client * client,
                                        u8 command, u8 *values);
   extern s32 i2c_smbus_write_block_data(struct i2c_client * client,
@@ -621,8 +623,6 @@ These ones were removed from i2c-core because they had no users, but could
 be added back later if needed:
 
   extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value);
-  extern s32 i2c_smbus_process_call(struct i2c_client * client,
-                                    u8 command, u16 value);
   extern s32 i2c_smbus_block_process_call(struct i2c_client *client,
                                           u8 command, u8 length,
                                           u8 *values)
index 2ced6d9..adfcd7b 100644 (file)
@@ -476,6 +476,10 @@ static struct i2c_board_info __initdata h3_i2c_board_info[] = {
                I2C_BOARD_INFO("tps65013", 0x48),
                /* .irq         = OMAP_GPIO_IRQ(??), */
        },
+       {
+               I2C_BOARD_INFO("isp1301_omap", 0x2d),
+               .irq            = OMAP_GPIO_IRQ(14),
+       },
 };
 
 static struct omap_gpio_switch h3_gpio_switches[] __initdata = {
index d4e3b6f..2fef2c8 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
+#include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/err.h>
 #include <linux/clk.h>
@@ -391,6 +392,13 @@ static struct omap_board_config_kernel h4_config[] = {
        { OMAP_TAG_LCD,         &h4_lcd_config },
 };
 
+static struct i2c_board_info __initdata h4_i2c_board_info[] = {
+       {
+               I2C_BOARD_INFO("isp1301_omap", 0x2d),
+               .irq            = OMAP_GPIO_IRQ(125),
+       },
+};
+
 static void __init omap_h4_init(void)
 {
        /*
@@ -411,6 +419,9 @@ static void __init omap_h4_init(void)
        }
 #endif
 
+       i2c_register_board_info(1, h4_i2c_board_info,
+                       ARRAY_SIZE(h4_i2c_board_info));
+
        platform_add_devices(h4_devices, ARRAY_SIZE(h4_devices));
        omap_board_config = h4_config;
        omap_board_config_size = ARRAY_SIZE(h4_config);
index 05191bb..0a23b57 100644 (file)
@@ -223,11 +223,25 @@ static struct platform_device rtc_device = {
 static __init int add_rtc_cmos(void)
 {
 #ifdef CONFIG_PNP
-       if (!pnp_platform_devices)
-               platform_device_register(&rtc_device);
-#else
+       static const char *ids[] __initconst =
+           { "PNP0b00", "PNP0b01", "PNP0b02", };
+       struct pnp_dev *dev;
+       struct pnp_id *id;
+       int i;
+
+       pnp_for_each_dev(dev) {
+               for (id = dev->id; id; id = id->next) {
+                       for (i = 0; i < ARRAY_SIZE(ids); i++) {
+                               if (compare_pnp_id(id, ids[i]) != 0)
+                                       return 0;
+                       }
+               }
+       }
+#endif
+
        platform_device_register(&rtc_device);
-#endif /* CONFIG_PNP */
+       dev_info(&rtc_device.dev,
+                "registered platform RTC device (no PNP device found)\n");
        return 0;
 }
 device_initcall(add_rtc_cmos);
index 3c578ef..24649ad 100644 (file)
@@ -260,115 +260,3 @@ static int __init init_acpi_device_notify(void)
 }
 
 arch_initcall(init_acpi_device_notify);
-
-
-#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE)
-
-#ifdef CONFIG_PM
-static u32 rtc_handler(void *context)
-{
-       acpi_clear_event(ACPI_EVENT_RTC);
-       acpi_disable_event(ACPI_EVENT_RTC, 0);
-       return ACPI_INTERRUPT_HANDLED;
-}
-
-static inline void rtc_wake_setup(void)
-{
-       acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
-       /*
-        * After the RTC handler is installed, the Fixed_RTC event should
-        * be disabled. Only when the RTC alarm is set will it be enabled.
-        */
-       acpi_clear_event(ACPI_EVENT_RTC);
-       acpi_disable_event(ACPI_EVENT_RTC, 0);
-}
-
-static void rtc_wake_on(struct device *dev)
-{
-       acpi_clear_event(ACPI_EVENT_RTC);
-       acpi_enable_event(ACPI_EVENT_RTC, 0);
-}
-
-static void rtc_wake_off(struct device *dev)
-{
-       acpi_disable_event(ACPI_EVENT_RTC, 0);
-}
-#else
-#define rtc_wake_setup()       do{}while(0)
-#define rtc_wake_on            NULL
-#define rtc_wake_off           NULL
-#endif
-
-/* Every ACPI platform has a mc146818 compatible "cmos rtc".  Here we find
- * its device node and pass extra config data.  This helps its driver use
- * capabilities that the now-obsolete mc146818 didn't have, and informs it
- * that this board's RTC is wakeup-capable (per ACPI spec).
- */
-#include <linux/mc146818rtc.h>
-
-static struct cmos_rtc_board_info rtc_info;
-
-
-/* PNP devices are registered in a subsys_initcall();
- * ACPI specifies the PNP IDs to use.
- */
-#include <linux/pnp.h>
-
-static int __init pnp_match(struct device *dev, void *data)
-{
-       static const char *ids[] = { "PNP0b00", "PNP0b01", "PNP0b02", };
-       struct pnp_dev *pnp = to_pnp_dev(dev);
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(ids); i++) {
-               if (compare_pnp_id(pnp->id, ids[i]) != 0)
-                       return 1;
-       }
-       return 0;
-}
-
-static struct device *__init get_rtc_dev(void)
-{
-       return bus_find_device(&pnp_bus_type, NULL, NULL, pnp_match);
-}
-
-static int __init acpi_rtc_init(void)
-{
-       struct device *dev = get_rtc_dev();
-
-       if (acpi_disabled)
-               return 0;
-
-       if (dev) {
-               rtc_wake_setup();
-               rtc_info.wake_on = rtc_wake_on;
-               rtc_info.wake_off = rtc_wake_off;
-
-               /* workaround bug in some ACPI tables */
-               if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) {
-                       DBG("bogus FADT month_alarm\n");
-                       acpi_gbl_FADT.month_alarm = 0;
-               }
-
-               rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm;
-               rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm;
-               rtc_info.rtc_century = acpi_gbl_FADT.century;
-
-               /* NOTE:  S4_RTC_WAKE is NOT currently useful to Linux */
-               if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE)
-                       printk(PREFIX "RTC can wake from S4\n");
-
-
-               dev->platform_data = &rtc_info;
-
-               /* RTC always wakes from S1/S2/S3, and often S4/STD */
-               device_init_wakeup(dev, 1);
-
-               put_device(dev);
-       } else
-               DBG("RTC unavailable?\n");
-       return 0;
-}
-module_init(acpi_rtc_init);
-
-#endif
index cacf89e..da64108 100644 (file)
@@ -17,6 +17,25 @@ config HID
        tristate "Generic HID support"
        depends on INPUT
        default y
+        select HID_A4TECH if !EMBEDDED
+        select HID_APPLE if !EMBEDDED
+        select HID_BELKIN if !EMBEDDED
+        select HID_BRIGHT if !EMBEDDED
+        select HID_CHERRY if !EMBEDDED
+        select HID_CHICONY if !EMBEDDED
+        select HID_CYPRESS if !EMBEDDED
+        select HID_DELL if !EMBEDDED
+        select HID_EZKEY if !EMBEDDED
+        select HID_GYRATION if !EMBEDDED
+        select HID_LOGITECH if !EMBEDDED
+        select HID_MICROSOFT if !EMBEDDED
+        select HID_MONTEREY if !EMBEDDED
+        select HID_PANTHERLORD if !EMBEDDED
+        select HID_PETALYNX if !EMBEDDED
+        select HID_SAMSUNG if !EMBEDDED
+        select HID_SONY if !EMBEDDED
+        select HID_SUNPLUS if !EMBEDDED
+
        ---help---
          A human interface device (HID) is a type of computer device that
          interacts directly with and takes input from humans. The term "HID"
@@ -67,4 +86,206 @@ config HIDRAW
 
 source "drivers/hid/usbhid/Kconfig"
 
+menu "Special HID drivers"
+       depends on HID
+
+config HID_COMPAT
+       bool "Load all HID drivers on hid core load"
+       default y
+       ---help---
+       Compatible option for older userspace. If you have system without udev
+       support of module loading through aliases and also old
+       module-init-tools which can't handle hid bus, choose Y here. Otherwise
+       say N. If you say N and your userspace is old enough, the only
+       functionality you lose is modules autoloading.
+
+       If unsure, say Y.
+
+config HID_A4TECH
+       tristate "A4 tech"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for A4 tech X5 and WOP-35 / Trust 450L mice.
+
+config HID_APPLE
+       tristate "Apple"
+       default m
+       depends on (USB_HID || BT_HIDP)
+       ---help---
+       Support for some Apple devices which less or more break
+       HID specification.
+
+       Say Y here if you want support for the special keys (Fn, Numlock) on
+       Apple iBooks, PowerBooks, MacBooks, MacBook Pros and aluminum USB
+       keyboards.
+
+       If unsure, say M.
+
+config HID_BELKIN
+       tristate "Belkin"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Belkin Flip KVM and Wireless keyboard.
+
+config HID_BRIGHT
+       tristate "Bright"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Bright ABNT-2 keyboard.
+
+config HID_CHERRY
+       tristate "Cherry"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Cherry Cymotion.
+
+config HID_CHICONY
+       tristate "Chicony"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Chicony Tactical pad.
+
+config HID_CYPRESS
+       tristate "Cypress"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Cypress mouse and barcodes.
+
+config HID_DELL
+       tristate "Dell"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Dell W7658.
+
+config HID_EZKEY
+       tristate "Ezkey"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Ezkey mouse and barcodes.
+
+config HID_GYRATION
+       tristate "Gyration"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Gyration remote.
+
+config HID_LOGITECH
+       tristate "Logitech"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for some Logitech devices which breaks less or more
+       HID specification.
+
+config LOGITECH_FF
+       bool "Logitech force feedback"
+       depends on HID_LOGITECH
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you have one of these devices:
+         - Logitech WingMan Cordless RumblePad
+         - Logitech WingMan Cordless RumblePad 2
+         - Logitech WingMan Force 3D
+         - Logitech Formula Force EX
+         - Logitech MOMO Force wheel
+
+         and if you want to enable force feedback for them.
+         Note: if you say N here, this device will still be supported, but without
+         force feedback.
+
+config LOGIRUMBLEPAD2_FF
+       bool "Logitech Rumblepad 2 force feedback"
+       depends on HID_LOGITECH
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you want to enable force feedback support for Logitech
+         Rumblepad 2 devices.
+
+config HID_MICROSOFT
+       tristate "Microsoft"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for some Microsoft devices which breaks less or more
+       HID specification.
+
+config HID_MONTEREY
+       tristate "Monterey"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Monterey Genius KB29E.
+
+config HID_PANTHERLORD
+       tristate "Pantherlord devices support"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for PantherLord/GreenAsia based device support.
+
+
+config PANTHERLORD_FF
+       bool "Pantherlord force feedback support"
+       depends on HID_PANTHERLORD
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you have a PantherLord/GreenAsia based game controller
+         or adapter and want to enable force feedback support for it.
+
+config HID_PETALYNX
+       tristate "Petalynx"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Petalynx Maxter remote.
+
+config HID_SAMSUNG
+       tristate "Samsung"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Samsung IR remote.
+
+config HID_SONY
+       tristate "Sony"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Sony PS3 controller.
+
+config HID_SUNPLUS
+       tristate "Sunplus"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Sunplus WDesktop input device.
+
+config THRUSTMASTER_FF
+       tristate "ThrustMaster devices support"
+       default m
+       depends on USB_HID
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
+         a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel.
+
+config ZEROPLUS_FF
+       tristate "Zeroplus based game controller support"
+       default m
+       depends on USB_HID
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you have a Zeroplus based game controller.
+
+endmenu
+
 endif # HID_SUPPORT
index 275dc52..b09e43e 100644 (file)
@@ -1,13 +1,46 @@
 #
 # Makefile for the HID driver
 #
-hid-objs                       := hid-core.o hid-input.o hid-input-quirks.o
+hid-objs                       := hid-core.o hid-input.o
 
 obj-$(CONFIG_HID)              += hid.o
 
 hid-$(CONFIG_HID_DEBUG)                += hid-debug.o
 hid-$(CONFIG_HIDRAW)           += hidraw.o
 
+ifdef CONFIG_HID_COMPAT
+obj-m                          += hid-dummy.o
+endif
+
+hid-logitech-objs              := hid-lg.o
+ifdef CONFIG_LOGITECH_FF
+       hid-logitech-objs       += hid-lgff.o
+endif
+ifdef CONFIG_LOGIRUMBLEPAD2_FF
+       hid-logitech-objs       += hid-lg2ff.o
+endif
+
+obj-$(CONFIG_HID_A4TECH)       += hid-a4tech.o
+obj-$(CONFIG_HID_APPLE)                += hid-apple.o
+obj-$(CONFIG_HID_BELKIN)       += hid-belkin.o
+obj-$(CONFIG_HID_BRIGHT)       += hid-bright.o
+obj-$(CONFIG_HID_CHERRY)       += hid-cherry.o
+obj-$(CONFIG_HID_CHICONY)      += hid-chicony.o
+obj-$(CONFIG_HID_CYPRESS)      += hid-cypress.o
+obj-$(CONFIG_HID_DELL)         += hid-dell.o
+obj-$(CONFIG_HID_EZKEY)                += hid-ezkey.o
+obj-$(CONFIG_HID_GYRATION)     += hid-gyration.o
+obj-$(CONFIG_HID_LOGITECH)     += hid-logitech.o
+obj-$(CONFIG_HID_MICROSOFT)    += hid-microsoft.o
+obj-$(CONFIG_HID_MONTEREY)     += hid-monterey.o
+obj-$(CONFIG_HID_PANTHERLORD)  += hid-pl.o
+obj-$(CONFIG_HID_PETALYNX)     += hid-petalynx.o
+obj-$(CONFIG_HID_SAMSUNG)      += hid-samsung.o
+obj-$(CONFIG_HID_SONY)         += hid-sony.o
+obj-$(CONFIG_HID_SUNPLUS)      += hid-sunplus.o
+obj-$(CONFIG_THRUSTMASTER_FF)  += hid-tmff.o
+obj-$(CONFIG_ZEROPLUS_FF)      += hid-zpff.o
+
 obj-$(CONFIG_USB_HID)          += usbhid/
 obj-$(CONFIG_USB_MOUSE)                += usbhid/
 obj-$(CONFIG_USB_KBD)          += usbhid/
diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c
new file mode 100644 (file)
index 0000000..ebca00e
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ *  HID driver for some a4tech "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define A4_2WHEEL_MOUSE_HACK_7 0x01
+#define A4_2WHEEL_MOUSE_HACK_B8        0x02
+
+struct a4tech_sc {
+       unsigned long quirks;
+       unsigned int hw_wheel;
+       __s32 delayed_value;
+};
+
+static int a4_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       struct a4tech_sc *a4 = hid_get_drvdata(hdev);
+
+       if (usage->type == EV_REL && usage->code == REL_WHEEL)
+               set_bit(REL_HWHEEL, *bit);
+
+       if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007)
+               return -1;
+
+       return 0;
+}
+
+static int a4_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       struct a4tech_sc *a4 = hid_get_drvdata(hdev);
+       struct input_dev *input;
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type)
+               return 0;
+
+       input = field->hidinput->input;
+
+       if (a4->quirks & A4_2WHEEL_MOUSE_HACK_B8) {
+               if (usage->type == EV_REL && usage->code == REL_WHEEL) {
+                       a4->delayed_value = value;
+                       return 1;
+               }
+
+               if (usage->hid == 0x000100b8) {
+                       input_event(input, EV_REL, value ? REL_HWHEEL :
+                                       REL_WHEEL, a4->delayed_value);
+                       return 1;
+               }
+       }
+
+       if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007) {
+               a4->hw_wheel = !!value;
+               return 1;
+       }
+
+       if (usage->code == REL_WHEEL && a4->hw_wheel) {
+               input_event(input, usage->type, REL_HWHEEL, value);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       struct a4tech_sc *a4;
+       int ret;
+
+       a4 = kzalloc(sizeof(*a4), GFP_KERNEL);
+       if (a4 == NULL) {
+               dev_err(&hdev->dev, "can't alloc device descriptor\n");
+               ret = -ENOMEM;
+               goto err_free;
+       }
+
+       a4->quirks = id->driver_data;
+
+       hid_set_drvdata(hdev, a4);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       kfree(a4);
+       return ret;
+}
+
+static void a4_remove(struct hid_device *hdev)
+{
+       struct a4tech_sc *a4 = hid_get_drvdata(hdev);
+
+       hid_hw_stop(hdev);
+       kfree(a4);
+}
+
+static const struct hid_device_id a4_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU),
+               .driver_data = A4_2WHEEL_MOUSE_HACK_7 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D),
+               .driver_data = A4_2WHEEL_MOUSE_HACK_B8 },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, a4_devices);
+
+static struct hid_driver a4_driver = {
+       .name = "a4tech",
+       .id_table = a4_devices,
+       .input_mapped = a4_input_mapped,
+       .event = a4_event,
+       .probe = a4_probe,
+       .remove = a4_remove,
+};
+
+static int a4_init(void)
+{
+       return hid_register_driver(&a4_driver);
+}
+
+static void a4_exit(void)
+{
+       hid_unregister_driver(&a4_driver);
+}
+
+module_init(a4_init);
+module_exit(a4_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(a4tech);
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
new file mode 100644 (file)
index 0000000..fd7f896
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ *  USB HID quirks support for Linux
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+#define APPLE_RDESC_JIS                0x0001
+#define APPLE_IGNORE_MOUSE     0x0002
+#define APPLE_HAS_FN           0x0004
+#define APPLE_HIDDEV           0x0008
+#define APPLE_ISO_KEYBOARD     0x0010
+#define APPLE_MIGHTYMOUSE      0x0020
+#define APPLE_INVERT_HWHEEL    0x0040
+#define APPLE_IGNORE_HIDINPUT  0x0080
+#define APPLE_NUMLOCK_EMULATION        0x0100
+
+#define APPLE_FLAG_FKEY                0x01
+
+static unsigned int fnmode = 1;
+module_param(fnmode, uint, 0644);
+MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, "
+               "[1] = fkeyslast, 2 = fkeysfirst)");
+
+struct apple_sc {
+       unsigned long quirks;
+       unsigned int fn_on;
+       DECLARE_BITMAP(pressed_fn, KEY_CNT);
+       DECLARE_BITMAP(pressed_numlock, KEY_CNT);
+};
+
+struct apple_key_translation {
+       u16 from;
+       u16 to;
+       u8 flags;
+};
+
+static struct apple_key_translation apple_fn_keys[] = {
+       { KEY_BACKSPACE, KEY_DELETE },
+       { KEY_F1,       KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
+       { KEY_F2,       KEY_BRIGHTNESSUP,   APPLE_FLAG_FKEY },
+       { KEY_F3,       KEY_FN_F5,          APPLE_FLAG_FKEY }, /* Exposé */
+       { KEY_F4,       KEY_FN_F4,          APPLE_FLAG_FKEY }, /* Dashboard */
+       { KEY_F5,       KEY_KBDILLUMDOWN,   APPLE_FLAG_FKEY },
+       { KEY_F6,       KEY_KBDILLUMUP,     APPLE_FLAG_FKEY },
+       { KEY_F7,       KEY_PREVIOUSSONG,   APPLE_FLAG_FKEY },
+       { KEY_F8,       KEY_PLAYPAUSE,      APPLE_FLAG_FKEY },
+       { KEY_F9,       KEY_NEXTSONG,       APPLE_FLAG_FKEY },
+       { KEY_F10,      KEY_MUTE,           APPLE_FLAG_FKEY },
+       { KEY_F11,      KEY_VOLUMEDOWN,     APPLE_FLAG_FKEY },
+       { KEY_F12,      KEY_VOLUMEUP,       APPLE_FLAG_FKEY },
+       { KEY_UP,       KEY_PAGEUP },
+       { KEY_DOWN,     KEY_PAGEDOWN },
+       { KEY_LEFT,     KEY_HOME },
+       { KEY_RIGHT,    KEY_END },
+       { }
+};
+
+static struct apple_key_translation powerbook_fn_keys[] = {
+       { KEY_BACKSPACE, KEY_DELETE },
+       { KEY_F1,       KEY_BRIGHTNESSDOWN,     APPLE_FLAG_FKEY },
+       { KEY_F2,       KEY_BRIGHTNESSUP,       APPLE_FLAG_FKEY },
+       { KEY_F3,       KEY_MUTE,               APPLE_FLAG_FKEY },
+       { KEY_F4,       KEY_VOLUMEDOWN,         APPLE_FLAG_FKEY },
+       { KEY_F5,       KEY_VOLUMEUP,           APPLE_FLAG_FKEY },
+       { KEY_F6,       KEY_NUMLOCK,            APPLE_FLAG_FKEY },
+       { KEY_F7,       KEY_SWITCHVIDEOMODE,    APPLE_FLAG_FKEY },
+       { KEY_F8,       KEY_KBDILLUMTOGGLE,     APPLE_FLAG_FKEY },
+       { KEY_F9,       KEY_KBDILLUMDOWN,       APPLE_FLAG_FKEY },
+       { KEY_F10,      KEY_KBDILLUMUP,         APPLE_FLAG_FKEY },
+       { KEY_UP,       KEY_PAGEUP },
+       { KEY_DOWN,     KEY_PAGEDOWN },
+       { KEY_LEFT,     KEY_HOME },
+       { KEY_RIGHT,    KEY_END },
+       { }
+};
+
+static struct apple_key_translation powerbook_numlock_keys[] = {
+       { KEY_J,        KEY_KP1 },
+       { KEY_K,        KEY_KP2 },
+       { KEY_L,        KEY_KP3 },
+       { KEY_U,        KEY_KP4 },
+       { KEY_I,        KEY_KP5 },
+       { KEY_O,        KEY_KP6 },
+       { KEY_7,        KEY_KP7 },
+       { KEY_8,        KEY_KP8 },
+       { KEY_9,        KEY_KP9 },
+       { KEY_M,        KEY_KP0 },
+       { KEY_DOT,      KEY_KPDOT },
+       { KEY_SLASH,    KEY_KPPLUS },
+       { KEY_SEMICOLON, KEY_KPMINUS },
+       { KEY_P,        KEY_KPASTERISK },
+       { KEY_MINUS,    KEY_KPEQUAL },
+       { KEY_0,        KEY_KPSLASH },
+       { KEY_F6,       KEY_NUMLOCK },
+       { KEY_KPENTER,  KEY_KPENTER },
+       { KEY_BACKSPACE, KEY_BACKSPACE },
+       { }
+};
+
+static struct apple_key_translation apple_iso_keyboard[] = {
+       { KEY_GRAVE,    KEY_102ND },
+       { KEY_102ND,    KEY_GRAVE },
+       { }
+};
+
+static struct apple_key_translation *apple_find_translation(
+               struct apple_key_translation *table, u16 from)
+{
+       struct apple_key_translation *trans;
+
+       /* Look for the translation */
+       for (trans = table; trans->from; trans++)
+               if (trans->from == from)
+                       return trans;
+
+       return NULL;
+}
+
+static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
+               struct hid_usage *usage, __s32 value)
+{
+       struct apple_sc *asc = hid_get_drvdata(hid);
+       struct apple_key_translation *trans;
+
+       if (usage->code == KEY_FN) {
+               asc->fn_on = !!value;
+               input_event(input, usage->type, usage->code, value);
+               return 1;
+       }
+
+       if (fnmode) {
+               int do_translate;
+
+               trans = apple_find_translation((hid->product < 0x220 ||
+                                       hid->product >= 0x300) ?
+                                       powerbook_fn_keys : apple_fn_keys,
+                                       usage->code);
+               if (trans) {
+                       if (test_bit(usage->code, asc->pressed_fn))
+                               do_translate = 1;
+                       else if (trans->flags & APPLE_FLAG_FKEY)
+                               do_translate = (fnmode == 2 && asc->fn_on) ||
+                                       (fnmode == 1 && !asc->fn_on);
+                       else
+                               do_translate = asc->fn_on;
+
+                       if (do_translate) {
+                               if (value)
+                                       set_bit(usage->code, asc->pressed_fn);
+                               else
+                                       clear_bit(usage->code, asc->pressed_fn);
+
+                               input_event(input, usage->type, trans->to,
+                                               value);
+
+                               return 1;
+                       }
+               }
+
+               if (asc->quirks & APPLE_NUMLOCK_EMULATION &&
+                               (test_bit(usage->code, asc->pressed_numlock) ||
+                               test_bit(LED_NUML, input->led))) {
+                       trans = apple_find_translation(powerbook_numlock_keys,
+                                       usage->code);
+
+                       if (trans) {
+                               if (value)
+                                       set_bit(usage->code,
+                                                       asc->pressed_numlock);
+                               else
+                                       clear_bit(usage->code,
+                                                       asc->pressed_numlock);
+
+                               input_event(input, usage->type, trans->to,
+                                               value);
+                       }
+
+                       return 1;
+               }
+       }
+
+       if (asc->quirks & APPLE_ISO_KEYBOARD) {
+               trans = apple_find_translation(apple_iso_keyboard, usage->code);
+               if (trans) {
+                       input_event(input, usage->type, trans->to, value);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int apple_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       struct apple_sc *asc = hid_get_drvdata(hdev);
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type)
+               return 0;
+
+       if ((asc->quirks & APPLE_INVERT_HWHEEL) &&
+                       usage->code == REL_HWHEEL) {
+               input_event(field->hidinput->input, usage->type, usage->code,
+                               -value);
+               return 1;
+       }
+
+       if ((asc->quirks & APPLE_HAS_FN) &&
+                       hidinput_apple_event(hdev, field->hidinput->input,
+                               usage, value))
+               return 1;
+
+
+       return 0;
+}
+
+/*
+ * MacBook JIS keyboard has wrong logical maximum
+ */
+static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       struct apple_sc *asc = hid_get_drvdata(hdev);
+
+       if ((asc->quirks & APPLE_RDESC_JIS) && rsize >= 60 &&
+                       rdesc[53] == 0x65 && rdesc[59] == 0x65) {
+               dev_info(&hdev->dev, "fixing up MacBook JIS keyboard report "
+                               "descriptor\n");
+               rdesc[53] = rdesc[59] = 0xe7;
+       }
+}
+
+static void apple_setup_input(struct input_dev *input)
+{
+       struct apple_key_translation *trans;
+
+       set_bit(KEY_NUMLOCK, input->keybit);
+
+       /* Enable all needed keys */
+       for (trans = apple_fn_keys; trans->from; trans++)
+               set_bit(trans->to, input->keybit);
+
+       for (trans = powerbook_fn_keys; trans->from; trans++)
+               set_bit(trans->to, input->keybit);
+
+       for (trans = powerbook_numlock_keys; trans->from; trans++)
+               set_bit(trans->to, input->keybit);
+
+       for (trans = apple_iso_keyboard; trans->from; trans++)
+               set_bit(trans->to, input->keybit);
+}
+
+static int apple_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if (usage->hid == (HID_UP_CUSTOM | 0x0003)) {
+               /* The fn key on Apple USB keyboards */
+               set_bit(EV_REP, hi->input->evbit);
+               hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN);
+               apple_setup_input(hi->input);
+               return 1;
+       }
+
+       /* we want the hid layer to go through standard path (set and ignore) */
+       return 0;
+}
+
+static int apple_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       struct apple_sc *asc = hid_get_drvdata(hdev);
+
+       if (asc->quirks & APPLE_MIGHTYMOUSE) {
+               if (usage->hid == HID_GD_Z)
+                       hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL);
+               else if (usage->code == BTN_1)
+                       hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_2);
+               else if (usage->code == BTN_2)
+                       hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_1);
+       }
+
+       return 0;
+}
+
+static int apple_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       struct apple_sc *asc;
+       unsigned int connect_mask = HID_CONNECT_DEFAULT;
+       int ret;
+
+       /* return something else or move to hid layer? device will reside
+          allocated */
+       if (id->bus == BUS_USB && (quirks & APPLE_IGNORE_MOUSE) &&
+                       to_usb_interface(hdev->dev.parent)->cur_altsetting->
+                       desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
+               return -ENODEV;
+
+       asc = kzalloc(sizeof(*asc), GFP_KERNEL);
+       if (asc == NULL) {
+               dev_err(&hdev->dev, "can't alloc apple descriptor\n");
+               return -ENOMEM;
+       }
+
+       asc->quirks = quirks;
+
+       hid_set_drvdata(hdev, asc);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       if (quirks & APPLE_HIDDEV)
+               connect_mask |= HID_CONNECT_HIDDEV_FORCE;
+       if (quirks & APPLE_IGNORE_HIDINPUT)
+               connect_mask &= ~HID_CONNECT_HIDINPUT;
+
+       ret = hid_hw_start(hdev, connect_mask);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       kfree(asc);
+       return ret;
+}
+
+static void apple_remove(struct hid_device *hdev)
+{
+       hid_hw_stop(hdev);
+       kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id apple_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
+               .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
+               .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
+               .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS},
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI),
+               .driver_data = APPLE_HAS_FN },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO),
+               .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS),
+               .driver_data = APPLE_HAS_FN },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
+               .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
+               .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
+               .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
+               .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
+               .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
+               .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+
+       /* Apple wireless Mighty Mouse */
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c),
+               .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
+
+       { }
+};
+MODULE_DEVICE_TABLE(hid, apple_devices);
+
+static struct hid_driver apple_driver = {
+       .name = "apple",
+       .id_table = apple_devices,
+       .report_fixup = apple_report_fixup,
+       .probe = apple_probe,
+       .remove = apple_remove,
+       .event = apple_event,
+       .input_mapping = apple_input_mapping,
+       .input_mapped = apple_input_mapped,
+};
+
+static int apple_init(void)
+{
+       int ret;
+
+       ret = hid_register_driver(&apple_driver);
+       if (ret)
+               printk(KERN_ERR "can't register apple driver\n");
+
+       return ret;
+}
+
+static void apple_exit(void)
+{
+       hid_unregister_driver(&apple_driver);
+}
+
+module_init(apple_init);
+module_exit(apple_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(apple);
diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c
new file mode 100644 (file)
index 0000000..12c8a9b
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ *  HID driver for some belkin "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define BELKIN_HIDDEV  0x01
+#define BELKIN_WKBD    0x02
+
+#define belkin_map_key_clear(c)        hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int belkin_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER ||
+                       !(quirks & BELKIN_WKBD))
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x03a: belkin_map_key_clear(KEY_SOUND);            break;
+       case 0x03b: belkin_map_key_clear(KEY_CAMERA);           break;
+       case 0x03c: belkin_map_key_clear(KEY_DOCUMENTS);        break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int belkin_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       int ret;
+
+       hid_set_drvdata(hdev, (void *)quirks);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
+               ((quirks & BELKIN_HIDDEV) ? HID_CONNECT_HIDDEV_FORCE : 0));
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id belkin_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM),
+               .driver_data = BELKIN_HIDDEV },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD),
+               .driver_data = BELKIN_WKBD },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, belkin_devices);
+
+static struct hid_driver belkin_driver = {
+       .name = "belkin",
+       .id_table = belkin_devices,
+       .input_mapping = belkin_input_mapping,
+       .probe = belkin_probe,
+};
+
+static int belkin_init(void)
+{
+       return hid_register_driver(&belkin_driver);
+}
+
+static void belkin_exit(void)
+{
+       hid_unregister_driver(&belkin_driver);
+}
+
+module_init(belkin_init);
+module_exit(belkin_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(belkin);
diff --git a/drivers/hid/hid-bright.c b/drivers/hid/hid-bright.c
new file mode 100644 (file)
index 0000000..38517a1
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *  HID driver for some bright "special" devices
+ *
+ *  Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * Based on hid-dell driver
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static int bright_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       usbhid_set_leds(hdev);
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id bright_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, bright_devices);
+
+static struct hid_driver bright_driver = {
+       .name = "bright",
+       .id_table = bright_devices,
+       .probe = bright_probe,
+};
+
+static int bright_init(void)
+{
+       return hid_register_driver(&bright_driver);
+}
+
+static void bright_exit(void)
+{
+       hid_unregister_driver(&bright_driver);
+}
+
+module_init(bright_init);
+module_exit(bright_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(bright);
diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c
new file mode 100644 (file)
index 0000000..b833b97
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  HID driver for some cherry "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * Cherry Cymotion keyboard have an invalid HID report descriptor,
+ * that needs fixing before we can parse it.
+ */
+static void ch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
+               dev_info(&hdev->dev, "fixing up Cherry Cymotion report "
+                               "descriptor\n");
+               rdesc[11] = rdesc[16] = 0xff;
+               rdesc[12] = rdesc[17] = 0x03;
+       }
+}
+
+#define ch_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x301: ch_map_key_clear(KEY_PROG1);        break;
+       case 0x302: ch_map_key_clear(KEY_PROG2);        break;
+       case 0x303: ch_map_key_clear(KEY_PROG3);        break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static const struct hid_device_id ch_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, ch_devices);
+
+static struct hid_driver ch_driver = {
+       .name = "cherry",
+       .id_table = ch_devices,
+       .report_fixup = ch_report_fixup,
+       .input_mapping = ch_input_mapping,
+};
+
+static int ch_init(void)
+{
+       return hid_register_driver(&ch_driver);
+}
+
+static void ch_exit(void)
+{
+       hid_unregister_driver(&ch_driver);
+}
+
+module_init(ch_init);
+module_exit(ch_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(cherry);
diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c
new file mode 100644 (file)
index 0000000..a54d409
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *  HID driver for some chicony "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define ch_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+               return 0;
+
+       set_bit(EV_REP, hi->input->evbit);
+       switch (usage->hid & HID_USAGE) {
+       case 0xff01: ch_map_key_clear(BTN_1);   break;
+       case 0xff02: ch_map_key_clear(BTN_2);   break;
+       case 0xff03: ch_map_key_clear(BTN_3);   break;
+       case 0xff04: ch_map_key_clear(BTN_4);   break;
+       case 0xff05: ch_map_key_clear(BTN_5);   break;
+       case 0xff06: ch_map_key_clear(BTN_6);   break;
+       case 0xff07: ch_map_key_clear(BTN_7);   break;
+       case 0xff08: ch_map_key_clear(BTN_8);   break;
+       case 0xff09: ch_map_key_clear(BTN_9);   break;
+       case 0xff0a: ch_map_key_clear(BTN_A);   break;
+       case 0xff0b: ch_map_key_clear(BTN_B);   break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static const struct hid_device_id ch_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, ch_devices);
+
+static struct hid_driver ch_driver = {
+       .name = "chicony",
+       .id_table = ch_devices,
+       .input_mapping = ch_input_mapping,
+};
+
+static int ch_init(void)
+{
+       return hid_register_driver(&ch_driver);
+}
+
+static void ch_exit(void)
+{
+       hid_unregister_driver(&ch_driver);
+}
+
+module_init(ch_init);
+module_exit(ch_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(chicony);
index 426ac5a..8a7d9db 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/hid-debug.h>
 #include <linux/hidraw.h>
 
+#include "hid-ids.h"
+
 /*
  * Version Information
  */
@@ -268,9 +270,9 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
 static u32 item_udata(struct hid_item *item)
 {
        switch (item->size) {
-               case 1: return item->data.u8;
-               case 2: return item->data.u16;
-               case 4: return item->data.u32;
+       case 1: return item->data.u8;
+       case 2: return item->data.u16;
+       case 4: return item->data.u32;
        }
        return 0;
 }
@@ -278,9 +280,9 @@ static u32 item_udata(struct hid_item *item)
 static s32 item_sdata(struct hid_item *item)
 {
        switch (item->size) {
-               case 1: return item->data.s8;
-               case 2: return item->data.s16;
-               case 4: return item->data.s32;
+       case 1: return item->data.s8;
+       case 2: return item->data.s16;
+       case 4: return item->data.s32;
        }
        return 0;
 }
@@ -292,87 +294,91 @@ static s32 item_sdata(struct hid_item *item)
 static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 {
        switch (item->tag) {
+       case HID_GLOBAL_ITEM_TAG_PUSH:
 
-               case HID_GLOBAL_ITEM_TAG_PUSH:
-
-                       if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
-                               dbg_hid("global enviroment stack overflow\n");
-                               return -1;
-                       }
+               if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
+                       dbg_hid("global enviroment stack overflow\n");
+                       return -1;
+               }
 
-                       memcpy(parser->global_stack + parser->global_stack_ptr++,
-                               &parser->global, sizeof(struct hid_global));
-                       return 0;
+               memcpy(parser->global_stack + parser->global_stack_ptr++,
+                       &parser->global, sizeof(struct hid_global));
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_POP:
+       case HID_GLOBAL_ITEM_TAG_POP:
 
-                       if (!parser->global_stack_ptr) {
-                               dbg_hid("global enviroment stack underflow\n");
-                               return -1;
-                       }
-
-                       memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr,
-                               sizeof(struct hid_global));
-                       return 0;
+               if (!parser->global_stack_ptr) {
+                       dbg_hid("global enviroment stack underflow\n");
+                       return -1;
+               }
 
-               case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
-                       parser->global.usage_page = item_udata(item);
-                       return 0;
+               memcpy(&parser->global, parser->global_stack +
+                       --parser->global_stack_ptr, sizeof(struct hid_global));
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
-                       parser->global.logical_minimum = item_sdata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
+               parser->global.usage_page = item_udata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
-                       if (parser->global.logical_minimum < 0)
-                               parser->global.logical_maximum = item_sdata(item);
-                       else
-                               parser->global.logical_maximum = item_udata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
+               parser->global.logical_minimum = item_sdata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
-                       parser->global.physical_minimum = item_sdata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
+               if (parser->global.logical_minimum < 0)
+                       parser->global.logical_maximum = item_sdata(item);
+               else
+                       parser->global.logical_maximum = item_udata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
-                       if (parser->global.physical_minimum < 0)
-                               parser->global.physical_maximum = item_sdata(item);
-                       else
-                               parser->global.physical_maximum = item_udata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
+               parser->global.physical_minimum = item_sdata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
-                       parser->global.unit_exponent = item_sdata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
+               if (parser->global.physical_minimum < 0)
+                       parser->global.physical_maximum = item_sdata(item);
+               else
+                       parser->global.physical_maximum = item_udata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_UNIT:
-                       parser->global.unit = item_udata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
+               parser->global.unit_exponent = item_sdata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
-                       if ((parser->global.report_size = item_udata(item)) > 32) {
-                               dbg_hid("invalid report_size %d\n", parser->global.report_size);
-                               return -1;
-                       }
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_UNIT:
+               parser->global.unit = item_udata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
-                       if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
-                               dbg_hid("invalid report_count %d\n", parser->global.report_count);
-                               return -1;
-                       }
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
+               parser->global.report_size = item_udata(item);
+               if (parser->global.report_size > 32) {
+                       dbg_hid("invalid report_size %d\n",
+                                       parser->global.report_size);
+                       return -1;
+               }
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_REPORT_ID:
-                       if ((parser->global.report_id = item_udata(item)) == 0) {
-                               dbg_hid("report_id 0 is invalid\n");
-                               return -1;
-                       }
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
+               parser->global.report_count = item_udata(item);
+               if (parser->global.report_count > HID_MAX_USAGES) {
+                       dbg_hid("invalid report_count %d\n",
+                                       parser->global.report_count);
+                       return -1;
+               }
+               return 0;
 
-               default:
-                       dbg_hid("unknown global tag 0x%x\n", item->tag);
+       case HID_GLOBAL_ITEM_TAG_REPORT_ID:
+               parser->global.report_id = item_udata(item);
+               if (parser->global.report_id == 0) {
+                       dbg_hid("report_id 0 is invalid\n");
                        return -1;
+               }
+               return 0;
+
+       default:
+               dbg_hid("unknown global tag 0x%x\n", item->tag);
+               return -1;
        }
 }
 
@@ -393,77 +399,76 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
        data = item_udata(item);
 
        switch (item->tag) {
-
-               case HID_LOCAL_ITEM_TAG_DELIMITER:
-
-                       if (data) {
-                               /*
-                                * We treat items before the first delimiter
-                                * as global to all usage sets (branch 0).
-                                * In the moment we process only these global
-                                * items and the first delimiter set.
-                                */
-                               if (parser->local.delimiter_depth != 0) {
-                                       dbg_hid("nested delimiters\n");
-                                       return -1;
-                               }
-                               parser->local.delimiter_depth++;
-                               parser->local.delimiter_branch++;
-                       } else {
-                               if (parser->local.delimiter_depth < 1) {
-                                       dbg_hid("bogus close delimiter\n");
-                                       return -1;
-                               }
-                               parser->local.delimiter_depth--;
+       case HID_LOCAL_ITEM_TAG_DELIMITER:
+
+               if (data) {
+                       /*
+                        * We treat items before the first delimiter
+                        * as global to all usage sets (branch 0).
+                        * In the moment we process only these global
+                        * items and the first delimiter set.
+                        */
+                       if (parser->local.delimiter_depth != 0) {
+                               dbg_hid("nested delimiters\n");
+                               return -1;
                        }
-                       return 1;
-
-               case HID_LOCAL_ITEM_TAG_USAGE:
-
-                       if (parser->local.delimiter_branch > 1) {
-                               dbg_hid("alternative usage ignored\n");
-                               return 0;
+                       parser->local.delimiter_depth++;
+                       parser->local.delimiter_branch++;
+               } else {
+                       if (parser->local.delimiter_depth < 1) {
+                               dbg_hid("bogus close delimiter\n");
+                               return -1;
                        }
+                       parser->local.delimiter_depth--;
+               }
+               return 1;
 
-                       if (item->size <= 2)
-                               data = (parser->global.usage_page << 16) + data;
+       case HID_LOCAL_ITEM_TAG_USAGE:
 
-                       return hid_add_usage(parser, data);
+               if (parser->local.delimiter_branch > 1) {
+                       dbg_hid("alternative usage ignored\n");
+                       return 0;
+               }
 
-               case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
+               if (item->size <= 2)
+                       data = (parser->global.usage_page << 16) + data;
 
-                       if (parser->local.delimiter_branch > 1) {
-                               dbg_hid("alternative usage ignored\n");
-                               return 0;
-                       }
+               return hid_add_usage(parser, data);
 
-                       if (item->size <= 2)
-                               data = (parser->global.usage_page << 16) + data;
+       case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
 
-                       parser->local.usage_minimum = data;
+               if (parser->local.delimiter_branch > 1) {
+                       dbg_hid("alternative usage ignored\n");
                        return 0;
+               }
 
-               case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+               if (item->size <= 2)
+                       data = (parser->global.usage_page << 16) + data;
 
-                       if (parser->local.delimiter_branch > 1) {
-                               dbg_hid("alternative usage ignored\n");
-                               return 0;
-                       }
+               parser->local.usage_minimum = data;
+               return 0;
 
-                       if (item->size <= 2)
-                               data = (parser->global.usage_page << 16) + data;
+       case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
 
-                       for (n = parser->local.usage_minimum; n <= data; n++)
-                               if (hid_add_usage(parser, n)) {
-                                       dbg_hid("hid_add_usage failed\n");
-                                       return -1;
-                               }
+               if (parser->local.delimiter_branch > 1) {
+                       dbg_hid("alternative usage ignored\n");
                        return 0;
+               }
 
-               default:
+               if (item->size <= 2)
+                       data = (parser->global.usage_page << 16) + data;
 
-                       dbg_hid("unknown local item tag 0x%x\n", item->tag);
-                       return 0;
+               for (n = parser->local.usage_minimum; n <= data; n++)
+                       if (hid_add_usage(parser, n)) {
+                               dbg_hid("hid_add_usage failed\n");
+                               return -1;
+                       }
+               return 0;
+
+       default:
+
+               dbg_hid("unknown local item tag 0x%x\n", item->tag);
+               return 0;
        }
        return 0;
 }
@@ -480,24 +485,24 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
        data = item_udata(item);
 
        switch (item->tag) {
-               case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
-                       ret = open_collection(parser, data & 0xff);
-                       break;
-               case HID_MAIN_ITEM_TAG_END_COLLECTION:
-                       ret = close_collection(parser);
-                       break;
-               case HID_MAIN_ITEM_TAG_INPUT:
-                       ret = hid_add_field(parser, HID_INPUT_REPORT, data);
-                       break;
-               case HID_MAIN_ITEM_TAG_OUTPUT:
-                       ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
-                       break;
-               case HID_MAIN_ITEM_TAG_FEATURE:
-                       ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
-                       break;
-               default:
-                       dbg_hid("unknown main item tag 0x%x\n", item->tag);
-                       ret = 0;
+       case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+               ret = open_collection(parser, data & 0xff);
+               break;
+       case HID_MAIN_ITEM_TAG_END_COLLECTION:
+               ret = close_collection(parser);
+               break;
+       case HID_MAIN_ITEM_TAG_INPUT:
+               ret = hid_add_field(parser, HID_INPUT_REPORT, data);
+               break;
+       case HID_MAIN_ITEM_TAG_OUTPUT:
+               ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
+               break;
+       case HID_MAIN_ITEM_TAG_FEATURE:
+               ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
+               break;
+       default:
+               dbg_hid("unknown main item tag 0x%x\n", item->tag);
+               ret = 0;
        }
 
        memset(&parser->local, 0, sizeof(parser->local));       /* Reset the local parser environment */
@@ -534,9 +539,10 @@ static void hid_free_report(struct hid_report *report)
  * Free a device structure, all reports, and all fields.
  */
 
-void hid_free_device(struct hid_device *device)
+static void hid_device_release(struct device *dev)
 {
-       unsigned i,j;
+       struct hid_device *device = container_of(dev, struct hid_device, dev);
+       unsigned i, j;
 
        for (i = 0; i < HID_REPORT_TYPES; i++) {
                struct hid_report_enum *report_enum = device->report_enum + i;
@@ -552,7 +558,6 @@ void hid_free_device(struct hid_device *device)
        kfree(device->collection);
        kfree(device);
 }
-EXPORT_SYMBOL_GPL(hid_free_device);
 
 /*
  * Fetch a report description item from the data stream. We support long
@@ -593,47 +598,52 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
        item->size = b & 3;
 
        switch (item->size) {
+       case 0:
+               return start;
 
-               case 0:
-                       return start;
-
-               case 1:
-                       if ((end - start) < 1)
-                               return NULL;
-                       item->data.u8 = *start++;
-                       return start;
+       case 1:
+               if ((end - start) < 1)
+                       return NULL;
+               item->data.u8 = *start++;
+               return start;
 
-               case 2:
-                       if ((end - start) < 2)
-                               return NULL;
-                       item->data.u16 = get_unaligned_le16(start);
-                       start = (__u8 *)((__le16 *)start + 1);
-                       return start;
+       case 2:
+               if ((end - start) < 2)
+                       return NULL;
+               item->data.u16 = get_unaligned_le16(start);
+               start = (__u8 *)((__le16 *)start + 1);
+               return start;
 
-               case 3:
-                       item->size++;
-                       if ((end - start) < 4)
-                               return NULL;
-                       item->data.u32 = get_unaligned_le32(start);
-                       start = (__u8 *)((__le32 *)start + 1);
-                       return start;
+       case 3:
+               item->size++;
+               if ((end - start) < 4)
+                       return NULL;
+               item->data.u32 = get_unaligned_le32(start);
+               start = (__u8 *)((__le32 *)start + 1);
+               return start;
        }
 
        return NULL;
 }
 
-/*
+/**
+ * hid_parse_report - parse device report
+ *
+ * @device: hid device
+ * @start: report start
+ * @size: report size
+ *
  * Parse a report description into a hid_device structure. Reports are
  * enumerated, fields are attached to these reports.
+ * 0 returned on success, otherwise nonzero error value.
  */
-
-struct hid_device *hid_parse_report(__u8 *start, unsigned size)
+int hid_parse_report(struct hid_device *device, __u8 *start,
+               unsigned size)
 {
-       struct hid_device *device;
        struct hid_parser *parser;
        struct hid_item item;
        __u8 *end;
-       unsigned i;
+       int ret;
        static int (*dispatch_type[])(struct hid_parser *parser,
                                      struct hid_item *item) = {
                hid_parser_main,
@@ -642,76 +652,57 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size)
                hid_parser_reserved
        };
 
-       if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL)))
-               return NULL;
+       if (device->driver->report_fixup)
+               device->driver->report_fixup(device, start, size);
 
-       if (!(device->collection = kzalloc(sizeof(struct hid_collection) *
-                                  HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) {
-               kfree(device);
-               return NULL;
-       }
-       device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
-
-       for (i = 0; i < HID_REPORT_TYPES; i++)
-               INIT_LIST_HEAD(&device->report_enum[i].report_list);
-
-       if (!(device->rdesc = kmalloc(size, GFP_KERNEL))) {
-               kfree(device->collection);
-               kfree(device);
-               return NULL;
-       }
+       device->rdesc = kmalloc(size, GFP_KERNEL);
+       if (device->rdesc == NULL)
+               return -ENOMEM;
        memcpy(device->rdesc, start, size);
        device->rsize = size;
 
-       if (!(parser = vmalloc(sizeof(struct hid_parser)))) {
-               kfree(device->rdesc);
-               kfree(device->collection);
-               kfree(device);
-               return NULL;
+       parser = vmalloc(sizeof(struct hid_parser));
+       if (!parser) {
+               ret = -ENOMEM;
+               goto err;
        }
+
        memset(parser, 0, sizeof(struct hid_parser));
        parser->device = device;
 
        end = start + size;
+       ret = -EINVAL;
        while ((start = fetch_item(start, end, &item)) != NULL) {
 
                if (item.format != HID_ITEM_FORMAT_SHORT) {
                        dbg_hid("unexpected long global item\n");
-                       hid_free_device(device);
-                       vfree(parser);
-                       return NULL;
+                       goto err;
                }
 
                if (dispatch_type[item.type](parser, &item)) {
                        dbg_hid("item %u %u %u %u parsing failed\n",
                                item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
-                       hid_free_device(device);
-                       vfree(parser);
-                       return NULL;
+                       goto err;
                }
 
                if (start == end) {
                        if (parser->collection_stack_ptr) {
                                dbg_hid("unbalanced collection at end of report description\n");
-                               hid_free_device(device);
-                               vfree(parser);
-                               return NULL;
+                               goto err;
                        }
                        if (parser->local.delimiter_depth) {
                                dbg_hid("unbalanced delimiter at end of report description\n");
-                               hid_free_device(device);
-                               vfree(parser);
-                               return NULL;
+                               goto err;
                        }
                        vfree(parser);
-                       return device;
+                       return 0;
                }
        }
 
        dbg_hid("item fetching failed at offset %d\n", (int)(end - start));
-       hid_free_device(device);
+err:
        vfree(parser);
-       return NULL;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(hid_parse_report);
 
@@ -724,9 +715,9 @@ EXPORT_SYMBOL_GPL(hid_parse_report);
 static s32 snto32(__u32 value, unsigned n)
 {
        switch (n) {
-               case 8:  return ((__s8)value);
-               case 16: return ((__s16)value);
-               case 32: return ((__s32)value);
+       case 8:  return ((__s8)value);
+       case 16: return ((__s16)value);
+       case 32: return ((__s32)value);
        }
        return value & (1 << (n - 1)) ? value | (-1 << n) : value;
 }
@@ -815,9 +806,73 @@ static __inline__ int search(__s32 *array, __s32 value, unsigned n)
        return -1;
 }
 
-static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt)
+/**
+ * hid_match_report - check if driver's raw_event should be called
+ *
+ * @hid: hid device
+ * @report_type: type to match against
+ *
+ * compare hid->driver->report_table->report_type to report->type
+ */
+static int hid_match_report(struct hid_device *hid, struct hid_report *report)
+{
+       const struct hid_report_id *id = hid->driver->report_table;
+
+       if (!id) /* NULL means all */
+               return 1;
+
+       for (; id->report_type != HID_TERMINATOR; id++)
+               if (id->report_type == HID_ANY_ID ||
+                               id->report_type == report->type)
+                       return 1;
+       return 0;
+}
+
+/**
+ * hid_match_usage - check if driver's event should be called
+ *
+ * @hid: hid device
+ * @usage: usage to match against
+ *
+ * compare hid->driver->usage_table->usage_{type,code} to
+ * usage->usage_{type,code}
+ */
+static int hid_match_usage(struct hid_device *hid, struct hid_usage *usage)
 {
+       const struct hid_usage_id *id = hid->driver->usage_table;
+
+       if (!id) /* NULL means all */
+               return 1;
+
+       for (; id->usage_type != HID_ANY_ID - 1; id++)
+               if ((id->usage_hid == HID_ANY_ID ||
+                               id->usage_hid == usage->hid) &&
+                               (id->usage_type == HID_ANY_ID ||
+                               id->usage_type == usage->type) &&
+                               (id->usage_code == HID_ANY_ID ||
+                                id->usage_code == usage->code))
+                       return 1;
+       return 0;
+}
+
+static void hid_process_event(struct hid_device *hid, struct hid_field *field,
+               struct hid_usage *usage, __s32 value, int interrupt)
+{
+       struct hid_driver *hdrv = hid->driver;
+       int ret;
+
        hid_dump_input(usage, value);
+
+       if (hdrv && hdrv->event && hid_match_usage(hid, usage)) {
+               ret = hdrv->event(hid, field, usage, value);
+               if (ret != 0) {
+                       if (ret < 0)
+                               dbg_hid("%s's event failed with %d\n",
+                                               hdrv->name, ret);
+                       return;
+               }
+       }
+
        if (hid->claimed & HID_CLAIMED_INPUT)
                hidinput_hid_event(hid, field, usage, value);
        if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event)
@@ -946,44 +1001,47 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
 }
 EXPORT_SYMBOL_GPL(hid_set_field);
 
-int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt)
+static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
+               const u8 *data)
 {
-       struct hid_report_enum *report_enum = hid->report_enum + type;
        struct hid_report *report;
-       int n, rsize, i;
+       unsigned int n = 0;     /* Normally report number is 0 */
 
-       if (!hid)
-               return -ENODEV;
+       /* Device uses numbered reports, data[0] is report number */
+       if (report_enum->numbered)
+               n = *data;
 
-       if (!size) {
-               dbg_hid("empty report\n");
-               return -1;
-       }
+       report = report_enum->report_id_hash[n];
+       if (report == NULL)
+               dbg_hid("undefined report_id %u received\n", n);
 
-       dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
+       return report;
+}
 
-       n = 0;                          /* Normally report number is 0 */
-       if (report_enum->numbered) {    /* Device uses numbered reports, data[0] is report number */
-               n = *data++;
-               size--;
-       }
+void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
+               int interrupt)
+{
+       struct hid_report_enum *report_enum = hid->report_enum + type;
+       struct hid_report *report;
+       unsigned int a;
+       int rsize, csize = size;
+       u8 *cdata = data;
 
-       /* dump the report */
-       dbg_hid("report %d (size %u) = ", n, size);
-       for (i = 0; i < size; i++)
-               dbg_hid_line(" %02x", data[i]);
-       dbg_hid_line("\n");
+       report = hid_get_report(report_enum, data);
+       if (!report)
+               return;
 
-       if (!(report = report_enum->report_id_hash[n])) {
-               dbg_hid("undefined report_id %d received\n", n);
-               return -1;
+       if (report_enum->numbered) {
+               cdata++;
+               csize--;
        }
 
        rsize = ((report->size - 1) >> 3) + 1;
 
-       if (size < rsize) {
-               dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize);
-               memset(data + size, 0, rsize - size);
+       if (csize < rsize) {
+               dbg_hid("report %d is too short, (%d < %d)\n", report->id,
+                               csize, rsize);
+               memset(cdata + csize, 0, rsize - csize);
        }
 
        if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
@@ -996,24 +1054,661 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
                        hidraw_report_event(hid, data, size);
        }
 
-       for (n = 0; n < report->maxfield; n++)
-               hid_input_field(hid, report->field[n], data, interrupt);
+       for (a = 0; a < report->maxfield; a++)
+               hid_input_field(hid, report->field[a], cdata, interrupt);
 
        if (hid->claimed & HID_CLAIMED_INPUT)
                hidinput_report_event(hid, report);
+}
+EXPORT_SYMBOL_GPL(hid_report_raw_event);
+
+/**
+ * hid_input_report - report data from lower layer (usb, bt...)
+ *
+ * @hid: hid device
+ * @type: HID report type (HID_*_REPORT)
+ * @data: report contents
+ * @size: size of data parameter
+ * @interrupt: called from atomic?
+ *
+ * This is data entry for lower layers.
+ */
+int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt)
+{
+       struct hid_report_enum *report_enum = hid->report_enum + type;
+       struct hid_driver *hdrv = hid->driver;
+       struct hid_report *report;
+       unsigned int i;
+       int ret;
+
+       if (!hid || !hid->driver)
+               return -ENODEV;
+
+       if (!size) {
+               dbg_hid("empty report\n");
+               return -1;
+       }
+
+       dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
+
+       report = hid_get_report(report_enum, data);
+       if (!report)
+               return -1;
+
+       /* dump the report */
+       dbg_hid("report %d (size %u) = ", report->id, size);
+       for (i = 0; i < size; i++)
+               dbg_hid_line(" %02x", data[i]);
+       dbg_hid_line("\n");
+
+       if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
+               ret = hdrv->raw_event(hid, report, data, size);
+               if (ret != 0)
+                       return ret < 0 ? ret : 0;
+       }
+
+       hid_report_raw_event(hid, type, data, size, interrupt);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(hid_input_report);
 
+static bool hid_match_one_id(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       return id->bus == hdev->bus &&
+               (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) &&
+               (id->product == HID_ANY_ID || id->product == hdev->product);
+}
+
+static const struct hid_device_id *hid_match_id(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       for (; id->bus; id++)
+               if (hid_match_one_id(hdev, id))
+                       return id;
+
+       return NULL;
+}
+
+static const struct hid_device_id hid_hiddev_list[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1) },
+       { }
+};
+
+static bool hid_hiddev(struct hid_device *hdev)
+{
+       return !!hid_match_id(hdev, hid_hiddev_list);
+}
+
+int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
+{
+       static const char *types[] = { "Device", "Pointer", "Mouse", "Device",
+               "Joystick", "Gamepad", "Keyboard", "Keypad",
+               "Multi-Axis Controller"
+       };
+       const char *type, *bus;
+       char buf[64];
+       unsigned int i;
+       int len;
+
+       if (hdev->bus != BUS_USB)
+               connect_mask &= ~HID_CONNECT_HIDDEV;
+       if (hid_hiddev(hdev))
+               connect_mask |= HID_CONNECT_HIDDEV_FORCE;
+
+       if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev,
+                               connect_mask & HID_CONNECT_HIDINPUT_FORCE))
+               hdev->claimed |= HID_CLAIMED_INPUT;
+       if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect &&
+                       !hdev->hiddev_connect(hdev,
+                               connect_mask & HID_CONNECT_HIDDEV_FORCE))
+               hdev->claimed |= HID_CLAIMED_HIDDEV;
+       if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev))
+               hdev->claimed |= HID_CLAIMED_HIDRAW;
+
+       if (!hdev->claimed) {
+               dev_err(&hdev->dev, "claimed by neither input, hiddev nor "
+                               "hidraw\n");
+               return -ENODEV;
+       }
+
+       if ((hdev->claimed & HID_CLAIMED_INPUT) &&
+                       (connect_mask & HID_CONNECT_FF) && hdev->ff_init)
+               hdev->ff_init(hdev);
+
+       len = 0;
+       if (hdev->claimed & HID_CLAIMED_INPUT)
+               len += sprintf(buf + len, "input");
+       if (hdev->claimed & HID_CLAIMED_HIDDEV)
+               len += sprintf(buf + len, "%shiddev%d", len ? "," : "",
+                               hdev->minor);
+       if (hdev->claimed & HID_CLAIMED_HIDRAW)
+               len += sprintf(buf + len, "%shidraw%d", len ? "," : "",
+                               ((struct hidraw *)hdev->hidraw)->minor);
+
+       type = "Device";
+       for (i = 0; i < hdev->maxcollection; i++) {
+               struct hid_collection *col = &hdev->collection[i];
+               if (col->type == HID_COLLECTION_APPLICATION &&
+                  (col->usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
+                  (col->usage & 0xffff) < ARRAY_SIZE(types)) {
+                       type = types[col->usage & 0xffff];
+                       break;
+               }
+       }
+
+       switch (hdev->bus) {
+       case BUS_USB:
+               bus = "USB";
+               break;
+       case BUS_BLUETOOTH:
+               bus = "BLUETOOTH";
+               break;
+       default:
+               bus = "<UNKNOWN>";
+       }
+
+       dev_info(&hdev->dev, "%s: %s HID v%x.%02x %s [%s] on %s\n",
+                       buf, bus, hdev->version >> 8, hdev->version & 0xff,
+                       type, hdev->name, hdev->phys);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(hid_connect);
+
+static const struct hid_device_id hid_blacklist[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
+
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
+       { }
+};
+
+static int hid_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver);
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+
+       if (!hid_match_id(hdev, hdrv->id_table))
+               return 0;
+
+       /* generic wants all non-blacklisted */
+       if (!strncmp(hdrv->name, "generic-", 8))
+               return !hid_match_id(hdev, hid_blacklist);
+
+       return 1;
+}
+
+static int hid_device_probe(struct device *dev)
+{
+       struct hid_driver *hdrv = container_of(dev->driver,
+                       struct hid_driver, driver);
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       const struct hid_device_id *id;
+       int ret = 0;
+
+       if (!hdev->driver) {
+               id = hid_match_id(hdev, hdrv->id_table);
+               if (id == NULL)
+                       return -ENODEV;
+
+               hdev->driver = hdrv;
+               if (hdrv->probe) {
+                       ret = hdrv->probe(hdev, id);
+               } else { /* default probe */
+                       ret = hid_parse(hdev);
+                       if (!ret)
+                               ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+               }
+               if (ret)
+                       hdev->driver = NULL;
+       }
+       return ret;
+}
+
+static int hid_device_remove(struct device *dev)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct hid_driver *hdrv = hdev->driver;
+
+       if (hdrv) {
+               if (hdrv->remove)
+                       hdrv->remove(hdev);
+               else /* default remove */
+                       hid_hw_stop(hdev);
+               hdev->driver = NULL;
+       }
+
+       return 0;
+}
+
+static int hid_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+
+       if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X",
+                       hdev->bus, hdev->vendor, hdev->product))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "HID_NAME=%s", hdev->name))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "HID_PHYS=%s", hdev->phys))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "MODALIAS=hid:b%04Xv%08Xp%08X",
+                       hdev->bus, hdev->vendor, hdev->product))
+               return -ENOMEM;
+
+       return 0;
+}
+
+static struct bus_type hid_bus_type = {
+       .name           = "hid",
+       .match          = hid_bus_match,
+       .probe          = hid_device_probe,
+       .remove         = hid_device_remove,
+       .uevent         = hid_uevent,
+};
+
+static const struct hid_device_id hid_ignore_list[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM)},
+       { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0001) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
+       { }
+};
+
+static bool hid_ignore(struct hid_device *hdev)
+{
+       switch (hdev->vendor) {
+       case USB_VENDOR_ID_CODEMERCS:
+               /* ignore all Code Mercenaries IOWarrior devices */
+               if (hdev->product >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST &&
+                               hdev->product <= USB_DEVICE_ID_CODEMERCS_IOW_LAST)
+                       return true;
+               break;
+       case USB_VENDOR_ID_LOGITECH:
+               if (hdev->product >= USB_DEVICE_ID_LOGITECH_HARMONY_FIRST &&
+                               hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST)
+                       return true;
+               break;
+       }
+
+       return !!hid_match_id(hdev, hid_ignore_list);
+}
+
+int hid_add_device(struct hid_device *hdev)
+{
+       static atomic_t id = ATOMIC_INIT(0);
+       int ret;
+
+       if (WARN_ON(hdev->status & HID_STAT_ADDED))
+               return -EBUSY;
+
+       /* we need to kill them here, otherwise they will stay allocated to
+        * wait for coming driver */
+       if (hid_ignore(hdev))
+               return -ENODEV;
+
+       /* XXX hack, any other cleaner solution < 20 bus_id bytes? */
+       sprintf(hdev->dev.bus_id, "%04X:%04X:%04X.%04X", hdev->bus,
+                       hdev->vendor, hdev->product, atomic_inc_return(&id));
+
+       ret = device_add(&hdev->dev);
+       if (!ret)
+               hdev->status |= HID_STAT_ADDED;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(hid_add_device);
+
+/**
+ * hid_allocate_device - allocate new hid device descriptor
+ *
+ * Allocate and initialize hid device, so that hid_destroy_device might be
+ * used to free it.
+ *
+ * New hid_device pointer is returned on success, otherwise ERR_PTR encoded
+ * error value.
+ */
+struct hid_device *hid_allocate_device(void)
+{
+       struct hid_device *hdev;
+       unsigned int i;
+       int ret = -ENOMEM;
+
+       hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
+       if (hdev == NULL)
+               return ERR_PTR(ret);
+
+       device_initialize(&hdev->dev);
+       hdev->dev.release = hid_device_release;
+       hdev->dev.bus = &hid_bus_type;
+
+       hdev->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS,
+                       sizeof(struct hid_collection), GFP_KERNEL);
+       if (hdev->collection == NULL)
+               goto err;
+       hdev->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
+
+       for (i = 0; i < HID_REPORT_TYPES; i++)
+               INIT_LIST_HEAD(&hdev->report_enum[i].report_list);
+
+       return hdev;
+err:
+       put_device(&hdev->dev);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(hid_allocate_device);
+
+static void hid_remove_device(struct hid_device *hdev)
+{
+       if (hdev->status & HID_STAT_ADDED) {
+               device_del(&hdev->dev);
+               hdev->status &= ~HID_STAT_ADDED;
+       }
+}
+
+/**
+ * hid_destroy_device - free previously allocated device
+ *
+ * @hdev: hid device
+ *
+ * If you allocate hid_device through hid_allocate_device, you should ever
+ * free by this function.
+ */
+void hid_destroy_device(struct hid_device *hdev)
+{
+       hid_remove_device(hdev);
+       put_device(&hdev->dev);
+}
+EXPORT_SYMBOL_GPL(hid_destroy_device);
+
+int __hid_register_driver(struct hid_driver *hdrv, struct module *owner,
+               const char *mod_name)
+{
+       hdrv->driver.name = hdrv->name;
+       hdrv->driver.bus = &hid_bus_type;
+       hdrv->driver.owner = owner;
+       hdrv->driver.mod_name = mod_name;
+
+       return driver_register(&hdrv->driver);
+}
+EXPORT_SYMBOL_GPL(__hid_register_driver);
+
+void hid_unregister_driver(struct hid_driver *hdrv)
+{
+       driver_unregister(&hdrv->driver);
+}
+EXPORT_SYMBOL_GPL(hid_unregister_driver);
+
+#ifdef CONFIG_HID_COMPAT
+static void hid_compat_load(struct work_struct *ws)
+{
+       request_module("hid-dummy");
+}
+static DECLARE_WORK(hid_compat_work, hid_compat_load);
+static struct workqueue_struct *hid_compat_wq;
+#endif
+
 static int __init hid_init(void)
 {
-       return hidraw_init();
+       int ret;
+
+       ret = bus_register(&hid_bus_type);
+       if (ret) {
+               printk(KERN_ERR "HID: can't register hid bus\n");
+               goto err;
+       }
+
+       ret = hidraw_init();
+       if (ret)
+               goto err_bus;
+
+#ifdef CONFIG_HID_COMPAT
+       hid_compat_wq = create_workqueue("hid_compat");
+       if (!hid_compat_wq) {
+               hidraw_exit();
+               goto err;
+       }
+       queue_work(hid_compat_wq, &hid_compat_work);
+#endif
+
+       return 0;
+err_bus:
+       bus_unregister(&hid_bus_type);
+err:
+       return ret;
 }
 
 static void __exit hid_exit(void)
 {
+#ifdef CONFIG_HID_COMPAT
+       destroy_workqueue(hid_compat_wq);
+#endif
        hidraw_exit();
+       bus_unregister(&hid_bus_type);
 }
 
 module_init(hid_init);
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
new file mode 100644 (file)
index 0000000..5d69d27
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ *  HID driver for some cypress "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define CP_RDESC_SWAPPED_MIN_MAX       0x01
+#define CP_2WHEEL_MOUSE_HACK           0x02
+#define CP_2WHEEL_MOUSE_HACK_ON                0x04
+
+/*
+ * Some USB barcode readers from cypress have usage min and usage max in
+ * the wrong order
+ */
+static void cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+       unsigned int i;
+
+       if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX))
+               return;
+
+       for (i = 0; i < rsize - 4; i++)
+               if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
+                       __u8 tmp;
+
+                       rdesc[i] = 0x19;
+                       rdesc[i + 2] = 0x29;
+                       tmp = rdesc[i + 3];
+                       rdesc[i + 3] = rdesc[i + 1];
+                       rdesc[i + 1] = tmp;
+               }
+}
+
+static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if (!(quirks & CP_2WHEEL_MOUSE_HACK))
+               return 0;
+
+       if (usage->type == EV_REL && usage->code == REL_WHEEL)
+               set_bit(REL_HWHEEL, *bit);
+       if (usage->hid == 0x00090005)
+               return -1;
+
+       return 0;
+}
+
+static int cp_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type || !(quirks & CP_2WHEEL_MOUSE_HACK))
+               return 0;
+
+       if (usage->hid == 0x00090005) {
+               if (value)
+                       quirks |=  CP_2WHEEL_MOUSE_HACK_ON;
+               else
+                       quirks &= ~CP_2WHEEL_MOUSE_HACK_ON;
+               hid_set_drvdata(hdev, (void *)quirks);
+               return 1;
+       }
+
+       if (usage->code == REL_WHEEL && (quirks & CP_2WHEEL_MOUSE_HACK_ON)) {
+               struct input_dev *input = field->hidinput->input;
+
+               input_event(input, usage->type, REL_HWHEEL, value);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int cp_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       int ret;
+
+       hid_set_drvdata(hdev, (void *)quirks);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id cp_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1),
+               .driver_data = CP_RDESC_SWAPPED_MIN_MAX },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2),
+               .driver_data = CP_RDESC_SWAPPED_MIN_MAX },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE),
+               .driver_data = CP_2WHEEL_MOUSE_HACK },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, cp_devices);
+
+static struct hid_driver cp_driver = {
+       .name = "cypress",
+       .id_table = cp_devices,
+       .report_fixup = cp_report_fixup,
+       .input_mapped = cp_input_mapped,
+       .event = cp_event,
+       .probe = cp_probe,
+};
+
+static int cp_init(void)
+{
+       return hid_register_driver(&cp_driver);
+}
+
+static void cp_exit(void)
+{
+       hid_unregister_driver(&cp_driver);
+}
+
+module_init(cp_init);
+module_exit(cp_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(cypress);
diff --git a/drivers/hid/hid-dell.c b/drivers/hid/hid-dell.c
new file mode 100644 (file)
index 0000000..1a0d0df
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *  HID driver for some dell "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static int dell_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       usbhid_set_leds(hdev);
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id dell_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, dell_devices);
+
+static struct hid_driver dell_driver = {
+       .name = "dell",
+       .id_table = dell_devices,
+       .probe = dell_probe,
+};
+
+static int dell_init(void)
+{
+       return hid_register_driver(&dell_driver);
+}
+
+static void dell_exit(void)
+{
+       hid_unregister_driver(&dell_driver);
+}
+
+module_init(dell_init);
+module_exit(dell_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(dell);
diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c
new file mode 100644 (file)
index 0000000..e148f86
--- /dev/null
@@ -0,0 +1,72 @@
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/hid.h>
+
+static int __init hid_dummy_init(void)
+{
+#ifdef CONFIG_HID_A4TECH_MODULE
+       HID_COMPAT_CALL_DRIVER(a4tech);
+#endif
+#ifdef CONFIG_HID_APPLE_MODULE
+       HID_COMPAT_CALL_DRIVER(apple);
+#endif
+#ifdef CONFIG_HID_BELKIN_MODULE
+       HID_COMPAT_CALL_DRIVER(belkin);
+#endif
+#ifdef CONFIG_HID_BRIGHT_MODULE
+       HID_COMPAT_CALL_DRIVER(bright);
+#endif
+#ifdef CONFIG_HID_CHERRY_MODULE
+       HID_COMPAT_CALL_DRIVER(cherry);
+#endif
+#ifdef CONFIG_HID_CHICONY_MODULE
+       HID_COMPAT_CALL_DRIVER(chicony);
+#endif
+#ifdef CONFIG_HID_CYPRESS_MODULE
+       HID_COMPAT_CALL_DRIVER(cypress);
+#endif
+#ifdef CONFIG_HID_DELL_MODULE
+       HID_COMPAT_CALL_DRIVER(dell);
+#endif
+#ifdef CONFIG_HID_EZKEY_MODULE
+       HID_COMPAT_CALL_DRIVER(ezkey);
+#endif
+#ifdef CONFIG_HID_GYRATION_MODULE
+       HID_COMPAT_CALL_DRIVER(gyration);
+#endif
+#ifdef CONFIG_HID_LOGITECH_MODULE
+       HID_COMPAT_CALL_DRIVER(logitech);
+#endif
+#ifdef CONFIG_HID_MICROSOFT_MODULE
+       HID_COMPAT_CALL_DRIVER(microsoft);
+#endif
+#ifdef CONFIG_HID_MONTEREY_MODULE
+       HID_COMPAT_CALL_DRIVER(monterey);
+#endif
+#ifdef CONFIG_HID_PANTHERLORD_MODULE
+       HID_COMPAT_CALL_DRIVER(pantherlord);
+#endif
+#ifdef CONFIG_HID_PETALYNX_MODULE
+       HID_COMPAT_CALL_DRIVER(petalynx);
+#endif
+#ifdef CONFIG_HID_SAMSUNG_MODULE
+       HID_COMPAT_CALL_DRIVER(samsung);
+#endif
+#ifdef CONFIG_HID_SONY_MODULE
+       HID_COMPAT_CALL_DRIVER(sony);
+#endif
+#ifdef CONFIG_HID_SUNPLUS_MODULE
+       HID_COMPAT_CALL_DRIVER(sunplus);
+#endif
+#ifdef CONFIG_THRUSTMASTER_FF_MODULE
+       HID_COMPAT_CALL_DRIVER(thrustmaster);
+#endif
+#ifdef CONFIG_ZEROPLUS_FF_MODULE
+       HID_COMPAT_CALL_DRIVER(zeroplus);
+#endif
+
+       return -EIO;
+}
+module_init(hid_dummy_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c
new file mode 100644 (file)
index 0000000..deb42f9
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *  HID driver for some ezkey "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define ez_map_rel(c)  hid_map_usage(hi, usage, bit, max, EV_REL, (c))
+#define ez_map_key(c)  hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
+
+static int ez_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x230: ez_map_key(BTN_MOUSE);      break;
+       case 0x231: ez_map_rel(REL_WHEEL);      break;
+       /*
+        * this keyboard has a scrollwheel implemented in
+        * totally broken way. We map this usage temporarily
+        * to HWHEEL and handle it in the event quirk handler
+        */
+       case 0x232: ez_map_rel(REL_HWHEEL);     break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int ez_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type)
+               return 0;
+
+       /* handle the temporary quirky mapping to HWHEEL */
+       if (usage->type == EV_REL && usage->code == REL_HWHEEL) {
+               struct input_dev *input = field->hidinput->input;
+               input_event(input, usage->type, REL_WHEEL, -value);
+               return 1;
+       }
+
+       return 0;
+}
+
+static const struct hid_device_id ez_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, ez_devices);
+
+static struct hid_driver ez_driver = {
+       .name = "ezkey",
+       .id_table = ez_devices,
+       .input_mapping = ez_input_mapping,
+       .event = ez_event,
+};
+
+static int ez_init(void)
+{
+       return hid_register_driver(&ez_driver);
+}
+
+static void ez_exit(void)
+{
+       hid_unregister_driver(&ez_driver);
+}
+
+module_init(ez_init);
+module_exit(ez_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(ezkey);
diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c
new file mode 100644 (file)
index 0000000..ac5120f
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  HID driver for some gyration "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define gy_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int gyration_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
+               return 0;
+
+       set_bit(EV_REP, hi->input->evbit);
+       switch (usage->hid & HID_USAGE) {
+       /* Reported on Gyration MCE Remote */
+       case 0x00d: gy_map_key_clear(KEY_HOME);         break;
+       case 0x024: gy_map_key_clear(KEY_DVD);          break;
+       case 0x025: gy_map_key_clear(KEY_PVR);          break;
+       case 0x046: gy_map_key_clear(KEY_MEDIA);        break;
+       case 0x047: gy_map_key_clear(KEY_MP3);          break;
+       case 0x049: gy_map_key_clear(KEY_CAMERA);       break;
+       case 0x04a: gy_map_key_clear(KEY_VIDEO);        break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int gyration_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       struct input_dev *input = field->hidinput->input;
+
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK &&
+                       (usage->hid & 0xff) == 0x82) {
+               input_event(input, usage->type, usage->code, 1);
+               input_sync(input);
+               input_event(input, usage->type, usage->code, 0);
+               input_sync(input);
+               return 1;
+       }
+
+       return 0;
+}
+
+static const struct hid_device_id gyration_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, gyration_devices);
+
+static struct hid_driver gyration_driver = {
+       .name = "gyration",
+       .id_table = gyration_devices,
+       .input_mapping = gyration_input_mapping,
+       .event = gyration_event,
+};
+
+static int gyration_init(void)
+{
+       return hid_register_driver(&gyration_driver);
+}
+
+static void gyration_exit(void)
+{
+       hid_unregister_driver(&gyration_driver);
+}
+
+module_init(gyration_init);
+module_exit(gyration_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(gyration);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
new file mode 100644 (file)
index 0000000..aad9ed1
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ *  USB HID quirks support for Linux
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef HID_IDS_H_FILE
+#define HID_IDS_H_FILE
+
+#define USB_VENDOR_ID_A4TECH           0x09da
+#define USB_DEVICE_ID_A4TECH_WCP32PU   0x0006
+#define USB_DEVICE_ID_A4TECH_X5_005D   0x000a
+
+#define USB_VENDOR_ID_AASHIMA          0x06d6
+#define USB_DEVICE_ID_AASHIMA_GAMEPAD  0x0025
+#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026
+
+#define USB_VENDOR_ID_ACECAD           0x0460
+#define USB_DEVICE_ID_ACECAD_FLAIR     0x0004
+#define USB_DEVICE_ID_ACECAD_302       0x0008
+
+#define USB_VENDOR_ID_ADS_TECH                 0x06e1
+#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X    0xa155
+
+#define USB_VENDOR_ID_AFATECH          0x15a4
+#define USB_DEVICE_ID_AFATECH_AF9016   0x9016
+
+#define USB_VENDOR_ID_AIPTEK           0x08ca
+#define USB_DEVICE_ID_AIPTEK_01                0x0001
+#define USB_DEVICE_ID_AIPTEK_10                0x0010
+#define USB_DEVICE_ID_AIPTEK_20                0x0020
+#define USB_DEVICE_ID_AIPTEK_21                0x0021
+#define USB_DEVICE_ID_AIPTEK_22                0x0022
+#define USB_DEVICE_ID_AIPTEK_23                0x0023
+#define USB_DEVICE_ID_AIPTEK_24                0x0024
+
+#define USB_VENDOR_ID_AIRCABLE         0x16CA
+#define USB_DEVICE_ID_AIRCABLE1                0x1502
+
+#define USB_VENDOR_ID_ALCOR            0x058f
+#define USB_DEVICE_ID_ALCOR_USBRS232   0x9720
+
+#define USB_VENDOR_ID_ALPS             0x0433
+#define USB_DEVICE_ID_IBM_GAMEPAD      0x1101
+
+#define USB_VENDOR_ID_APPLE            0x05ac
+#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE        0x0304
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI      0x020e
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO       0x020f
+#define USB_DEVICE_ID_APPLE_GEYSER_ANSI        0x0214
+#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215
+#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216
+#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI       0x0217
+#define USB_DEVICE_ID_APPLE_GEYSER3_ISO        0x0218
+#define USB_DEVICE_ID_APPLE_GEYSER3_JIS        0x0219
+#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI       0x021a
+#define USB_DEVICE_ID_APPLE_GEYSER4_ISO        0x021b
+#define USB_DEVICE_ID_APPLE_GEYSER4_JIS        0x021c
+#define USB_DEVICE_ID_APPLE_ALU_ANSI   0x0220
+#define USB_DEVICE_ID_APPLE_ALU_ISO    0x0221
+#define USB_DEVICE_ID_APPLE_ALU_JIS    0x0222
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI    0x0223
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO     0x0224
+#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS     0x0225
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI    0x0229
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO     0x022a
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS     0x022b
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI  0x022c
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO   0x022d
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS   0x022e
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI   0x0230
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO    0x0231
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS    0x0232
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY   0x030a
+#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY    0x030b
+#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL      0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+
+#define USB_VENDOR_ID_ASUS             0x0b05
+#define USB_DEVICE_ID_ASUS_LCM         0x1726
+
+#define USB_VENDOR_ID_ATEN             0x0557
+#define USB_DEVICE_ID_ATEN_UC100KM     0x2004
+#define USB_DEVICE_ID_ATEN_CS124U      0x2202
+#define USB_DEVICE_ID_ATEN_2PORTKVM    0x2204
+#define USB_DEVICE_ID_ATEN_4PORTKVM    0x2205
+#define USB_DEVICE_ID_ATEN_4PORTKVMC   0x2208
+
+#define USB_VENDOR_ID_AVERMEDIA                0x07ca
+#define USB_DEVICE_ID_AVER_FM_MR800    0xb800
+
+#define USB_VENDOR_ID_BELKIN           0x050d
+#define USB_DEVICE_ID_FLIP_KVM         0x3201
+
+#define USB_VENDOR_ID_BRIGHT           0x1241
+#define USB_DEVICE_ID_BRIGHT_ABNT2     0x1503
+
+#define USB_VENDOR_ID_BERKSHIRE                0x0c98
+#define USB_DEVICE_ID_BERKSHIRE_PCWD   0x1140
+
+#define USB_VENDOR_ID_CHERRY           0x046a
+#define USB_DEVICE_ID_CHERRY_CYMOTION  0x0023
+
+#define USB_VENDOR_ID_CHIC             0x05fe
+#define USB_DEVICE_ID_CHIC_GAMEPAD     0x0014
+
+#define USB_VENDOR_ID_CHICONY          0x04f2
+#define USB_DEVICE_ID_CHICONY_TACTICAL_PAD     0x0418
+
+#define USB_VENDOR_ID_CIDC             0x1677
+
+#define USB_VENDOR_ID_CMEDIA           0x0d8c
+#define USB_DEVICE_ID_CM109            0x000e
+
+#define USB_VENDOR_ID_CODEMERCS                0x07c0
+#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST      0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW_LAST       0x15ff
+
+#define USB_VENDOR_ID_CYGNAL           0x10c4
+#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X      0x818a
+
+#define USB_VENDOR_ID_CYPRESS          0x04b4
+#define USB_DEVICE_ID_CYPRESS_MOUSE    0x0001
+#define USB_DEVICE_ID_CYPRESS_HIDCOM   0x5500
+#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE       0x7417
+#define USB_DEVICE_ID_CYPRESS_BARCODE_1        0xde61
+#define USB_DEVICE_ID_CYPRESS_BARCODE_2        0xde64
+
+#define USB_VENDOR_ID_DELL             0x413c
+#define USB_DEVICE_ID_DELL_W7658       0x2005
+#define USB_DEVICE_ID_DELL_SK8115      0x2105
+
+#define USB_VENDOR_ID_DELORME          0x1163
+#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
+#define USB_DEVICE_ID_DELORME_EM_LT20  0x0200
+
+#define USB_VENDOR_ID_DMI              0x0c0b
+#define USB_DEVICE_ID_DMI_ENC          0x5fab
+
+#define USB_VENDOR_ID_ELO              0x04E7
+#define USB_DEVICE_ID_ELO_TS2700       0x0020
+
+#define USB_VENDOR_ID_ESSENTIAL_REALITY        0x0d7f
+#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
+
+#define USB_VENDOR_ID_EZKEY            0x0518
+#define USB_DEVICE_ID_BTC_8193         0x0002
+
+#define USB_VENDOR_ID_GAMERON          0x0810
+#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001
+
+#define USB_VENDOR_ID_GENERAL_TOUCH    0x0dfc
+
+#define USB_VENDOR_ID_GLAB             0x06c2
+#define USB_DEVICE_ID_4_PHIDGETSERVO_30        0x0038
+#define USB_DEVICE_ID_1_PHIDGETSERVO_30        0x0039
+#define USB_DEVICE_ID_0_0_4_IF_KIT     0x0040
+#define USB_DEVICE_ID_0_16_16_IF_KIT   0x0044
+#define USB_DEVICE_ID_8_8_8_IF_KIT     0x0045
+#define USB_DEVICE_ID_0_8_7_IF_KIT     0x0051
+#define USB_DEVICE_ID_0_8_8_IF_KIT     0x0053
+#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL     0x0058
+
+#define USB_VENDOR_ID_GOTOP            0x08f2
+#define USB_DEVICE_ID_SUPER_Q2         0x007f
+#define USB_DEVICE_ID_GOGOPEN          0x00ce
+#define USB_DEVICE_ID_PENPOWER         0x00f4
+
+#define USB_VENDOR_ID_GREENASIA                0x0e8f
+
+#define USB_VENDOR_ID_GRETAGMACBETH    0x0971
+#define USB_DEVICE_ID_GRETAGMACBETH_HUEY       0x2005
+
+#define USB_VENDOR_ID_GRIFFIN          0x077d
+#define USB_DEVICE_ID_POWERMATE                0x0410
+#define USB_DEVICE_ID_SOUNDKNOB                0x04AA
+
+#define USB_VENDOR_ID_GTCO             0x078c
+#define USB_DEVICE_ID_GTCO_90          0x0090
+#define USB_DEVICE_ID_GTCO_100         0x0100
+#define USB_DEVICE_ID_GTCO_101         0x0101
+#define USB_DEVICE_ID_GTCO_103         0x0103
+#define USB_DEVICE_ID_GTCO_104         0x0104
+#define USB_DEVICE_ID_GTCO_105         0x0105
+#define USB_DEVICE_ID_GTCO_106         0x0106
+#define USB_DEVICE_ID_GTCO_107         0x0107
+#define USB_DEVICE_ID_GTCO_108         0x0108
+#define USB_DEVICE_ID_GTCO_200         0x0200
+#define USB_DEVICE_ID_GTCO_201         0x0201
+#define USB_DEVICE_ID_GTCO_202         0x0202
+#define USB_DEVICE_ID_GTCO_203         0x0203
+#define USB_DEVICE_ID_GTCO_204         0x0204
+#define USB_DEVICE_ID_GTCO_205         0x0205
+#define USB_DEVICE_ID_GTCO_206         0x0206
+#define USB_DEVICE_ID_GTCO_207         0x0207
+#define USB_DEVICE_ID_GTCO_300         0x0300
+#define USB_DEVICE_ID_GTCO_301         0x0301
+#define USB_DEVICE_ID_GTCO_302         0x0302
+#define USB_DEVICE_ID_GTCO_303         0x0303
+#define USB_DEVICE_ID_GTCO_304         0x0304
+#define USB_DEVICE_ID_GTCO_305         0x0305
+#define USB_DEVICE_ID_GTCO_306         0x0306
+#define USB_DEVICE_ID_GTCO_307         0x0307
+#define USB_DEVICE_ID_GTCO_308         0x0308
+#define USB_DEVICE_ID_GTCO_309         0x0309
+#define USB_DEVICE_ID_GTCO_400         0x0400
+#define USB_DEVICE_ID_GTCO_401         0x0401
+#define USB_DEVICE_ID_GTCO_402         0x0402
+#define USB_DEVICE_ID_GTCO_403         0x0403
+#define USB_DEVICE_ID_GTCO_404         0x0404
+#define USB_DEVICE_ID_GTCO_405         0x0405
+#define USB_DEVICE_ID_GTCO_500         0x0500
+#define USB_DEVICE_ID_GTCO_501         0x0501
+#define USB_DEVICE_ID_GTCO_502         0x0502
+#define USB_DEVICE_ID_GTCO_503         0x0503
+#define USB_DEVICE_ID_GTCO_504         0x0504
+#define USB_DEVICE_ID_GTCO_1000                0x1000
+#define USB_DEVICE_ID_GTCO_1001                0x1001
+#define USB_DEVICE_ID_GTCO_1002                0x1002
+#define USB_DEVICE_ID_GTCO_1003                0x1003
+#define USB_DEVICE_ID_GTCO_1004                0x1004
+#define USB_DEVICE_ID_GTCO_1005                0x1005
+#define USB_DEVICE_ID_GTCO_1006                0x1006
+#define USB_DEVICE_ID_GTCO_1007                0x1007
+
+#define USB_VENDOR_ID_GYRATION         0x0c16
+#define USB_DEVICE_ID_GYRATION_REMOTE  0x0002
+
+#define USB_VENDOR_ID_HAPP             0x078b
+#define USB_DEVICE_ID_UGCI_DRIVING     0x0010
+#define USB_DEVICE_ID_UGCI_FLYING      0x0020
+#define USB_DEVICE_ID_UGCI_FIGHTING    0x0030
+
+#define USB_VENDOR_ID_IMATION          0x0718
+#define USB_DEVICE_ID_DISC_STAKKA      0xd000
+
+#define USB_VENDOR_ID_KBGEAR           0x084e
+#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
+
+#define USB_VENDOR_ID_LABTEC           0x1020
+#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006
+
+#define USB_VENDOR_ID_LD               0x0f11
+#define USB_DEVICE_ID_LD_CASSY         0x1000
+#define USB_DEVICE_ID_LD_POCKETCASSY   0x1010
+#define USB_DEVICE_ID_LD_MOBILECASSY   0x1020
+#define USB_DEVICE_ID_LD_JWM           0x1080
+#define USB_DEVICE_ID_LD_DMMP          0x1081
+#define USB_DEVICE_ID_LD_UMIP          0x1090
+#define USB_DEVICE_ID_LD_XRAY1         0x1100
+#define USB_DEVICE_ID_LD_XRAY2         0x1101
+#define USB_DEVICE_ID_LD_VIDEOCOM      0x1200
+#define USB_DEVICE_ID_LD_COM3LAB       0x2000
+#define USB_DEVICE_ID_LD_TELEPORT      0x2010
+#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
+#define USB_DEVICE_ID_LD_POWERCONTROL  0x2030
+#define USB_DEVICE_ID_LD_MACHINETEST   0x2040
+
+#define USB_VENDOR_ID_LOGITECH         0x046d
+#define USB_DEVICE_ID_LOGITECH_LX3     0xc044
+#define USB_DEVICE_ID_LOGITECH_V150    0xc047
+#define USB_DEVICE_ID_LOGITECH_RECEIVER        0xc101
+#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110
+#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD       0xc211
+#define USB_DEVICE_ID_LOGITECH_EXTREME_3D      0xc215
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2      0xc218
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2    0xc219
+#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D     0xc283
+#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO     0xc286
+#define USB_DEVICE_ID_LOGITECH_WHEEL   0xc294
+#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL      0xc295
+#define USB_DEVICE_ID_LOGITECH_ELITE_KBD       0xc30a
+#define USB_DEVICE_ID_LOGITECH_KBD     0xc311
+#define USB_DEVICE_ID_S510_RECEIVER    0xc50c
+#define USB_DEVICE_ID_S510_RECEIVER_2  0xc517
+#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500  0xc512
+#define USB_DEVICE_ID_MX3000_RECEIVER  0xc513
+#define USB_DEVICE_ID_DINOVO_DESKTOP   0xc704
+#define USB_DEVICE_ID_DINOVO_EDGE      0xc714
+#define USB_DEVICE_ID_DINOVO_MINI      0xc71f
+#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2     0xca03
+
+#define USB_VENDOR_ID_MCC              0x09db
+#define USB_DEVICE_ID_MCC_PMD1024LS    0x0076
+#define USB_DEVICE_ID_MCC_PMD1208LS    0x007a
+
+#define USB_VENDOR_ID_MGE              0x0463
+#define USB_DEVICE_ID_MGE_UPS          0xffff
+#define USB_DEVICE_ID_MGE_UPS1         0x0001
+
+#define USB_VENDOR_ID_MICROCHIP                0x04d8
+#define USB_DEVICE_ID_PICKIT1          0x0032
+#define USB_DEVICE_ID_PICKIT2          0x0033
+
+#define USB_VENDOR_ID_MICROSOFT                0x045e
+#define USB_DEVICE_ID_SIDEWINDER_GV    0x003b
+#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d
+#define USB_DEVICE_ID_MS_NE4K          0x00db
+#define USB_DEVICE_ID_MS_LK6K          0x00f9
+#define USB_DEVICE_ID_MS_PRESENTER_8K_BT       0x0701
+#define USB_DEVICE_ID_MS_PRESENTER_8K_USB      0x0713
+
+
+#define USB_VENDOR_ID_MONTEREY         0x0566
+#define USB_DEVICE_ID_GENIUS_KB29E     0x3004
+
+#define USB_VENDOR_ID_NCR              0x0404
+#define USB_DEVICE_ID_NCR_FIRST                0x0300
+#define USB_DEVICE_ID_NCR_LAST         0x03ff
+
+#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
+#define USB_DEVICE_ID_N_S_HARMONY       0xc359
+
+#define USB_VENDOR_ID_NATSU             0x08b7
+#define USB_DEVICE_ID_NATSU_GAMEPAD     0x0001
+
+#define USB_VENDOR_ID_NEC              0x073e
+#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
+
+#define USB_VENDOR_ID_ONTRAK           0x0a07
+#define USB_DEVICE_ID_ONTRAK_ADU100    0x0064
+
+#define USB_VENDOR_ID_PANJIT           0x134c
+
+#define USB_VENDOR_ID_PANTHERLORD      0x0810
+#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK    0x0001
+
+#define USB_VENDOR_ID_PETALYNX         0x18b1
+#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE   0x0037
+
+#define USB_VENDOR_ID_PLAYDOTCOM       0x0b43
+#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII     0x0003
+
+#define USB_VENDOR_ID_SAITEK           0x06a3
+#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
+
+#define USB_VENDOR_ID_SAMSUNG          0x0419
+#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE        0x0001
+
+#define USB_VENDOR_ID_SONY                     0x054c
+#define USB_DEVICE_ID_SONY_PS3_CONTROLLER      0x0268
+
+#define USB_VENDOR_ID_SOUNDGRAPH       0x15c2
+#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD      0x0038
+
+#define USB_VENDOR_ID_SUN              0x0430
+#define USB_DEVICE_ID_RARITAN_KVM_DONGLE       0xcdab
+
+#define USB_VENDOR_ID_SUNPLUS          0x04fc
+#define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8
+
+#define USB_VENDOR_ID_TENX             0x1130
+#define USB_DEVICE_ID_TENX_IBUDDY1     0x0001
+#define USB_DEVICE_ID_TENX_IBUDDY2     0x0002
+
+#define USB_VENDOR_ID_THRUSTMASTER     0x044f
+
+#define USB_VENDOR_ID_TOPMAX           0x0663
+#define USB_DEVICE_ID_TOPMAX_COBRAPAD  0x0103
+
+#define USB_VENDOR_ID_TURBOX           0x062a
+#define USB_DEVICE_ID_TURBOX_KEYBOARD  0x0201
+
+#define USB_VENDOR_ID_VERNIER          0x08f7
+#define USB_DEVICE_ID_VERNIER_LABPRO   0x0001
+#define USB_DEVICE_ID_VERNIER_GOTEMP   0x0002
+#define USB_DEVICE_ID_VERNIER_SKIP     0x0003
+#define USB_DEVICE_ID_VERNIER_CYCLOPS  0x0004
+#define USB_DEVICE_ID_VERNIER_LCSPEC   0x0006
+
+#define USB_VENDOR_ID_WACOM            0x056a
+
+#define USB_VENDOR_ID_WISEGROUP                0x0925
+#define USB_DEVICE_ID_1_PHIDGETSERVO_20        0x8101
+#define USB_DEVICE_ID_4_PHIDGETSERVO_20        0x8104
+#define USB_DEVICE_ID_8_8_4_IF_KIT     0x8201
+#define USB_DEVICE_ID_QUAD_USB_JOYPAD  0x8800
+#define USB_DEVICE_ID_DUAL_USB_JOYPAD  0x8866
+
+#define USB_VENDOR_ID_WISEGROUP_LTD    0x6666
+#define USB_VENDOR_ID_WISEGROUP_LTD2   0x6677
+#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
+
+#define USB_VENDOR_ID_YEALINK          0x6993
+#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K      0xb001
+
+#define USB_VENDOR_ID_ZEROPLUS         0x0c12
+
+#define USB_VENDOR_ID_KYE              0x0458
+#define USB_DEVICE_ID_KYE_GPEN_560     0x5003
+
+#endif
diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c
deleted file mode 100644 (file)
index 16feea0..0000000
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- *  HID-input usage mapping quirks
- *
- *  This is used to handle HID-input mappings for devices violating
- *  HUT 1.12 specification.
- *
- * Copyright (c) 2007-2008 Jiri Kosina
- */
-
-/*
- * 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
- */
-
-#include <linux/input.h>
-#include <linux/hid.h>
-
-#define map_abs(c)      do { usage->code = c; usage->type = EV_ABS; *bit = input->absbit; *max = ABS_MAX; } while (0)
-#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_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)
-
-static int quirk_belkin_wkbd(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x03a: map_key_clear(KEY_SOUND);           break;
-               case 0x03b: map_key_clear(KEY_CAMERA);          break;
-               case 0x03c: map_key_clear(KEY_DOCUMENTS);       break;
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_cherry_cymotion(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x301: map_key_clear(KEY_PROG1);           break;
-               case 0x302: map_key_clear(KEY_PROG2);           break;
-               case 0x303: map_key_clear(KEY_PROG3);           break;
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_logitech_ultrax_remote(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
-               return 0;
-
-       set_bit(EV_REP, input->evbit);
-       switch(usage->hid & HID_USAGE) {
-               /* Reported on Logitech Ultra X Media Remote */
-               case 0x004: map_key_clear(KEY_AGAIN);           break;
-               case 0x00d: map_key_clear(KEY_HOME);            break;
-               case 0x024: map_key_clear(KEY_SHUFFLE);         break;
-               case 0x025: map_key_clear(KEY_TV);              break;
-               case 0x026: map_key_clear(KEY_MENU);            break;
-               case 0x031: map_key_clear(KEY_AUDIO);           break;
-               case 0x032: map_key_clear(KEY_TEXT);            break;
-               case 0x033: map_key_clear(KEY_LAST);            break;
-               case 0x047: map_key_clear(KEY_MP3);             break;
-               case 0x048: map_key_clear(KEY_DVD);             break;
-               case 0x049: map_key_clear(KEY_MEDIA);           break;
-               case 0x04a: map_key_clear(KEY_VIDEO);           break;
-               case 0x04b: map_key_clear(KEY_ANGLE);           break;
-               case 0x04c: map_key_clear(KEY_LANGUAGE);        break;
-               case 0x04d: map_key_clear(KEY_SUBTITLE);        break;
-               case 0x051: map_key_clear(KEY_RED);             break;
-               case 0x052: map_key_clear(KEY_CLOSE);           break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_gyration_remote(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
-               return 0;
-
-       set_bit(EV_REP, input->evbit);
-       switch(usage->hid & HID_USAGE) {
-               /* Reported on Gyration MCE Remote */
-               case 0x00d: map_key_clear(KEY_HOME);            break;
-               case 0x024: map_key_clear(KEY_DVD);             break;
-               case 0x025: map_key_clear(KEY_PVR);             break;
-               case 0x046: map_key_clear(KEY_MEDIA);           break;
-               case 0x047: map_key_clear(KEY_MP3);             break;
-               case 0x049: map_key_clear(KEY_CAMERA);          break;
-               case 0x04a: map_key_clear(KEY_VIDEO);           break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_chicony_tactical_pad(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
-               return 0;
-
-       set_bit(EV_REP, input->evbit);
-       switch (usage->hid & HID_USAGE) {
-               case 0xff01: map_key_clear(BTN_1);              break;
-               case 0xff02: map_key_clear(BTN_2);              break;
-               case 0xff03: map_key_clear(BTN_3);              break;
-               case 0xff04: map_key_clear(BTN_4);              break;
-               case 0xff05: map_key_clear(BTN_5);              break;
-               case 0xff06: map_key_clear(BTN_6);              break;
-               case 0xff07: map_key_clear(BTN_7);              break;
-               case 0xff08: map_key_clear(BTN_8);              break;
-               case 0xff09: map_key_clear(BTN_9);              break;
-               case 0xff0a: map_key_clear(BTN_A);              break;
-               case 0xff0b: map_key_clear(BTN_B);              break;
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_microsoft_ergonomy_kb(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
-               return 0;
-
-       switch(usage->hid & HID_USAGE) {
-               case 0xfd06: map_key_clear(KEY_CHAT);           break;
-               case 0xfd07: map_key_clear(KEY_PHONE);          break;
-               case 0xff05:
-                       set_bit(EV_REP, input->evbit);
-                       map_key_clear(KEY_F13);
-                       set_bit(KEY_F14, input->keybit);
-                       set_bit(KEY_F15, input->keybit);
-                       set_bit(KEY_F16, input->keybit);
-                       set_bit(KEY_F17, input->keybit);
-                       set_bit(KEY_F18, input->keybit);
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_microsoft_presenter_8k(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
-               return 0;
-
-       set_bit(EV_REP, input->evbit);
-       switch(usage->hid & HID_USAGE) {
-               case 0xfd08: map_key_clear(KEY_FORWARD);        break;
-               case 0xfd09: map_key_clear(KEY_BACK);           break;
-               case 0xfd0b: map_key_clear(KEY_PLAYPAUSE);      break;
-               case 0xfd0e: map_key_clear(KEY_CLOSE);          break;
-               case 0xfd0f: map_key_clear(KEY_PLAY);           break;
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_petalynx_remote(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if (((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) &&
-                       ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER))
-               return 0;
-
-       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LOGIVENDOR)
-               switch(usage->hid & HID_USAGE) {
-                       case 0x05a: map_key_clear(KEY_TEXT);            break;
-                       case 0x05b: map_key_clear(KEY_RED);             break;
-                       case 0x05c: map_key_clear(KEY_GREEN);           break;
-                       case 0x05d: map_key_clear(KEY_YELLOW);          break;
-                       case 0x05e: map_key_clear(KEY_BLUE);            break;
-                       default:
-                               return 0;
-               }
-
-       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER)
-               switch(usage->hid & HID_USAGE) {
-                       case 0x0f6: map_key_clear(KEY_NEXT);            break;
-                       case 0x0fa: map_key_clear(KEY_BACK);            break;
-                       default:
-                               return 0;
-               }
-       return 1;
-}
-
-static int quirk_logitech_wireless(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x1001: map_key_clear(KEY_MESSENGER);      break;
-               case 0x1003: map_key_clear(KEY_SOUND);          break;
-               case 0x1004: map_key_clear(KEY_VIDEO);          break;
-               case 0x1005: map_key_clear(KEY_AUDIO);          break;
-               case 0x100a: map_key_clear(KEY_DOCUMENTS);      break;
-               case 0x1011: map_key_clear(KEY_PREVIOUSSONG);   break;
-               case 0x1012: map_key_clear(KEY_NEXTSONG);       break;
-               case 0x1013: map_key_clear(KEY_CAMERA);         break;
-               case 0x1014: map_key_clear(KEY_MESSENGER);      break;
-               case 0x1015: map_key_clear(KEY_RECORD);         break;
-               case 0x1016: map_key_clear(KEY_PLAYER);         break;
-               case 0x1017: map_key_clear(KEY_EJECTCD);        break;
-               case 0x1018: map_key_clear(KEY_MEDIA);          break;
-               case 0x1019: map_key_clear(KEY_PROG1);          break;
-               case 0x101a: map_key_clear(KEY_PROG2);          break;
-               case 0x101b: map_key_clear(KEY_PROG3);          break;
-               case 0x101f: map_key_clear(KEY_ZOOMIN);         break;
-               case 0x1020: map_key_clear(KEY_ZOOMOUT);        break;
-               case 0x1021: map_key_clear(KEY_ZOOMRESET);      break;
-               case 0x1023: map_key_clear(KEY_CLOSE);          break;
-               case 0x1027: map_key_clear(KEY_MENU);           break;
-               /* this one is marked as 'Rotate' */
-               case 0x1028: map_key_clear(KEY_ANGLE);          break;
-               case 0x1029: map_key_clear(KEY_SHUFFLE);        break;
-               case 0x102a: map_key_clear(KEY_BACK);           break;
-               case 0x102b: map_key_clear(KEY_CYCLEWINDOWS);   break;
-               case 0x1041: map_key_clear(KEY_BATTERY);        break;
-               case 0x1042: map_key_clear(KEY_WORDPROCESSOR);  break;
-               case 0x1043: map_key_clear(KEY_SPREADSHEET);    break;
-               case 0x1044: map_key_clear(KEY_PRESENTATION);   break;
-               case 0x1045: map_key_clear(KEY_UNDO);           break;
-               case 0x1046: map_key_clear(KEY_REDO);           break;
-               case 0x1047: map_key_clear(KEY_PRINT);          break;
-               case 0x1048: map_key_clear(KEY_SAVE);           break;
-               case 0x1049: map_key_clear(KEY_PROG1);          break;
-               case 0x104a: map_key_clear(KEY_PROG2);          break;
-               case 0x104b: map_key_clear(KEY_PROG3);          break;
-               case 0x104c: map_key_clear(KEY_PROG4);          break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_cherry_genius_29e(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x156: map_key_clear(KEY_WORDPROCESSOR);   break;
-               case 0x157: map_key_clear(KEY_SPREADSHEET);     break;
-               case 0x158: map_key_clear(KEY_PRESENTATION);    break;
-               case 0x15c: map_key_clear(KEY_STOP);            break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x230: map_key(BTN_MOUSE);                 break;
-               case 0x231: map_rel(REL_WHEEL);                 break;
-               /* 
-                * this keyboard has a scrollwheel implemented in
-                * totally broken way. We map this usage temporarily
-                * to HWHEEL and handle it in the event quirk handler
-                */
-               case 0x232: map_rel(REL_HWHEEL);                break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_sunplus_wdesktop(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x2003: map_key_clear(KEY_ZOOMIN);         break;
-               case 0x2103: map_key_clear(KEY_ZOOMOUT);        break;
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-#define VENDOR_ID_BELKIN                       0x1020
-#define DEVICE_ID_BELKIN_WIRELESS_KEYBOARD     0x0006
-
-#define VENDOR_ID_CHERRY                       0x046a
-#define DEVICE_ID_CHERRY_CYMOTION              0x0023
-
-#define VENDOR_ID_CHICONY                      0x04f2
-#define DEVICE_ID_CHICONY_TACTICAL_PAD         0x0418
-
-#define VENDOR_ID_EZKEY                                0x0518
-#define DEVICE_ID_BTC_8193                     0x0002
-
-#define VENDOR_ID_GYRATION                     0x0c16
-#define DEVICE_ID_GYRATION_REMOTE              0x0002
-
-#define VENDOR_ID_LOGITECH                     0x046d
-#define DEVICE_ID_LOGITECH_RECEIVER            0xc101
-#define DEVICE_ID_S510_RECEIVER                        0xc50c
-#define DEVICE_ID_S510_RECEIVER_2              0xc517
-#define DEVICE_ID_MX3000_RECEIVER              0xc513
-
-#define VENDOR_ID_MICROSOFT                    0x045e
-#define DEVICE_ID_MS4K                         0x00db
-#define DEVICE_ID_MS6K                         0x00f9
-#define DEVICE_IS_MS_PRESENTER_8K_BT           0x0701
-#define DEVICE_ID_MS_PRESENTER_8K_USB          0x0713
-
-#define VENDOR_ID_MONTEREY                     0x0566
-#define DEVICE_ID_GENIUS_KB29E                 0x3004
-
-#define VENDOR_ID_PETALYNX                     0x18b1
-#define DEVICE_ID_PETALYNX_MAXTER_REMOTE       0x0037
-
-#define VENDOR_ID_SUNPLUS                      0x04fc
-#define DEVICE_ID_SUNPLUS_WDESKTOP             0x05d8
-
-static const struct hid_input_blacklist {
-       __u16 idVendor;
-       __u16 idProduct;
-       int (*quirk)(struct hid_usage *, struct input_dev *, unsigned long **, int *);
-} hid_input_blacklist[] = {
-       { VENDOR_ID_BELKIN, DEVICE_ID_BELKIN_WIRELESS_KEYBOARD, quirk_belkin_wkbd },
-
-       { VENDOR_ID_CHERRY, DEVICE_ID_CHERRY_CYMOTION, quirk_cherry_cymotion },
-
-       { VENDOR_ID_CHICONY, DEVICE_ID_CHICONY_TACTICAL_PAD, quirk_chicony_tactical_pad },
-
-       { VENDOR_ID_EZKEY, DEVICE_ID_BTC_8193, quirk_btc_8193 },
-
-       { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote },
-
-       { VENDOR_ID_LOGITECH, DEVICE_ID_LOGITECH_RECEIVER, quirk_logitech_ultrax_remote },
-       { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER, quirk_logitech_wireless },
-       { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER_2, quirk_logitech_wireless },
-       { VENDOR_ID_LOGITECH, DEVICE_ID_MX3000_RECEIVER, quirk_logitech_wireless },
-
-       { VENDOR_ID_MICROSOFT, DEVICE_ID_MS4K, quirk_microsoft_ergonomy_kb },
-       { VENDOR_ID_MICROSOFT, DEVICE_ID_MS6K, quirk_microsoft_ergonomy_kb },
-       { VENDOR_ID_MICROSOFT, DEVICE_IS_MS_PRESENTER_8K_BT, quirk_microsoft_presenter_8k },
-       { VENDOR_ID_MICROSOFT, DEVICE_ID_MS_PRESENTER_8K_USB, quirk_microsoft_presenter_8k },
-
-       { VENDOR_ID_MONTEREY, DEVICE_ID_GENIUS_KB29E, quirk_cherry_genius_29e },
-
-       { VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote },
-
-       { VENDOR_ID_SUNPLUS, DEVICE_ID_SUNPLUS_WDESKTOP, quirk_sunplus_wdesktop },
-
-       { 0, 0, NULL }
-};
-
-int hidinput_mapping_quirks(struct hid_usage *usage, 
-                                  struct input_dev *input, 
-                                  unsigned long **bit, int *max)
-{
-       struct hid_device *device = input_get_drvdata(input);
-       int i = 0;
-       
-       while (hid_input_blacklist[i].quirk) {
-               if (hid_input_blacklist[i].idVendor == device->vendor &&
-                               hid_input_blacklist[i].idProduct == device->product)
-                       return hid_input_blacklist[i].quirk(usage, input, bit, max);
-               i++;
-       }
-       return 0;
-}
-
-int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
-{
-       struct input_dev *input;
-
-       input = field->hidinput->input;
-
-       if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
-               || ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) {
-               if (value) hid->quirks |=  HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
-               else       hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
-               return 1;
-       }
-
-       if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) &&
-                       (usage->type == EV_REL) &&
-                       (usage->code == REL_WHEEL)) {
-               hid->delayed_value = value;
-               return 1;
-       }
-
-       if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) &&
-                       (usage->hid == 0x000100b8)) {
-               input_event(input, EV_REL, value ? REL_HWHEEL : REL_WHEEL, hid->delayed_value);
-               return 1;
-       }
-
-       if ((hid->quirks & HID_QUIRK_INVERT_HWHEEL) && (usage->code == REL_HWHEEL)) {
-               input_event(input, usage->type, usage->code, -value);
-               return 1;
-       }
-
-       if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) {
-               input_event(input, usage->type, REL_HWHEEL, value);
-               return 1;
-       }
-
-       if ((hid->quirks & HID_QUIRK_APPLE_HAS_FN) && hidinput_apple_event(hid, input, usage, value))
-               return 1;
-
-       /* Handling MS keyboards special buttons */
-       if (hid->quirks & HID_QUIRK_MICROSOFT_KEYS && 
-                       usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
-               int key = 0;
-               static int last_key = 0;
-               switch (value) {
-                       case 0x01: key = KEY_F14; break;
-                       case 0x02: key = KEY_F15; break;
-                       case 0x04: key = KEY_F16; break;
-                       case 0x08: key = KEY_F17; break;
-                       case 0x10: key = KEY_F18; break;
-                       default: break;
-               }
-               if (key) {
-                       input_event(input, usage->type, key, 1);
-                       last_key = key;
-               } else {
-                       input_event(input, usage->type, last_key, 0);
-               }
-       }
-
-       /* handle the temporary quirky mapping to HWHEEL */
-       if (hid->quirks & HID_QUIRK_HWHEEL_WHEEL_INVERT &&
-                       usage->type == EV_REL && usage->code == REL_HWHEEL) {
-               input_event(input, usage->type, REL_WHEEL, -value);
-               return 1;
-       }
-
-       /* Gyration MCE remote "Sleep" key */
-       if (hid->vendor == VENDOR_ID_GYRATION &&
-           hid->product == DEVICE_ID_GYRATION_REMOTE &&
-           (usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK &&
-           (usage->hid & 0xff) == 0x82) {
-               input_event(input, usage->type, usage->code, 1);
-               input_sync(input);
-               input_event(input, usage->type, usage->code, 0);
-               input_sync(input);
-               return 1;
-       }
-       return 0;
-}
-
-
index 1b2e8dc..7f183b7 100644 (file)
 #include <linux/hid.h>
 #include <linux/hid-debug.h>
 
-static int hid_apple_fnmode = 1;
-module_param_named(pb_fnmode, hid_apple_fnmode, int, 0644);
-MODULE_PARM_DESC(pb_fnmode,
-               "Mode of fn key on Apple keyboards (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)");
-
 #define unk    KEY_UNKNOWN
 
 static const unsigned char hid_keyboard[256] = {
@@ -58,227 +53,20 @@ static const unsigned char hid_keyboard[256] = {
        150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
 };
 
-/* extended mapping for certain Logitech hardware (Logitech cordless desktop LX500) */
-#define LOGITECH_EXPANDED_KEYMAP_SIZE 80
-static int logitech_expanded_keymap[LOGITECH_EXPANDED_KEYMAP_SIZE] = {
-         0,216,  0,213,175,156,  0,  0,  0,  0,
-       144,  0,  0,  0,  0,  0,  0,  0,  0,212,
-       174,167,152,161,112,  0,  0,  0,154,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,183,184,185,186,187,
-       188,189,190,191,192,193,194,  0,  0,  0
-};
-
 static const struct {
        __s32 x;
        __s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
-#define map_abs(c)     do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
-#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_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)
-
-#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
-
-struct hidinput_key_translation {
-       u16 from;
-       u16 to;
-       u8 flags;
-};
-
-#define APPLE_FLAG_FKEY 0x01
-
-static struct hidinput_key_translation apple_fn_keys[] = {
-       { KEY_BACKSPACE, KEY_DELETE },
-       { KEY_F1,       KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
-       { KEY_F2,       KEY_BRIGHTNESSUP,   APPLE_FLAG_FKEY },
-       { KEY_F3,       KEY_FN_F5,          APPLE_FLAG_FKEY }, /* Exposé */
-       { KEY_F4,       KEY_FN_F4,          APPLE_FLAG_FKEY }, /* Dashboard */
-       { KEY_F5,       KEY_KBDILLUMDOWN,   APPLE_FLAG_FKEY },
-       { KEY_F6,       KEY_KBDILLUMUP,     APPLE_FLAG_FKEY },
-       { KEY_F7,       KEY_PREVIOUSSONG,   APPLE_FLAG_FKEY },
-       { KEY_F8,       KEY_PLAYPAUSE,      APPLE_FLAG_FKEY },
-       { KEY_F9,       KEY_NEXTSONG,       APPLE_FLAG_FKEY },
-       { KEY_F10,      KEY_MUTE,           APPLE_FLAG_FKEY },
-       { KEY_F11,      KEY_VOLUMEDOWN,     APPLE_FLAG_FKEY },
-       { KEY_F12,      KEY_VOLUMEUP,       APPLE_FLAG_FKEY },
-       { KEY_UP,       KEY_PAGEUP },
-       { KEY_DOWN,     KEY_PAGEDOWN },
-       { KEY_LEFT,     KEY_HOME },
-       { KEY_RIGHT,    KEY_END },
-       { }
-};
-
-static struct hidinput_key_translation powerbook_fn_keys[] = {
-       { KEY_BACKSPACE, KEY_DELETE },
-       { KEY_F1,       KEY_BRIGHTNESSDOWN,     APPLE_FLAG_FKEY },
-       { KEY_F2,       KEY_BRIGHTNESSUP,       APPLE_FLAG_FKEY },
-       { KEY_F3,       KEY_MUTE,               APPLE_FLAG_FKEY },
-       { KEY_F4,       KEY_VOLUMEDOWN,         APPLE_FLAG_FKEY },
-       { KEY_F5,       KEY_VOLUMEUP,           APPLE_FLAG_FKEY },
-       { KEY_F6,       KEY_NUMLOCK,            APPLE_FLAG_FKEY },
-       { KEY_F7,       KEY_SWITCHVIDEOMODE,    APPLE_FLAG_FKEY },
-       { KEY_F8,       KEY_KBDILLUMTOGGLE,     APPLE_FLAG_FKEY },
-       { KEY_F9,       KEY_KBDILLUMDOWN,       APPLE_FLAG_FKEY },
-       { KEY_F10,      KEY_KBDILLUMUP,         APPLE_FLAG_FKEY },
-       { KEY_UP,       KEY_PAGEUP },
-       { KEY_DOWN,     KEY_PAGEDOWN },
-       { KEY_LEFT,     KEY_HOME },
-       { KEY_RIGHT,    KEY_END },
-       { }
-};
-
-static struct hidinput_key_translation powerbook_numlock_keys[] = {
-       { KEY_J,        KEY_KP1 },
-       { KEY_K,        KEY_KP2 },
-       { KEY_L,        KEY_KP3 },
-       { KEY_U,        KEY_KP4 },
-       { KEY_I,        KEY_KP5 },
-       { KEY_O,        KEY_KP6 },
-       { KEY_7,        KEY_KP7 },
-       { KEY_8,        KEY_KP8 },
-       { KEY_9,        KEY_KP9 },
-       { KEY_M,        KEY_KP0 },
-       { KEY_DOT,      KEY_KPDOT },
-       { KEY_SLASH,    KEY_KPPLUS },
-       { KEY_SEMICOLON, KEY_KPMINUS },
-       { KEY_P,        KEY_KPASTERISK },
-       { KEY_MINUS,    KEY_KPEQUAL },
-       { KEY_0,        KEY_KPSLASH },
-       { KEY_F6,       KEY_NUMLOCK },
-       { KEY_KPENTER,  KEY_KPENTER },
-       { KEY_BACKSPACE, KEY_BACKSPACE },
-       { }
-};
-
-static struct hidinput_key_translation apple_iso_keyboard[] = {
-       { KEY_GRAVE,    KEY_102ND },
-       { KEY_102ND,    KEY_GRAVE },
-       { }
-};
+#define map_abs(c)     hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c))
+#define map_rel(c)     hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c))
+#define map_key(c)     hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c))
+#define map_led(c)     hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c))
 
-static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from)
-{
-       struct hidinput_key_translation *trans;
-
-       /* Look for the translation */
-       for (trans = table; trans->from; trans++)
-               if (trans->from == from)
-                       return trans;
-
-       return NULL;
-}
-
-int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
-               struct hid_usage *usage, __s32 value)
-{
-       struct hidinput_key_translation *trans;
-
-       if (usage->code == KEY_FN) {
-               if (value) hid->quirks |=  HID_QUIRK_APPLE_FN_ON;
-               else       hid->quirks &= ~HID_QUIRK_APPLE_FN_ON;
-
-               input_event(input, usage->type, usage->code, value);
-
-               return 1;
-       }
-
-       if (hid_apple_fnmode) {
-               int do_translate;
-
-               trans = find_translation((hid->product < 0x220 ||
-                                         hid->product >= 0x300) ?
-                                        powerbook_fn_keys : apple_fn_keys,
-                                        usage->code);
-               if (trans) {
-                       if (test_bit(usage->code, hid->apple_pressed_fn))
-                               do_translate = 1;
-                       else if (trans->flags & APPLE_FLAG_FKEY)
-                               do_translate =
-                                       (hid_apple_fnmode == 2 &&  (hid->quirks & HID_QUIRK_APPLE_FN_ON)) ||
-                                       (hid_apple_fnmode == 1 && !(hid->quirks & HID_QUIRK_APPLE_FN_ON));
-                       else
-                               do_translate = (hid->quirks & HID_QUIRK_APPLE_FN_ON);
-
-                       if (do_translate) {
-                               if (value)
-                                       set_bit(usage->code, hid->apple_pressed_fn);
-                               else
-                                       clear_bit(usage->code, hid->apple_pressed_fn);
-
-                               input_event(input, usage->type, trans->to, value);
-
-                               return 1;
-                       }
-               }
-
-               if (hid->quirks & HID_QUIRK_APPLE_NUMLOCK_EMULATION && (
-                               test_bit(usage->code, hid->pb_pressed_numlock) ||
-                               test_bit(LED_NUML, input->led))) {
-                       trans = find_translation(powerbook_numlock_keys, usage->code);
-
-                       if (trans) {
-                               if (value)
-                                       set_bit(usage->code, hid->pb_pressed_numlock);
-                               else
-                                       clear_bit(usage->code, hid->pb_pressed_numlock);
-
-                               input_event(input, usage->type, trans->to, value);
-                       }
-
-                       return 1;
-               }
-       }
-
-       if (hid->quirks & HID_QUIRK_APPLE_ISO_KEYBOARD) {
-               trans = find_translation(apple_iso_keyboard, usage->code);
-               if (trans) {
-                       input_event(input, usage->type, trans->to, value);
-                       return 1;
-               }
-       }
-
-       return 0;
-}
-
-static void hidinput_apple_setup(struct input_dev *input)
-{
-       struct hidinput_key_translation *trans;
-
-       set_bit(KEY_NUMLOCK, input->keybit);
-
-       /* Enable all needed keys */
-       for (trans = apple_fn_keys; trans->from; trans++)
-               set_bit(trans->to, input->keybit);
-
-       for (trans = powerbook_fn_keys; trans->from; trans++)
-               set_bit(trans->to, input->keybit);
-
-       for (trans = powerbook_numlock_keys; trans->from; trans++)
-               set_bit(trans->to, input->keybit);
-
-       for (trans = apple_iso_keyboard; trans->from; trans++)
-               set_bit(trans->to, input->keybit);
-
-}
-#else
-inline int hidinput_apple_event(struct hid_device *hid,
-                                      struct input_dev *input,
-                                      struct hid_usage *usage, __s32 value)
-{
-       return 0;
-}
-
-static inline void hidinput_apple_setup(struct input_dev *input)
-{
-}
-#endif
+#define map_abs_clear(c)       hid_map_usage_clear(hidinput, usage, &bit, \
+               &max, EV_ABS, (c))
+#define map_key_clear(c)       hid_map_usage_clear(hidinput, usage, &bit, \
+               &max, EV_KEY, (c))
 
 static inline int match_scancode(int code, int scancode)
 {
@@ -366,7 +154,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
 {
        struct input_dev *input = hidinput->input;
        struct hid_device *device = input_get_drvdata(input);
-       int max = 0, code, ret;
+       int max = 0, code;
        unsigned long *bit = NULL;
 
        field->hidinput = hidinput;
@@ -385,406 +173,345 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                goto ignore;
        }
 
-       /* handle input mappings for quirky devices */
-       ret = hidinput_mapping_quirks(usage, input, &bit, &max);
-       if (ret)
-               goto mapped;
+       if (device->driver->input_mapping) {
+               int ret = device->driver->input_mapping(device, hidinput, field,
+                               usage, &bit, &max);
+               if (ret > 0)
+                       goto mapped;
+               if (ret < 0)
+                       goto ignore;
+       }
 
        switch (usage->hid & HID_USAGE_PAGE) {
+       case HID_UP_UNDEFINED:
+               goto ignore;
 
-               case HID_UP_UNDEFINED:
-                       goto ignore;
-
-               case HID_UP_KEYBOARD:
+       case HID_UP_KEYBOARD:
+               set_bit(EV_REP, input->evbit);
 
-                       set_bit(EV_REP, input->evbit);
+               if ((usage->hid & HID_USAGE) < 256) {
+                       if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
+                       map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);
+               } else
+                       map_key(KEY_UNKNOWN);
 
-                       if ((usage->hid & HID_USAGE) < 256) {
-                               if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
-                               map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);
-                       } else
-                               map_key(KEY_UNKNOWN);
+               break;
 
-                       break;
+       case HID_UP_BUTTON:
+               code = ((usage->hid - 1) & 0xf);
 
-               case HID_UP_BUTTON:
-
-                       code = ((usage->hid - 1) & 0xf);
-
-                       switch (field->application) {
-                               case HID_GD_MOUSE:
-                               case HID_GD_POINTER:  code += 0x110; break;
-                               case HID_GD_JOYSTICK: code += 0x120; break;
-                               case HID_GD_GAMEPAD:  code += 0x130; break;
-                               default:
-                                       switch (field->physical) {
-                                               case HID_GD_MOUSE:
-                                               case HID_GD_POINTER:  code += 0x110; break;
-                                               case HID_GD_JOYSTICK: code += 0x120; break;
-                                               case HID_GD_GAMEPAD:  code += 0x130; break;
-                                               default:              code += 0x100;
-                                       }
-                       }
-
-                       /* Special handling for Logitech Cordless Desktop */
-                       if (field->application != HID_GD_MOUSE) {
-                               if (device->quirks & HID_QUIRK_LOGITECH_EXPANDED_KEYMAP) {
-                                       int hid = usage->hid & HID_USAGE;
-                                       if (hid < LOGITECH_EXPANDED_KEYMAP_SIZE && logitech_expanded_keymap[hid] != 0)
-                                               code = logitech_expanded_keymap[hid];
-                               }
-                       } else {
-                               if (device->quirks & HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL) {
-                                       int hid = usage->hid & HID_USAGE;
-                                       if (hid == 7 || hid == 8)
-                                               goto ignore;
-                               }
+               switch (field->application) {
+               case HID_GD_MOUSE:
+               case HID_GD_POINTER:  code += 0x110; break;
+               case HID_GD_JOYSTICK: code += 0x120; break;
+               case HID_GD_GAMEPAD:  code += 0x130; break;
+               default:
+                       switch (field->physical) {
+                       case HID_GD_MOUSE:
+                       case HID_GD_POINTER:  code += 0x110; break;
+                       case HID_GD_JOYSTICK: code += 0x120; break;
+                       case HID_GD_GAMEPAD:  code += 0x130; break;
+                       default:              code += 0x100;
                        }
+               }
 
-                       map_key(code);
-                       break;
-
-
-               case HID_UP_SIMULATION:
-
-                       switch (usage->hid & 0xffff) {
-                               case 0xba: map_abs(ABS_RUDDER);   break;
-                               case 0xbb: map_abs(ABS_THROTTLE); break;
-                               case 0xc4: map_abs(ABS_GAS);      break;
-                               case 0xc5: map_abs(ABS_BRAKE);    break;
-                               case 0xc8: map_abs(ABS_WHEEL);    break;
-                               default:   goto ignore;
+               map_key(code);
+               break;
+
+       case HID_UP_SIMULATION:
+               switch (usage->hid & 0xffff) {
+               case 0xba: map_abs(ABS_RUDDER);   break;
+               case 0xbb: map_abs(ABS_THROTTLE); break;
+               case 0xc4: map_abs(ABS_GAS);      break;
+               case 0xc5: map_abs(ABS_BRAKE);    break;
+               case 0xc8: map_abs(ABS_WHEEL);    break;
+               default:   goto ignore;
+               }
+               break;
+
+       case HID_UP_GENDESK:
+               if ((usage->hid & 0xf0) == 0x80) {      /* SystemControl */
+                       switch (usage->hid & 0xf) {
+                       case 0x1: map_key_clear(KEY_POWER);  break;
+                       case 0x2: map_key_clear(KEY_SLEEP);  break;
+                       case 0x3: map_key_clear(KEY_WAKEUP); break;
+                       default: goto unknown;
                        }
                        break;
+               }
 
-               case HID_UP_GENDESK:
-
-                       if ((usage->hid & 0xf0) == 0x80) {      /* SystemControl */
-                               switch (usage->hid & 0xf) {
-                                       case 0x1: map_key_clear(KEY_POWER);  break;
-                                       case 0x2: map_key_clear(KEY_SLEEP);  break;
-                                       case 0x3: map_key_clear(KEY_WAKEUP); break;
-                                       default: goto unknown;
-                               }
-                               break;
-                       }
-
-                       if ((usage->hid & 0xf0) == 0x90) {      /* D-pad */
-                               switch (usage->hid) {
-                                       case HID_GD_UP:    usage->hat_dir = 1; break;
-                                       case HID_GD_DOWN:  usage->hat_dir = 5; break;
-                                       case HID_GD_RIGHT: usage->hat_dir = 3; break;
-                                       case HID_GD_LEFT:  usage->hat_dir = 7; break;
-                                       default: goto unknown;
-                               }
-                               if (field->dpad) {
-                                       map_abs(field->dpad);
-                                       goto ignore;
-                               }
-                               map_abs(ABS_HAT0X);
-                               break;
-                       }
-
+               if ((usage->hid & 0xf0) == 0x90) {      /* D-pad */
                        switch (usage->hid) {
-
-                               /* These usage IDs map directly to the usage codes. */
-                               case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
-                               case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
-                               case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
-                                       if (field->flags & HID_MAIN_ITEM_RELATIVE)
-                                               map_rel(usage->hid & 0xf);
-                                       else
-                                               map_abs(usage->hid & 0xf);
-                                       break;
-
-                               case HID_GD_HATSWITCH:
-                                       usage->hat_min = field->logical_minimum;
-                                       usage->hat_max = field->logical_maximum;
-                                       map_abs(ABS_HAT0X);
-                                       break;
-
-                               case HID_GD_START:      map_key_clear(BTN_START);       break;
-                               case HID_GD_SELECT:     map_key_clear(BTN_SELECT);      break;
-
-                               default: goto unknown;
+                       case HID_GD_UP:    usage->hat_dir = 1; break;
+                       case HID_GD_DOWN:  usage->hat_dir = 5; break;
+                       case HID_GD_RIGHT: usage->hat_dir = 3; break;
+                       case HID_GD_LEFT:  usage->hat_dir = 7; break;
+                       default: goto unknown;
                        }
-
-                       break;
-
-               case HID_UP_LED:
-
-                       switch (usage->hid & 0xffff) {                        /* HID-Value:                   */
-                               case 0x01:  map_led (LED_NUML);     break;    /*   "Num Lock"                 */
-                               case 0x02:  map_led (LED_CAPSL);    break;    /*   "Caps Lock"                */
-                               case 0x03:  map_led (LED_SCROLLL);  break;    /*   "Scroll Lock"              */
-                               case 0x04:  map_led (LED_COMPOSE);  break;    /*   "Compose"                  */
-                               case 0x05:  map_led (LED_KANA);     break;    /*   "Kana"                     */
-                               case 0x27:  map_led (LED_SLEEP);    break;    /*   "Stand-By"                 */
-                               case 0x4c:  map_led (LED_SUSPEND);  break;    /*   "System Suspend"           */
-                               case 0x09:  map_led (LED_MUTE);     break;    /*   "Mute"                     */
-                               case 0x4b:  map_led (LED_MISC);     break;    /*   "Generic Indicator"        */
-                               case 0x19:  map_led (LED_MAIL);     break;    /*   "Message Waiting"          */
-                               case 0x4d:  map_led (LED_CHARGING); break;    /*   "External Power Connected" */
-
-                               default: goto ignore;
+                       if (field->dpad) {
+                               map_abs(field->dpad);
+                               goto ignore;
                        }
+                       map_abs(ABS_HAT0X);
                        break;
+               }
 
-               case HID_UP_DIGITIZER:
-
-                       switch (usage->hid & 0xff) {
-
-                               case 0x30: /* TipPressure */
-                                       if (!test_bit(BTN_TOUCH, input->keybit)) {
-                                               device->quirks |= HID_QUIRK_NOTOUCH;
-                                               set_bit(EV_KEY, input->evbit);
-                                               set_bit(BTN_TOUCH, input->keybit);
-                                       }
-
-                                       map_abs_clear(ABS_PRESSURE);
-                                       break;
-
-                               case 0x32: /* InRange */
-                                       switch (field->physical & 0xff) {
-                                               case 0x21: map_key(BTN_TOOL_MOUSE); break;
-                                               case 0x22: map_key(BTN_TOOL_FINGER); break;
-                                               default: map_key(BTN_TOOL_PEN); break;
-                                       }
-                                       break;
+               switch (usage->hid) {
+               /* These usage IDs map directly to the usage codes. */
+               case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
+               case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
+               case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
+                       if (field->flags & HID_MAIN_ITEM_RELATIVE)
+                               map_rel(usage->hid & 0xf);
+                       else
+                               map_abs(usage->hid & 0xf);
+                       break;
 
-                               case 0x3c: /* Invert */
-                                       map_key_clear(BTN_TOOL_RUBBER);
-                                       break;
+               case HID_GD_HATSWITCH:
+                       usage->hat_min = field->logical_minimum;
+                       usage->hat_max = field->logical_maximum;
+                       map_abs(ABS_HAT0X);
+                       break;
 
-                               case 0x33: /* Touch */
-                               case 0x42: /* TipSwitch */
-                               case 0x43: /* TipSwitch2 */
-                                       device->quirks &= ~HID_QUIRK_NOTOUCH;
-                                       map_key_clear(BTN_TOUCH);
-                                       break;
+               case HID_GD_START:      map_key_clear(BTN_START);       break;
+               case HID_GD_SELECT:     map_key_clear(BTN_SELECT);      break;
 
-                               case 0x44: /* BarrelSwitch */
-                                       map_key_clear(BTN_STYLUS);
-                                       break;
+               default: goto unknown;
+               }
 
-                               default:  goto unknown;
+               break;
+
+       case HID_UP_LED:
+               switch (usage->hid & 0xffff) {                /* HID-Value:                   */
+               case 0x01:  map_led (LED_NUML);     break;    /*   "Num Lock"                 */
+               case 0x02:  map_led (LED_CAPSL);    break;    /*   "Caps Lock"                */
+               case 0x03:  map_led (LED_SCROLLL);  break;    /*   "Scroll Lock"              */
+               case 0x04:  map_led (LED_COMPOSE);  break;    /*   "Compose"                  */
+               case 0x05:  map_led (LED_KANA);     break;    /*   "Kana"                     */
+               case 0x27:  map_led (LED_SLEEP);    break;    /*   "Stand-By"                 */
+               case 0x4c:  map_led (LED_SUSPEND);  break;    /*   "System Suspend"           */
+               case 0x09:  map_led (LED_MUTE);     break;    /*   "Mute"                     */
+               case 0x4b:  map_led (LED_MISC);     break;    /*   "Generic Indicator"        */
+               case 0x19:  map_led (LED_MAIL);     break;    /*   "Message Waiting"          */
+               case 0x4d:  map_led (LED_CHARGING); break;    /*   "External Power Connected" */
+
+               default: goto ignore;
+               }
+               break;
+
+       case HID_UP_DIGITIZER:
+               switch (usage->hid & 0xff) {
+               case 0x30: /* TipPressure */
+                       if (!test_bit(BTN_TOUCH, input->keybit)) {
+                               device->quirks |= HID_QUIRK_NOTOUCH;
+                               set_bit(EV_KEY, input->evbit);
+                               set_bit(BTN_TOUCH, input->keybit);
                        }
+                       map_abs_clear(ABS_PRESSURE);
                        break;
 
-               case HID_UP_CONSUMER:   /* USB HUT v1.1, pages 56-62 */
-
-                       switch (usage->hid & HID_USAGE) {
-                               case 0x000: goto ignore;
-                               case 0x034: map_key_clear(KEY_SLEEP);           break;
-                               case 0x036: map_key_clear(BTN_MISC);            break;
-
-                               case 0x040: map_key_clear(KEY_MENU);            break;
-                               case 0x045: map_key_clear(KEY_RADIO);           break;
-
-                               case 0x083: map_key_clear(KEY_LAST);            break;
-                               case 0x088: map_key_clear(KEY_PC);              break;
-                               case 0x089: map_key_clear(KEY_TV);              break;
-                               case 0x08a: map_key_clear(KEY_WWW);             break;
-                               case 0x08b: map_key_clear(KEY_DVD);             break;
-                               case 0x08c: map_key_clear(KEY_PHONE);           break;
-                               case 0x08d: map_key_clear(KEY_PROGRAM);         break;
-                               case 0x08e: map_key_clear(KEY_VIDEOPHONE);      break;
-                               case 0x08f: map_key_clear(KEY_GAMES);           break;
-                               case 0x090: map_key_clear(KEY_MEMO);            break;
-                               case 0x091: map_key_clear(KEY_CD);              break;
-                               case 0x092: map_key_clear(KEY_VCR);             break;
-                               case 0x093: map_key_clear(KEY_TUNER);           break;
-                               case 0x094: map_key_clear(KEY_EXIT);            break;
-                               case 0x095: map_key_clear(KEY_HELP);            break;
-                               case 0x096: map_key_clear(KEY_TAPE);            break;
-                               case 0x097: map_key_clear(KEY_TV2);             break;
-                               case 0x098: map_key_clear(KEY_SAT);             break;
-                               case 0x09a: map_key_clear(KEY_PVR);             break;
-
-                               case 0x09c: map_key_clear(KEY_CHANNELUP);       break;
-                               case 0x09d: map_key_clear(KEY_CHANNELDOWN);     break;
-                               case 0x0a0: map_key_clear(KEY_VCR2);            break;
-
-                               case 0x0b0: map_key_clear(KEY_PLAY);            break;
-                               case 0x0b1: map_key_clear(KEY_PAUSE);           break;
-                               case 0x0b2: map_key_clear(KEY_RECORD);          break;
-                               case 0x0b3: map_key_clear(KEY_FASTFORWARD);     break;
-                               case 0x0b4: map_key_clear(KEY_REWIND);          break;
-                               case 0x0b5: map_key_clear(KEY_NEXTSONG);        break;
-                               case 0x0b6: map_key_clear(KEY_PREVIOUSSONG);    break;
-                               case 0x0b7: map_key_clear(KEY_STOPCD);          break;
-                               case 0x0b8: map_key_clear(KEY_EJECTCD);         break;
-                               case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT);    break;
-
-                               case 0x0cd: map_key_clear(KEY_PLAYPAUSE);       break;
-                               case 0x0e0: map_abs_clear(ABS_VOLUME);          break;
-                               case 0x0e2: map_key_clear(KEY_MUTE);            break;
-                               case 0x0e5: map_key_clear(KEY_BASSBOOST);       break;
-                               case 0x0e9: map_key_clear(KEY_VOLUMEUP);        break;
-                               case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);      break;
-
-                               case 0x182: map_key_clear(KEY_BOOKMARKS);       break;
-                               case 0x183: map_key_clear(KEY_CONFIG);          break;
-                               case 0x184: map_key_clear(KEY_WORDPROCESSOR);   break;
-                               case 0x185: map_key_clear(KEY_EDITOR);          break;
-                               case 0x186: map_key_clear(KEY_SPREADSHEET);     break;
-                               case 0x187: map_key_clear(KEY_GRAPHICSEDITOR);  break;
-                               case 0x188: map_key_clear(KEY_PRESENTATION);    break;
-                               case 0x189: map_key_clear(KEY_DATABASE);        break;
-                               case 0x18a: map_key_clear(KEY_MAIL);            break;
-                               case 0x18b: map_key_clear(KEY_NEWS);            break;
-                               case 0x18c: map_key_clear(KEY_VOICEMAIL);       break;
-                               case 0x18d: map_key_clear(KEY_ADDRESSBOOK);     break;
-                               case 0x18e: map_key_clear(KEY_CALENDAR);        break;
-                               case 0x191: map_key_clear(KEY_FINANCE);         break;
-                               case 0x192: map_key_clear(KEY_CALC);            break;
-                               case 0x194: map_key_clear(KEY_FILE);            break;
-                               case 0x196: map_key_clear(KEY_WWW);             break;
-                               case 0x19c: map_key_clear(KEY_LOGOFF);          break;
-                               case 0x19e: map_key_clear(KEY_COFFEE);          break;
-                               case 0x1a6: map_key_clear(KEY_HELP);            break;
-                               case 0x1a7: map_key_clear(KEY_DOCUMENTS);       break;
-                               case 0x1ab: map_key_clear(KEY_SPELLCHECK);      break;
-                               case 0x1b6: map_key_clear(KEY_MEDIA);           break;
-                               case 0x1b7: map_key_clear(KEY_SOUND);           break;
-                               case 0x1bc: map_key_clear(KEY_MESSENGER);       break;
-                               case 0x1bd: map_key_clear(KEY_INFO);            break;
-                               case 0x201: map_key_clear(KEY_NEW);             break;
-                               case 0x202: map_key_clear(KEY_OPEN);            break;
-                               case 0x203: map_key_clear(KEY_CLOSE);           break;
-                               case 0x204: map_key_clear(KEY_EXIT);            break;
-                               case 0x207: map_key_clear(KEY_SAVE);            break;
-                               case 0x208: map_key_clear(KEY_PRINT);           break;
-                               case 0x209: map_key_clear(KEY_PROPS);           break;
-                               case 0x21a: map_key_clear(KEY_UNDO);            break;
-                               case 0x21b: map_key_clear(KEY_COPY);            break;
-                               case 0x21c: map_key_clear(KEY_CUT);             break;
-                               case 0x21d: map_key_clear(KEY_PASTE);           break;
-                               case 0x21f: map_key_clear(KEY_FIND);            break;
-                               case 0x221: map_key_clear(KEY_SEARCH);          break;
-                               case 0x222: map_key_clear(KEY_GOTO);            break;
-                               case 0x223: map_key_clear(KEY_HOMEPAGE);        break;
-                               case 0x224: map_key_clear(KEY_BACK);            break;
-                               case 0x225: map_key_clear(KEY_FORWARD);         break;
-                               case 0x226: map_key_clear(KEY_STOP);            break;
-                               case 0x227: map_key_clear(KEY_REFRESH);         break;
-                               case 0x22a: map_key_clear(KEY_BOOKMARKS);       break;
-                               case 0x22d: map_key_clear(KEY_ZOOMIN);          break;
-                               case 0x22e: map_key_clear(KEY_ZOOMOUT);         break;
-                               case 0x22f: map_key_clear(KEY_ZOOMRESET);       break;
-                               case 0x233: map_key_clear(KEY_SCROLLUP);        break;
-                               case 0x234: map_key_clear(KEY_SCROLLDOWN);      break;
-                               case 0x238: map_rel(REL_HWHEEL);                break;
-                               case 0x25f: map_key_clear(KEY_CANCEL);          break;
-                               case 0x279: map_key_clear(KEY_REDO);            break;
-
-                               case 0x289: map_key_clear(KEY_REPLY);           break;
-                               case 0x28b: map_key_clear(KEY_FORWARDMAIL);     break;
-                               case 0x28c: map_key_clear(KEY_SEND);            break;
-
-                               default:    goto ignore;
+               case 0x32: /* InRange */
+                       switch (field->physical & 0xff) {
+                       case 0x21: map_key(BTN_TOOL_MOUSE); break;
+                       case 0x22: map_key(BTN_TOOL_FINGER); break;
+                       default: map_key(BTN_TOOL_PEN); break;
                        }
                        break;
 
-               case HID_UP_HPVENDOR:   /* Reported on a Dutch layout HP5308 */
-
-                       set_bit(EV_REP, input->evbit);
-                       switch (usage->hid & HID_USAGE) {
-                               case 0x021: map_key_clear(KEY_PRINT);           break;
-                               case 0x070: map_key_clear(KEY_HP);              break;
-                               case 0x071: map_key_clear(KEY_CAMERA);          break;
-                               case 0x072: map_key_clear(KEY_SOUND);           break;
-                               case 0x073: map_key_clear(KEY_QUESTION);        break;
-                               case 0x080: map_key_clear(KEY_EMAIL);           break;
-                               case 0x081: map_key_clear(KEY_CHAT);            break;
-                               case 0x082: map_key_clear(KEY_SEARCH);          break;
-                               case 0x083: map_key_clear(KEY_CONNECT);         break;
-                               case 0x084: map_key_clear(KEY_FINANCE);         break;
-                               case 0x085: map_key_clear(KEY_SPORT);           break;
-                               case 0x086: map_key_clear(KEY_SHOP);            break;
-                               default:    goto ignore;
-                       }
+               case 0x3c: /* Invert */
+                       map_key_clear(BTN_TOOL_RUBBER);
                        break;
 
-               case HID_UP_MSVENDOR:
-
-                       goto ignore;
-
-               case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */
-
-                       set_bit(EV_REP, input->evbit);
-                       switch(usage->hid & HID_USAGE) {
-                               case 0x003:
-                                       /* The fn key on Apple USB keyboards */
-                                       map_key_clear(KEY_FN);
-                                       hidinput_apple_setup(input);
-                                       break;
+               case 0x33: /* Touch */
+               case 0x42: /* TipSwitch */
+               case 0x43: /* TipSwitch2 */
+                       device->quirks &= ~HID_QUIRK_NOTOUCH;
+                       map_key_clear(BTN_TOUCH);
+                       break;
 
-                               default:    goto ignore;
-                       }
+               case 0x44: /* BarrelSwitch */
+                       map_key_clear(BTN_STYLUS);
                        break;
 
-               case HID_UP_LOGIVENDOR:
+               default:  goto unknown;
+               }
+               break;
+
+       case HID_UP_CONSUMER:   /* USB HUT v1.1, pages 56-62 */
+               switch (usage->hid & HID_USAGE) {
+               case 0x000: goto ignore;
+               case 0x034: map_key_clear(KEY_SLEEP);           break;
+               case 0x036: map_key_clear(BTN_MISC);            break;
+
+               case 0x040: map_key_clear(KEY_MENU);            break;
+               case 0x045: map_key_clear(KEY_RADIO);           break;
+
+               case 0x083: map_key_clear(KEY_LAST);            break;
+               case 0x088: map_key_clear(KEY_PC);              break;
+               case 0x089: map_key_clear(KEY_TV);              break;
+               case 0x08a: map_key_clear(KEY_WWW);             break;
+               case 0x08b: map_key_clear(KEY_DVD);             break;
+               case 0x08c: map_key_clear(KEY_PHONE);           break;
+               case 0x08d: map_key_clear(KEY_PROGRAM);         break;
+               case 0x08e: map_key_clear(KEY_VIDEOPHONE);      break;
+               case 0x08f: map_key_clear(KEY_GAMES);           break;
+               case 0x090: map_key_clear(KEY_MEMO);            break;
+               case 0x091: map_key_clear(KEY_CD);              break;
+               case 0x092: map_key_clear(KEY_VCR);             break;
+               case 0x093: map_key_clear(KEY_TUNER);           break;
+               case 0x094: map_key_clear(KEY_EXIT);            break;
+               case 0x095: map_key_clear(KEY_HELP);            break;
+               case 0x096: map_key_clear(KEY_TAPE);            break;
+               case 0x097: map_key_clear(KEY_TV2);             break;
+               case 0x098: map_key_clear(KEY_SAT);             break;
+               case 0x09a: map_key_clear(KEY_PVR);             break;
+
+               case 0x09c: map_key_clear(KEY_CHANNELUP);       break;
+               case 0x09d: map_key_clear(KEY_CHANNELDOWN);     break;
+               case 0x0a0: map_key_clear(KEY_VCR2);            break;
+
+               case 0x0b0: map_key_clear(KEY_PLAY);            break;
+               case 0x0b1: map_key_clear(KEY_PAUSE);           break;
+               case 0x0b2: map_key_clear(KEY_RECORD);          break;
+               case 0x0b3: map_key_clear(KEY_FASTFORWARD);     break;
+               case 0x0b4: map_key_clear(KEY_REWIND);          break;
+               case 0x0b5: map_key_clear(KEY_NEXTSONG);        break;
+               case 0x0b6: map_key_clear(KEY_PREVIOUSSONG);    break;
+               case 0x0b7: map_key_clear(KEY_STOPCD);          break;
+               case 0x0b8: map_key_clear(KEY_EJECTCD);         break;
+               case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT);    break;
+
+               case 0x0cd: map_key_clear(KEY_PLAYPAUSE);       break;
+               case 0x0e0: map_abs_clear(ABS_VOLUME);          break;
+               case 0x0e2: map_key_clear(KEY_MUTE);            break;
+               case 0x0e5: map_key_clear(KEY_BASSBOOST);       break;
+               case 0x0e9: map_key_clear(KEY_VOLUMEUP);        break;
+               case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);      break;
+
+               case 0x182: map_key_clear(KEY_BOOKMARKS);       break;
+               case 0x183: map_key_clear(KEY_CONFIG);          break;
+               case 0x184: map_key_clear(KEY_WORDPROCESSOR);   break;
+               case 0x185: map_key_clear(KEY_EDITOR);          break;
+               case 0x186: map_key_clear(KEY_SPREADSHEET);     break;
+               case 0x187: map_key_clear(KEY_GRAPHICSEDITOR);  break;
+               case 0x188: map_key_clear(KEY_PRESENTATION);    break;
+               case 0x189: map_key_clear(KEY_DATABASE);        break;
+               case 0x18a: map_key_clear(KEY_MAIL);            break;
+               case 0x18b: map_key_clear(KEY_NEWS);            break;
+               case 0x18c: map_key_clear(KEY_VOICEMAIL);       break;
+               case 0x18d: map_key_clear(KEY_ADDRESSBOOK);     break;
+               case 0x18e: map_key_clear(KEY_CALENDAR);        break;
+               case 0x191: map_key_clear(KEY_FINANCE);         break;
+               case 0x192: map_key_clear(KEY_CALC);            break;
+               case 0x194: map_key_clear(KEY_FILE);            break;
+               case 0x196: map_key_clear(KEY_WWW);             break;
+               case 0x19c: map_key_clear(KEY_LOGOFF);          break;
+               case 0x19e: map_key_clear(KEY_COFFEE);          break;
+               case 0x1a6: map_key_clear(KEY_HELP);            break;
+               case 0x1a7: map_key_clear(KEY_DOCUMENTS);       break;
+               case 0x1ab: map_key_clear(KEY_SPELLCHECK);      break;
+               case 0x1b6: map_key_clear(KEY_MEDIA);           break;
+               case 0x1b7: map_key_clear(KEY_SOUND);           break;
+               case 0x1bc: map_key_clear(KEY_MESSENGER);       break;
+               case 0x1bd: map_key_clear(KEY_INFO);            break;
+               case 0x201: map_key_clear(KEY_NEW);             break;
+               case 0x202: map_key_clear(KEY_OPEN);            break;
+               case 0x203: map_key_clear(KEY_CLOSE);           break;
+               case 0x204: map_key_clear(KEY_EXIT);            break;
+               case 0x207: map_key_clear(KEY_SAVE);            break;
+               case 0x208: map_key_clear(KEY_PRINT);           break;
+               case 0x209: map_key_clear(KEY_PROPS);           break;
+               case 0x21a: map_key_clear(KEY_UNDO);            break;
+               case 0x21b: map_key_clear(KEY_COPY);            break;
+               case 0x21c: map_key_clear(KEY_CUT);             break;
+               case 0x21d: map_key_clear(KEY_PASTE);           break;
+               case 0x21f: map_key_clear(KEY_FIND);            break;
+               case 0x221: map_key_clear(KEY_SEARCH);          break;
+               case 0x222: map_key_clear(KEY_GOTO);            break;
+               case 0x223: map_key_clear(KEY_HOMEPAGE);        break;
+               case 0x224: map_key_clear(KEY_BACK);            break;
+               case 0x225: map_key_clear(KEY_FORWARD);         break;
+               case 0x226: map_key_clear(KEY_STOP);            break;
+               case 0x227: map_key_clear(KEY_REFRESH);         break;
+               case 0x22a: map_key_clear(KEY_BOOKMARKS);       break;
+               case 0x22d: map_key_clear(KEY_ZOOMIN);          break;
+               case 0x22e: map_key_clear(KEY_ZOOMOUT);         break;
+               case 0x22f: map_key_clear(KEY_ZOOMRESET);       break;
+               case 0x233: map_key_clear(KEY_SCROLLUP);        break;
+               case 0x234: map_key_clear(KEY_SCROLLDOWN);      break;
+               case 0x238: map_rel(REL_HWHEEL);                break;
+               case 0x25f: map_key_clear(KEY_CANCEL);          break;
+               case 0x279: map_key_clear(KEY_REDO);            break;
+
+               case 0x289: map_key_clear(KEY_REPLY);           break;
+               case 0x28b: map_key_clear(KEY_FORWARDMAIL);     break;
+               case 0x28c: map_key_clear(KEY_SEND);            break;
+
+               default:    goto ignore;
+               }
+               break;
+
+       case HID_UP_HPVENDOR:   /* Reported on a Dutch layout HP5308 */
+               set_bit(EV_REP, input->evbit);
+               switch (usage->hid & HID_USAGE) {
+               case 0x021: map_key_clear(KEY_PRINT);           break;
+               case 0x070: map_key_clear(KEY_HP);              break;
+               case 0x071: map_key_clear(KEY_CAMERA);          break;
+               case 0x072: map_key_clear(KEY_SOUND);           break;
+               case 0x073: map_key_clear(KEY_QUESTION);        break;
+               case 0x080: map_key_clear(KEY_EMAIL);           break;
+               case 0x081: map_key_clear(KEY_CHAT);            break;
+               case 0x082: map_key_clear(KEY_SEARCH);          break;
+               case 0x083: map_key_clear(KEY_CONNECT);         break;
+               case 0x084: map_key_clear(KEY_FINANCE);         break;
+               case 0x085: map_key_clear(KEY_SPORT);           break;
+               case 0x086: map_key_clear(KEY_SHOP);            break;
+               default:    goto ignore;
+               }
+               break;
 
-                       goto ignore;
-               
-               case HID_UP_PID:
+       case HID_UP_MSVENDOR:
+               goto ignore;
 
-                       switch(usage->hid & HID_USAGE) {
-                               case 0xa4: map_key_clear(BTN_DEAD);     break;
-                               default: goto ignore;
-                       }
-                       break;
+       case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */
+               set_bit(EV_REP, input->evbit);
+               goto ignore;
 
-               default:
-               unknown:
-                       if (field->report_size == 1) {
-                               if (field->report->type == HID_OUTPUT_REPORT) {
-                                       map_led(LED_MISC);
-                                       break;
-                               }
-                               map_key(BTN_MISC);
-                               break;
-                       }
-                       if (field->flags & HID_MAIN_ITEM_RELATIVE) {
-                               map_rel(REL_MISC);
+       case HID_UP_LOGIVENDOR:
+               goto ignore;
+       
+       case HID_UP_PID:
+               switch (usage->hid & HID_USAGE) {
+               case 0xa4: map_key_clear(BTN_DEAD);     break;
+               default: goto ignore;
+               }
+               break;
+
+       default:
+       unknown:
+               if (field->report_size == 1) {
+                       if (field->report->type == HID_OUTPUT_REPORT) {
+                               map_led(LED_MISC);
                                break;
                        }
-                       map_abs(ABS_MISC);
+                       map_key(BTN_MISC);
+                       break;
+               }
+               if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+                       map_rel(REL_MISC);
                        break;
+               }
+               map_abs(ABS_MISC);
+               break;
        }
 
 mapped:
-       if (device->quirks & HID_QUIRK_MIGHTYMOUSE) {
-               if (usage->hid == HID_GD_Z)
-                       map_rel(REL_HWHEEL);
-               else if (usage->code == BTN_1)
-                       map_key(BTN_2);
-               else if (usage->code == BTN_2)
-                       map_key(BTN_1);
-       }
-
-       if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5 |
-                       HID_QUIRK_2WHEEL_MOUSE_HACK_B8)) && (usage->type == EV_REL) &&
-                       (usage->code == REL_WHEEL))
-               set_bit(REL_HWHEEL, bit);
-
-       if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
-               || ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007)))
+       if (device->driver->input_mapped && device->driver->input_mapped(device,
+                               hidinput, field, usage, &bit, &max) < 0)
                goto ignore;
 
-       if ((device->quirks & HID_QUIRK_BAD_RELATIVE_KEYS) &&
-               usage->type == EV_KEY && (field->flags & HID_MAIN_ITEM_RELATIVE))
-               field->flags &= ~HID_MAIN_ITEM_RELATIVE;
-
        set_bit(usage->type, input->evbit);
 
-       if (device->quirks & HID_QUIRK_DUPLICATE_USAGES &&
-                       (usage->type == EV_KEY ||
-                        usage->type == EV_REL ||
-                        usage->type == EV_ABS))
-               clear_bit(usage->code, bit);
-
        while (usage->code <= max && test_and_set_bit(usage->code, bit))
                usage->code = find_next_zero_bit(bit, max + 1, usage->code);
 
@@ -858,10 +585,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
        if (!usage->type)
                return;
 
-       /* handle input events for quirky devices */
-       if (hidinput_event_quirks(hid, field, usage, value))
-               return;
-
        if (usage->hat_min < usage->hat_max || usage->hat_dir) {
                int hat_dir = usage->hat_dir;
                if (!hat_dir)
@@ -961,14 +684,14 @@ static int hidinput_open(struct input_dev *dev)
 {
        struct hid_device *hid = input_get_drvdata(dev);
 
-       return hid->hid_open(hid);
+       return hid->ll_driver->open(hid);
 }
 
 static void hidinput_close(struct input_dev *dev)
 {
        struct hid_device *hid = input_get_drvdata(dev);
 
-       hid->hid_close(hid);
+       hid->ll_driver->close(hid);
 }
 
 /*
@@ -977,7 +700,7 @@ static void hidinput_close(struct input_dev *dev)
  * Read all reports and initialize the absolute field values.
  */
 
-int hidinput_connect(struct hid_device *hid)
+int hidinput_connect(struct hid_device *hid, unsigned int force)
 {
        struct hid_report *report;
        struct hid_input *hidinput = NULL;
@@ -985,19 +708,20 @@ int hidinput_connect(struct hid_device *hid)
        int i, j, k;
        int max_report_type = HID_OUTPUT_REPORT;
 
-       if (hid->quirks & HID_QUIRK_IGNORE_HIDINPUT)
-               return -1;
-
        INIT_LIST_HEAD(&hid->inputs);
 
-       for (i = 0; i < hid->maxcollection; i++)
-               if (hid->collection[i].type == HID_COLLECTION_APPLICATION ||
-                   hid->collection[i].type == HID_COLLECTION_PHYSICAL)
-                       if (IS_INPUT_APPLICATION(hid->collection[i].usage))
-                               break;
+       if (!force) {
+               for (i = 0; i < hid->maxcollection; i++) {
+                       struct hid_collection *col = &hid->collection[i];
+                       if (col->type == HID_COLLECTION_APPLICATION ||
+                                       col->type == HID_COLLECTION_PHYSICAL)
+                               if (IS_INPUT_APPLICATION(col->usage))
+                                       break;
+               }
 
-       if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0)
-               return -1;
+               if (i == hid->maxcollection)
+                       return -1;
+       }
 
        if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
                max_report_type = HID_INPUT_REPORT;
@@ -1019,7 +743,8 @@ int hidinput_connect(struct hid_device *hid)
                                }
 
                                input_set_drvdata(input_dev, hid);
-                               input_dev->event = hid->hidinput_input_event;
+                               input_dev->event =
+                                       hid->ll_driver->hidinput_input_event;
                                input_dev->open = hidinput_open;
                                input_dev->close = hidinput_close;
                                input_dev->setkeycode = hidinput_setkeycode;
@@ -1032,7 +757,7 @@ int hidinput_connect(struct hid_device *hid)
                                input_dev->id.vendor  = hid->vendor;
                                input_dev->id.product = hid->product;
                                input_dev->id.version = hid->version;
-                               input_dev->dev.parent = hid->dev;
+                               input_dev->dev.parent = hid->dev.parent;
                                hidinput->input = input_dev;
                                list_add_tail(&hidinput->list, &hid->inputs);
                        }
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
new file mode 100644 (file)
index 0000000..406d8c8
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ *  HID driver for some logitech "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+#include "hid-lg.h"
+
+#define LG_RDESC               0x001
+#define LG_BAD_RELATIVE_KEYS   0x002
+#define LG_DUPLICATE_USAGES    0x004
+#define LG_RESET_LEDS          0x008
+#define LG_EXPANDED_KEYMAP     0x010
+#define LG_IGNORE_DOUBLED_WHEEL        0x020
+#define LG_WIRELESS            0x040
+#define LG_INVERT_HWHEEL       0x080
+#define LG_NOGET               0x100
+#define LG_FF                  0x200
+#define LG_FF2                 0x400
+
+/*
+ * Certain Logitech keyboards send in report #3 keys which are far
+ * above the logical maximum described in descriptor. This extends
+ * the original value of 0x28c of logical maximum to 0x104d
+ */
+static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 &&
+                       rdesc[84] == 0x8c && rdesc[85] == 0x02) {
+               dev_info(&hdev->dev, "fixing up Logitech keyboard report "
+                               "descriptor\n");
+               rdesc[84] = rdesc[89] = 0x4d;
+               rdesc[85] = rdesc[90] = 0x10;
+       }
+}
+
+#define lg_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+               EV_KEY, (c))
+
+static int lg_ultrax_remote_mapping(struct hid_input *hi,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
+               return 0;
+
+       set_bit(EV_REP, hi->input->evbit);
+       switch (usage->hid & HID_USAGE) {
+       /* Reported on Logitech Ultra X Media Remote */
+       case 0x004: lg_map_key_clear(KEY_AGAIN);        break;
+       case 0x00d: lg_map_key_clear(KEY_HOME);         break;
+       case 0x024: lg_map_key_clear(KEY_SHUFFLE);      break;
+       case 0x025: lg_map_key_clear(KEY_TV);           break;
+       case 0x026: lg_map_key_clear(KEY_MENU);         break;
+       case 0x031: lg_map_key_clear(KEY_AUDIO);        break;
+       case 0x032: lg_map_key_clear(KEY_TEXT);         break;
+       case 0x033: lg_map_key_clear(KEY_LAST);         break;
+       case 0x047: lg_map_key_clear(KEY_MP3);          break;
+       case 0x048: lg_map_key_clear(KEY_DVD);          break;
+       case 0x049: lg_map_key_clear(KEY_MEDIA);        break;
+       case 0x04a: lg_map_key_clear(KEY_VIDEO);        break;
+       case 0x04b: lg_map_key_clear(KEY_ANGLE);        break;
+       case 0x04c: lg_map_key_clear(KEY_LANGUAGE);     break;
+       case 0x04d: lg_map_key_clear(KEY_SUBTITLE);     break;
+       case 0x051: lg_map_key_clear(KEY_RED);          break;
+       case 0x052: lg_map_key_clear(KEY_CLOSE);        break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x1001: lg_map_key_clear(KEY_MESSENGER);           break;
+       case 0x1003: lg_map_key_clear(KEY_SOUND);               break;
+       case 0x1004: lg_map_key_clear(KEY_VIDEO);               break;
+       case 0x1005: lg_map_key_clear(KEY_AUDIO);               break;
+       case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);           break;
+       case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);        break;
+       case 0x1012: lg_map_key_clear(KEY_NEXTSONG);            break;
+       case 0x1013: lg_map_key_clear(KEY_CAMERA);              break;
+       case 0x1014: lg_map_key_clear(KEY_MESSENGER);           break;
+       case 0x1015: lg_map_key_clear(KEY_RECORD);              break;
+       case 0x1016: lg_map_key_clear(KEY_PLAYER);              break;
+       case 0x1017: lg_map_key_clear(KEY_EJECTCD);             break;
+       case 0x1018: lg_map_key_clear(KEY_MEDIA);               break;
+       case 0x1019: lg_map_key_clear(KEY_PROG1);               break;
+       case 0x101a: lg_map_key_clear(KEY_PROG2);               break;
+       case 0x101b: lg_map_key_clear(KEY_PROG3);               break;
+       case 0x101f: lg_map_key_clear(KEY_ZOOMIN);              break;
+       case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);             break;
+       case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);           break;
+       case 0x1023: lg_map_key_clear(KEY_CLOSE);               break;
+       case 0x1027: lg_map_key_clear(KEY_MENU);                break;
+       /* this one is marked as 'Rotate' */
+       case 0x1028: lg_map_key_clear(KEY_ANGLE);               break;
+       case 0x1029: lg_map_key_clear(KEY_SHUFFLE);             break;
+       case 0x102a: lg_map_key_clear(KEY_BACK);                break;
+       case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
+       case 0x1041: lg_map_key_clear(KEY_BATTERY);             break;
+       case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);       break;
+       case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);         break;
+       case 0x1044: lg_map_key_clear(KEY_PRESENTATION);        break;
+       case 0x1045: lg_map_key_clear(KEY_UNDO);                break;
+       case 0x1046: lg_map_key_clear(KEY_REDO);                break;
+       case 0x1047: lg_map_key_clear(KEY_PRINT);               break;
+       case 0x1048: lg_map_key_clear(KEY_SAVE);                break;
+       case 0x1049: lg_map_key_clear(KEY_PROG1);               break;
+       case 0x104a: lg_map_key_clear(KEY_PROG2);               break;
+       case 0x104b: lg_map_key_clear(KEY_PROG3);               break;
+       case 0x104c: lg_map_key_clear(KEY_PROG4);               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       /* extended mapping for certain Logitech hardware (Logitech cordless
+          desktop LX500) */
+       static const u8 e_keymap[] = {
+                 0,216,  0,213,175,156,  0,  0,  0,  0,
+               144,  0,  0,  0,  0,  0,  0,  0,  0,212,
+               174,167,152,161,112,  0,  0,  0,154,  0,
+                 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                 0,  0,  0,  0,  0,183,184,185,186,187,
+               188,189,190,191,192,193,194,  0,  0,  0
+       };
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+       unsigned int hid = usage->hid;
+
+       if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
+                       lg_ultrax_remote_mapping(hi, usage, bit, max))
+               return 1;
+
+       if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
+               return 1;
+
+       if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
+               return 0;
+
+       hid &= HID_USAGE;
+
+       /* Special handling for Logitech Cordless Desktop */
+       if (field->application == HID_GD_MOUSE) {
+               if ((quirks & LG_IGNORE_DOUBLED_WHEEL) &&
+                               (hid == 7 || hid == 8))
+                       return -1;
+       } else {
+               if ((quirks & LG_EXPANDED_KEYMAP) &&
+                               hid < ARRAY_SIZE(e_keymap) &&
+                               e_keymap[hid] != 0) {
+                       hid_map_usage(hi, usage, bit, max, EV_KEY,
+                                       e_keymap[hid]);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
+                       (field->flags & HID_MAIN_ITEM_RELATIVE))
+               field->flags &= ~HID_MAIN_ITEM_RELATIVE;
+
+       if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
+                        usage->type == EV_REL || usage->type == EV_ABS))
+               clear_bit(usage->code, *bit);
+
+       return 0;
+}
+
+static int lg_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
+               input_event(field->hidinput->input, usage->type, usage->code,
+                               -value);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       unsigned int connect_mask = HID_CONNECT_DEFAULT;
+       int ret;
+
+       hid_set_drvdata(hdev, (void *)quirks);
+
+       if (quirks & LG_NOGET)
+               hdev->quirks |= HID_QUIRK_NOGET;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       if (quirks & (LG_FF | LG_FF2))
+               connect_mask &= ~HID_CONNECT_FF;
+
+       ret = hid_hw_start(hdev, connect_mask);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       if (quirks & LG_RESET_LEDS)
+               usbhid_set_leds(hdev);
+
+       if (quirks & LG_FF)
+               lgff_init(hdev);
+       if (quirks & LG_FF2)
+               lg2ff_init(hdev);
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id lg_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
+               .driver_data = LG_RDESC | LG_WIRELESS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
+               .driver_data = LG_RDESC | LG_WIRELESS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
+               .driver_data = LG_RDESC | LG_WIRELESS },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
+               .driver_data = LG_BAD_RELATIVE_KEYS },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
+               .driver_data = LG_DUPLICATE_USAGES },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
+               .driver_data = LG_DUPLICATE_USAGES },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
+               .driver_data = LG_DUPLICATE_USAGES },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD),
+               .driver_data = LG_RESET_LEDS },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
+               .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
+               .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3),
+               .driver_data = LG_INVERT_HWHEEL },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150),
+               .driver_data = LG_INVERT_HWHEEL },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
+               .driver_data = LG_NOGET },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
+               .driver_data = LG_NOGET | LG_FF },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
+               .driver_data = LG_FF2 },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, lg_devices);
+
+static struct hid_driver lg_driver = {
+       .name = "logitech",
+       .id_table = lg_devices,
+       .report_fixup = lg_report_fixup,
+       .input_mapping = lg_input_mapping,
+       .input_mapped = lg_input_mapped,
+       .event = lg_event,
+       .probe = lg_probe,
+};
+
+static int lg_init(void)
+{
+       return hid_register_driver(&lg_driver);
+}
+
+static void lg_exit(void)
+{
+       hid_unregister_driver(&lg_driver);
+}
+
+module_init(lg_init);
+module_exit(lg_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(logitech);
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
new file mode 100644 (file)
index 0000000..27ae750
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __HID_LG_H
+#define __HID_LG_H
+
+#include <linux/autoconf.h>
+
+#ifdef CONFIG_LOGITECH_FF
+int lgff_init(struct hid_device *hdev);
+#else
+static inline int lgff_init(struct hid_device *hdev) { return -1; }
+#endif
+
+#ifdef CONFIG_LOGIRUMBLEPAD2_FF
+int lg2ff_init(struct hid_device *hdev);
+#else
+static inline int lg2ff_init(struct hid_device *hdev) { return -1; }
+#endif
+
+#endif
similarity index 90%
rename from drivers/hid/usbhid/hid-lg2ff.c
rename to drivers/hid/hid-lg2ff.c
index d469bd0..4e6dc6e 100644 (file)
@@ -24,7 +24,9 @@
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
-#include "usbhid.h"
+
+#include "usbhid/usbhid.h"
+#include "hid-lg.h"
 
 struct lg2ff_device {
        struct hid_report *report;
@@ -57,7 +59,7 @@ static int play_effect(struct input_dev *dev, void *data,
        return 0;
 }
 
-int hid_lg2ff_init(struct hid_device *hid)
+int lg2ff_init(struct hid_device *hid)
 {
        struct lg2ff_device *lg2ff;
        struct hid_report *report;
@@ -69,18 +71,18 @@ int hid_lg2ff_init(struct hid_device *hid)
        int error;
 
        if (list_empty(report_list)) {
-               printk(KERN_ERR "hid-lg2ff: no output report found\n");
+               dev_err(&hid->dev, "no output report found\n");
                return -ENODEV;
        }
 
        report = list_entry(report_list->next, struct hid_report, list);
 
        if (report->maxfield < 1) {
-               printk(KERN_ERR "hid-lg2ff: output report is empty\n");
+               dev_err(&hid->dev, "output report is empty\n");
                return -ENODEV;
        }
        if (report->field[0]->report_count < 7) {
-               printk(KERN_ERR "hid-lg2ff: not enough values in the field\n");
+               dev_err(&hid->dev, "not enough values in the field\n");
                return -ENODEV;
        }
 
@@ -107,7 +109,7 @@ int hid_lg2ff_init(struct hid_device *hid)
 
        usbhid_submit_report(hid, report, USB_DIR_OUT);
 
-       printk(KERN_INFO "Force feedback for Logitech Rumblepad 2 by "
+       dev_info(&hid->dev, "Force feedback for Logitech Rumblepad 2 by "
               "Anssi Hannula <anssi.hannula@gmail.com>\n");
 
        return 0;
similarity index 81%
rename from drivers/hid/usbhid/hid-lgff.c
rename to drivers/hid/hid-lgff.c
index 4b7ab6a..51aff08 100644 (file)
@@ -30,7 +30,9 @@
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
-#include "usbhid.h"
+
+#include "usbhid/usbhid.h"
+#include "hid-lg.h"
 
 struct dev_type {
        u16 idVendor;
@@ -48,6 +50,12 @@ static const signed short ff_joystick[] = {
        -1
 };
 
+static const signed short ff_wheel[] = {
+       FF_CONSTANT,
+       FF_AUTOCENTER,
+       -1
+};
+
 static const struct dev_type devices[] = {
        { 0x046d, 0xc211, ff_rumble },
        { 0x046d, 0xc219, ff_rumble },
@@ -55,7 +63,7 @@ static const struct dev_type devices[] = {
        { 0x046d, 0xc286, ff_joystick },
        { 0x046d, 0xc294, ff_joystick },
        { 0x046d, 0xc295, ff_joystick },
-       { 0x046d, 0xca03, ff_joystick },
+       { 0x046d, 0xca03, ff_wheel },
 };
 
 static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
@@ -100,7 +108,24 @@ static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *ef
        return 0;
 }
 
-int hid_lgff_init(struct hid_device* hid)
+static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       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);
+       __s32 *value = report->field[0]->value;
+       magnitude = (magnitude >> 12) & 0xf;
+       *value++ = 0xfe;
+       *value++ = 0x0d;
+       *value++ = magnitude;   /* clockwise strength */
+       *value++ = magnitude;   /* counter-clockwise strength */
+       *value++ = 0x80;
+       *value++ = 0x00;
+       *value = 0x00;
+       usbhid_submit_report(hid, report, USB_DIR_OUT);
+}
+
+int lgff_init(struct hid_device* hid)
 {
        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;
@@ -145,6 +170,9 @@ int hid_lgff_init(struct hid_device* hid)
        if (error)
                return error;
 
+       if ( test_bit(FF_AUTOCENTER, dev->ffbit) )
+               dev->ff->set_autocenter = hid_lgff_set_autocenter;
+
        printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
 
        return 0;
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
new file mode 100644 (file)
index 0000000..d718b16
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ *  HID driver for some microsoft "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define MS_HIDINPUT    0x01
+#define MS_ERGONOMY    0x02
+#define MS_PRESENTER   0x04
+#define MS_RDESC       0x08
+#define MS_NOGET       0x10
+
+/*
+ * Microsoft Wireless Desktop Receiver (Model 1028) has several
+ * 'Usage Min/Max' where it ought to have 'Physical Min/Max'
+ */
+static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((quirks & MS_RDESC) && rsize == 571 && rdesc[284] == 0x19 &&
+                       rdesc[286] == 0x2a && rdesc[304] == 0x19 &&
+                       rdesc[306] == 0x29 && rdesc[352] == 0x1a &&
+                       rdesc[355] == 0x2a && rdesc[557] == 0x19 &&
+                       rdesc[559] == 0x29) {
+               dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver "
+                               "Model 1028 report descriptor\n");
+               rdesc[284] = rdesc[304] = rdesc[557] = 0x35;
+               rdesc[352] = 0x36;
+               rdesc[286] = rdesc[355] = 0x46;
+               rdesc[306] = rdesc[559] = 0x45;
+       }
+}
+
+#define ms_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       struct input_dev *input = hi->input;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0xfd06: ms_map_key_clear(KEY_CHAT);        break;
+       case 0xfd07: ms_map_key_clear(KEY_PHONE);       break;
+       case 0xff05:
+               set_bit(EV_REP, input->evbit);
+               ms_map_key_clear(KEY_F13);
+               set_bit(KEY_F14, input->keybit);
+               set_bit(KEY_F15, input->keybit);
+               set_bit(KEY_F16, input->keybit);
+               set_bit(KEY_F17, input->keybit);
+               set_bit(KEY_F18, input->keybit);
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       set_bit(EV_REP, hi->input->evbit);
+       switch (usage->hid & HID_USAGE) {
+       case 0xfd08: ms_map_key_clear(KEY_FORWARD);     break;
+       case 0xfd09: ms_map_key_clear(KEY_BACK);        break;
+       case 0xfd0b: ms_map_key_clear(KEY_PLAYPAUSE);   break;
+       case 0xfd0e: ms_map_key_clear(KEY_CLOSE);       break;
+       case 0xfd0f: ms_map_key_clear(KEY_PLAY);        break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+               return 0;
+
+       if (quirks & MS_ERGONOMY) {
+               int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);
+               if (ret)
+                       return ret;
+       }
+
+       if ((quirks & MS_PRESENTER) &&
+                       ms_presenter_8k_quirk(hi, usage, bit, max))
+               return 1;
+
+       return 0;
+}
+
+static int ms_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type)
+               return 0;
+
+       /* Handling MS keyboards special buttons */
+       if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
+               struct input_dev *input = field->hidinput->input;
+               static unsigned int last_key = 0;
+               unsigned int key = 0;
+               switch (value) {
+               case 0x01: key = KEY_F14; break;
+               case 0x02: key = KEY_F15; break;
+               case 0x04: key = KEY_F16; break;
+               case 0x08: key = KEY_F17; break;
+               case 0x10: key = KEY_F18; break;
+               }
+               if (key) {
+                       input_event(input, usage->type, key, 1);
+                       last_key = key;
+               } else
+                       input_event(input, usage->type, last_key, 0);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       int ret;
+
+       hid_set_drvdata(hdev, (void *)quirks);
+
+       if (quirks & MS_NOGET)
+               hdev->quirks |= HID_QUIRK_NOGET;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((quirks & MS_HIDINPUT) ?
+                               HID_CONNECT_HIDINPUT_FORCE : 0));
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id ms_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
+               .driver_data = MS_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K),
+               .driver_data = MS_ERGONOMY },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K),
+               .driver_data = MS_ERGONOMY | MS_RDESC },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB),
+               .driver_data = MS_PRESENTER },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0),
+               .driver_data = MS_NOGET },
+
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
+               .driver_data = MS_PRESENTER },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, ms_devices);
+
+static struct hid_driver ms_driver = {
+       .name = "microsoft",
+       .id_table = ms_devices,
+       .report_fixup = ms_report_fixup,
+       .input_mapping = ms_input_mapping,
+       .event = ms_event,
+       .probe = ms_probe,
+};
+
+static int ms_init(void)
+{
+       return hid_register_driver(&ms_driver);
+}
+
+static void ms_exit(void)
+{
+       hid_unregister_driver(&ms_driver);
+}
+
+module_init(ms_init);
+module_exit(ms_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(microsoft);
diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c
new file mode 100644 (file)
index 0000000..f3a85a0
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *  HID driver for some monterey "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static void mr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 30 && rdesc[29] == 0x05 && rdesc[30] == 0x09) {
+               dev_info(&hdev->dev, "fixing up button/consumer in HID report "
+                               "descriptor\n");
+               rdesc[30] = 0x0c;
+       }
+}
+
+#define mr_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int mr_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x156: mr_map_key_clear(KEY_WORDPROCESSOR);        break;
+       case 0x157: mr_map_key_clear(KEY_SPREADSHEET);          break;
+       case 0x158: mr_map_key_clear(KEY_PRESENTATION);         break;
+       case 0x15c: mr_map_key_clear(KEY_STOP);                 break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static const struct hid_device_id mr_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, mr_devices);
+
+static struct hid_driver mr_driver = {
+       .name = "monterey",
+       .id_table = mr_devices,
+       .report_fixup = mr_report_fixup,
+       .input_mapping = mr_input_mapping,
+};
+
+static int mr_init(void)
+{
+       return hid_register_driver(&mr_driver);
+}
+
+static void mr_exit(void)
+{
+       hid_unregister_driver(&mr_driver);
+}
+
+module_init(mr_init);
+module_exit(mr_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(monterey);
diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c
new file mode 100644 (file)
index 0000000..10945fe
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *  HID driver for some petalynx "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/* Petalynx Maxter Remote has maximum for consumer page set too low */
+static void pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 60 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 &&
+                       rdesc[41] == 0x00 && rdesc[59] == 0x26 &&
+                       rdesc[60] == 0xf9 && rdesc[61] == 0x00) {
+               dev_info(&hdev->dev, "fixing up Petalynx Maxter Remote report "
+                               "descriptor\n");
+               rdesc[60] = 0xfa;
+               rdesc[40] = 0xfa;
+       }
+}
+
+#define pl_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int pl_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LOGIVENDOR) {
+               switch (usage->hid & HID_USAGE) {
+               case 0x05a: pl_map_key_clear(KEY_TEXT);         break;
+               case 0x05b: pl_map_key_clear(KEY_RED);          break;
+               case 0x05c: pl_map_key_clear(KEY_GREEN);        break;
+               case 0x05d: pl_map_key_clear(KEY_YELLOW);       break;
+               case 0x05e: pl_map_key_clear(KEY_BLUE);         break;
+               default:
+                       return 0;
+               }
+               return 1;
+       }
+
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
+               switch (usage->hid & HID_USAGE) {
+               case 0x0f6: pl_map_key_clear(KEY_NEXT);         break;
+               case 0x0fa: pl_map_key_clear(KEY_BACK);         break;
+               default:
+                       return 0;
+               }
+               return 1;
+       }
+
+       return 0;
+}
+
+static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       hdev->quirks |= HID_QUIRK_NOGET;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id pl_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, pl_devices);
+
+static struct hid_driver pl_driver = {
+       .name = "petalynx",
+       .id_table = pl_devices,
+       .report_fixup = pl_report_fixup,
+       .input_mapping = pl_input_mapping,
+       .probe = pl_probe,
+};
+
+static int pl_init(void)
+{
+       return hid_register_driver(&pl_driver);
+}
+
+static void pl_exit(void)
+{
+       hid_unregister_driver(&pl_driver);
+}
+
+module_init(pl_init);
+module_exit(pl_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(petalynx);
similarity index 68%
rename from drivers/hid/usbhid/hid-plff.c
rename to drivers/hid/hid-pl.c
index 9eb83cf..acd8155 100644 (file)
@@ -9,7 +9,7 @@
  *   - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT)
  *
  *  0e8f:0003 "GreenAsia Inc.    USB Joystick     "
- *   - tested with Köng Gaming gamepad
+ *   - tested with K??ng Gaming gamepad
  *
  *  Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com>
  */
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
-#include "usbhid.h"
+
+#include "hid-ids.h"
+
+#ifdef CONFIG_PANTHERLORD_FF
+#include "usbhid/usbhid.h"
 
 struct plff_device {
        struct hid_report *report;
@@ -66,7 +70,7 @@ static int hid_plff_play(struct input_dev *dev, void *data,
        return 0;
 }
 
-int hid_plff_init(struct hid_device *hid)
+static int plff_init(struct hid_device *hid)
 {
        struct plff_device *plff;
        struct hid_report *report;
@@ -86,7 +90,7 @@ int hid_plff_init(struct hid_device *hid)
           currently unknown. */
 
        if (list_empty(report_list)) {
-               printk(KERN_ERR "hid-plff: no output reports found\n");
+               dev_err(&hid->dev, "no output reports found\n");
                return -ENODEV;
        }
 
@@ -95,18 +99,19 @@ int hid_plff_init(struct hid_device *hid)
                report_ptr = report_ptr->next;
 
                if (report_ptr == report_list) {
-                       printk(KERN_ERR "hid-plff: required output report is missing\n");
+                       dev_err(&hid->dev, "required output report is "
+                                       "missing\n");
                        return -ENODEV;
                }
 
                report = list_entry(report_ptr, struct hid_report, list);
                if (report->maxfield < 1) {
-                       printk(KERN_ERR "hid-plff: no fields in the report\n");
+                       dev_err(&hid->dev, "no fields in the report\n");
                        return -ENODEV;
                }
 
                if (report->field[0]->report_count < 4) {
-                       printk(KERN_ERR "hid-plff: not enough values in the field\n");
+                       dev_err(&hid->dev, "not enough values in the field\n");
                        return -ENODEV;
                }
 
@@ -132,8 +137,70 @@ int hid_plff_init(struct hid_device *hid)
                usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
        }
 
-       printk(KERN_INFO "hid-plff: Force feedback for PantherLord/GreenAsia "
+       dev_info(&hid->dev, "Force feedback for PantherLord/GreenAsia "
               "devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
 
        return 0;
 }
+#else
+static inline int plff_init(struct hid_device *hid)
+{
+       return 0;
+}
+#endif
+
+static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       if (id->driver_data)
+               hdev->quirks |= HID_QUIRK_MULTI_INPUT;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err;
+       }
+
+       plff_init(hdev);
+
+       return 0;
+err:
+       return ret;
+}
+
+static const struct hid_device_id pl_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR),
+               .driver_data = 1 }, /* Twin USB Joystick */
+       { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, /* GreenAsia Inc. USB Joystick */
+       { }
+};
+MODULE_DEVICE_TABLE(hid, pl_devices);
+
+static struct hid_driver pl_driver = {
+       .name = "pantherlord",
+       .id_table = pl_devices,
+       .probe = pl_probe,
+};
+
+static int pl_init(void)
+{
+       return hid_register_driver(&pl_driver);
+}
+
+static void pl_exit(void)
+{
+       hid_unregister_driver(&pl_driver);
+}
+
+module_init(pl_init);
+module_exit(pl_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(pantherlord);
diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c
new file mode 100644 (file)
index 0000000..15f3c04
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *  HID driver for some samsung "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * Samsung IrDA remote controller (reports as Cypress USB Mouse).
+ *
+ * Vendor specific report #4 has a size of 48 bit,
+ * and therefore is not accepted when inspecting the descriptors.
+ * As a workaround we reinterpret the report as:
+ *   Variable type, count 6, size 8 bit, log. maximum 255
+ * The burden to reconstruct the data is moved into user space.
+ */
+static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 182 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
+                       rdesc[177] == 0x75 && rdesc[178] == 0x30 &&
+                       rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
+                       rdesc[182] == 0x40) {
+               dev_info(&hdev->dev, "fixing up Samsung IrDA report "
+                               "descriptor\n");
+               rdesc[176] = 0xff;
+               rdesc[178] = 0x08;
+               rdesc[180] = 0x06;
+               rdesc[182] = 0x42;
+       }
+}
+
+static int samsung_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, (HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT) |
+                       HID_CONNECT_HIDDEV_FORCE);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id samsung_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, samsung_devices);
+
+static struct hid_driver samsung_driver = {
+       .name = "samsung",
+       .id_table = samsung_devices,
+       .report_fixup = samsung_report_fixup,
+       .probe = samsung_probe,
+};
+
+static int samsung_init(void)
+{
+       return hid_register_driver(&samsung_driver);
+}
+
+static void samsung_exit(void)
+{
+       hid_unregister_driver(&samsung_driver);
+}
+
+module_init(samsung_init);
+module_exit(samsung_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(samsung);
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
new file mode 100644 (file)
index 0000000..3af8095
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ *  HID driver for some sony "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+/*
+ * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
+ * to "operational".  Without this, the ps3 controller will not report any
+ * events.
+ */
+static int sony_set_operational(struct hid_device *hdev)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct usb_device *dev = interface_to_usbdev(intf);
+       __u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+       int ret;
+       char *buf = kmalloc(18, GFP_KERNEL);
+
+       if (!buf)
+               return -ENOMEM;
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                                HID_REQ_GET_REPORT,
+                                USB_DIR_IN | USB_TYPE_CLASS |
+                                USB_RECIP_INTERFACE,
+                                (3 << 8) | 0xf2, ifnum, buf, 17,
+                                USB_CTRL_GET_TIMEOUT);
+       if (ret < 0)
+               dev_err(&hdev->dev, "can't set operational mode\n");
+
+       kfree(buf);
+
+       return ret;
+}
+
+static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
+                       HID_CONNECT_HIDDEV_FORCE);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       ret = sony_set_operational(hdev);
+       if (ret)
+               goto err_stop;
+
+       return 0;
+err_stop:
+       hid_hw_stop(hdev);
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id sony_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, sony_devices);
+
+static struct hid_driver sony_driver = {
+       .name = "sony",
+       .id_table = sony_devices,
+       .probe = sony_probe,
+};
+
+static int sony_init(void)
+{
+       return hid_register_driver(&sony_driver);
+}
+
+static void sony_exit(void)
+{
+       hid_unregister_driver(&sony_driver);
+}
+
+module_init(sony_init);
+module_exit(sony_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(sony);
diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c
new file mode 100644 (file)
index 0000000..5ba68f7
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *  HID driver for some sunplus "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static void sp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 &&
+                       rdesc[106] == 0x03) {
+               dev_info(&hdev->dev, "fixing up Sunplus Wireless Desktop "
+                               "report descriptor\n");
+               rdesc[105] = rdesc[110] = 0x03;
+               rdesc[106] = rdesc[111] = 0x21;
+       }
+}
+
+#define sp_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+               EV_KEY, (c))
+static int sp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x2003: sp_map_key_clear(KEY_ZOOMIN);              break;
+       case 0x2103: sp_map_key_clear(KEY_ZOOMOUT);     break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static const struct hid_device_id sp_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, sp_devices);
+
+static struct hid_driver sp_driver = {
+       .name = "sunplus",
+       .id_table = sp_devices,
+       .report_fixup = sp_report_fixup,
+       .input_mapping = sp_input_mapping,
+};
+
+static int sp_init(void)
+{
+       return hid_register_driver(&sp_driver);
+}
+
+static void sp_exit(void)
+{
+       hid_unregister_driver(&sp_driver);
+}
+
+module_init(sp_init);
+module_exit(sp_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(sunplus);
similarity index 60%
rename from drivers/hid/usbhid/hid-tmff.c
rename to drivers/hid/hid-tmff.c
index 144578b..1b7cba0 100644 (file)
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+#include <linux/hid.h>
 #include <linux/input.h>
-
-#undef DEBUG
 #include <linux/usb.h>
 
-#include <linux/hid.h>
-#include "usbhid.h"
+#include "hid-ids.h"
+
+#include "usbhid/usbhid.h"
 
 /* Usages for thrustmaster devices I know about */
 #define THRUSTMASTER_USAGE_FF  (HID_UP_GENDESK | 0xbb)
 
-struct dev_type {
-       u16 idVendor;
-       u16 idProduct;
-       const signed short *ff;
-};
-
 static const signed short ff_rumble[] = {
        FF_RUMBLE,
        -1
@@ -54,21 +48,13 @@ static const signed short ff_joystick[] = {
        -1
 };
 
-static const struct dev_type devices[] = {
-       { 0x44f, 0xb300, ff_rumble },
-       { 0x44f, 0xb304, ff_rumble },
-       { 0x44f, 0xb651, ff_rumble },   /* FGT Rumble Force Wheel */
-       { 0x44f, 0xb654, ff_joystick }, /* FGT Force Feedback Wheel */
-};
-
 struct tmff_device {
        struct hid_report *report;
        struct hid_field *ff_field;
 };
 
 /* Changes values from 0 to 0xffff into values from minimum to maximum */
-static inline int hid_tmff_scale_u16(unsigned int in,
-                               int minimum, int maximum)
+static inline int tmff_scale_u16(unsigned int in, int minimum, int maximum)
 {
        int ret;
 
@@ -81,8 +67,7 @@ static inline int hid_tmff_scale_u16(unsigned int in,
 }
 
 /* Changes values from -0x80 to 0x7f into values from minimum to maximum */
-static inline int hid_tmff_scale_s8(int in,
-                                   int minimum, int maximum)
+static inline int tmff_scale_s8(int in, int minimum, int maximum)
 {
        int ret;
 
@@ -94,7 +79,8 @@ static inline int hid_tmff_scale_s8(int in,
        return ret;
 }
 
-static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+static int tmff_play(struct input_dev *dev, void *data,
+               struct ff_effect *effect)
 {
        struct hid_device *hid = input_get_drvdata(dev);
        struct tmff_device *tmff = data;
@@ -104,10 +90,10 @@ static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *ef
 
        switch (effect->type) {
        case FF_CONSTANT:
-               x = hid_tmff_scale_s8(effect->u.ramp.start_level,
+               x = tmff_scale_s8(effect->u.ramp.start_level,
                                        ff_field->logical_minimum,
                                        ff_field->logical_maximum);
-               y = hid_tmff_scale_s8(effect->u.ramp.end_level,
+               y = tmff_scale_s8(effect->u.ramp.end_level,
                                        ff_field->logical_minimum,
                                        ff_field->logical_maximum);
 
@@ -118,10 +104,10 @@ static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *ef
                break;
 
        case FF_RUMBLE:
-               left = hid_tmff_scale_u16(effect->u.rumble.weak_magnitude,
+               left = tmff_scale_u16(effect->u.rumble.weak_magnitude,
                                        ff_field->logical_minimum,
                                        ff_field->logical_maximum);
-               right = hid_tmff_scale_u16(effect->u.rumble.strong_magnitude,
+               right = tmff_scale_u16(effect->u.rumble.strong_magnitude,
                                        ff_field->logical_minimum,
                                        ff_field->logical_maximum);
 
@@ -134,14 +120,14 @@ static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *ef
        return 0;
 }
 
-int hid_tmff_init(struct hid_device *hid)
+static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
 {
        struct tmff_device *tmff;
        struct hid_report *report;
        struct list_head *report_list;
-       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       struct hid_input *hidinput = list_entry(hid->inputs.next,
+                                                       struct hid_input, list);
        struct input_dev *input_dev = hidinput->input;
-       const signed short *ff_bits = ff_joystick;
        int error;
        int i;
 
@@ -163,63 +149,121 @@ int hid_tmff_init(struct hid_device *hid)
                        switch (field->usage[0].hid) {
                        case THRUSTMASTER_USAGE_FF:
                                if (field->report_count < 2) {
-                                       warn("ignoring FF field with report_count < 2");
+                                       dev_warn(&hid->dev, "ignoring FF field "
+                                               "with report_count < 2\n");
                                        continue;
                                }
 
-                               if (field->logical_maximum == field->logical_minimum) {
-                                       warn("ignoring FF field with logical_maximum == logical_minimum");
+                               if (field->logical_maximum ==
+                                               field->logical_minimum) {
+                                       dev_warn(&hid->dev, "ignoring FF field "
+                                                       "with logical_maximum "
+                                                       "== logical_minimum\n");
                                        continue;
                                }
 
                                if (tmff->report && tmff->report != report) {
-                                       warn("ignoring FF field in other report");
+                                       dev_warn(&hid->dev, "ignoring FF field "
+                                                       "in other report\n");
                                        continue;
                                }
 
                                if (tmff->ff_field && tmff->ff_field != field) {
-                                       warn("ignoring duplicate FF field");
+                                       dev_warn(&hid->dev, "ignoring "
+                                                       "duplicate FF field\n");
                                        continue;
                                }
 
                                tmff->report = report;
                                tmff->ff_field = field;
 
-                               for (i = 0; i < ARRAY_SIZE(devices); i++) {
-                                       if (input_dev->id.vendor == devices[i].idVendor &&
-                      &nb