Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (59 commits)
  HID: fix up 'EMBEDDED' mess in Kconfig
  HID: roccat: cleanup preprocessor macros
  HID: roccat: refactor special event handling
  HID: roccat: fix special button support
  HID: roccat: Correctly mark init and exit functions
  HID: hidraw: Use Interrupt Endpoint for OUT Transfers if Available
  HID: hid-samsung: remove redundant key mappings
  HID: add omitted hid-zydacron.c file
  HID: hid-samsung: add support for Creative Desktop Wireless 6000
  HID: picolcd: Eliminate use after free
  HID: Zydacron Remote Control driver
  HID: Use kmemdup
  HID: magicmouse: fix input registration
  HID: make Prodikeys driver standalone config option
  HID: Prodikeys PC-MIDI HID Driver
  HID: hidraw: fix indentation
  HID: ntrig: add filtering module parameters
  HID: ntrig: add sysfs access to filter parameters
  HID: ntrig: add sensitivity and responsiveness support
  HID: add multi-input quirk for eGalax Touchcontroller
  ...
This commit is contained in:
Linus Torvalds 2010-05-21 10:51:03 -07:00
commit 8b108c609a
28 changed files with 6913 additions and 163 deletions

View file

@ -0,0 +1,43 @@
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/operation_mode
Date: March 2010
Contact: Bruno Prémont <bonbons@linux-vserver.org>
Description: Make it possible to switch the PicoLCD device between LCD
(firmware) and bootloader (flasher) operation modes.
Reading: returns list of available modes, the active mode being
enclosed in brackets ('[' and ']')
Writing: causes operation mode switch. Permitted values are
the non-active mode names listed when read.
Note: when switching mode the current PicoLCD HID device gets
disconnected and reconnects after above delay (see attribute
operation_mode_delay for its value).
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/operation_mode_delay
Date: April 2010
Contact: Bruno Prémont <bonbons@linux-vserver.org>
Description: Delay PicoLCD waits before restarting in new mode when
operation_mode has changed.
Reading/Writing: It is expressed in ms and permitted range is
0..30000ms.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/fb_update_rate
Date: March 2010
Contact: Bruno Prémont <bonbons@linux-vserver.org>
Description: Make it possible to adjust defio refresh rate.
Reading: returns list of available refresh rates (expressed in Hz),
the active refresh rate being enclosed in brackets ('[' and ']')
Writing: accepts new refresh rate expressed in integer Hz
within permitted rates.
Note: As device can barely do 2 complete refreshes a second
it only makes sense to adjust this value if only one or two
tiles get changed and it's not appropriate to expect the application
to flush it's tiny changes explicitely at higher than default rate.

View file

@ -0,0 +1,29 @@
What: /sys/bus/hid/drivers/prodikeys/.../channel
Date: April 2010
KernelVersion: 2.6.34
Contact: Don Prince <dhprince.devel@yahoo.co.uk>
Description:
Allows control (via software) the midi channel to which
that the pc-midi keyboard will output.midi data.
Range: 0..15
Type: Read/write
What: /sys/bus/hid/drivers/prodikeys/.../sustain
Date: April 2010
KernelVersion: 2.6.34
Contact: Don Prince <dhprince.devel@yahoo.co.uk>
Description:
Allows control (via software) the sustain duration of a
note held by the pc-midi driver.
0 means sustain mode is disabled.
Range: 0..5000 (milliseconds)
Type: Read/write
What: /sys/bus/hid/drivers/prodikeys/.../octave
Date: April 2010
KernelVersion: 2.6.34
Contact: Don Prince <dhprince.devel@yahoo.co.uk>
Description:
Controls the octave shift modifier in the pc-midi driver.
The octave can be shifted via software up/down 2 octaves.
0 means the no ocatve shift.
Range: -2..2 (minus 2 to plus 2)
Type: Read/Write

View file

@ -0,0 +1,111 @@
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_dpi
Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: It is possible to switch the dpi setting of the mouse with the
press of a button.
When read, this file returns the raw number of the actual dpi
setting reported by the mouse. This number has to be further
processed to receive the real dpi value.
VALUE DPI
1 800
2 1200
3 1600
4 2000
5 2400
6 3200
This file is readonly.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_profile
Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: When read, this file returns the number of the actual profile.
This file is readonly.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/firmware_version
Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: When read, this file returns the raw integer version number of the
firmware reported by the mouse. Using the integer value eases
further usage in other programs. To receive the real version
number the decimal point has to be shifted 2 positions to the
left. E.g. a returned value of 138 means 1.38
This file is readonly.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/kone_driver_version
Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: When read, this file returns the driver version.
The format of the string is "v<major>.<minor>.<patchlevel>".
This attribute is used by the userland tools to find the sysfs-
paths of installed kone-mice and determine the capabilites of
the driver. Versions of this driver for old kernels replace
usbhid instead of generic-usb. The way to scan for this file
has been chosen to provide a consistent way for all supported
kernel versions.
This file is readonly.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5]
Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: The mouse can store 5 profiles which can be switched by the
press of a button. A profile holds informations like button
mappings, sensitivity, the colors of the 5 leds and light
effects.
When read, these files return the respective profile. The
returned data is 975 bytes in size.
When written, this file lets one write the respective profile
data back to the mouse. The data has to be 975 bytes long.
The mouse will reject invalid data, whereas the profile number
stored in the profile doesn't need to fit the number of the
store.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/settings
Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: When read, this file returns the settings stored in the mouse.
The size of the data is 36 bytes and holds information like the
startup_profile, tcu state and calibration_data.
When written, this file lets write settings back to the mouse.
The data has to be 36 bytes long. The mouse will reject invalid
data.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/startup_profile
Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: The integer value of this attribute ranges from 1 to 5.
When read, this attribute returns the number of the profile
that's active when the mouse is powered on.
When written, this file sets the number of the startup profile
and the mouse activates this profile immediately.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/tcu
Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: The mouse has a "Tracking Control Unit" which lets the user
calibrate the laser power to fit the mousepad surface.
When read, this file returns the current state of the TCU,
where 0 means off and 1 means on.
Writing 0 in this file will switch the TCU off.
Writing 1 in this file will start the calibration which takes
around 6 seconds to complete and activates the TCU.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/weight
Date: March 2010
Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
Description: The mouse can be equipped with one of four supplied weights
ranging from 5 to 20 grams which are recognized by the mouse
and its value can be read out. When read, this file returns the
raw value returned by the mouse which eases further processing
in other software.
The values map to the weights as follows:
VALUE WEIGHT
0 none
1 5g
2 10g
3 15g
4 20g
This file is readonly.

View file

@ -0,0 +1,10 @@
What: /sys/class/hidraw/hidraw*/device/speed
Date: April 2010
Kernel Version: 2.6.35
Contact: linux-bluetooth@vger.kernel.org
Description:
The /sys/class/hidraw/hidraw*/device/speed file controls
reporting speed of wacom bluetooth tablet. Reading from
this file returns 1 if tablet reports in high speed mode
or 0 otherwise. Writing to this file one of these values
switches reporting speed.

View file

@ -86,6 +86,12 @@ config HID_BELKIN
---help---
Support for Belkin Flip KVM and Wireless keyboard.
config HID_CANDO
tristate "Cando dual touch panel"
depends on USB_HID
---help---
Support for Cando dual touch panel.
config HID_CHERRY
tristate "Cherry" if EMBEDDED
depends on USB_HID
@ -100,6 +106,21 @@ config HID_CHICONY
---help---
Support for Chicony Tactical pad.
config HID_PRODIKEYS
tristate "Prodikeys PC-MIDI Keyboard support"
depends on USB_HID && SND
select SND_RAWMIDI
---help---
Support for Prodikeys PC-MIDI Keyboard device support.
Say Y here to enable support for this device.
- Prodikeys PC-MIDI keyboard.
The Prodikeys PC-MIDI acts as a USB Audio device, with one MIDI
input and one MIDI output. These MIDI jacks appear as
a sound "card" in the ALSA sound system.
Note: if you say N here, this device will still function as a basic
multimedia keyboard, but will lack support for the musical keyboard
and some additional multimedia keys.
config HID_CYPRESS
tristate "Cypress" if EMBEDDED
depends on USB_HID
@ -108,9 +129,8 @@ config HID_CYPRESS
Support for cypress mouse and barcode readers.
config HID_DRAGONRISE
tristate "DragonRise Inc. support" if EMBEDDED
tristate "DragonRise Inc. support"
depends on USB_HID
default !EMBEDDED
---help---
Say Y here if you have DragonRise Inc.game controllers.
@ -122,6 +142,12 @@ config DRAGONRISE_FF
Say Y here if you want to enable force feedback support for DragonRise Inc.
game controllers.
config HID_EGALAX
tristate "eGalax multi-touch panel"
depends on USB_HID
---help---
Support for the eGalax dual-touch panel.
config HID_EZKEY
tristate "Ezkey" if EMBEDDED
depends on USB_HID
@ -137,16 +163,14 @@ config HID_KYE
Support for Kye/Genius Ergo Mouse.
config HID_GYRATION
tristate "Gyration" if EMBEDDED
tristate "Gyration"
depends on USB_HID
default !EMBEDDED
---help---
Support for Gyration remote control.
config HID_TWINHAN
tristate "Twinhan" if EMBEDDED
tristate "Twinhan"
depends on USB_HID
default !EMBEDDED
---help---
Support for Twinhan IR remote control.
@ -233,16 +257,14 @@ config HID_NTRIG
Support for N-Trig touch screen.
config HID_ORTEK
tristate "Ortek" if EMBEDDED
tristate "Ortek"
depends on USB_HID
default !EMBEDDED
---help---
Support for Ortek WKB-2000 wireless keyboard + mouse trackpad.
config HID_PANTHERLORD
tristate "Pantherlord support" if EMBEDDED
tristate "Pantherlord support"
depends on USB_HID
default !EMBEDDED
---help---
Say Y here if you have a PantherLord/GreenAsia based game controller
or adapter.
@ -256,29 +278,90 @@ config PANTHERLORD_FF
or adapter and want to enable force feedback support for it.
config HID_PETALYNX
tristate "Petalynx" if EMBEDDED
tristate "Petalynx"
depends on USB_HID
default !EMBEDDED
---help---
Support for Petalynx Maxter remote control.
config HID_PICOLCD
tristate "PicoLCD (graphic version)"
depends on USB_HID
---help---
This provides support for Minibox PicoLCD devices, currently
only the graphical ones are supported.
This includes support for the following device features:
- Keypad
- Switching between Firmware and Flash mode
- EEProm / Flash access (via debugfs)
Features selectively enabled:
- Framebuffer for monochrome 256x64 display
- Backlight control
- Contrast control
- General purpose outputs
Features that are not (yet) supported:
- IR
config HID_PICOLCD_FB
bool "Framebuffer support" if EMBEDDED
default !EMBEDDED
depends on HID_PICOLCD
depends on HID_PICOLCD=FB || FB=y
select FB_DEFERRED_IO
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
---help---
Provide access to PicoLCD's 256x64 monochrome display via a
frambuffer device.
config HID_PICOLCD_BACKLIGHT
bool "Backlight control" if EMBEDDED
default !EMBEDDED
depends on HID_PICOLCD
depends on HID_PICOLCD=BACKLIGHT_CLASS_DEVICE || BACKLIGHT_CLASS_DEVICE=y
---help---
Provide access to PicoLCD's backlight control via backlight
class.
config HID_PICOLCD_LCD
bool "Contrast control" if EMBEDDED
default !EMBEDDED
depends on HID_PICOLCD
depends on HID_PICOLCD=LCD_CLASS_DEVICE || LCD_CLASS_DEVICE=y
---help---
Provide access to PicoLCD's LCD contrast via lcd class.
config HID_PICOLCD_LEDS
bool "GPO via leds class" if EMBEDDED
default !EMBEDDED
depends on HID_PICOLCD
depends on HID_PICOLCD=LEDS_CLASS || LEDS_CLASS=y
---help---
Provide access to PicoLCD's GPO pins via leds class.
config HID_QUANTA
tristate "Quanta Optical Touch"
depends on USB_HID
---help---
Support for Quanta Optical Touch dual-touch panels.
config HID_SAMSUNG
tristate "Samsung" if EMBEDDED
config HID_ROCCAT_KONE
tristate "Roccat Kone Mouse support"
depends on USB_HID
default !EMBEDDED
---help---
Support for Samsung InfraRed remote control.
Support for Roccat Kone mouse.
config HID_SAMSUNG
tristate "Samsung"
depends on USB_HID
---help---
Support for Samsung InfraRed remote control or keyboards.
config HID_SONY
tristate "Sony" if EMBEDDED
tristate "Sony"
depends on USB_HID
default !EMBEDDED
---help---
Support for Sony PS3 controller.
@ -289,16 +372,14 @@ config HID_STANTUM
Support for Stantum multitouch panel.
config HID_SUNPLUS
tristate "Sunplus" if EMBEDDED
tristate "Sunplus"
depends on USB_HID
default !EMBEDDED
---help---
Support for Sunplus wireless desktop.
config HID_GREENASIA
tristate "GreenAsia (Product ID 0x12) support" if EMBEDDED
tristate "GreenAsia (Product ID 0x12) support"
depends on USB_HID
default !EMBEDDED
---help---
Say Y here if you have a GreenAsia (Product ID 0x12) based game
controller or adapter.
@ -313,9 +394,8 @@ config GREENASIA_FF
and want to enable force feedback support for it.
config HID_SMARTJOYPLUS
tristate "SmartJoy PLUS PS2/USB adapter support" if EMBEDDED
tristate "SmartJoy PLUS PS2/USB adapter support"
depends on USB_HID
default !EMBEDDED
---help---
Support for SmartJoy PLUS PS2/USB adapter.
@ -328,16 +408,14 @@ config SMARTJOYPLUS_FF
enable force feedback support for it.
config HID_TOPSEED
tristate "TopSeed Cyberlink remote control support" if EMBEDDED
tristate "TopSeed Cyberlink remote control support"
depends on USB_HID
default !EMBEDDED
---help---
Say Y if you have a TopSeed Cyberlink remote control.
Say Y if you have a TopSeed Cyberlink or BTC Emprex remote control.
config HID_THRUSTMASTER
tristate "ThrustMaster devices support" if EMBEDDED
tristate "ThrustMaster devices support"
depends on USB_HID
default !EMBEDDED
---help---
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
a THRUSTMASTER Ferrari GT Rumble Wheel.
@ -357,10 +435,17 @@ config HID_WACOM
---help---
Support for Wacom Graphire Bluetooth tablet.
config HID_WACOM_POWER_SUPPLY
bool "Wacom Bluetooth devices power supply status support"
depends on HID_WACOM
select POWER_SUPPLY
---help---
Say Y here if you want to enable power supply status monitoring for
Wacom Bluetooth devices.
config HID_ZEROPLUS
tristate "Zeroplus based game controller support" if EMBEDDED
tristate "Zeroplus based game controller support"
depends on USB_HID
default !EMBEDDED
---help---
Say Y here if you have a Zeroplus based game controller.
@ -372,6 +457,12 @@ config ZEROPLUS_FF
Say Y here if you have a Zeroplus based game controller and want
to have force feedback support for it.
config HID_ZYDACRON
tristate "Zydacron remote control support"
depends on USB_HID
---help---
Support for Zydacron remote control.
endmenu
endif # HID_SUPPORT

View file

@ -26,10 +26,12 @@ obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
obj-$(CONFIG_HID_APPLE) += hid-apple.o
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_CANDO) += hid-cando.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_DRAGONRISE) += hid-drff.o
obj-$(CONFIG_HID_EGALAX) += hid-egalax.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
@ -41,9 +43,12 @@ obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
obj-$(CONFIG_HID_MOSART) += hid-mosart.o
obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
obj-$(CONFIG_HID_ORTEK) += hid-ortek.o
obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
obj-$(CONFIG_HID_QUANTA) += hid-quanta.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
obj-$(CONFIG_HID_SONY) += hid-sony.o
@ -54,6 +59,7 @@ obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
obj-$(CONFIG_HID_WACOM) += hid-wacom.o
obj-$(CONFIG_USB_HID) += usbhid/

View file

@ -1,7 +1,7 @@
/*
* HID driver for 3M PCT multitouch panels
*
* Copyright (c) 2009 Stephane Chatty <chatty@enac.fr>
* Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
*
*/
@ -25,7 +25,7 @@ MODULE_LICENSE("GPL");
#include "hid-ids.h"
struct mmm_finger {
__s32 x, y;
__s32 x, y, w, h;
__u8 rank;
bool touch, valid;
};
@ -82,7 +82,18 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
/* touchscreen emulation */
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
return 1;
case HID_DG_WIDTH:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TOUCH_MAJOR);
return 1;
case HID_DG_HEIGHT:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TOUCH_MINOR);
input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
1, 1, 0, 0);
return 1;
case HID_DG_CONTACTID:
field->logical_maximum = 59;
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TRACKING_ID);
return 1;
@ -128,9 +139,15 @@ static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
/* this finger is just placeholder data, ignore */
} else if (f->touch) {
/* this finger is on the screen */
int wide = (f->w > f->h);
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR,
wide ? f->w : f->h);
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR,
wide ? f->h : f->w);
input_mt_sync(input);
/*
* touchscreen emulation: maintain the age rank
@ -197,6 +214,14 @@ static int mmm_event(struct hid_device *hid, struct hid_field *field,
case HID_DG_CONFIDENCE:
md->valid = value;
break;
case HID_DG_WIDTH:
if (md->valid)
md->f[md->curid].w = value;
break;
case HID_DG_HEIGHT:
if (md->valid)
md->f[md->curid].h = value;
break;
case HID_DG_CONTACTID:
if (md->valid) {
md->curid = value;
@ -255,6 +280,7 @@ static void mmm_remove(struct hid_device *hdev)
static const struct hid_device_id mmm_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
{ }
};
MODULE_DEVICE_TABLE(hid, mmm_devices);
@ -287,5 +313,4 @@ static void __exit mmm_exit(void)
module_init(mmm_init);
module_exit(mmm_exit);
MODULE_LICENSE("GPL");

272
drivers/hid/hid-cando.c Normal file
View file

@ -0,0 +1,272 @@
/*
* HID driver for Cando dual-touch panels
*
* Copyright (c) 2010 Stephane Chatty <chatty@enac.fr>
*
*/
/*
* 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/slab.h>
MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
MODULE_DESCRIPTION("Cando dual-touch panel");
MODULE_LICENSE("GPL");
#include "hid-ids.h"
struct cando_data {
__u16 x, y;
__u8 id;
__s8 oldest; /* id of the oldest finger in previous frame */
bool valid; /* valid finger data, or just placeholder? */
bool first; /* is this the first finger in this frame? */
__s8 firstid; /* id of the first finger in the frame */
__u16 firstx, firsty; /* (x, y) of the first finger in the frame */
};
static int cando_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
switch (usage->hid & HID_USAGE_PAGE) {
case HID_UP_GENDESK:
switch (usage->hid) {
case HID_GD_X:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_POSITION_X);
/* touchscreen emulation */
input_set_abs_params(hi->input, ABS_X,
field->logical_minimum,
field->logical_maximum, 0, 0);
return 1;
case HID_GD_Y:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_POSITION_Y);
/* touchscreen emulation */
input_set_abs_params(hi->input, ABS_Y,
field->logical_minimum,
field->logical_maximum, 0, 0);
return 1;
}
return 0;
case HID_UP_DIGITIZER:
switch (usage->hid) {
case HID_DG_TIPSWITCH:
case HID_DG_CONTACTMAX:
return -1;
case HID_DG_INRANGE:
/* touchscreen emulation */
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
return 1;
case HID_DG_CONTACTID:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TRACKING_ID);
return 1;
}
return 0;
}
return 0;
}
static int cando_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if (usage->type == EV_KEY || usage->type == EV_ABS)
clear_bit(usage->code, *bit);
return 0;
}
/*
* this function is called when a whole finger has been parsed,
* so that it can decide what to send to the input layer.
*/
static void cando_filter_event(struct cando_data *td, struct input_dev *input)
{
td->first = !td->first; /* touchscreen emulation */
if (!td->valid) {
/*
* touchscreen emulation: if this is the second finger and
* the first was valid, the first was the oldest; if the
* first was not valid and there was a valid finger in the
* previous frame, this is a release.
*/
if (td->first) {
td->firstid = -1;
} else if (td->firstid >= 0) {
input_event(input, EV_ABS, ABS_X, td->firstx);
input_event(input, EV_ABS, ABS_Y, td->firsty);
td->oldest = td->firstid;
} else if (td->oldest >= 0) {
input_event(input, EV_KEY, BTN_TOUCH, 0);
td->oldest = -1;
}
return;
}
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id);
input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y);
input_mt_sync(input);
/*
* touchscreen emulation: if there was no touching finger previously,
* emit touch event
*/
if (td->oldest < 0) {
input_event(input, EV_KEY, BTN_TOUCH, 1);
td->oldest = td->id;
}
/*
* touchscreen emulation: if this is the first finger, wait for the
* second; the oldest is then the second if it was the oldest already
* or if there was no first, the first otherwise.
*/
if (td->first) {
td->firstx = td->x;
td->firsty = td->y;
td->firstid = td->id;
} else {
int x, y, oldest;
if (td->id == td->oldest || td->firstid < 0) {
x = td->x;
y = td->y;
oldest = td->id;
} else {
x = td->firstx;
y = td->firsty;
oldest = td->firstid;
}
input_event(input, EV_ABS, ABS_X, x);
input_event(input, EV_ABS, ABS_Y, y);
td->oldest = oldest;
}
}
static int cando_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct cando_data *td = hid_get_drvdata(hid);
if (hid->claimed & HID_CLAIMED_INPUT) {
struct input_dev *input = field->hidinput->input;
switch (usage->hid) {
case HID_DG_INRANGE:
td->valid = value;
break;
case HID_DG_CONTACTID:
td->id = value;
break;
case HID_GD_X:
td->x = value;
break;
case HID_GD_Y:
td->y = value;
cando_filter_event(td, input);
break;
case HID_DG_TIPSWITCH:
/* avoid interference from generic hidinput handling */
break;
default:
/* fallback to the generic hidinput handling */
return 0;
}
}
/* we have handled the hidinput part, now remains hiddev */
if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
hid->hiddev_hid_event(hid, field, usage, value);
return 1;
}
static int cando_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
struct cando_data *td;
td = kmalloc(sizeof(struct cando_data), GFP_KERNEL);
if (!td) {
dev_err(&hdev->dev, "cannot allocate Cando Touch data\n");
return -ENOMEM;
}
hid_set_drvdata(hdev, td);
td->first = false;
td->oldest = -1;
td->valid = false;
ret = hid_parse(hdev);
if (!ret)
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret)
kfree(td);
return ret;
}
static void cando_remove(struct hid_device *hdev)
{
hid_hw_stop(hdev);
kfree(hid_get_drvdata(hdev));
hid_set_drvdata(hdev, NULL);
}
static const struct hid_device_id cando_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
USB_DEVICE_ID_CANDO_MULTI_TOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) },
{ }
};
MODULE_DEVICE_TABLE(hid, cando_devices);
static const struct hid_usage_id cando_grabbed_usages[] = {
{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
};
static struct hid_driver cando_driver = {
.name = "cando-touch",
.id_table = cando_devices,
.probe = cando_probe,
.remove = cando_remove,
.input_mapping = cando_input_mapping,
.input_mapped = cando_input_mapped,
.usage_table = cando_grabbed_usages,
.event = cando_event,
};
static int __init cando_init(void)
{
return hid_register_driver(&cando_driver);
}
static void __exit cando_exit(void)
{
hid_unregister_driver(&cando_driver);
}
module_init(cando_init);
module_exit(cando_exit);

View file

@ -653,10 +653,9 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
if (device->driver->report_fixup)
device->driver->report_fixup(device, start, size);
device->rdesc = kmalloc(size, GFP_KERNEL);
device->rdesc = kmemdup(start, size, GFP_KERNEL);
if (device->rdesc == NULL)
return -ENOMEM;
memcpy(device->rdesc, start, size);
device->rsize = size;
parser = vmalloc(sizeof(struct hid_parser));
@ -940,13 +939,8 @@ static void hid_output_field(struct hid_field *field, __u8 *data)
unsigned count = field->report_count;
unsigned offset = field->report_offset;
unsigned size = field->report_size;
unsigned bitsused = offset + count * size;
unsigned n;
/* make sure the unused bits in the last byte are zeros */
if (count > 0 && size > 0 && (bitsused % 8) != 0)
data[(bitsused-1)/8] &= (1 << (bitsused % 8)) - 1;
for (n = 0; n < count; n++) {
if (field->logical_minimum < 0) /* signed values */
implement(data, offset + n * size, size, s32ton(field->value[n], size));
@ -966,6 +960,7 @@ void hid_output_report(struct hid_report *report, __u8 *data)
if (report->id > 0)
*data++ = report->id;
memset(data, 0, ((report->size - 1) >> 3) + 1);
for (n = 0; n < report->maxfield; n++)
hid_output_field(report->field[n], data);
}
@ -1086,35 +1081,28 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC);
if (!buf) {
report = hid_get_report(report_enum, data);
if (!buf)
goto nomem;
}
snprintf(buf, HID_DEBUG_BUFSIZE - 1,
"\nreport (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
hid_debug_event(hid, buf);
report = hid_get_report(report_enum, data);
if (!report) {
kfree(buf);
return -1;
}
/* dump the report */
snprintf(buf, HID_DEBUG_BUFSIZE - 1,
"report %d (size %u) = ", report->id, size);
"\nreport (size %u) (%snumbered) = ", size, report_enum->numbered ? "" : "un");
hid_debug_event(hid, buf);
for (i = 0; i < size; i++) {
snprintf(buf, HID_DEBUG_BUFSIZE - 1,
" %02x", data[i]);
hid_debug_event(hid, buf);
}
hid_debug_event(hid, "\n");
kfree(buf);
nomem:
report = hid_get_report(report_enum, data);
if (!report)
return -1;
if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
ret = hdrv->raw_event(hid, report, data, size);
if (ret != 0)
@ -1167,6 +1155,8 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
unsigned int i;
int len;
if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE)
connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV);
if (hdev->bus != BUS_USB)
connect_mask &= ~HID_CONNECT_HIDDEV;
if (hid_hiddev(hdev))
@ -1246,6 +1236,7 @@ EXPORT_SYMBOL_GPL(hid_disconnect);
/* a list of devices for which there is a specialized driver on HID bus */
static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
{ 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) },
@ -1290,14 +1281,19 @@ static const struct hid_device_id hid_blacklist[] = {
{ 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_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) },
{ 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_BARCODE_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) },
@ -1331,6 +1327,8 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
{ 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) },
@ -1342,7 +1340,9 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
@ -1359,8 +1359,10 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
{ }
@ -1757,7 +1759,7 @@ int hid_add_device(struct hid_device *hdev)
/* we need to kill them here, otherwise they will stay allocated to
* wait for coming driver */
if (hid_ignore(hdev))
if (!(hdev->quirks & HID_QUIRK_NO_IGNORE) && hid_ignore(hdev))
return -ENODEV;
/* XXX hack, any other cleaner solution after the driver core
@ -1765,11 +1767,12 @@ int hid_add_device(struct hid_device *hdev)
dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus,
hdev->vendor, hdev->product, atomic_inc_return(&id));
hid_debug_register(hdev, dev_name(&hdev->dev));
ret = device_add(&hdev->dev);
if (!ret)
hdev->status |= HID_STAT_ADDED;
hid_debug_register(hdev, dev_name(&hdev->dev));
else
hid_debug_unregister(hdev);
return ret;
}

281
drivers/hid/hid-egalax.c Normal file
View file

@ -0,0 +1,281 @@
/*
* HID driver for eGalax dual-touch panels
*
* Copyright (c) 2010 Stephane Chatty <chatty@enac.fr>
*
*/
/*
* 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 <linux/slab.h>
#include "usbhid/usbhid.h"
MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
MODULE_DESCRIPTION("eGalax dual-touch panel");
MODULE_LICENSE("GPL");
#include "hid-ids.h"
struct egalax_data {
__u16 x, y, z;
__u8 id;
bool first; /* is this the first finger in the frame? */
bool valid; /* valid finger data, or just placeholder? */
bool activity; /* at least one active finger previously? */
__u16 lastx, lasty; /* latest valid (x, y) in the frame */
};
static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
switch (usage->hid & HID_USAGE_PAGE) {
case HID_UP_GENDESK:
switch (usage->hid) {
case HID_GD_X:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_POSITION_X);
/* touchscreen emulation */
input_set_abs_params(hi->input, ABS_X,
field->logical_minimum,
field->logical_maximum, 0, 0);
return 1;
case HID_GD_Y:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_POSITION_Y);
/* touchscreen emulation */
input_set_abs_params(hi->input, ABS_Y,
field->logical_minimum,
field->logical_maximum, 0, 0);
return 1;
}
return 0;
case HID_UP_DIGITIZER:
switch (usage->hid) {
case HID_DG_TIPSWITCH:
/* touchscreen emulation */
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
return 1;
case HID_DG_INRANGE:
case HID_DG_CONFIDENCE:
case HID_DG_CONTACTCOUNT:
case HID_DG_CONTACTMAX:
return -1;
case HID_DG_CONTACTID:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TRACKING_ID);
return 1;
case HID_DG_TIPPRESSURE:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_PRESSURE);
return 1;
}
return 0;
}
/* ignore others (from other reports we won't get anyway) */
return -1;
}
static int egalax_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if (usage->type == EV_KEY || usage->type == EV_ABS)
clear_bit(usage->code, *bit);
return 0;
}
/*
* this function is called when a whole finger has been parsed,
* so that it can decide what to send to the input layer.
*/
static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
{
td->first = !td->first; /* touchscreen emulation */
if (td->valid) {
/* emit multitouch events */
input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id);
input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y);
input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z);
input_mt_sync(input);
/*
* touchscreen emulation: store (x, y) as
* the last valid values in this frame
*/
td->lastx = td->x;
td->lasty = td->y;
}
/*
* touchscreen emulation: if this is the second finger and at least
* one in this frame is valid, the latest valid in the frame is
* the oldest on the panel, the one we want for single touch
*/
if (!td->first && td->activity) {
input_event(input, EV_ABS, ABS_X, td->lastx);
input_event(input, EV_ABS, ABS_Y, td->lasty);
}
if (!td->valid) {
/*
* touchscreen emulation: if the first finger is invalid
* and there previously was finger activity, this is a release
*/
if (td->first && td->activity) {
input_event(input, EV_KEY, BTN_TOUCH, 0);
td->activity = false;
}
return;
}
/* touchscreen emulation: if no previous activity, emit touch event */
if (!td->activity) {
input_event(input, EV_KEY, BTN_TOUCH, 1);
td->activity = true;
}
}
static int egalax_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct egalax_data *td = hid_get_drvdata(hid);
if (hid->claimed & HID_CLAIMED_INPUT) {
struct input_dev *input = field->hidinput->input;
switch (usage->hid) {
case HID_DG_INRANGE:
case HID_DG_CONFIDENCE:
/* avoid interference from generic hidinput handling */
break;
case HID_DG_TIPSWITCH:
td->valid = value;
break;
case HID_DG_TIPPRESSURE:
td->z = value;
break;
case HID_DG_CONTACTID:
td->id = value;
break;
case HID_GD_X:
td->x = value;
break;
case HID_GD_Y:
td->y = value;
/* this is the last field in a finger */
egalax_filter_event(td, input);
break;
case HID_DG_CONTACTCOUNT:
/* touch emulation: this is the last field in a frame */
td->first = false;
break;
default:
/* fallback to the generic hidinput handling */
return 0;
}
}
/* we have handled the hidinput part, now remains hiddev */
if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
hid->hiddev_hid_event(hid, field, usage, value);
return 1;
}
static int egalax_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
struct egalax_data *td;
struct hid_report *report;
td = kmalloc(sizeof(struct egalax_data), GFP_KERNEL);
if (!td) {
dev_err(&hdev->dev, "cannot allocate eGalax data\n");
return -ENOMEM;
}
hid_set_drvdata(hdev, td);
ret = hid_parse(hdev);
if (ret)
goto end;
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret)
goto end;
report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[5];
if (report) {
report->field[0]->value[0] = 2;
usbhid_submit_report(hdev, report, USB_DIR_OUT);
}
end:
if (ret)
kfree(td);
return ret;
}
static void egalax_remove(struct hid_device *hdev)
{
hid_hw_stop(hdev);
kfree(hid_get_drvdata(hdev));
hid_set_drvdata(hdev, NULL);
}
static const struct hid_device_id egalax_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
{ }
};
MODULE_DEVICE_TABLE(hid, egalax_devices);
static const struct hid_usage_id egalax_grabbed_usages[] = {
{ HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
};
static struct hid_driver egalax_driver = {
.name = "egalax-touch",
.id_table = egalax_devices,
.probe = egalax_probe,
.remove = egalax_remove,
.input_mapping = egalax_input_mapping,
.input_mapped = egalax_input_mapped,
.usage_table = egalax_grabbed_usages,
.event = egalax_event,
};
static int __init egalax_init(void)
{
return hid_register_driver(&egalax_driver);
}
static void __exit egalax_exit(void)
{
hid_unregister_driver(&egalax_driver);
}
module_init(egalax_init);
module_exit(egalax_exit);

View file

@ -20,6 +20,7 @@
#define USB_VENDOR_ID_3M 0x0596
#define USB_DEVICE_ID_3M1968 0x0500
#define USB_DEVICE_ID_3M2256 0x0502
#define USB_VENDOR_ID_A4TECH 0x09da
#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
@ -123,6 +124,13 @@
#define USB_VENDOR_ID_BERKSHIRE 0x0c98
#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140
#define USB_VENDOR_ID_BTC 0x046e
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578
#define USB_VENDOR_ID_CANDO 0x2087
#define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01
#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6 0x0b03
#define USB_VENDOR_ID_CH 0x068e
#define USB_DEVICE_ID_CH_PRO_PEDALS 0x00f2
#define USB_DEVICE_ID_CH_COMBATSTICK 0x00f4
@ -148,6 +156,9 @@
#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500
#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff
#define USB_VENDOR_ID_CREATIVELABS 0x041e
#define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801
#define USB_VENDOR_ID_CYGNAL 0x10c4
#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a
@ -171,6 +182,10 @@
#define USB_VENDOR_ID_DRAGONRISE 0x0079
#define USB_VENDOR_ID_DWAV 0x0eef
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d
#define USB_VENDOR_ID_ELO 0x04E7
#define USB_DEVICE_ID_ELO_TS2700 0x0020
@ -342,6 +357,8 @@
#define USB_VENDOR_ID_MICROCHIP 0x04d8
#define USB_DEVICE_ID_PICKIT1 0x0032
#define USB_DEVICE_ID_PICKIT2 0x0033
#define USB_DEVICE_ID_PICOLCD 0xc002
#define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002
#define USB_VENDOR_ID_MICROSOFT 0x045e
#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b
@ -400,6 +417,9 @@
#define USB_VENDOR_ID_PRODIGE 0x05af
#define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062
#define USB_VENDOR_ID_ROCCAT 0x1e7d
#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced
#define USB_VENDOR_ID_SAITEK 0x06a3
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
@ -409,6 +429,7 @@
#define USB_VENDOR_ID_SAMSUNG 0x0419
#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001
#define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE 0x0600
#define USB_VENDOR_ID_SONY 0x054c
#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b
@ -457,6 +478,7 @@
#define USB_VENDOR_ID_WACOM 0x056a
#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81
#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0xbd
#define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
@ -475,6 +497,9 @@
#define USB_VENDOR_ID_ZEROPLUS 0x0c12
#define USB_VENDOR_ID_ZYDACRON 0x13EC
#define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL 0x0006
#define USB_VENDOR_ID_KYE 0x0458
#define USB_DEVICE_ID_KYE_ERGO_525V 0x0087
#define USB_DEVICE_ID_KYE_GPEN_560 0x5003

View file

@ -126,6 +126,9 @@ static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
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;
/* The following two entries are Playlist 1 and 2 on the MX3200 */
case 0x100f: lg_map_key_clear(KEY_FN_1); break;
case 0x1010: lg_map_key_clear(KEY_FN_2); 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;
@ -137,6 +140,7 @@ static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
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 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS); 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;
@ -147,6 +151,11 @@ static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
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 0x102d: lg_map_key_clear(KEY_WWW); break;
/* The following two are 'Start/answer call' and 'End/reject call'
on the MX3200 */
case 0x1031: lg_map_key_clear(KEY_OK); break;
case 0x1032: lg_map_key_clear(KEY_CANCEL); 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;

View file

@ -354,12 +354,15 @@ static int magicmouse_probe(struct hid_device *hdev,
goto err_free;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT);
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
dev_err(&hdev->dev, "magicmouse hw start failed\n");
goto err_free;
}
/* we are handling the input ourselves */
hidinput_disconnect(hdev);
report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID);
if (!report) {
dev_err(&hdev->dev, "unable to register touch report\n");

View file

@ -24,6 +24,34 @@
#define NTRIG_DUPLICATE_USAGES 0x001
static unsigned int min_width;
module_param(min_width, uint, 0644);
MODULE_PARM_DESC(min_width, "Minimum touch contact width to accept.");
static unsigned int min_height;
module_param(min_height, uint, 0644);
MODULE_PARM_DESC(min_height, "Minimum touch contact height to accept.");
static unsigned int activate_slack = 1;
module_param(activate_slack, uint, 0644);
MODULE_PARM_DESC(activate_slack, "Number of touch frames to ignore at "
"the start of touch input.");
static unsigned int deactivate_slack = 4;
module_param(deactivate_slack, uint, 0644);
MODULE_PARM_DESC(deactivate_slack, "Number of empty frames to ignore before "
"deactivating touch.");
static unsigned int activation_width = 64;
module_param(activation_width, uint, 0644);
MODULE_PARM_DESC(activation_width, "Width threshold to immediately start "
"processing touch events.");
static unsigned int activation_height = 32;
module_param(activation_height, uint, 0644);
MODULE_PARM_DESC(activation_height, "Height threshold to immediately start "
"processing touch events.");
struct ntrig_data {
/* Incoming raw values for a single contact */
__u16 x, y, w, h;
@ -37,6 +65,309 @@ struct ntrig_data {
__u8 mt_footer[4];
__u8 mt_foot_count;
/* The current activation state. */
__s8 act_state;
/* Empty frames to ignore before recognizing the end of activity */
__s8 deactivate_slack;
/* Frames to ignore before acknowledging the start of activity */
__s8 activate_slack;
/* Minimum size contact to accept */
__u16 min_width;
__u16 min_height;
/* Threshold to override activation slack */
__u16 activation_width;
__u16 activation_height;
__u16 sensor_logical_width;
__u16 sensor_logical_height;
__u16 sensor_physical_width;
__u16 sensor_physical_height;
};
static ssize_t show_phys_width(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->sensor_physical_width);
}
static DEVICE_ATTR(sensor_physical_width, S_IRUGO, show_phys_width, NULL);
static ssize_t show_phys_height(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->sensor_physical_height);
}
static DEVICE_ATTR(sensor_physical_height, S_IRUGO, show_phys_height, NULL);
static ssize_t show_log_width(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->sensor_logical_width);
}
static DEVICE_ATTR(sensor_logical_width, S_IRUGO, show_log_width, NULL);
static ssize_t show_log_height(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->sensor_logical_height);
}
static DEVICE_ATTR(sensor_logical_height, S_IRUGO, show_log_height, NULL);
static ssize_t show_min_width(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->min_width *
nd->sensor_physical_width /
nd->sensor_logical_width);
}
static ssize_t set_min_width(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
if (strict_strtoul(buf, 0, &val))
return -EINVAL;
if (val > nd->sensor_physical_width)
return -EINVAL;
nd->min_width = val * nd->sensor_logical_width /
nd->sensor_physical_width;
return count;
}
static DEVICE_ATTR(min_width, S_IWUSR | S_IRUGO, show_min_width, set_min_width);
static ssize_t show_min_height(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->min_height *
nd->sensor_physical_height /
nd->sensor_logical_height);
}
static ssize_t set_min_height(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
if (strict_strtoul(buf, 0, &val))
return -EINVAL;
if (val > nd->sensor_physical_height)
return -EINVAL;
nd->min_height = val * nd->sensor_logical_height /
nd->sensor_physical_height;
return count;
}
static DEVICE_ATTR(min_height, S_IWUSR | S_IRUGO, show_min_height,
set_min_height);
static ssize_t show_activate_slack(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->activate_slack);
}
static ssize_t set_activate_slack(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
if (strict_strtoul(buf, 0, &val))
return -EINVAL;
if (val > 0x7f)
return -EINVAL;
nd->activate_slack = val;
return count;
}
static DEVICE_ATTR(activate_slack, S_IWUSR | S_IRUGO, show_activate_slack,
set_activate_slack);
static ssize_t show_activation_width(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->activation_width *
nd->sensor_physical_width /
nd->sensor_logical_width);
}
static ssize_t set_activation_width(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
if (strict_strtoul(buf, 0, &val))
return -EINVAL;
if (val > nd->sensor_physical_width)
return -EINVAL;
nd->activation_width = val * nd->sensor_logical_width /
nd->sensor_physical_width;
return count;
}
static DEVICE_ATTR(activation_width, S_IWUSR | S_IRUGO, show_activation_width,
set_activation_width);
static ssize_t show_activation_height(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->activation_height *
nd->sensor_physical_height /
nd->sensor_logical_height);
}
static ssize_t set_activation_height(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
if (strict_strtoul(buf, 0, &val))
return -EINVAL;
if (val > nd->sensor_physical_height)
return -EINVAL;
nd->activation_height = val * nd->sensor_logical_height /
nd->sensor_physical_height;
return count;
}
static DEVICE_ATTR(activation_height, S_IWUSR | S_IRUGO,
show_activation_height, set_activation_height);
static ssize_t show_deactivate_slack(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", -nd->deactivate_slack);
}
static ssize_t set_deactivate_slack(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
if (strict_strtoul(buf, 0, &val))
return -EINVAL;
/*
* No more than 8 terminal frames have been observed so far
* and higher slack is highly likely to leave the single
* touch emulation stuck down.
*/
if (val > 7)
return -EINVAL;
nd->deactivate_slack = -val;
return count;
}
static DEVICE_ATTR(deactivate_slack, S_IWUSR | S_IRUGO, show_deactivate_slack,
set_deactivate_slack);
static struct attribute *sysfs_attrs[] = {
&dev_attr_sensor_physical_width.attr,
&dev_attr_sensor_physical_height.attr,
&dev_attr_sensor_logical_width.attr,
&dev_attr_sensor_logical_height.attr,
&dev_attr_min_height.attr,
&dev_attr_min_width.attr,
&dev_attr_activate_slack.attr,
&dev_attr_activation_width.attr,
&dev_attr_activation_height.attr,
&dev_attr_deactivate_slack.attr,
NULL
};
static struct attribute_group ntrig_attribute_group = {
.attrs = sysfs_attrs
};
/*
@ -49,6 +380,8 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
struct ntrig_data *nd = hid_get_drvdata(hdev);
/* No special mappings needed for the pen and single touch */
if (field->physical)
return 0;
@ -62,6 +395,21 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
input_set_abs_params(hi->input, ABS_X,
field->logical_minimum,
field->logical_maximum, 0, 0);
if (!nd->sensor_logical_width) {
nd->sensor_logical_width =
field->logical_maximum -
field->logical_minimum;
nd->sensor_physical_width =
field->physical_maximum -
field->physical_minimum;
nd->activation_width = activation_width *
nd->sensor_logical_width /
nd->sensor_physical_width;
nd->min_width = min_width *
nd->sensor_logical_width /
nd->sensor_physical_width;
}
return 1;
case HID_GD_Y:
hid_map_usage(hi, usage, bit, max,
@ -69,6 +417,21 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
input_set_abs_params(hi->input, ABS_Y,
field->logical_minimum,
field->logical_maximum, 0, 0);
if (!nd->sensor_logical_height) {
nd->sensor_logical_height =
field->logical_maximum -
field->logical_minimum;
nd->sensor_physical_height =
field->physical_maximum -
field->physical_minimum;
nd->activation_height = activation_height *
nd->sensor_logical_height /
nd->sensor_physical_height;
nd->min_height = min_height *
nd->sensor_logical_height /
nd->sensor_physical_height;
}
return 1;
}
return 0;
@ -201,20 +564,68 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
if (nd->mt_foot_count != 4)
break;
/* Pen activity signal, trigger end of touch. */
/* Pen activity signal. */
if (nd->mt_footer[2]) {
/*
* When the pen deactivates touch, we see a
* bogus frame with ContactCount > 0.
* We can
* save a bit of work by ensuring act_state < 0
* even if deactivation slack is turned off.
*/
nd->act_state = deactivate_slack - 1;
nd->confidence = 0;
break;
}
/* If the contact was invalid */
if (!(nd->confidence && nd->mt_footer[0])
|| nd->w <= 250
|| nd->h <= 190) {
nd->confidence = 0;
/*
* The first footer value indicates the presence of a
* finger.
*/
if (nd->mt_footer[0]) {
/*
* We do not want to process contacts under
* the size threshold, but do not want to
* ignore them for activation state
*/
if (nd->w < nd->min_width ||
nd->h < nd->min_height)
nd->confidence = 0;
} else
break;
if (nd->act_state > 0) {
/*
* Contact meets the activation size threshold
*/
if (nd->w >= nd->activation_width &&
nd->h >= nd->activation_height) {
if (nd->id)
/*
* first contact, activate now
*/
nd->act_state = 0;
else {
/*
* avoid corrupting this frame
* but ensure next frame will
* be active
*/
nd->act_state = 1;
break;
}
} else
/*
* Defer adjusting the activation state
* until the end of the frame.
*/
break;
}
/* Discarding this contact */
if (!nd->confidence)
break;
/* emit a normal (X, Y) for the first point only */
if (nd->id == 0) {
/*
@ -227,8 +638,15 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
input_event(input, EV_ABS, ABS_X, nd->x);
input_event(input, EV_ABS, ABS_Y, nd->y);
}
/* Emit MT events */
input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y);
/*
* Translate from height and width to size
* and orientation.
*/
if (nd->w > nd->h) {
input_event(input, EV_ABS,
ABS_MT_ORIENTATION, 1);
@ -248,12 +666,88 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
break;
case HID_DG_CONTACTCOUNT: /* End of a multitouch group */
if (!nd->reading_mt)
if (!nd->reading_mt) /* Just to be sure */
break;
nd->reading_mt = 0;
if (nd->first_contact_touch) {
/*
* Activation state machine logic:
*
* Fundamental states:
* state > 0: Inactive
* state <= 0: Active
* state < -deactivate_slack:
* Pen termination of touch
*
* Specific values of interest
* state == activate_slack
* no valid input since the last reset
*
* state == 0
* general operational state
*
* state == -deactivate_slack
* read sufficient empty frames to accept
* the end of input and reset
*/
if (nd->act_state > 0) { /* Currently inactive */
if (value)
/*
* Consider each live contact as
* evidence of intentional activity.
*/
nd->act_state = (nd->act_state > value)
? nd->act_state - value
: 0;
else
/*
* Empty frame before we hit the
* activity threshold, reset.
*/
nd->act_state = nd->activate_slack;
/*
* Entered this block inactive and no
* coordinates sent this frame, so hold off
* on button state.
*/
break;
} else { /* Currently active */
if (value && nd->act_state >=
nd->deactivate_slack)
/*
* Live point: clear accumulated
* deactivation count.
*/
nd->act_state = 0;
else if (nd->act_state <= nd->deactivate_slack)
/*
* We've consumed the deactivation
* slack, time to deactivate and reset.
*/
nd->act_state =
nd->activate_slack;
else { /* Move towards deactivation */
nd->act_state--;
break;
}
}
if (nd->first_contact_touch && nd->act_state <= 0) {
/*
* Check to see if we're ready to start
* emitting touch events.
*
* Note: activation slack will decrease over
* the course of the frame, and it will be
* inconsistent from the start to the end of
* the frame. However if the frame starts
* with slack, first_contact_touch will still
* be 0 and we will not get to this point.
*/
input_report_key(input, BTN_TOOL_DOUBLETAP, 1);
input_report_key(input, BTN_TOUCH, 1);
} else {
@ -263,7 +757,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
break;
default:
/* fallback to the generic hidinput handling */
/* fall-back to the generic hidinput handling */
return 0;
}
}
@ -293,6 +787,16 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
nd->reading_mt = 0;
nd->min_width = 0;
nd->min_height = 0;
nd->activate_slack = activate_slack;
nd->act_state = activate_slack;
nd->deactivate_slack = -deactivate_slack;
nd->sensor_logical_width = 0;
nd->sensor_logical_height = 0;
nd->sensor_physical_width = 0;
nd->sensor_physical_height = 0;
hid_set_drvdata(hdev, nd);
ret = hid_parse(hdev);
@ -344,6 +848,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (report)
usbhid_submit_report(hdev, report, USB_DIR_OUT);
ret = sysfs_create_group(&hdev->dev.kobj,
&ntrig_attribute_group);
return 0;
err_free:
@ -353,6 +859,8 @@ err_free:
static void ntrig_remove(struct hid_device *hdev)
{
sysfs_remove_group(&hdev->dev.kobj,
&ntrig_attribute_group);
hid_hw_stop(hdev);
kfree(hid_get_drvdata(hdev));
}

2631
drivers/hid/hid-picolcd.c Normal file

File diff suppressed because it is too large Load diff

910
drivers/hid/hid-prodikeys.c Normal file
View file

@ -0,0 +1,910 @@
/*
* HID driver for the Prodikeys PC-MIDI Keyboard
* providing midi & extra multimedia keys functionality
*
* Copyright (c) 2009 Don Prince <dhprince.devel@yahoo.co.uk>
*
* Controls for Octave Shift Up/Down, Channel, and
* Sustain Duration available via sysfs.
*
*/
/*
* 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/module.h>
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/hid.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
#include "usbhid/usbhid.h"
#include "hid-ids.h"
#define pk_debug(format, arg...) \
pr_debug("hid-prodikeys: " format "\n" , ## arg)
#define pk_error(format, arg...) \
pr_err("hid-prodikeys: " format "\n" , ## arg)
struct pcmidi_snd;
struct pk_device {
unsigned long quirks;
struct hid_device *hdev;
struct pcmidi_snd *pm; /* pcmidi device context */
};
struct pcmidi_snd;
struct pcmidi_sustain {
unsigned long in_use;
struct pcmidi_snd *pm;
struct timer_list timer;
unsigned char status;
unsigned char note;
unsigned char velocity;
};
#define PCMIDI_SUSTAINED_MAX 32
struct pcmidi_snd {
struct pk_device *pk;
unsigned short ifnum;
struct hid_report *pcmidi_report6;
struct input_dev *input_ep82;
unsigned short midi_mode;
unsigned short midi_sustain_mode;
unsigned short midi_sustain;
unsigned short midi_channel;
short midi_octave;
struct pcmidi_sustain sustained_notes[PCMIDI_SUSTAINED_MAX];
unsigned short fn_state;
unsigned short last_key[24];
spinlock_t rawmidi_in_lock;
struct snd_card *card;
struct snd_rawmidi *rwmidi;
struct snd_rawmidi_substream *in_substream;
struct snd_rawmidi_substream *out_substream;
unsigned long in_triggered;
unsigned long out_active;
};
#define PK_QUIRK_NOGET 0x00010000
#define PCMIDI_MIDDLE_C 60
#define PCMIDI_CHANNEL_MIN 0
#define PCMIDI_CHANNEL_MAX 15
#define PCMIDI_OCTAVE_MIN (-2)
#define PCMIDI_OCTAVE_MAX 2
#define PCMIDI_SUSTAIN_MIN 0
#define PCMIDI_SUSTAIN_MAX 5000
static const char shortname[] = "PC-MIDI";
static const char longname[] = "Prodikeys PC-MIDI Keyboard";
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
module_param_array(id, charp, NULL, 0444);
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the PC-MIDI virtual audio driver");
MODULE_PARM_DESC(id, "ID string for the PC-MIDI virtual audio driver");
MODULE_PARM_DESC(enable, "Enable for the PC-MIDI virtual audio driver");
/* Output routine for the sysfs channel file */
static ssize_t show_channel(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel);
return sprintf(buf, "%u (min:%u, max:%u)\n", pk->pm->midi_channel,
PCMIDI_CHANNEL_MIN, PCMIDI_CHANNEL_MAX);
}
/* Input routine for the sysfs channel file */
static ssize_t store_channel(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
unsigned channel = 0;
if (sscanf(buf, "%u", &channel) > 0 && channel <= PCMIDI_CHANNEL_MAX) {
dbg_hid("pcmidi sysfs write channel=%u\n", channel);
pk->pm->midi_channel = channel;
return strlen(buf);
}
return -EINVAL;
}
static DEVICE_ATTR(channel, S_IRUGO | S_IWUGO, show_channel,
store_channel);
static struct device_attribute *sysfs_device_attr_channel = {
&dev_attr_channel,
};
/* Output routine for the sysfs sustain file */
static ssize_t show_sustain(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain);
return sprintf(buf, "%u (off:%u, max:%u (ms))\n", pk->pm->midi_sustain,
PCMIDI_SUSTAIN_MIN, PCMIDI_SUSTAIN_MAX);
}
/* Input routine for the sysfs sustain file */
static ssize_t store_sustain(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
unsigned sustain = 0;
if (sscanf(buf, "%u", &sustain) > 0 && sustain <= PCMIDI_SUSTAIN_MAX) {
dbg_hid("pcmidi sysfs write sustain=%u\n", sustain);
pk->pm->midi_sustain = sustain;
pk->pm->midi_sustain_mode =
(0 == sustain || !pk->pm->midi_mode) ? 0 : 1;
return strlen(buf);
}
return -EINVAL;
}
static DEVICE_ATTR(sustain, S_IRUGO | S_IWUGO, show_sustain,
store_sustain);
static struct device_attribute *sysfs_device_attr_sustain = {
&dev_attr_sustain,
};
/* Output routine for the sysfs octave file */
static ssize_t show_octave(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave);
return sprintf(buf, "%d (min:%d, max:%d)\n", pk->pm->midi_octave,
PCMIDI_OCTAVE_MIN, PCMIDI_OCTAVE_MAX);
}
/* Input routine for the sysfs octave file */
static ssize_t store_octave(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
int octave = 0;
if (sscanf(buf, "%d", &octave) > 0 &&
octave >= PCMIDI_OCTAVE_MIN && octave <= PCMIDI_OCTAVE_MAX) {
dbg_hid("pcmidi sysfs write octave=%d\n", octave);
pk->pm->midi_octave = octave;
return strlen(buf);
}
return -EINVAL;
}
static DEVICE_ATTR(octave, S_IRUGO | S_IWUGO, show_octave,
store_octave);
static struct device_attribute *sysfs_device_attr_octave = {
&dev_attr_octave,
};
static void pcmidi_send_note(struct pcmidi_snd *pm,
unsigned char status, unsigned char note, unsigned char velocity)
{
unsigned long flags;
unsigned char buffer[3];
buffer[0] = status;
buffer[1] = note;
buffer[2] = velocity;
spin_lock_irqsave(&pm->rawmidi_in_lock, flags);
if (!pm->in_substream)
goto drop_note;
if (!test_bit(pm->in_substream->number, &pm->in_triggered))
goto drop_note;
snd_rawmidi_receive(pm->in_substream, buffer, 3);
drop_note:
spin_unlock_irqrestore(&pm->rawmidi_in_lock, flags);
return;
}
void pcmidi_sustained_note_release(unsigned long data)
{
struct pcmidi_sustain *pms = (struct pcmidi_sustain *)data;
pcmidi_send_note(pms->pm, pms->status, pms->note, pms->velocity);
pms->in_use = 0;
}
void init_sustain_timers(struct pcmidi_snd *pm)
{
struct pcmidi_sustain *pms;
unsigned i;
for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) {
pms = &pm->sustained_notes[i];
pms->in_use = 0;
pms->pm = pm;
setup_timer(&pms->timer, pcmidi_sustained_note_release,
(unsigned long)pms);
}
}
void stop_sustain_timers(struct pcmidi_snd *pm)
{
struct pcmidi_sustain *pms;
unsigned i;
for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) {
pms = &pm->sustained_notes[i];
pms->in_use = 1;
del_timer_sync(&pms->timer);
}
}
static int pcmidi_get_output_report(struct pcmidi_snd *pm)
{
struct hid_device *hdev = pm->pk->hdev;
struct hid_report *report;
list_for_each_entry(report,
&hdev->report_enum[HID_OUTPUT_REPORT].report_list, list) {
if (!(6 == report->id))
continue;
if (report->maxfield < 1) {
dev_err(&hdev->dev, "output report is empty\n");
break;
}
if (report->field[0]->report_count != 2) {
dev_err(&hdev->dev, "field count too low\n");
break;
}
pm->pcmidi_report6 = report;
return 0;
}
/* should never get here */
return -ENODEV;
}
static void pcmidi_submit_output_report(struct pcmidi_snd *pm, int state)
{
struct hid_device *hdev = pm->pk->hdev;
struct hid_report *report = pm->pcmidi_report6;
report->field[0]->value[0] = 0x01;
report->field[0]->value[1] = state;
usbhid_submit_report(hdev, report, USB_DIR_OUT);
}
static int pcmidi_handle_report1(struct pcmidi_snd *pm, u8 *data)
{
u32 bit_mask;
bit_mask = data[1];
bit_mask = (bit_mask << 8) | data[2];
bit_mask = (bit_mask << 8) | data[3];
dbg_hid("pcmidi mode: %d\n", pm->midi_mode);
/*KEY_MAIL or octave down*/
if (pm->midi_mode && bit_mask == 0x004000) {
/* octave down */
pm->midi_octave--;
if (pm->midi_octave < -2)
pm->midi_octave = -2;
dbg_hid("pcmidi mode: %d octave: %d\n",
pm->midi_mode, pm->midi_octave);
return 1;
}
/*KEY_WWW or sustain*/
else if (pm->midi_mode && bit_mask == 0x000004) {
/* sustain on/off*/
pm->midi_sustain_mode ^= 0x1;
return 1;
}
return 0; /* continue key processing */
}
static int pcmidi_handle_report3(struct pcmidi_snd *pm, u8 *data, int size)
{
struct pcmidi_sustain *pms;
unsigned i, j;
unsigned char status, note, velocity;
unsigned num_notes = (size-1)/2;
for (j = 0; j < num_notes; j++) {
note = data[j*2+1];
velocity = data[j*2+2];
if (note < 0x81) { /* note on */
status = 128 + 16 + pm->midi_channel; /* 1001nnnn */
note = note - 0x54 + PCMIDI_MIDDLE_C +
(pm->midi_octave * 12);
if (0 == velocity)
velocity = 1; /* force note on */
} else { /* note off */
status = 128 + pm->midi_channel; /* 1000nnnn */
note = note - 0x94 + PCMIDI_MIDDLE_C +
(pm->midi_octave*12);
if (pm->midi_sustain_mode) {
for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) {
pms = &pm->sustained_notes[i];
if (!pms->in_use) {
pms->status = status;
pms->note = note;
pms->velocity = velocity;
pms->in_use = 1;
mod_timer(&pms->timer,
jiffies +
msecs_to_jiffies(pm->midi_sustain));
return 1;
}
}
}
}
pcmidi_send_note(pm, status, note, velocity);
}
return 1;
}
static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)
{
unsigned key;
u32 bit_mask;
u32 bit_index;
bit_mask = data[1];
bit_mask = (bit_mask << 8) | data[2];
bit_mask = (bit_mask << 8) | data[3];
/* break keys */
for (bit_index = 0; bit_index < 24; bit_index++) {
key = pm->last_key[bit_index];
if (!((0x01 << bit_index) & bit_mask)) {
input_event(pm->input_ep82, EV_KEY,
pm->last_key[bit_index], 0);
pm->last_key[bit_index] = 0;
}
}
/* make keys */
for (bit_index = 0; bit_index < 24; bit_index++) {
key = 0;
switch ((0x01 << bit_index) & bit_mask) {
case 0x000010: /* Fn lock*/
pm->fn_state ^= 0x000010;
if (pm->fn_state)
pcmidi_submit_output_report(pm, 0xc5);
else
pcmidi_submit_output_report(pm, 0xc6);
continue;
case 0x020000: /* midi launcher..send a key (qwerty) or not? */
pcmidi_submit_output_report(pm, 0xc1);
pm->midi_mode ^= 0x01;
dbg_hid("pcmidi mode: %d\n", pm->midi_mode);
continue;
case 0x100000: /* KEY_MESSENGER or octave up */
dbg_hid("pcmidi mode: %d\n", pm->midi_mode);
if (pm->midi_mode) {
pm->midi_octave++;
if (pm->midi_octave > 2)
pm->midi_octave = 2;
dbg_hid("pcmidi mode: %d octave: %d\n",
pm->midi_mode, pm->midi_octave);
continue;
} else
key = KEY_MESSENGER;
break;
case 0x400000:
key = KEY_CALENDAR;
break;
case 0x080000:
key = KEY_ADDRESSBOOK;
break;
case 0x040000:
key = KEY_DOCUMENTS;
break;
case 0x800000:
key = KEY_WORDPROCESSOR;
break;
case 0x200000:
key = KEY_SPREADSHEET;
break;
case 0x010000:
key = KEY_COFFEE;
break;
case 0x000100:
key = KEY_HELP;
break;
case 0x000200:
key = KEY_SEND;
break;
case 0x000400:
key = KEY_REPLY;
break;
case 0x000800:
key = KEY_FORWARDMAIL;
break;
case 0x001000:
key = KEY_NEW;
break;
case 0x002000:
key = KEY_OPEN;
break;
case 0x004000:
key = KEY_CLOSE;
break;
case 0x008000:
key = KEY_SAVE;
break;
case 0x000001:
key = KEY_UNDO;
break;
case 0x000002:
key = KEY_REDO;
break;
case 0x000004:
key = KEY_SPELLCHECK;
break;
case 0x000008:
key = KEY_PRINT;
break;
}
if (key) {
input_event(pm->input_ep82, EV_KEY, key, 1);
pm->last_key[bit_index] = key;
}
}
return 1;
}
int pcmidi_handle_report(
struct pcmidi_snd *pm, unsigned report_id, u8 *data, int size)
{
int ret = 0;
switch (report_id) {
case 0x01: /* midi keys (qwerty)*/
ret = pcmidi_handle_report1(pm, data);
break;
case 0x03: /* midi keyboard (musical)*/
ret = pcmidi_handle_report3(pm, data, size);
break;
case 0x04: /* multimedia/midi keys (qwerty)*/
ret = pcmidi_handle_report4(pm, data);
break;
}
return ret;
}
void pcmidi_setup_extra_keys(struct pcmidi_snd *pm, struct input_dev *input)
{
/* reassigned functionality for N/A keys
MY PICTURES => KEY_WORDPROCESSOR
MY MUSIC=> KEY_SPREADSHEET
*/
unsigned int keys[] = {
KEY_FN,
KEY_MESSENGER, KEY_CALENDAR,
KEY_ADDRESSBOOK, KEY_DOCUMENTS,
KEY_WORDPROCESSOR,
KEY_SPREADSHEET,
KEY_COFFEE,
KEY_HELP, KEY_SEND,
KEY_REPLY, KEY_FORWARDMAIL,
KEY_NEW, KEY_OPEN,
KEY_CLOSE, KEY_SAVE,
KEY_UNDO, KEY_REDO,
KEY_SPELLCHECK, KEY_PRINT,
0
};
unsigned int *pkeys = &keys[0];
unsigned short i;
if (pm->ifnum != 1) /* only set up ONCE for interace 1 */
return;
pm->input_ep82 = input;
for (i = 0; i < 24; i++)
pm->last_key[i] = 0;
while (*pkeys != 0) {
set_bit(*pkeys, pm->input_ep82->keybit);
++pkeys;
}
}
static int pcmidi_set_operational(struct pcmidi_snd *pm)
{
if (pm->ifnum != 1)
return 0; /* only set up ONCE for interace 1 */
pcmidi_get_output_report(pm);
pcmidi_submit_output_report(pm, 0xc1);
return 0;
}
static int pcmidi_snd_free(struct snd_device *dev)
{
return 0;
}
static int pcmidi_in_open(struct snd_rawmidi_substream *substream)
{
struct pcmidi_snd *pm = substream->rmidi->private_data;
dbg_hid("pcmidi in open\n");
pm->in_substream = substream;
return 0;
}
static int pcmidi_in_close(struct snd_rawmidi_substream *substream)
{
dbg_hid("pcmidi in close\n");
return 0;
}
static void pcmidi_in_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct pcmidi_snd *pm = substream->rmidi->private_data;
dbg_hid("pcmidi in trigger %d\n", up);
pm->in_triggered = up;
}
static struct snd_rawmidi_ops pcmidi_in_ops = {
.open = pcmidi_in_open,
.close = pcmidi_in_close,
.trigger = pcmidi_in_trigger
};
int pcmidi_snd_initialise(struct pcmidi_snd *pm)
{
static int dev;
struct snd_card *card;
struct snd_rawmidi *rwmidi;
int err;
static struct snd_device_ops ops = {
.dev_free = pcmidi_snd_free,
};
if (pm->ifnum != 1)
return 0; /* only set up midi device ONCE for interace 1 */
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
dev++;
return -ENOENT;
}
/* Setup sound card */
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
if (err < 0) {
pk_error("failed to create pc-midi sound card\n");
err = -ENOMEM;
goto fail;
}
pm->card = card;
/* Setup sound device */
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, pm, &ops);
if (err < 0) {
pk_error("failed to create pc-midi sound device: error %d\n",
err);
goto fail;
}
strncpy(card->driver, shortname, sizeof(card->driver));
strncpy(card->shortname, shortname, sizeof(card->shortname));
strncpy(card->longname, longname, sizeof(card->longname));
/* Set up rawmidi */
err = snd_rawmidi_new(card, card->shortname, 0,
0, 1, &rwmidi);
if (err < 0) {
pk_error("failed to create pc-midi rawmidi device: error %d\n",
err);
goto fail;
}
pm->rwmidi = rwmidi;
strncpy(rwmidi->name, card->shortname, sizeof(rwmidi->name));
rwmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT;
rwmidi->private_data = pm;
snd_rawmidi_set_ops(rwmidi, SNDRV_RAWMIDI_STREAM_INPUT,
&pcmidi_in_ops);
snd_card_set_dev(card, &pm->pk->hdev->dev);
/* create sysfs variables */
err = device_create_file(&pm->pk->hdev->dev,
sysfs_device_attr_channel);
if (err < 0) {
pk_error("failed to create sysfs attribute channel: error %d\n",
err);
goto fail;
}
err = device_create_file(&pm->pk->hdev->dev,
sysfs_device_attr_sustain);
if (err < 0) {
pk_error("failed to create sysfs attribute sustain: error %d\n",
err);
goto fail_attr_sustain;
}
err = device_create_file(&pm->pk->hdev->dev,
sysfs_device_attr_octave);
if (err < 0) {
pk_error("failed to create sysfs attribute octave: error %d\n",
err);
goto fail_attr_octave;
}
spin_lock_init(&pm->rawmidi_in_lock);
init_sustain_timers(pm);
pcmidi_set_operational(pm);
/* register it */
err = snd_card_register(card);
if (err < 0) {
pk_error("failed to register pc-midi sound card: error %d\n",
err);
goto fail_register;
}
dbg_hid("pcmidi_snd_initialise finished ok\n");
return 0;
fail_register:
stop_sustain_timers(pm);
device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_octave);
fail_attr_octave:
device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_sustain);
fail_attr_sustain:
device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_channel);
fail:
if (pm->card) {
snd_card_free(pm->card);
pm->card = NULL;
}
return err;
}
int pcmidi_snd_terminate(struct pcmidi_snd *pm)
{
if (pm->card) {
stop_sustain_timers(pm);
device_remove_file(&pm->pk->hdev->dev,
sysfs_device_attr_channel);
device_remove_file(&pm->pk->hdev->dev,
sysfs_device_attr_sustain);
device_remove_file(&pm->pk->hdev->dev,
sysfs_device_attr_octave);
snd_card_disconnect(pm->card);
snd_card_free_when_closed(pm->card);
}
return 0;
}
/*
* PC-MIDI report descriptor for report id is wrong.
*/
static void pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize)
{
if (rsize == 178 &&
rdesc[111] == 0x06 && rdesc[112] == 0x00 &&
rdesc[113] == 0xff) {
dev_info(&hdev->dev, "fixing up pc-midi keyboard report "
"descriptor\n");
rdesc[144] = 0x18; /* report 4: was 0x10 report count */
}
}
static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
struct pcmidi_snd *pm;
pm = pk->pm;
if (HID_UP_MSVENDOR == (usage->hid & HID_USAGE_PAGE) &&
1 == pm->ifnum) {
pcmidi_setup_extra_keys(pm, hi->input);
return 0;
}
return 0;
}
static int pk_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
int ret = 0;
if (1 == pk->pm->ifnum) {
if (report->id == data[0])
switch (report->id) {
case 0x01: /* midi keys (qwerty)*/
case 0x03: /* midi keyboard (musical)*/
case 0x04: /* extra/midi keys (qwerty)*/
ret = pcmidi_handle_report(pk->pm,
report->id, data, size);
break;
}
}
return ret;
}
static int pk_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
unsigned long quirks = id->driver_data;
struct pk_device *pk;
struct pcmidi_snd *pm = NULL;
pk = kzalloc(sizeof(*pk), GFP_KERNEL);
if (pk == NULL) {
dev_err(&hdev->dev, "prodikeys: can't alloc descriptor\n");
return -ENOMEM;
}
pk->hdev = hdev;
pm = kzalloc(sizeof(*pm), GFP_KERNEL);
if (pm == NULL) {
dev_err(&hdev->dev,
"prodikeys: can't alloc descriptor\n");
ret = -ENOMEM;
goto err_free;
}
pm->pk = pk;
pk->pm = pm;
pm->ifnum = ifnum;
hid_set_drvdata(hdev, pk);
ret = hid_parse(hdev);
if (ret) {
dev_err(&hdev->dev, "prodikeys: hid parse failed\n");
goto err_free;
}
if (quirks & PK_QUIRK_NOGET) { /* hid_parse cleared all the quirks */
hdev->quirks |= HID_QUIRK_NOGET;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
dev_err(&hdev->dev, "prodikeys: hw start failed\n");
goto err_free;
}
ret = pcmidi_snd_initialise(pm);
if (ret < 0)
goto err_stop;
return 0;
err_stop:
hid_hw_stop(hdev);
err_free:
if (pm != NULL)
kfree(pm);
kfree(pk);
return ret;
}
static void pk_remove(struct hid_device *hdev)
{
struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
struct pcmidi_snd *pm;
pm = pk->pm;
if (pm) {
pcmidi_snd_terminate(pm);
kfree(pm);
}
hid_hw_stop(hdev);
kfree(pk);
}
static const struct hid_device_id pk_devices[] = {
{HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS,
USB_DEVICE_ID_PRODIKEYS_PCMIDI),
.driver_data = PK_QUIRK_NOGET},
{ }
};
MODULE_DEVICE_TABLE(hid, pk_devices);
static struct hid_driver pk_driver = {
.name = "prodikeys",
.id_table = pk_devices,
.report_fixup = pk_report_fixup,
.input_mapping = pk_input_mapping,
.raw_event = pk_raw_event,
.probe = pk_probe,
.remove = pk_remove,
};
static int pk_init(void)
{
int ret;
ret = hid_register_driver(&pk_driver);
if (ret)
printk(KERN_ERR "can't register prodikeys driver\n");
return ret;
}
static void pk_exit(void)
{
hid_unregister_driver(&pk_driver);
}
module_init(pk_init);
module_exit(pk_exit);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,994 @@
/*
* Roccat Kone driver for Linux
*
* Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
*/
/*
* 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.
*/
/*
* Roccat Kone is a gamer mouse which consists of a mouse part and a keyboard
* part. The keyboard part enables the mouse to execute stored macros with mixed
* key- and button-events.
*
* TODO implement on-the-fly polling-rate change
* The windows driver has the ability to change the polling rate of the
* device on the press of a mousebutton.
* Is it possible to remove and reinstall the urb in raw-event- or any
* other handler, or to defer this action to be executed somewhere else?
*
* TODO implement notification mechanism for overlong macro execution
* If user wants to execute an overlong macro only the names of macroset
* and macro are given. Should userland tap hidraw or is there an
* additional streaming mechanism?
*
* TODO is it possible to overwrite group for sysfs attributes via udev?
*/
#include <linux/device.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "hid-ids.h"
#include "hid-roccat-kone.h"
static void kone_set_settings_checksum(struct kone_settings *settings)
{
uint16_t checksum = 0;
unsigned char *address = (unsigned char *)settings;
int i;
for (i = 0; i < sizeof(struct kone_settings) - 2; ++i, ++address)
checksum += *address;
settings->checksum = cpu_to_le16(checksum);
}
/*
* Checks success after writing data to mouse
* On success returns 0
* On failure returns errno
*/
static int kone_check_write(struct usb_device *usb_dev)
{
int len;
unsigned char *data;
data = kmalloc(1, GFP_KERNEL);
if (!data)
return -ENOMEM;
do {
/*
* Mouse needs 50 msecs until it says ok, but there are
* 30 more msecs needed for next write to work.
*/
msleep(80);
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE |
USB_DIR_IN,
kone_command_confirm_write, 0, data, 1,
USB_CTRL_SET_TIMEOUT);
if (len != 1) {
kfree(data);
return -EIO;
}
/*
* value of 3 seems to mean something like
* "not finished yet, but it looks good"
* So check again after a moment.
*/
} while (*data == 3);
if (*data == 1) { /* everything alright */
kfree(data);
return 0;
} else { /* unknown answer */
dev_err(&usb_dev->dev, "got retval %d when checking write\n",
*data);
kfree(data);
return -EIO;
}
}
/*
* Reads settings from mouse and stores it in @buf
* @buf has to be alloced with GFP_KERNEL
* On success returns 0
* On failure returns errno
*/
static int kone_get_settings(struct usb_device *usb_dev,
struct kone_settings *buf)
{
int len;
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
kone_command_settings, 0, buf,
sizeof(struct kone_settings), USB_CTRL_SET_TIMEOUT);
if (len != sizeof(struct kone_settings))
return -EIO;
return 0;
}
/*
* Writes settings from @buf to mouse
* On success returns 0
* On failure returns errno
*/
static int kone_set_settings(struct usb_device *usb_dev,
struct kone_settings const *settings)
{
int len;
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
USB_REQ_SET_CONFIGURATION,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
kone_command_settings, 0, (char *)settings,
sizeof(struct kone_settings),
USB_CTRL_SET_TIMEOUT);
if (len != sizeof(struct kone_settings))
return -EIO;
if (kone_check_write(usb_dev))
return -EIO;
return 0;
}
/*
* Reads profile data from mouse and stores it in @buf
* @number: profile number to read
* On success returns 0
* On failure returns errno
*/
static int kone_get_profile(struct usb_device *usb_dev,
struct kone_profile *buf, int number)
{
int len;
if (number < 1 || number > 5)
return -EINVAL;
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
kone_command_profile, number, buf,
sizeof(struct kone_profile), USB_CTRL_SET_TIMEOUT);
if (len != sizeof(struct kone_profile))
return -EIO;
return 0;
}
/*
* Writes profile data to mouse.
* @number: profile number to write
* On success returns 0
* On failure returns errno
*/
static int kone_set_profile(struct usb_device *usb_dev,
struct kone_profile const *profile, int number)
{
int len;
if (number < 1 || number > 5)
return -EINVAL;
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
USB_REQ_SET_CONFIGURATION,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
kone_command_profile, number, (char *)profile,
sizeof(struct kone_profile),
USB_CTRL_SET_TIMEOUT);
if (len != sizeof(struct kone_profile))
return len;
if (kone_check_write(usb_dev))
return -EIO;
return 0;
}
/*
* Reads value of "fast-clip-weight" and stores it in @result
* On success returns 0
* On failure returns errno
*/
static int kone_get_weight(struct usb_device *usb_dev, int *result)
{
int len;
uint8_t *data;
data = kmalloc(1, GFP_KERNEL);
if (!data)
return -ENOMEM;
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
kone_command_weight, 0, data, 1, USB_CTRL_SET_TIMEOUT);
if (len != 1) {
kfree(data);
return -EIO;
}
*result = (int)*data;
kfree(data);
return 0;
}
/*
* Reads firmware_version of mouse and stores it in @result
* On success returns 0
* On failure returns errno
*/
static int kone_get_firmware_version(struct usb_device *usb_dev, int *result)
{
int len;
unsigned char *data;
data = kmalloc(2, GFP_KERNEL);
if (!data)
return -ENOMEM;
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
kone_command_firmware_version, 0, data, 2,
USB_CTRL_SET_TIMEOUT);
if (len != 2) {
kfree(data);
return -EIO;
}
*result = le16_to_cpu(*data);
kfree(data);
return 0;
}
static ssize_t kone_sysfs_read_settings(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
struct device *dev = container_of(kobj, struct device, kobj);
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
if (off >= sizeof(struct kone_settings))
return 0;
if (off + count > sizeof(struct kone_settings))
count = sizeof(struct kone_settings) - off;
mutex_lock(&kone->kone_lock);
memcpy(buf, &kone->settings + off, count);
mutex_unlock(&kone->kone_lock);
return count;
}
/*
* Writing settings automatically activates startup_profile.
* This function keeps values in kone_device up to date and assumes that in
* case of error the old data is still valid
*/
static ssize_t kone_sysfs_write_settings(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
struct device *dev = container_of(kobj, struct device, kobj);
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval = 0, difference;
/* I need to get my data in one piece */
if (off != 0 || count != sizeof(struct kone_settings))
return -EINVAL;
mutex_lock(&kone->kone_lock);
difference = memcmp(buf, &kone->settings, sizeof(struct kone_settings));
if (difference) {
retval = kone_set_settings(usb_dev,
(struct kone_settings const *)buf);
if (!retval)
memcpy(&kone->settings, buf,
sizeof(struct kone_settings));
}
mutex_unlock(&kone->kone_lock);
if (retval)
return retval;
/*
* If we get here, treat settings as okay and update actual values
* according to startup_profile
*/
kone->actual_profile = kone->settings.startup_profile;
kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi;
return sizeof(struct kone_settings);
}
static ssize_t kone_sysfs_read_profilex(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count, int number) {
struct device *dev = container_of(kobj, struct device, kobj);
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
if (off >= sizeof(struct kone_profile))
return 0;
if (off + count > sizeof(struct kone_profile))
count = sizeof(struct kone_profile) - off;
mutex_lock(&kone->kone_lock);
memcpy(buf, &kone->profiles[number - 1], sizeof(struct kone_profile));
mutex_unlock(&kone->kone_lock);
return count;
}
static ssize_t kone_sysfs_read_profile1(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 1);
}
static ssize_t kone_sysfs_read_profile2(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 2);
}
static ssize_t kone_sysfs_read_profile3(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 3);
}
static ssize_t kone_sysfs_read_profile4(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 4);
}
static ssize_t kone_sysfs_read_profile5(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
return kone_sysfs_read_profilex(kobj, attr, buf, off, count, 5);
}
/* Writes data only if different to stored data */
static ssize_t kone_sysfs_write_profilex(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count, int number) {
struct device *dev = container_of(kobj, struct device, kobj);
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
struct kone_profile *profile;
int retval = 0, difference;
/* I need to get my data in one piece */
if (off != 0 || count != sizeof(struct kone_profile))
return -EINVAL;
profile = &kone->profiles[number - 1];
mutex_lock(&kone->kone_lock);
difference = memcmp(buf, profile, sizeof(struct kone_profile));
if (difference) {
retval = kone_set_profile(usb_dev,
(struct kone_profile const *)buf, number);
if (!retval)
memcpy(profile, buf, sizeof(struct kone_profile));
}
mutex_unlock(&kone->kone_lock);
if (retval)
return retval;
return sizeof(struct kone_profile);
}
static ssize_t kone_sysfs_write_profile1(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 1);
}
static ssize_t kone_sysfs_write_profile2(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 2);
}
static ssize_t kone_sysfs_write_profile3(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 3);
}
static ssize_t kone_sysfs_write_profile4(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 4);
}
static ssize_t kone_sysfs_write_profile5(struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
return kone_sysfs_write_profilex(kobj, attr, buf, off, count, 5);
}
static ssize_t kone_sysfs_show_actual_profile(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
return snprintf(buf, PAGE_SIZE, "%d\n", kone->actual_profile);
}
static ssize_t kone_sysfs_show_actual_dpi(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
return snprintf(buf, PAGE_SIZE, "%d\n", kone->actual_dpi);
}
/* weight is read each time, since we don't get informed when it's changed */
static ssize_t kone_sysfs_show_weight(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int weight = 0;
int retval;
mutex_lock(&kone->kone_lock);
retval = kone_get_weight(usb_dev, &weight);
mutex_unlock(&kone->kone_lock);
if (retval)
return retval;
return snprintf(buf, PAGE_SIZE, "%d\n", weight);
}
static ssize_t kone_sysfs_show_firmware_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
return snprintf(buf, PAGE_SIZE, "%d\n", kone->firmware_version);
}
static ssize_t kone_sysfs_show_tcu(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
return snprintf(buf, PAGE_SIZE, "%d\n", kone->settings.tcu);
}
static int kone_tcu_command(struct usb_device *usb_dev, int number)
{
int len;
char *value;
value = kmalloc(1, GFP_KERNEL);
if (!value)
return -ENOMEM;
*value = number;
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
USB_REQ_SET_CONFIGURATION,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
kone_command_calibrate, 0, value, 1,
USB_CTRL_SET_TIMEOUT);
kfree(value);
return ((len != 1) ? -EIO : 0);
}
/*
* Calibrating the tcu is the only action that changes settings data inside the
* mouse, so this data needs to be reread
*/
static ssize_t kone_sysfs_set_tcu(struct device *dev,
struct device_attribute *attr, char const *buf, size_t size)
{
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
unsigned long state;
retval = strict_strtoul(buf, 10, &state);
if (retval)
return retval;
if (state != 0 && state != 1)
return -EINVAL;
mutex_lock(&kone->kone_lock);
if (state == 1) { /* state activate */
retval = kone_tcu_command(usb_dev, 1);
if (retval)
goto exit_unlock;
retval = kone_tcu_command(usb_dev, 2);
if (retval)
goto exit_unlock;
ssleep(5); /* tcu needs this time for calibration */
retval = kone_tcu_command(usb_dev, 3);
if (retval)
goto exit_unlock;
retval = kone_tcu_command(usb_dev, 0);
if (retval)
goto exit_unlock;
retval = kone_tcu_command(usb_dev, 4);
if (retval)
goto exit_unlock;
/*
* Kone needs this time to settle things.
* Reading settings too early will result in invalid data.
* Roccat's driver waits 1 sec, maybe this time could be
* shortened.
*/
ssleep(1);
}
/* calibration changes values in settings, so reread */
retval = kone_get_settings(usb_dev, &kone->settings);
if (retval)
goto exit_no_settings;
/* only write settings back if activation state is different */
if (kone->settings.tcu != state) {
kone->settings.tcu = state;
kone_set_settings_checksum(&kone->settings);
retval = kone_set_settings(usb_dev, &kone->settings);
if (retval) {
dev_err(&usb_dev->dev, "couldn't set tcu state\n");
/*
* try to reread valid settings into buffer overwriting
* first error code
*/
retval = kone_get_settings(usb_dev, &kone->settings);
if (retval)
goto exit_no_settings;
goto exit_unlock;
}
}
retval = size;
exit_no_settings:
dev_err(&usb_dev->dev, "couldn't read settings\n");
exit_unlock:
mutex_unlock(&kone->kone_lock);
return retval;
}
static ssize_t kone_sysfs_show_startup_profile(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
return snprintf(buf, PAGE_SIZE, "%d\n", kone->settings.startup_profile);
}
static ssize_t kone_sysfs_set_startup_profile(struct device *dev,
struct device_attribute *attr, char const *buf, size_t size)
{
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
unsigned long new_startup_profile;
retval = strict_strtoul(buf, 10, &new_startup_profile);
if (retval)
return retval;
if (new_startup_profile < 1 || new_startup_profile > 5)
return -EINVAL;
mutex_lock(&kone->kone_lock);
kone->settings.startup_profile = new_startup_profile;
kone_set_settings_checksum(&kone->settings);
retval = kone_set_settings(usb_dev, &kone->settings);
mutex_unlock(&kone->kone_lock);
if (retval)
return retval;
/* changing the startup profile immediately activates this profile */
kone->actual_profile = new_startup_profile;
kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi;
return size;
}
/*
* This file is used by userland software to find devices that are handled by
* this driver. This provides a consistent way for actual and older kernels
* where this driver replaced usbhid instead of generic-usb.
* Driver capabilities are determined by version number.
*/
static ssize_t kone_sysfs_show_driver_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, ROCCAT_KONE_DRIVER_VERSION "\n");
}
/*
* Read actual dpi settings.
* Returns raw value for further processing. Refer to enum kone_polling_rates to
* get real value.
*/
static DEVICE_ATTR(actual_dpi, 0440, kone_sysfs_show_actual_dpi, NULL);
static DEVICE_ATTR(actual_profile, 0440, kone_sysfs_show_actual_profile, NULL);
/*
* The mouse can be equipped with one of four supplied weights from 5 to 20
* grams which are recognized and its value can be read out.
* This returns the raw value reported by the mouse for easy evaluation by
* software. Refer to enum kone_weights to get corresponding real weight.
*/
static DEVICE_ATTR(weight, 0440, kone_sysfs_show_weight, NULL);
/*
* Prints firmware version stored in mouse as integer.
* The raw value reported by the mouse is returned for easy evaluation, to get
* the real version number the decimal point has to be shifted 2 positions to
* the left. E.g. a value of 138 means 1.38.
*/
static DEVICE_ATTR(firmware_version, 0440,
kone_sysfs_show_firmware_version, NULL);
/*
* Prints state of Tracking Control Unit as number where 0 = off and 1 = on
* Writing 0 deactivates tcu and writing 1 calibrates and activates the tcu
*/
static DEVICE_ATTR(tcu, 0660, kone_sysfs_show_tcu, kone_sysfs_set_tcu);
/* Prints and takes the number of the profile the mouse starts with */
static DEVICE_ATTR(startup_profile, 0660,
kone_sysfs_show_startup_profile,
kone_sysfs_set_startup_profile);
static DEVICE_ATTR(kone_driver_version, 0440,
kone_sysfs_show_driver_version, NULL);
static struct attribute *kone_attributes[] = {
&dev_attr_actual_dpi.attr,
&dev_attr_actual_profile.attr,
&dev_attr_weight.attr,
&dev_attr_firmware_version.attr,
&dev_attr_tcu.attr,
&dev_attr_startup_profile.attr,
&dev_attr_kone_driver_version.attr,
NULL
};
static struct attribute_group kone_attribute_group = {
.attrs = kone_attributes
};
static struct bin_attribute kone_settings_attr = {
.attr = { .name = "settings", .mode = 0660 },
.size = sizeof(struct kone_settings),
.read = kone_sysfs_read_settings,
.write = kone_sysfs_write_settings
};
static struct bin_attribute kone_profile1_attr = {
.attr = { .name = "profile1", .mode = 0660 },
.size = sizeof(struct kone_profile),
.read = kone_sysfs_read_profile1,
.write = kone_sysfs_write_profile1
};
static struct bin_attribute kone_profile2_attr = {
.attr = { .name = "profile2", .mode = 0660 },
.size = sizeof(struct kone_profile),
.read = kone_sysfs_read_profile2,
.write = kone_sysfs_write_profile2
};
static struct bin_attribute kone_profile3_attr = {
.attr = { .name = "profile3", .mode = 0660 },
.size = sizeof(struct kone_profile),
.read = kone_sysfs_read_profile3,
.write = kone_sysfs_write_profile3
};
static struct bin_attribute kone_profile4_attr = {
.attr = { .name = "profile4", .mode = 0660 },
.size = sizeof(struct kone_profile),
.read = kone_sysfs_read_profile4,
.write = kone_sysfs_write_profile4
};
static struct bin_attribute kone_profile5_attr = {
.attr = { .name = "profile5", .mode = 0660 },
.size = sizeof(struct kone_profile),
.read = kone_sysfs_read_profile5,
.write = kone_sysfs_write_profile5
};
static int kone_create_sysfs_attributes(struct usb_interface *intf)
{
int retval;
retval = sysfs_create_group(&intf->dev.kobj, &kone_attribute_group);
if (retval)
goto exit_1;
retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_settings_attr);
if (retval)
goto exit_2;
retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile1_attr);
if (retval)
goto exit_3;
retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile2_attr);
if (retval)
goto exit_4;
retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile3_attr);
if (retval)
goto exit_5;
retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile4_attr);
if (retval)
goto exit_6;
retval = sysfs_create_bin_file(&intf->dev.kobj, &kone_profile5_attr);
if (retval)
goto exit_7;
return 0;
exit_7:
sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile4_attr);
exit_6:
sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile3_attr);
exit_5:
sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile2_attr);
exit_4:
sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile1_attr);
exit_3:
sysfs_remove_bin_file(&intf->dev.kobj, &kone_settings_attr);
exit_2:
sysfs_remove_group(&intf->dev.kobj, &kone_attribute_group);
exit_1:
return retval;
}
static void kone_remove_sysfs_attributes(struct usb_interface *intf)
{
sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile5_attr);
sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile4_attr);
sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile3_attr);
sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile2_attr);
sysfs_remove_bin_file(&intf->dev.kobj, &kone_profile1_attr);
sysfs_remove_bin_file(&intf->dev.kobj, &kone_settings_attr);
sysfs_remove_group(&intf->dev.kobj, &kone_attribute_group);
}
static int kone_init_kone_device_struct(struct usb_device *usb_dev,
struct kone_device *kone)
{
uint i;
int retval;
mutex_init(&kone->kone_lock);
for (i = 0; i < 5; ++i) {
retval = kone_get_profile(usb_dev, &kone->profiles[i], i + 1);
if (retval)
return retval;
}
retval = kone_get_settings(usb_dev, &kone->settings);
if (retval)
return retval;
retval = kone_get_firmware_version(usb_dev, &kone->firmware_version);
if (retval)
return retval;
kone->actual_profile = kone->settings.startup_profile;
kone->actual_dpi = kone->profiles[kone->actual_profile].startup_dpi;
return 0;
}
/*
* Since IGNORE_MOUSE quirk moved to hid-apple, there is no way to bind only to
* mousepart if usb_hid is compiled into the kernel and kone is compiled as
* module.
* Secial behaviour is bound only to mousepart since only mouseevents contain
* additional notifications.
*/
static int kone_init_specials(struct hid_device *hdev)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct usb_device *usb_dev = interface_to_usbdev(intf);
struct kone_device *kone;
int retval;
if (intf->cur_altsetting->desc.bInterfaceProtocol
== USB_INTERFACE_PROTOCOL_MOUSE) {
kone = kzalloc(sizeof(*kone), GFP_KERNEL);
if (!kone) {
dev_err(&hdev->dev, "can't alloc device descriptor\n");
return -ENOMEM;
}
hid_set_drvdata(hdev, kone);
retval = kone_init_kone_device_struct(usb_dev, kone);
if (retval) {
dev_err(&hdev->dev,
"couldn't init struct kone_device\n");
goto exit_free;
}
retval = kone_create_sysfs_attributes(intf);
if (retval) {
dev_err(&hdev->dev, "cannot create sysfs files\n");
goto exit_free;
}
} else {
hid_set_drvdata(hdev, NULL);
}
return 0;
exit_free:
kfree(kone);
return retval;
}
static void kone_remove_specials(struct hid_device *hdev)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
if (intf->cur_altsetting->desc.bInterfaceProtocol
== USB_INTERFACE_PROTOCOL_MOUSE) {
kone_remove_sysfs_attributes(intf);
kfree(hid_get_drvdata(hdev));
}
}
static int kone_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int retval;
retval = hid_parse(hdev);
if (retval) {
dev_err(&hdev->dev, "parse failed\n");
goto exit;
}
retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (retval) {
dev_err(&hdev->dev, "hw start failed\n");
goto exit;
}
retval = kone_init_specials(hdev);
if (retval) {
dev_err(&hdev->dev, "couldn't install mouse\n");
goto exit_stop;
}
return 0;
exit_stop:
hid_hw_stop(hdev);
exit:
return retval;
}
static void kone_remove(struct hid_device *hdev)
{
kone_remove_specials(hdev);
hid_hw_stop(hdev);
}
/* handle special events and keep actual profile and dpi values up to date */
static void kone_keep_values_up_to_date(struct kone_device *kone,
struct kone_mouse_event const *event)
{
switch (event->event) {
case kone_mouse_event_switch_profile:
case kone_mouse_event_osd_profile:
kone->actual_profile = event->value;
kone->actual_dpi = kone->profiles[kone->actual_profile - 1].
startup_dpi;
break;
case kone_mouse_event_switch_dpi:
case kone_mouse_event_osd_dpi:
kone->actual_dpi = event->value;
break;
}
}
/*
* Is called for keyboard- and mousepart.
* Only mousepart gets informations about special events in its extended event
* structure.
*/
static int kone_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct kone_device *kone = hid_get_drvdata(hdev);
struct kone_mouse_event *event = (struct kone_mouse_event *)data;
/* keyboard events are always processed by default handler */
if (size != sizeof(struct kone_mouse_event))
return 0;
/*
* Firmware 1.38 introduced new behaviour for tilt and special buttons.
* Pressed button is reported in each movement event.
* Workaround sends only one event per press.
*/
if (memcmp(&kone->last_mouse_event.tilt, &event->tilt, 5))
memcpy(&kone->last_mouse_event, event,
sizeof(struct kone_mouse_event));
else
memset(&event->tilt, 0, 5);
kone_keep_values_up_to_date(kone, event);
return 0; /* always do further processing */
}
static const struct hid_device_id kone_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
{ }
};
MODULE_DEVICE_TABLE(hid, kone_devices);
static struct hid_driver kone_driver = {
.name = "kone",
.id_table = kone_devices,
.probe = kone_probe,
.remove = kone_remove,
.raw_event = kone_raw_event
};
static int __init kone_init(void)
{
return hid_register_driver(&kone_driver);
}
static void __exit kone_exit(void)
{
hid_unregister_driver(&kone_driver);
}
module_init(kone_init);
module_exit(kone_exit);
MODULE_AUTHOR("Stefan Achatz");
MODULE_DESCRIPTION("USB Roccat Kone driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,224 @@
#ifndef __HID_ROCCAT_KONE_H
#define __HID_ROCCAT_KONE_H
/*
* Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
*/
/*
* 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/types.h>
#define ROCCAT_KONE_DRIVER_VERSION "v0.3.1"
#pragma pack(push)
#pragma pack(1)
struct kone_keystroke {
uint8_t key;
uint8_t action;
uint16_t period; /* in milliseconds */
};
enum kone_keystroke_buttons {
kone_keystroke_button_1 = 0xf0, /* left mouse button */
kone_keystroke_button_2 = 0xf1, /* right mouse button */
kone_keystroke_button_3 = 0xf2, /* wheel */
kone_keystroke_button_9 = 0xf3, /* side button up */
kone_keystroke_button_8 = 0xf4 /* side button down */
};
enum kone_keystroke_actions {
kone_keystroke_action_press = 0,
kone_keystroke_action_release = 1
};
struct kone_button_info {
uint8_t number; /* range 1-8 */
uint8_t type;
uint8_t macro_type; /* 0 = short, 1 = overlong */
uint8_t macro_set_name[16]; /* can be max 15 chars long */
uint8_t macro_name[16]; /* can be max 15 chars long */
uint8_t count;
struct kone_keystroke keystrokes[20];
};
enum kone_button_info_types {
/* valid button types until firmware 1.32 */
kone_button_info_type_button_1 = 0x1, /* click (left mouse button) */
kone_button_info_type_button_2 = 0x2, /* menu (right mouse button)*/
kone_button_info_type_button_3 = 0x3, /* scroll (wheel) */
kone_button_info_type_double_click = 0x4,
kone_button_info_type_key = 0x5,
kone_button_info_type_macro = 0x6,
kone_button_info_type_off = 0x7,
/* TODO clarify function and rename */
kone_button_info_type_osd_xy_prescaling = 0x8,
kone_button_info_type_osd_dpi = 0x9,
kone_button_info_type_osd_profile = 0xa,
kone_button_info_type_button_9 = 0xb, /* ie forward */
kone_button_info_type_button_8 = 0xc, /* ie backward */
kone_button_info_type_dpi_up = 0xd, /* internal */
kone_button_info_type_dpi_down = 0xe, /* internal */
kone_button_info_type_button_7 = 0xf, /* tilt left */
kone_button_info_type_button_6 = 0x10, /* tilt right */
kone_button_info_type_profile_up = 0x11, /* internal */
kone_button_info_type_profile_down = 0x12, /* internal */
/* additional valid button types since firmware 1.38 */
kone_button_info_type_multimedia_open_player = 0x20,
kone_button_info_type_multimedia_next_track = 0x21,
kone_button_info_type_multimedia_prev_track = 0x22,
kone_button_info_type_multimedia_play_pause = 0x23,
kone_button_info_type_multimedia_stop = 0x24,
kone_button_info_type_multimedia_mute = 0x25,
kone_button_info_type_multimedia_volume_up = 0x26,
kone_button_info_type_multimedia_volume_down = 0x27
};
enum kone_button_info_numbers {
kone_button_top = 1,
kone_button_wheel_tilt_left = 2,
kone_button_wheel_tilt_right = 3,
kone_button_forward = 4,
kone_button_backward = 5,
kone_button_middle = 6,
kone_button_plus = 7,
kone_button_minus = 8,
};
struct kone_light_info {
uint8_t number; /* number of light 1-5 */
uint8_t mod; /* 1 = on, 2 = off */
uint8_t red; /* range 0x00-0xff */
uint8_t green; /* range 0x00-0xff */
uint8_t blue; /* range 0x00-0xff */
};
struct kone_profile {
uint16_t size; /* always 975 */
uint16_t unused; /* always 0 */
/*
* range 1-5
* This number does not need to correspond with location where profile
* saved
*/
uint8_t profile; /* range 1-5 */
uint16_t main_sensitivity; /* range 100-1000 */
uint8_t xy_sensitivity_enabled; /* 1 = on, 2 = off */
uint16_t x_sensitivity; /* range 100-1000 */
uint16_t y_sensitivity; /* range 100-1000 */
uint8_t dpi_rate; /* bit 1 = 800, ... */
uint8_t startup_dpi; /* range 1-6 */
uint8_t polling_rate; /* 1 = 125Hz, 2 = 500Hz, 3 = 1000Hz */
/* kone has no dcu
* value is always 2 in firmwares <= 1.32 and
* 1 in firmwares > 1.32
*/
uint8_t dcu_flag;
uint8_t light_effect_1; /* range 1-3 */
uint8_t light_effect_2; /* range 1-5 */
uint8_t light_effect_3; /* range 1-4 */
uint8_t light_effect_speed; /* range 0-255 */
struct kone_light_info light_infos[5];
/* offset is kone_button_info_numbers - 1 */
struct kone_button_info button_infos[8];
uint16_t checksum; /* \brief holds checksum of struct */
};
enum kone_polling_rates {
kone_polling_rate_125 = 1,
kone_polling_rate_500 = 2,
kone_polling_rate_1000 = 3
};
struct kone_settings {
uint16_t size; /* always 36 */
uint8_t startup_profile; /* 1-5 */
uint8_t unknown1;
uint8_t tcu; /* 0 = off, 1 = on */
uint8_t unknown2[23];
uint8_t calibration_data[4];
uint8_t unknown3[2];
uint16_t checksum;
};
/*
* 12 byte mouse event read by interrupt_read
*/
struct kone_mouse_event {
uint8_t report_number; /* always 1 */
uint8_t button;
uint16_t x;
uint16_t y;
uint8_t wheel; /* up = 1, down = -1 */
uint8_t tilt; /* right = 1, left = -1 */
uint8_t unknown;
uint8_t event;
uint8_t value; /* press = 0, release = 1 */
uint8_t macro_key; /* 0 to 8 */
};
enum kone_mouse_events {
/* osd events are thought to be display on screen */
kone_mouse_event_osd_dpi = 0xa0,
kone_mouse_event_osd_profile = 0xb0,
/* TODO clarify meaning and occurence of kone_mouse_event_calibration */
kone_mouse_event_calibration = 0xc0,
kone_mouse_event_call_overlong_macro = 0xe0,
/* switch events notify if user changed values with mousebutton click */
kone_mouse_event_switch_dpi = 0xf0,
kone_mouse_event_switch_profile = 0xf1
};
enum kone_commands {
kone_command_profile = 0x5a,
kone_command_settings = 0x15a,
kone_command_firmware_version = 0x25a,
kone_command_weight = 0x45a,
kone_command_calibrate = 0x55a,
kone_command_confirm_write = 0x65a,
kone_command_firmware = 0xe5a
};
#pragma pack(pop)
struct kone_device {
/*
* Storing actual values when we get informed about changes since there
* is no way of getting this information from the device on demand
*/
int actual_profile, actual_dpi;
/* Used for neutralizing abnormal button behaviour */
struct kone_mouse_event last_mouse_event;
/*
* It's unlikely that multiple sysfs attributes are accessed at a time,
* so only one mutex is used to secure hardware access and profiles and
* settings of this struct.
*/
struct mutex kone_lock;
/*
* Storing the data here reduces IO and ensures that data is available
* when its needed (E.g. interrupt handler).
*/
struct kone_profile profiles[5];
struct kone_settings settings;
/*
* firmware doesn't change unless firmware update is implemented,
* so it's read only once
*/
int firmware_version;
};
#endif

View file

@ -7,6 +7,18 @@
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>
*
*
* This driver supports several HID devices:
*
* [0419:0001] Samsung IrDA remote controller (reports as Cypress USB Mouse).
* various hid report fixups for different variants.
*
* [0419:0600] Creative Desktop Wireless 6000 keyboard/mouse combo
* several key mappings used from the consumer usage page
* deviate from the USB HUT 1.12 standard.
*
*/
/*
@ -17,14 +29,13 @@
*/
#include <linux/device.h>
#include <linux/usb.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
/*
* Samsung IrDA remote controller (reports as Cypress USB Mouse).
*
* There are several variants for 0419:0001:
*
* 1. 184 byte report descriptor
@ -43,21 +54,21 @@
* 4. 171 byte report descriptor
* Report #3 has an array field with logical range 0..1 instead of 1..3.
*/
static inline void samsung_dev_trace(struct hid_device *hdev,
static inline void samsung_irda_dev_trace(struct hid_device *hdev,
unsigned int rsize)
{
dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report "
"descriptor\n", rsize);
}
static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
static void samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize)
{
if (rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
rdesc[177] == 0x75 && rdesc[178] == 0x30 &&
rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
rdesc[182] == 0x40) {
samsung_dev_trace(hdev, 184);
samsung_irda_dev_trace(hdev, 184);
rdesc[176] = 0xff;
rdesc[178] = 0x08;
rdesc[180] = 0x06;
@ -65,24 +76,80 @@ static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
} else
if (rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 &&
rdesc[194] == 0x25 && rdesc[195] == 0x12) {
samsung_dev_trace(hdev, 203);
samsung_irda_dev_trace(hdev, 203);
rdesc[193] = 0x1;
rdesc[195] = 0xf;
} else
if (rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 &&
rdesc[126] == 0x25 && rdesc[127] == 0x11) {
samsung_dev_trace(hdev, 135);
samsung_irda_dev_trace(hdev, 135);
rdesc[125] = 0x1;
rdesc[127] = 0xe;
} else
if (rsize == 171 && rdesc[160] == 0x15 && rdesc[161] == 0x0 &&
rdesc[162] == 0x25 && rdesc[163] == 0x01) {
samsung_dev_trace(hdev, 171);
samsung_irda_dev_trace(hdev, 171);
rdesc[161] = 0x1;
rdesc[163] = 0x3;
}
}
#define samsung_kbd_mouse_map_key_clear(c) \
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
static int samsung_kbd_mouse_input_mapping(struct hid_device *hdev,
struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
if (1 != ifnum || HID_UP_CONSUMER != (usage->hid & HID_USAGE_PAGE))
return 0;
dbg_hid("samsung wireless keyboard/mouse input mapping event [0x%x]\n",
usage->hid & HID_USAGE);
switch (usage->hid & HID_USAGE) {
/* report 2 */
case 0x183: samsung_kbd_mouse_map_key_clear(KEY_MEDIA); break;
case 0x195: samsung_kbd_mouse_map_key_clear(KEY_EMAIL); break;
case 0x196: samsung_kbd_mouse_map_key_clear(KEY_CALC); break;
case 0x197: samsung_kbd_mouse_map_key_clear(KEY_COMPUTER); break;
case 0x22b: samsung_kbd_mouse_map_key_clear(KEY_SEARCH); break;
case 0x22c: samsung_kbd_mouse_map_key_clear(KEY_WWW); break;
case 0x22d: samsung_kbd_mouse_map_key_clear(KEY_BACK); break;
case 0x22e: samsung_kbd_mouse_map_key_clear(KEY_FORWARD); break;
case 0x22f: samsung_kbd_mouse_map_key_clear(KEY_FAVORITES); break;
case 0x230: samsung_kbd_mouse_map_key_clear(KEY_REFRESH); break;
case 0x231: samsung_kbd_mouse_map_key_clear(KEY_STOP); break;
default:
return 0;
}
return 1;
}
static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize)
{
if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product)
samsung_irda_report_fixup(hdev, rdesc, rsize);
}
static int samsung_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
int ret = 0;
if (USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE == hdev->product)
ret = samsung_kbd_mouse_input_mapping(hdev,
hi, field, usage, bit, max);
return ret;
}
static int samsung_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
@ -95,10 +162,12 @@ static int samsung_probe(struct hid_device *hdev,
goto err_free;
}
if (hdev->rsize == 184) {
/* disable hidinput, force hiddev */
cmask = (cmask & ~HID_CONNECT_HIDINPUT) |
HID_CONNECT_HIDDEV_FORCE;
if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product) {
if (hdev->rsize == 184) {
/* disable hidinput, force hiddev */
cmask = (cmask & ~HID_CONNECT_HIDINPUT) |
HID_CONNECT_HIDDEV_FORCE;
}
}
ret = hid_hw_start(hdev, cmask);
@ -114,6 +183,7 @@ err_free:
static const struct hid_device_id samsung_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
{ }
};
MODULE_DEVICE_TABLE(hid, samsung_devices);
@ -122,6 +192,7 @@ static struct hid_driver samsung_driver = {
.name = "samsung",
.id_table = samsung_devices,
.report_fixup = samsung_report_fixup,
.input_mapping = samsung_input_mapping,
.probe = samsung_probe,
};

View file

@ -3,6 +3,9 @@
*
* Copyright (c) 2008 Lev Babiev
* based on hid-cherry driver
*
* Modified to also support BTC "Emprex 3009URF III Vista MCE Remote" by
* Wayne Thomas 2010.
*/
/*
@ -24,23 +27,29 @@ static int ts_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) != 0x0ffbc0000)
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
return 0;
switch (usage->hid & HID_USAGE) {
case 0x00d: ts_map_key_clear(KEY_HOME); break;
case 0x024: ts_map_key_clear(KEY_MENU); break;
case 0x025: ts_map_key_clear(KEY_TV); break;
case 0x048: ts_map_key_clear(KEY_RED); break;
case 0x047: ts_map_key_clear(KEY_GREEN); break;
case 0x049: ts_map_key_clear(KEY_YELLOW); break;
case 0x04a: ts_map_key_clear(KEY_BLUE); break;
case 0x04b: ts_map_key_clear(KEY_ANGLE); break;
case 0x04c: ts_map_key_clear(KEY_LANGUAGE); break;
case 0x04d: ts_map_key_clear(KEY_SUBTITLE); break;
case 0x031: ts_map_key_clear(KEY_AUDIO); break;
case 0x032: ts_map_key_clear(KEY_TEXT); break;
case 0x033: ts_map_key_clear(KEY_CHANNEL); break;
case 0x00d: ts_map_key_clear(KEY_MEDIA); break;
case 0x024: ts_map_key_clear(KEY_MENU); break;
case 0x025: ts_map_key_clear(KEY_TV); break;
case 0x031: ts_map_key_clear(KEY_AUDIO); break;
case 0x032: ts_map_key_clear(KEY_TEXT); break;
case 0x033: ts_map_key_clear(KEY_CHANNEL); break;
case 0x047: ts_map_key_clear(KEY_MP3); break;
case 0x048: ts_map_key_clear(KEY_TV2); break;
case 0x049: ts_map_key_clear(KEY_CAMERA); break;
case 0x04a: ts_map_key_clear(KEY_VIDEO); break;
case 0x04b: ts_map_key_clear(KEY_ANGLE); break;
case 0x04c: ts_map_key_clear(KEY_LANGUAGE); break;
case 0x04d: ts_map_key_clear(KEY_SUBTITLE); break;
case 0x050: ts_map_key_clear(KEY_RADIO); break;
case 0x05a: ts_map_key_clear(KEY_TEXT); break;
case 0x05b: ts_map_key_clear(KEY_RED); break;
case 0x05c: ts_map_key_clear(KEY_GREEN); break;
case 0x05d: ts_map_key_clear(KEY_YELLOW); break;
case 0x05e: ts_map_key_clear(KEY_BLUE); break;
default:
return 0;
}
@ -50,6 +59,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,
static const struct hid_device_id ts_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) },
{ }
};
MODULE_DEVICE_TABLE(hid, ts_devices);

View file

@ -22,14 +22,159 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
#include <linux/power_supply.h>
#endif
#include "hid-ids.h"
struct wacom_data {
__u16 tool;
unsigned char butstate;
unsigned char high_speed;
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
int battery_capacity;
struct power_supply battery;
struct power_supply ac;
#endif
};
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
/*percent of battery capacity, 0 means AC online*/
static unsigned short batcap[8] = { 1, 15, 25, 35, 50, 70, 100, 0 };
static enum power_supply_property wacom_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY
};
static enum power_supply_property wacom_ac_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE
};
static int wacom_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct wacom_data *wdata = container_of(psy,
struct wacom_data, battery);
int power_state = batcap[wdata->battery_capacity];
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_CAPACITY:
/* show 100% battery capacity when charging */
if (power_state == 0)
val->intval = 100;
else
val->intval = power_state;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int wacom_ac_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct wacom_data *wdata = container_of(psy, struct wacom_data, ac);
int power_state = batcap[wdata->battery_capacity];
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
/* fall through */
case POWER_SUPPLY_PROP_ONLINE:
if (power_state == 0)
val->intval = 1;
else
val->intval = 0;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
#endif
static void wacom_poke(struct hid_device *hdev, u8 speed)
{
struct wacom_data *wdata = hid_get_drvdata(hdev);
int limit, ret;
char rep_data[2];
rep_data[0] = 0x03 ; rep_data[1] = 0x00;
limit = 3;
do {
ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
HID_FEATURE_REPORT);
} while (ret < 0 && limit-- > 0);
if (ret >= 0) {
if (speed == 0)
rep_data[0] = 0x05;
else
rep_data[0] = 0x06;
rep_data[1] = 0x00;
limit = 3;
do {
ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
HID_FEATURE_REPORT);
} while (ret < 0 && limit-- > 0);
if (ret >= 0) {
wdata->high_speed = speed;
return;
}
}
/*
* Note that if the raw queries fail, it's not a hard failure and it
* is safe to continue
*/
dev_warn(&hdev->dev, "failed to poke device, command %d, err %d\n",
rep_data[0], ret);
return;
}
static ssize_t wacom_show_speed(struct device *dev,
struct device_attribute
*attr, char *buf)
{
struct wacom_data *wdata = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%i\n", wdata->high_speed);
}
static ssize_t wacom_store_speed(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
int new_speed;
if (sscanf(buf, "%1d", &new_speed ) != 1)
return -EINVAL;
if (new_speed == 0 || new_speed == 1) {
wacom_poke(hdev, new_speed);
return strnlen(buf, PAGE_SIZE);
} else
return -EINVAL;
}
static DEVICE_ATTR(speed, S_IRUGO | S_IWUGO,
wacom_show_speed, wacom_store_speed);
static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *raw_data, int size)
{
@ -148,6 +293,12 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
input_sync(input);
}
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
/* Store current battery capacity */
rw = (data[7] >> 2 & 0x07);
if (rw != wdata->battery_capacity)
wdata->battery_capacity = rw;
#endif
return 1;
}
@ -157,9 +308,7 @@ static int wacom_probe(struct hid_device *hdev,
struct hid_input *hidinput;
struct input_dev *input;
struct wacom_data *wdata;
char rep_data[2];
int ret;
int limit;
wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
if (wdata == NULL) {
@ -182,31 +331,53 @@ static int wacom_probe(struct hid_device *hdev,
goto err_free;
}
/*
* Note that if the raw queries fail, it's not a hard failure and it
* is safe to continue
*/
ret = device_create_file(&hdev->dev, &dev_attr_speed);
if (ret)
dev_warn(&hdev->dev,
"can't create sysfs speed attribute err: %d\n", ret);
/* Set Wacom mode2 */
rep_data[0] = 0x03; rep_data[1] = 0x00;
limit = 3;
do {
ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
HID_FEATURE_REPORT);
} while (ret < 0 && limit-- > 0);
if (ret < 0)
dev_warn(&hdev->dev, "failed to poke device #1, %d\n", ret);
/* Set Wacom mode 2 with high reporting speed */
wacom_poke(hdev, 1);
/* 0x06 - high reporting speed, 0x05 - low speed */
rep_data[0] = 0x06; rep_data[1] = 0x00;
limit = 3;
do {
ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
HID_FEATURE_REPORT);
} while (ret < 0 && limit-- > 0);
if (ret < 0)
dev_warn(&hdev->dev, "failed to poke device #2, %d\n", ret);
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
wdata->battery.properties = wacom_battery_props;
wdata->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
wdata->battery.get_property = wacom_battery_get_property;
wdata->battery.name = "wacom_battery";
wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
wdata->battery.use_for_apm = 0;
ret = power_supply_register(&hdev->dev, &wdata->battery);
if (ret) {
dev_warn(&hdev->dev,
"can't create sysfs battery attribute, err: %d\n", ret);
/*
* battery attribute is not critical for the tablet, but if it
* failed then there is no need to create ac attribute
*/
goto move_on;
}
wdata->ac.properties = wacom_ac_props;
wdata->ac.num_properties = ARRAY_SIZE(wacom_ac_props);
wdata->ac.get_property = wacom_ac_get_property;
wdata->ac.name = "wacom_ac";
wdata->ac.type = POWER_SUPPLY_TYPE_MAINS;
wdata->ac.use_for_apm = 0;
ret = power_supply_register(&hdev->dev, &wdata->ac);
if (ret) {
dev_warn(&hdev->dev,
"can't create ac battery attribute, err: %d\n", ret);
/*
* ac attribute is not critical for the tablet, but if it
* failed then we don't want to battery attribute to exist
*/
power_supply_unregister(&wdata->battery);
}
move_on:
#endif
hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
input = hidinput->input;
@ -251,13 +422,21 @@ err_free:
static void wacom_remove(struct hid_device *hdev)
{
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
struct wacom_data *wdata = hid_get_drvdata(hdev);
#endif
hid_hw_stop(hdev);
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
power_supply_unregister(&wdata->battery);
power_supply_unregister(&wdata->ac);
#endif
kfree(hid_get_drvdata(hdev));
}
static const struct hid_device_id wacom_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
{ }
};
MODULE_DEVICE_TABLE(hid, wacom_devices);

237
drivers/hid/hid-zydacron.c Normal file
View file

@ -0,0 +1,237 @@
/*
* HID driver for zydacron remote control
*
* Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>
*/
/*
* 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"
struct zc_device {
struct input_dev *input_ep81;
unsigned short last_key[4];
};
/*
* Zydacron remote control has an invalid HID report descriptor,
* that needs fixing before we can parse it.
*/
static void zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize)
{
if (rsize >= 253 &&
rdesc[0x96] == 0xbc && rdesc[0x97] == 0xff &&
rdesc[0xca] == 0xbc && rdesc[0xcb] == 0xff &&
rdesc[0xe1] == 0xbc && rdesc[0xe2] == 0xff) {
dev_info(&hdev->dev,
"fixing up zydacron remote control report "
"descriptor\n");
rdesc[0x96] = rdesc[0xca] = rdesc[0xe1] = 0x0c;
rdesc[0x97] = rdesc[0xcb] = rdesc[0xe2] = 0x00;
}
}
#define zc_map_key_clear(c) \
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
static int zc_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
int i;
struct zc_device *zc = hid_get_drvdata(hdev);
zc->input_ep81 = hi->input;
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
return 0;
dbg_hid("zynacron input mapping event [0x%x]\n",
usage->hid & HID_USAGE);
switch (usage->hid & HID_USAGE) {
/* report 2 */
case 0x10:
zc_map_key_clear(KEY_MODE);
break;
case 0x30:
zc_map_key_clear(KEY_SCREEN);
break;
case 0x70:
zc_map_key_clear(KEY_INFO);
break;
/* report 3 */
case 0x04:
zc_map_key_clear(KEY_RADIO);
break;
/* report 4 */
case 0x0d:
zc_map_key_clear(KEY_PVR);
break;
case 0x25:
zc_map_key_clear(KEY_TV);
break;
case 0x47:
zc_map_key_clear(KEY_AUDIO);
break;
case 0x49:
zc_map_key_clear(KEY_AUX);
break;
case 0x4a:
zc_map_key_clear(KEY_VIDEO);
break;
case 0x48:
zc_map_key_clear(KEY_DVD);
break;
case 0x24:
zc_map_key_clear(KEY_MENU);
break;
case 0x32:
zc_map_key_clear(KEY_TEXT);
break;
default:
return 0;
}
for (i = 0; i < 4; i++)
zc->last_key[i] = 0;
return 1;
}
static int zc_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct zc_device *zc = hid_get_drvdata(hdev);
int ret = 0;
unsigned key;
unsigned short index;
if (report->id == data[0]) {
/* break keys */
for (index = 0; index < 4; index++) {
key = zc->last_key[index];
if (key) {
input_event(zc->input_ep81, EV_KEY, key, 0);
zc->last_key[index] = 0;
}
}
key = 0;
switch (report->id) {
case 0x02:
case 0x03:
switch (data[1]) {
case 0x10:
key = KEY_MODE;
index = 0;
break;
case 0x30:
key = KEY_SCREEN;
index = 1;
break;
case 0x70:
key = KEY_INFO;
index = 2;
break;
case 0x04:
key = KEY_RADIO;
index = 3;
break;
}
if (key) {
input_event(zc->input_ep81, EV_KEY, key, 1);
zc->last_key[index] = key;
}
ret = 1;
break;
}
}
return ret;
}
static int zc_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
struct zc_device *zc;
zc = kzalloc(sizeof(*zc), GFP_KERNEL);
if (zc == NULL) {
dev_err(&hdev->dev, "zydacron: can't alloc descriptor\n");
return -ENOMEM;
}
hid_set_drvdata(hdev, zc);
ret = hid_parse(hdev);
if (ret) {
dev_err(&hdev->dev, "zydacron: parse failed\n");
goto err_free;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
dev_err(&hdev->dev, "zydacron: hw start failed\n");
goto err_free;
}
return 0;
err_free:
kfree(zc);
return ret;
}
static void zc_remove(struct hid_device *hdev)
{
struct zc_device *zc = hid_get_drvdata(hdev);
hid_hw_stop(hdev);
if (NULL != zc)
kfree(zc);
}
static const struct hid_device_id zc_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
{ }
};
MODULE_DEVICE_TABLE(hid, zc_devices);
static struct hid_driver zc_driver = {
.name = "zydacron",
.id_table = zc_devices,
.report_fixup = zc_report_fixup,
.input_mapping = zc_input_mapping,
.raw_event = zc_raw_event,
.probe = zc_probe,
.remove = zc_remove,
};
static int __init zc_init(void)
{
return hid_register_driver(&zc_driver);
}
static void __exit zc_exit(void)
{
hid_unregister_driver(&zc_driver);
}
module_init(zc_init);
module_exit(zc_exit);
MODULE_LICENSE("GPL");

View file

@ -106,38 +106,48 @@ out:
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
unsigned int minor = iminor(file->f_path.dentry->d_inode);
/* FIXME: What stops hidraw_table going NULL */
struct hid_device *dev = hidraw_table[minor]->hid;
struct hid_device *dev;
__u8 *buf;
int ret = 0;
if (!dev->hid_output_raw_report)
return -ENODEV;
mutex_lock(&minors_lock);
dev = hidraw_table[minor]->hid;
if (!dev->hid_output_raw_report) {
ret = -ENODEV;
goto out;
}
if (count > HID_MAX_BUFFER_SIZE) {
printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
task_pid_nr(current));
return -EINVAL;
ret = -EINVAL;
goto out;
}
if (count < 2) {
printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
task_pid_nr(current));
return -EINVAL;
}
buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (copy_from_user(buf, buffer, count)) {
ret = -EFAULT;
ret = -EINVAL;
goto out;
}
buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto out;
}
if (copy_from_user(buf, buffer, count)) {
ret = -EFAULT;
goto out_free;
}
ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT);
out:
out_free:
kfree(buf);
out:
mutex_unlock(&minors_lock);
return ret;
}
@ -165,11 +175,8 @@ static int hidraw_open(struct inode *inode, struct file *file)
goto out;
}
lock_kernel();
mutex_lock(&minors_lock);
if (!hidraw_table[minor]) {
printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
minor);
kfree(list);
err = -ENODEV;
goto out_unlock;
@ -197,7 +204,6 @@ static int hidraw_open(struct inode *inode, struct file *file)
out_unlock:
mutex_unlock(&minors_lock);
unlock_kernel();
out:
return err;
@ -209,11 +215,8 @@ static int hidraw_release(struct inode * inode, struct file * file)
struct hidraw *dev;
struct hidraw_list *list = file->private_data;
if (!hidraw_table[minor]) {
printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
minor);
if (!hidraw_table[minor])
return -ENODEV;
}
list_del(&list->node);
dev = hidraw_table[minor];
@ -238,11 +241,12 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
struct inode *inode = file->f_path.dentry->d_inode;
unsigned int minor = iminor(inode);
long ret = 0;
/* FIXME: What stops hidraw_table going NULL */
struct hidraw *dev = hidraw_table[minor];
struct hidraw *dev;
void __user *user_arg = (void __user*) arg;
lock_kernel();
mutex_lock(&minors_lock);
dev = hidraw_table[minor];
switch (cmd) {
case HIDIOCGRDESCSIZE:
if (put_user(dev->hid->rsize, (int __user *)arg))
@ -311,11 +315,11 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
-EFAULT : len;
break;
}
}
}
ret = -ENOTTY;
}
unlock_kernel();
mutex_unlock(&minors_lock);
return ret;
}

View file

@ -623,6 +623,7 @@ int usbhid_wait_io(struct hid_device *hid)
return 0;
}
EXPORT_SYMBOL_GPL(usbhid_wait_io);
static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)
{
@ -806,16 +807,36 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co
struct usb_host_interface *interface = intf->cur_altsetting;
int ret;
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
HID_REQ_SET_REPORT,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
((report_type + 1) << 8) | *buf,
interface->desc.bInterfaceNumber, buf + 1, count - 1,
USB_CTRL_SET_TIMEOUT);
/* count also the report id */
if (ret > 0)
ret++;
if (usbhid->urbout) {
int actual_length;
int skipped_report_id = 0;
if (buf[0] == 0x0) {
/* Don't send the Report ID */
buf++;
count--;
skipped_report_id = 1;
}
ret = usb_interrupt_msg(dev, usbhid->urbout->pipe,
buf, count, &actual_length,
USB_CTRL_SET_TIMEOUT);
/* return the number of bytes transferred */
if (ret == 0) {
ret = actual_length;
/* count also the report id */
if (skipped_report_id)
ret++;
}
} else {
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
HID_REQ_SET_REPORT,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
((report_type + 1) << 8) | *buf,
interface->desc.bInterfaceNumber, buf + 1, count - 1,
USB_CTRL_SET_TIMEOUT);
/* count also the report id */
if (ret > 0)
ret++;
}
return ret;
}
@ -1017,12 +1038,15 @@ static int usbhid_start(struct hid_device *hid)
/* Some keyboards don't work until their LEDs have been set.
* Since BIOSes do set the LEDs, it must be safe for any device
* that supports the keyboard boot protocol.
* In addition, enable remote wakeup by default for all keyboard
* devices supporting the boot protocol.
*/
if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT &&
interface->desc.bInterfaceProtocol ==
USB_INTERFACE_PROTOCOL_KEYBOARD)
USB_INTERFACE_PROTOCOL_KEYBOARD) {
usbhid_set_leds(hid);
device_set_wakeup_enable(&dev->dev, 1);
}
return 0;
fail:
@ -1131,6 +1155,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
hid->product = le16_to_cpu(dev->descriptor.idProduct);
hid->name[0] = 0;
hid->quirks = usbhid_lookup_quirk(hid->vendor, hid->product);
if (intf->cur_altsetting->desc.bInterfaceProtocol ==
USB_INTERFACE_PROTOCOL_MOUSE)
hid->type = HID_TYPE_USBMOUSE;
@ -1287,6 +1312,11 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
{
set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
if (hid->driver && hid->driver->suspend) {
status = hid->driver->suspend(hid, message);
if (status < 0)
return status;
}
} else {
usbhid_mark_busy(usbhid);
spin_unlock_irq(&usbhid->lock);
@ -1294,6 +1324,11 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
}
} else {
if (hid->driver && hid->driver->suspend) {
status = hid->driver->suspend(hid, message);
if (status < 0)
return status;
}
spin_lock_irq(&usbhid->lock);
set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
@ -1348,6 +1383,11 @@ static int hid_resume(struct usb_interface *intf)
hid_io_error(hid);
usbhid_restart_queues(usbhid);
if (status >= 0 && hid->driver && hid->driver->resume) {
int ret = hid->driver->resume(hid);
if (ret < 0)
status = ret;
}
dev_dbg(&intf->dev, "resume status %d\n", status);
return 0;
}
@ -1356,9 +1396,16 @@ static int hid_reset_resume(struct usb_interface *intf)
{
struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data;
int status;
clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
return hid_post_reset(intf);
status = hid_post_reset(intf);
if (status >= 0 && hid->driver && hid->driver->reset_resume) {
int ret = hid->driver->reset_resume(hid);
if (ret < 0)
status = ret;
}
return status;
}
#endif /* CONFIG_PM */

View file

@ -33,6 +33,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },

View file

@ -267,6 +267,7 @@ static int hiddev_open(struct inode *inode, struct file *file)
struct hiddev_list *list;
int res, i;
/* See comment in hiddev_connect() for BKL explanation */
lock_kernel();
i = iminor(inode) - HIDDEV_MINOR_BASE;
@ -894,8 +895,22 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
hiddev->hid = hid;
hiddev->exist = 1;
/* when lock_kernel() usage is fixed in usb_open(),
* we could also fix it here */
/*
* BKL here is used to avoid race after usb_register_dev().
* Once the device node has been created, open() could happen on it.
* The code below will then fail, as hiddev_table hasn't been
* updated.
*
* The obvious fix -- introducing mutex to guard hiddev_table[]
* doesn't work, as usb_open() and usb_register_dev() both take
* minor_rwsem, thus we'll have ABBA deadlock.
*
* Before BKL pushdown, usb_open() had been acquiring it in right
* order, so _open() was safe to use it to protect from this race.
* Now the order is different, but AB-BA deadlock still doesn't occur
* as BKL is dropped on schedule() (i.e. while sleeping on
* minor_rwsem). Fugly.
*/
lock_kernel();
retval = usb_register_dev(usbhid->intf, &hiddev_class);
if (retval) {

View file

@ -311,6 +311,7 @@ static int usb_kbd_probe(struct usb_interface *iface,
goto fail2;
usb_set_intfdata(iface, kbd);
device_set_wakeup_enable(&dev->dev, 1);
return 0;
fail2:

View file

@ -308,11 +308,13 @@ struct hid_item {
#define HID_QUIRK_NOTOUCH 0x00000002
#define HID_QUIRK_IGNORE 0x00000004
#define HID_QUIRK_NOGET 0x00000008
#define HID_QUIRK_HIDDEV_FORCE 0x00000010
#define HID_QUIRK_BADPAD 0x00000020
#define HID_QUIRK_MULTI_INPUT 0x00000040
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000
#define HID_QUIRK_NO_IGNORE 0x40000000
/*
* This is the global environment of the parser. This information is
@ -589,6 +591,9 @@ struct hid_usage_id {
* @report_fixup: called before report descriptor parsing (NULL means nop)
* @input_mapping: invoked on input registering before mapping an usage
* @input_mapped: invoked on input registering after mapping an usage
* @suspend: invoked on suspend (NULL means nop)
* @resume: invoked on resume if device was not reset (NULL means nop)
* @reset_resume: invoked on resume if device was reset (NULL means nop)
*
* raw_event and event should return 0 on no action performed, 1 when no
* further processing should be done and negative on error
@ -629,6 +634,11 @@ struct hid_driver {
int (*input_mapped)(struct hid_device *hdev,
struct hid_input *hidinput, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max);
#ifdef CONFIG_PM
int (*suspend)(struct hid_device *hdev, pm_message_t message);
int (*resume)(struct hid_device *hdev);
int (*reset_resume)(struct hid_device *hdev);
#endif
/* private: */
struct device_driver driver;
};