diff options
Diffstat (limited to 'drivers')
29 files changed, 2566 insertions, 659 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index b4b8b21..d812392 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -68,9 +68,15 @@ config HID_A4TECH ---help--- Support for A4 tech X5 and WOP-35 / Trust 450L mice. -config HID_ACRUX_FF - tristate "ACRUX force feedback" +config HID_ACRUX + tristate "ACRUX game controller support" depends on USB_HID + ---help--- + Say Y here if you want to enable support for ACRUX game controllers. + +config HID_ACRUX_FF + tristate "ACRUX force feedback support" + depends on HID_ACRUX select INPUT_FF_MEMLESS ---help--- Say Y here if you want to enable force feedback support for ACRUX @@ -140,7 +146,12 @@ config HID_DRAGONRISE tristate "DragonRise Inc. game controller" depends on USB_HID ---help--- - Say Y here if you have DragonRise Inc.game controllers. + Say Y here if you have DragonRise Inc. game controllers. + These might be branded as: + - Tesun USB-703 + - Media-tech MT1504 "Rogue" + - DVTech JS19 "Gear" + - Defender Game Master config DRAGONRISE_FF bool "DragonRise Inc. force feedback" @@ -173,6 +184,14 @@ config HID_EZKEY ---help--- Support for Ezkey BTC 8193 keyboard. +config HID_KEYTOUCH + tristate "Keyoutch HID devices" + depends on USB_HID + ---help--- + Support for Keytouch HID devices not fully compliant with + the specification. Currently supported: + - Keytouch IEC 60945 + config HID_KYE tristate "Kye/Genius Ergo Mouse" if EMBEDDED depends on USB_HID @@ -211,6 +230,12 @@ config HID_KENSINGTON ---help--- Support for Kensington Slimblade Trackball. +config HID_LCPOWER + tristate "LC-Power" + depends on USB_HID + ---help--- + Support for LC-Power RC1000MCE RF remote control. + config HID_LOGITECH tristate "Logitech devices" if EMBEDDED depends on USB_HID @@ -315,10 +340,10 @@ config HID_NTRIG Support for N-Trig touch screen. config HID_ORTEK - tristate "Ortek WKB-2000 wireless keyboard and mouse trackpad" + tristate "Ortek PKB-1700/WKB-2000 wireless keyboard and mouse trackpad" depends on USB_HID ---help--- - Support for Ortek WKB-2000 wireless keyboard + mouse trackpad. + Support for Ortek PKB-1700/WKB-2000 wireless keyboard + mouse trackpad. config HID_PANTHERLORD tristate "Pantherlord/GreenAsia game controller" @@ -413,10 +438,22 @@ config HID_ROCCAT Say Y here if you have a Roccat mouse or keyboard and want OSD or macro execution support. +config HID_ROCCAT_COMMON + tristate + +config HID_ROCCAT_ARVO + tristate "Roccat Arvo keyboard support" + depends on USB_HID + select HID_ROCCAT + select HID_ROCCAT_COMMON + ---help--- + Support for Roccat Arvo keyboard. + config HID_ROCCAT_KONE tristate "Roccat Kone Mouse support" depends on USB_HID select HID_ROCCAT + select HID_ROCCAT_COMMON ---help--- Support for Roccat Kone mouse. @@ -424,13 +461,23 @@ config HID_ROCCAT_KONEPLUS tristate "Roccat Kone[+] mouse support" depends on USB_HID select HID_ROCCAT + select HID_ROCCAT_COMMON ---help--- Support for Roccat Kone[+] mouse. +config HID_ROCCAT_KOVAPLUS + tristate "Roccat Kova[+] mouse support" + depends on USB_HID + select HID_ROCCAT + select HID_ROCCAT_COMMON + ---help--- + Support for Roccat Kova[+] mouse. + config HID_ROCCAT_PYRA tristate "Roccat Pyra mouse support" depends on USB_HID select HID_ROCCAT + select HID_ROCCAT_COMMON ---help--- Support for Roccat Pyra mouse. diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 29e9898..06c68ae 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -27,20 +27,22 @@ endif obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o -obj-$(CONFIG_HID_ACRUX_FF) += hid-axff.o +obj-$(CONFIG_HID_ACRUX) += hid-axff.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_DRAGONRISE) += hid-dr.o obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o +obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o +obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o @@ -55,8 +57,11 @@ 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) += hid-roccat.o +obj-$(CONFIG_HID_ROCCAT_COMMON) += hid-roccat-common.o +obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o +obj-$(CONFIG_HID_ROCCAT_KOVAPLUS) += hid-roccat-kovaplus.o obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index e5b961d..b455428 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -33,6 +33,8 @@ #include <linux/hid.h> #include "hid-ids.h" + +#ifdef CONFIG_HID_ACRUX_FF #include "usbhid/usbhid.h" struct axff_device { @@ -109,6 +111,12 @@ err_free_mem: kfree(axff); return error; } +#else +static inline int axff_init(struct hid_device *hid) +{ + return 0; +} +#endif static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -139,9 +147,25 @@ static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) error); } + /* + * We need to start polling device right away, otherwise + * it will go into a coma. + */ + error = hid_hw_open(hdev); + if (error) { + dev_err(&hdev->dev, "hw open failed\n"); + return error; + } + return 0; } +static void ax_remove(struct hid_device *hdev) +{ + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + static const struct hid_device_id ax_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), }, { } @@ -149,9 +173,10 @@ static const struct hid_device_id ax_devices[] = { MODULE_DEVICE_TABLE(hid, ax_devices); static struct hid_driver ax_driver = { - .name = "acrux", - .id_table = ax_devices, - .probe = ax_probe, + .name = "acrux", + .id_table = ax_devices, + .probe = ax_probe, + .remove = ax_remove, }; static int __init ax_init(void) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index a5f6439..c3d6626 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1159,6 +1159,32 @@ static bool hid_hiddev(struct hid_device *hdev) return !!hid_match_id(hdev, hid_hiddev_list); } + +static ssize_t +read_report_descriptor(struct file *filp, 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 hid_device *hdev = container_of(dev, struct hid_device, dev); + + if (off >= hdev->rsize) + return 0; + + if (off + count > hdev->rsize) + count = hdev->rsize - off; + + memcpy(buf, hdev->rdesc + off, count); + + return count; +} + +static struct bin_attribute dev_bin_attr_report_desc = { + .attr = { .name = "report_descriptor", .mode = 0444 }, + .read = read_report_descriptor, + .size = HID_MAX_DESCRIPTOR_SIZE, +}; + int hid_connect(struct hid_device *hdev, unsigned int connect_mask) { static const char *types[] = { "Device", "Pointer", "Mouse", "Device", @@ -1169,6 +1195,7 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) char buf[64]; unsigned int i; int len; + int ret; if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE) connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV); @@ -1230,6 +1257,11 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) bus = "<UNKNOWN>"; } + ret = device_create_bin_file(&hdev->dev, &dev_bin_attr_report_desc); + if (ret) + hid_warn(hdev, + "can't create sysfs report descriptor attribute err: %d\n", ret); + hid_info(hdev, "%s: %s HID v%x.%02x %s [%s] on %s\n", buf, bus, hdev->version >> 8, hdev->version & 0xff, type, hdev->name, hdev->phys); @@ -1240,6 +1272,7 @@ EXPORT_SYMBOL_GPL(hid_connect); void hid_disconnect(struct hid_device *hdev) { + device_remove_bin_file(&hdev->dev, &dev_bin_attr_report_desc); if (hdev->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(hdev); if (hdev->claimed & HID_CLAIMED_HIDDEV) @@ -1256,9 +1289,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { 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_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, -#if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE) { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, -#endif { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, @@ -1328,6 +1359,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) }, @@ -1347,8 +1379,10 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, USB_DEVICE_ID_IRTOUCH_INFRARED_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, @@ -1369,6 +1403,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, @@ -1401,12 +1436,15 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { 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_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, { 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) }, diff --git a/drivers/hid/hid-drff.c b/drivers/hid/hid-dr.c index afcf3d6..61eece4 100644 --- a/drivers/hid/hid-drff.c +++ b/drivers/hid/hid-dr.c @@ -145,6 +145,110 @@ static inline int drff_init(struct hid_device *hid) } #endif +/* + * The original descriptor of joystick with PID 0x0011, represented by DVTech PC + * JS19. It seems both copied from another device and a result of confusion + * either about the specification or about the program used to create the + * descriptor. In any case, it's a wonder it works on Windows. + * + * Usage Page (Desktop), ; Generic desktop controls (01h) + * Usage (Joystik), ; Joystik (04h, application collection) + * Collection (Application), + * Collection (Logical), + * Report Size (8), + * Report Count (5), + * Logical Minimum (0), + * Logical Maximum (255), + * Physical Minimum (0), + * Physical Maximum (255), + * Usage (X), ; X (30h, dynamic value) + * Usage (X), ; X (30h, dynamic value) + * Usage (X), ; X (30h, dynamic value) + * Usage (X), ; X (30h, dynamic value) + * Usage (Y), ; Y (31h, dynamic value) + * Input (Variable), + * Report Size (4), + * Report Count (1), + * Logical Maximum (7), + * Physical Maximum (315), + * Unit (Degrees), + * Usage (00h), + * Input (Variable, Null State), + * Unit, + * Report Size (1), + * Report Count (10), + * Logical Maximum (1), + * Physical Maximum (1), + * Usage Page (Button), ; Button (09h) + * Usage Minimum (01h), + * Usage Maximum (0Ah), + * Input (Variable), + * Usage Page (FF00h), ; FF00h, vendor-defined + * Report Size (1), + * Report Count (10), + * Logical Maximum (1), + * Physical Maximum (1), + * Usage (01h), + * Input (Variable), + * End Collection, + * Collection (Logical), + * Report Size (8), + * Report Count (4), + * Physical Maximum (255), + * Logical Maximum (255), + * Usage (02h), + * Output (Variable), + * End Collection, + * End Collection + */ + +/* Size of the original descriptor of the PID 0x0011 joystick */ +#define PID0011_RDESC_ORIG_SIZE 101 + +/* Fixed report descriptor for PID 0x011 joystick */ +static __u8 pid0011_rdesc_fixed[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x04, /* Usage (Joystik), */ + 0xA1, 0x01, /* Collection (Application), */ + 0xA1, 0x02, /* Collection (Logical), */ + 0x14, /* Logical Minimum (0), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x01, /* Input (Constant), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x95, 0x02, /* Report Count (2), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x04, /* Report Count (4), */ + 0x81, 0x01, /* Input (Constant), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x0A, /* Report Count (10), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x0A, /* Usage Maximum (0Ah), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x0A, /* Report Count (10), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + switch (hdev->product) { + case 0x0011: + if (*rsize == PID0011_RDESC_ORIG_SIZE) { + rdesc = pid0011_rdesc_fixed; + *rsize = sizeof(pid0011_rdesc_fixed); + } + break; + } + return rdesc; +} + static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; @@ -163,7 +267,16 @@ static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err; } - drff_init(hdev); + switch (hdev->product) { + case 0x0006: + ret = drff_init(hdev); + if (ret) { + dev_err(&hdev->dev, "force feedback init failed\n"); + hid_hw_stop(hdev); + goto err; + } + break; + } return 0; err: @@ -172,6 +285,7 @@ err: static const struct hid_device_id dr_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006), }, + { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011), }, { } }; MODULE_DEVICE_TABLE(hid, dr_devices); @@ -179,6 +293,7 @@ MODULE_DEVICE_TABLE(hid, dr_devices); static struct hid_driver dr_driver = { .name = "dragonrise", .id_table = dr_devices, + .report_fixup = dr_report_fixup, .probe = dr_probe, }; diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c index 3975e03..e88b951 100644 --- a/drivers/hid/hid-gyration.c +++ b/drivers/hid/hid-gyration.c @@ -43,6 +43,11 @@ static int gyration_input_mapping(struct hid_device *hdev, struct hid_input *hi, case 0x048: gy_map_key_clear(KEY_MEDIA); break; case 0x049: gy_map_key_clear(KEY_CAMERA); break; case 0x04a: gy_map_key_clear(KEY_VIDEO); break; + case 0x05a: gy_map_key_clear(KEY_TEXT); break; + case 0x05b: gy_map_key_clear(KEY_RED); break; + case 0x05c: gy_map_key_clear(KEY_GREEN); break; + case 0x05d: gy_map_key_clear(KEY_YELLOW); break; + case 0x05e: gy_map_key_clear(KEY_BLUE); break; default: return 0; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index b1dd7ad..d485894 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -348,6 +348,9 @@ #define USB_VENDOR_ID_KWORLD 0x1b80 #define USB_DEVICE_ID_KWORLD_RADIO_FM700 0xd700 +#define USB_VENDOR_ID_KEYTOUCH 0x0926 +#define USB_DEVICE_ID_KEYTOUCH_IEC 0x3333 + #define USB_VENDOR_ID_KYE 0x0458 #define USB_DEVICE_ID_KYE_ERGO_525V 0x0087 #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 @@ -355,6 +358,9 @@ #define USB_VENDOR_ID_LABTEC 0x1020 #define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 +#define USB_VENDOR_ID_LCPOWER 0x1241 +#define USB_DEVICE_ID_LCPOWER_LC1000 0xf767 + #define USB_VENDOR_ID_LD 0x0f11 #define USB_DEVICE_ID_LD_CASSY 0x1000 #define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 @@ -386,6 +392,7 @@ #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 +#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298 #define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 #define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a @@ -469,6 +476,7 @@ #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 #define USB_VENDOR_ID_ORTEK 0x05a4 +#define USB_DEVICE_ID_ORTEK_PKB1700 0x1700 #define USB_DEVICE_ID_ORTEK_WKB2000 0x2000 #define USB_VENDOR_ID_PANJIT 0x134c @@ -499,8 +507,10 @@ #define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001 #define USB_VENDOR_ID_ROCCAT 0x1e7d +#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 +#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f552bf..cd74203 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -290,14 +290,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel goto ignore; } - if (field->report_type == HID_FEATURE_REPORT) { - if (device->driver->feature_mapping) { - device->driver->feature_mapping(device, hidinput, field, - usage); - } - goto ignore; - } - if (device->driver->input_mapping) { int ret = device->driver->input_mapping(device, hidinput, field, usage, &bit, &max); @@ -835,6 +827,24 @@ static void hidinput_close(struct input_dev *dev) hid_hw_close(hid); } +static void report_features(struct hid_device *hid) +{ + struct hid_driver *drv = hid->driver; + struct hid_report_enum *rep_enum; + struct hid_report *rep; + int i, j; + + if (!drv->feature_mapping) + return; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) + for (i = 0; i < rep->maxfield; i++) + for (j = 0; j < rep->field[i]->maxusage; j++) + drv->feature_mapping(hid, rep->field[i], + rep->field[i]->usage + j); +} + /* * Register the input device; print a message. * Configure the input layer interface @@ -863,7 +873,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) return -1; } - for (k = HID_INPUT_REPORT; k <= HID_FEATURE_REPORT; k++) { + report_features(hid); + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { if (k == HID_OUTPUT_REPORT && hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) continue; @@ -928,6 +940,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) return 0; out_cleanup: + list_del(&hidinput->list); input_free_device(hidinput->input); kfree(hidinput); out_unwind: diff --git a/drivers/hid/hid-keytouch.c b/drivers/hid/hid-keytouch.c new file mode 100644 index 0000000..07cd825 --- /dev/null +++ b/drivers/hid/hid-keytouch.c @@ -0,0 +1,66 @@ +/* + * HID driver for Keytouch devices not fully compliant with HID standard + * + * Copyright (c) 2011 Jiri Kosina + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +/* Replace the broken report descriptor of this device with rather + * a default one */ +static __u8 keytouch_fixed_rdesc[] = { +0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, +0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, +0x81, 0x01, 0x95, 0x03, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91, +0x02, 0x95, 0x05, 0x75, 0x01, 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, +0x26, 0xff, 0x00, 0x05, 0x07, 0x19, 0x00, 0x2a, 0xff, 0x00, 0x81, 0x00, 0xc0 +}; + +static __u8 *keytouch_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + hid_info(hdev, "fixing up Keytouch IEC report descriptor\n"); + + rdesc = keytouch_fixed_rdesc; + *rsize = sizeof(keytouch_fixed_rdesc); + + return rdesc; +} + +static const struct hid_device_id keytouch_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, + { } +}; +MODULE_DEVICE_TABLE(hid, keytouch_devices); + +static struct hid_driver keytouch_driver = { + .name = "keytouch", + .id_table = keytouch_devices, + .report_fixup = keytouch_report_fixup, +}; + +static int __init keytouch_init(void) +{ + return hid_register_driver(&keytouch_driver); +} + +static void __exit keytouch_exit(void) +{ + hid_unregister_driver(&keytouch_driver); +} + +module_init(keytouch_init); +module_exit(keytouch_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jiri Kosina"); diff --git a/drivers/hid/hid-lcpower.c b/drivers/hid/hid-lcpower.c new file mode 100644 index 0000000..c4fe9bd0 --- /dev/null +++ b/drivers/hid/hid-lcpower.c @@ -0,0 +1,70 @@ +/* + * HID driver for LC Power Model RC1000MCE + * + * Copyright (c) 2011 Chris Schlund + * based on hid-topseed module + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define ts_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +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) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x046: ts_map_key_clear(KEY_YELLOW); break; + case 0x047: ts_map_key_clear(KEY_GREEN); break; + case 0x049: ts_map_key_clear(KEY_BLUE); break; + case 0x04a: ts_map_key_clear(KEY_RED); break; + case 0x00d: ts_map_key_clear(KEY_HOME); break; + case 0x025: ts_map_key_clear(KEY_TV); break; + case 0x048: ts_map_key_clear(KEY_VCR); break; + case 0x024: ts_map_key_clear(KEY_MENU); break; + default: + return 0; + } + + return 1; +} + +static const struct hid_device_id ts_devices[] = { + { HID_USB_DEVICE( USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ts_devices); + +static struct hid_driver ts_driver = { + .name = "LC RC1000MCE", + .id_table = ts_devices, + .input_mapping = ts_input_mapping, +}; + +static int __init ts_init(void) +{ + return hid_register_driver(&ts_driver); +} + +static void __exit ts_exit(void) +{ + hid_unregister_driver(&ts_driver); +} + +module_init(ts_init); +module_exit(ts_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index aef4104..3da9040 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -377,6 +377,8 @@ static const struct hid_device_id lg_devices[] = { .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL), + .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 698e645..318cc40 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -258,7 +258,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda input_report_abs(input, ABS_MT_TRACKING_ID, id); input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2); input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2); - input_report_abs(input, ABS_MT_ORIENTATION, orientation); + input_report_abs(input, ABS_MT_ORIENTATION, -orientation); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); @@ -397,7 +397,7 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0); input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0); - input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); /* Note: Touch Y position from the device is inverted relative * to how pointer motion is reported (and relative to how USB diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index af5a709..ee01e65 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -139,7 +139,7 @@ struct mt_class mt_classes[] = { { } }; -static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi, +static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { if (usage->hid == HID_DG_INPUTMODE) { diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index beb4034..9fae2eb 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -110,6 +110,36 @@ static int ntrig_version_string(unsigned char *raw, char *buf) return sprintf(buf, "%u.%u.%u.%u.%u", a, b, c, d, e); } +static inline int ntrig_get_mode(struct hid_device *hdev) +{ + struct hid_report *report = hdev->report_enum[HID_FEATURE_REPORT]. + report_id_hash[0x0d]; + + if (!report) + return -EINVAL; + + usbhid_submit_report(hdev, report, USB_DIR_IN); + usbhid_wait_io(hdev); + return (int)report->field[0]->value[0]; +} + +static inline void ntrig_set_mode(struct hid_device *hdev, const int mode) +{ + struct hid_report *report; + __u8 mode_commands[4] = { 0xe, 0xf, 0x1b, 0x10 }; + + if (mode < 0 || mode > 3) + return; + + report = hdev->report_enum[HID_FEATURE_REPORT]. + report_id_hash[mode_commands[mode]]; + + if (!report) + return; + + usbhid_submit_report(hdev, report, USB_DIR_IN); +} + static void ntrig_report_version(struct hid_device *hdev) { int ret; @@ -539,277 +569,288 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, static int ntrig_event (struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { - struct input_dev *input = field->hidinput->input; struct ntrig_data *nd = hid_get_drvdata(hid); + struct input_dev *input; + + /* Skip processing if not a claimed input */ + if (!(hid->claimed & HID_CLAIMED_INPUT)) + goto not_claimed_input; + + /* This function is being called before the structures are fully + * initialized */ + if(!(field->hidinput && field->hidinput->input)) + return -EINVAL; + + input = field->hidinput->input; /* No special handling needed for the pen */ if (field->application == HID_DG_PEN) return 0; - if (hid->claimed & HID_CLAIMED_INPUT) { - switch (usage->hid) { - case 0xff000001: - /* Tag indicating the start of a multitouch group */ - nd->reading_mt = 1; - nd->first_contact_touch = 0; - break; - case HID_DG_TIPSWITCH: - nd->tipswitch = value; - /* Prevent emission of touch until validated */ - return 1; - case HID_DG_CONFIDENCE: - nd->confidence = value; - break; - case HID_GD_X: - nd->x = value; - /* Clear the contact footer */ - nd->mt_foot_count = 0; - break; - case HID_GD_Y: - nd->y = value; - break; - case HID_DG_CONTACTID: - nd->id = value; - break; - case HID_DG_WIDTH: - nd->w = value; - break; - case HID_DG_HEIGHT: - nd->h = value; + switch (usage->hid) { + case 0xff000001: + /* Tag indicating the start of a multitouch group */ + nd->reading_mt = 1; + nd->first_contact_touch = 0; + break; + case HID_DG_TIPSWITCH: + nd->tipswitch = value; + /* Prevent emission of touch until validated */ + return 1; + case HID_DG_CONFIDENCE: + nd->confidence = value; + break; + case HID_GD_X: + nd->x = value; + /* Clear the contact footer */ + nd->mt_foot_count = 0; + break; + case HID_GD_Y: + nd->y = value; + break; + case HID_DG_CONTACTID: + nd->id = value; + break; + case HID_DG_WIDTH: + nd->w = value; + break; + case HID_DG_HEIGHT: + nd->h = value; + /* + * when in single touch mode, this is the last + * report received in a finger event. We want + * to emit a normal (X, Y) position + */ + if (!nd->reading_mt) { /* - * when in single touch mode, this is the last - * report received in a finger event. We want - * to emit a normal (X, Y) position + * TipSwitch indicates the presence of a + * finger in single touch mode. */ - if (!nd->reading_mt) { - /* - * TipSwitch indicates the presence of a - * finger in single touch mode. - */ - input_report_key(input, BTN_TOUCH, - nd->tipswitch); - input_report_key(input, BTN_TOOL_DOUBLETAP, - nd->tipswitch); - input_event(input, EV_ABS, ABS_X, nd->x); - input_event(input, EV_ABS, ABS_Y, nd->y); - } + input_report_key(input, BTN_TOUCH, + nd->tipswitch); + input_report_key(input, BTN_TOOL_DOUBLETAP, + nd->tipswitch); + input_event(input, EV_ABS, ABS_X, nd->x); + input_event(input, EV_ABS, ABS_Y, nd->y); + } + break; + case 0xff000002: + /* + * we receive this when the device is in multitouch + * mode. The first of the three values tagged with + * this usage tells if the contact point is real + * or a placeholder + */ + + /* Shouldn't get more than 4 footer packets, so skip */ + if (nd->mt_foot_count >= 4) break; - case 0xff000002: - /* - * we receive this when the device is in multitouch - * mode. The first of the three values tagged with - * this usage tells if the contact point is real - * or a placeholder - */ - /* Shouldn't get more than 4 footer packets, so skip */ - if (nd->mt_foot_count >= 4) - break; + nd->mt_footer[nd->mt_foot_count++] = value; - nd->mt_footer[nd->mt_foot_count++] = value; + /* if the footer isn't complete break */ + if (nd->mt_foot_count != 4) + break; - /* if the footer isn't complete break */ - if (nd->mt_foot_count != 4) - break; + /* 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; + } - /* 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; + /* + * 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; - break; - } + } else + break; + if (nd->act_state > 0) { /* - * The first footer value indicates the presence of a - * finger. + * Contact meets the activation size threshold */ - 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 + if (nd->w >= nd->activation_width && + nd->h >= nd->activation_height) { + if (nd->id) /* - * Defer adjusting the activation state - * until the end of the frame. + * 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; - } - - /* Discarding this contact */ - if (!nd->confidence) - break; - - /* emit a normal (X, Y) for the first point only */ - if (nd->id == 0) { + } + } else /* - * TipSwitch is superfluous in multitouch - * mode. The footer events tell us - * if there is a finger on the screen or - * not. + * Defer adjusting the activation state + * until the end of the frame. */ - nd->first_contact_touch = nd->confidence; - input_event(input, EV_ABS, ABS_X, nd->x); - input_event(input, EV_ABS, ABS_Y, nd->y); - } + break; + } - /* 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); + /* Discarding this contact */ + if (!nd->confidence) + break; + /* emit a normal (X, Y) for the first point only */ + if (nd->id == 0) { /* - * Translate from height and width to size - * and orientation. + * TipSwitch is superfluous in multitouch + * mode. The footer events tell us + * if there is a finger on the screen or + * not. */ - if (nd->w > nd->h) { - input_event(input, EV_ABS, - ABS_MT_ORIENTATION, 1); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MAJOR, nd->w); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MINOR, nd->h); - } else { - input_event(input, EV_ABS, - ABS_MT_ORIENTATION, 0); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MAJOR, nd->h); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MINOR, nd->w); - } - input_mt_sync(field->hidinput->input); - break; + nd->first_contact_touch = nd->confidence; + input_event(input, EV_ABS, ABS_X, nd->x); + input_event(input, EV_ABS, ABS_Y, nd->y); + } - case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ - if (!nd->reading_mt) /* Just to be sure */ - break; + /* 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); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MAJOR, nd->w); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MINOR, nd->h); + } else { + input_event(input, EV_ABS, + ABS_MT_ORIENTATION, 0); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MAJOR, nd->h); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MINOR, nd->w); + } + input_mt_sync(field->hidinput->input); + break; - nd->reading_mt = 0; + case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ + if (!nd->reading_mt) /* Just to be sure */ + break; + nd->reading_mt = 0; + + + /* + * 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; /* - * 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 + * Entered this block inactive and no + * coordinates sent this frame, so hold off + * on button state. */ - - 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; - + break; + } else { /* Currently active */ + if (value && nd->act_state >= + nd->deactivate_slack) /* - * Entered this block inactive and no - * coordinates sent this frame, so hold off - * on button state. + * Live point: clear accumulated + * deactivation count. */ - 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) { + nd->act_state = 0; + else if (nd->act_state <= nd->deactivate_slack) /* - * 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. + * We've consumed the deactivation + * slack, time to deactivate and reset. */ - input_report_key(input, BTN_TOOL_DOUBLETAP, 1); - input_report_key(input, BTN_TOUCH, 1); - } else { - input_report_key(input, BTN_TOOL_DOUBLETAP, 0); - input_report_key(input, BTN_TOUCH, 0); + nd->act_state = + nd->activate_slack; + else { /* Move towards deactivation */ + nd->act_state--; + break; } - break; + } - default: - /* fall-back to the generic hidinput handling */ - return 0; + 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 { + input_report_key(input, BTN_TOOL_DOUBLETAP, 0); + input_report_key(input, BTN_TOUCH, 0); } + break; + + default: + /* fall-back to the generic hidinput handling */ + return 0; } +not_claimed_input: + /* 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); @@ -826,7 +867,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) struct hid_report *report; if (id->driver_data) - hdev->quirks |= HID_QUIRK_MULTI_INPUT; + hdev->quirks |= HID_QUIRK_MULTI_INPUT + | HID_QUIRK_NO_INIT_REPORTS; nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL); if (!nd) { @@ -893,8 +935,19 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) /* This is needed for devices with more recent firmware versions */ report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0x0a]; - if (report) - usbhid_submit_report(hdev, report, USB_DIR_OUT); + if (report) { + /* Let the device settle to ensure the wakeup message gets + * through */ + usbhid_wait_io(hdev); + usbhid_submit_report(hdev, report, USB_DIR_IN); + + /* + * Sanity check: if the current mode is invalid reset it to + * something reasonable. + */ + if (ntrig_get_mode(hdev) >= 4) + ntrig_set_mode(hdev, 3); + } ntrig_report_version(hdev); diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c index e90edfc..f9b7dd4 100644 --- a/drivers/hid/hid-ortek.c +++ b/drivers/hid/hid-ortek.c @@ -1,7 +1,6 @@ /* - * HID driver for Ortek WKB-2000 (wireless keyboard + mouse trackpad). - * Fixes LogicalMaximum error in USB report description, see - * http://bugzilla.kernel.org/show_bug.cgi?id=14787 + * HID driver for Ortek PKB-1700/WKB-2000 (wireless keyboard + mouse trackpad). + * Fixes LogicalMaximum error in HID report description. * * Copyright (c) 2010 Johnathon Harris <jmharris@gmail.com> */ @@ -30,6 +29,7 @@ static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc, } static const struct hid_device_id ortek_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { } }; diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c new file mode 100644 index 0000000..2307471 --- /dev/null +++ b/drivers/hid/hid-roccat-arvo.c @@ -0,0 +1,450 @@ +/* + * Roccat Arvo driver for Linux + * + * Copyright (c) 2011 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 Arvo is a gamer keyboard with 5 macro keys that can be configured in + * 5 profiles. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/hid-roccat.h> +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-arvo.h" + +static struct class *arvo_class; + +static ssize_t arvo_sysfs_show_mode_key(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + struct usb_device *usb_dev = + interface_to_usbdev(to_usb_interface(dev->parent->parent)); + struct arvo_mode_key temp_buf; + int retval; + + mutex_lock(&arvo->arvo_lock); + retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_MODE_KEY, + &temp_buf, sizeof(struct arvo_mode_key)); + mutex_unlock(&arvo->arvo_lock); + if (retval) + return retval; + + return snprintf(buf, PAGE_SIZE, "%d\n", temp_buf.state); +} + +static ssize_t arvo_sysfs_set_mode_key(struct device *dev, + struct device_attribute *attr, char const *buf, size_t size) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + struct usb_device *usb_dev = + interface_to_usbdev(to_usb_interface(dev->parent->parent)); + struct arvo_mode_key temp_buf; + unsigned long state; + int retval; + + retval = strict_strtoul(buf, 10, &state); + if (retval) + return retval; + + temp_buf.command = ARVO_COMMAND_MODE_KEY; + temp_buf.state = state; + + mutex_lock(&arvo->arvo_lock); + retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_MODE_KEY, + &temp_buf, sizeof(struct arvo_mode_key)); + mutex_unlock(&arvo->arvo_lock); + if (retval) + return retval; + + return size; +} + +static ssize_t arvo_sysfs_show_key_mask(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + struct usb_device *usb_dev = + interface_to_usbdev(to_usb_interface(dev->parent->parent)); + struct arvo_key_mask temp_buf; + int retval; + + mutex_lock(&arvo->arvo_lock); + retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_KEY_MASK, + &temp_buf, sizeof(struct arvo_key_mask)); + mutex_unlock(&arvo->arvo_lock); + if (retval) + return retval; + + return snprintf(buf, PAGE_SIZE, "%d\n", temp_buf.key_mask); +} + +static ssize_t arvo_sysfs_set_key_mask(struct device *dev, + struct device_attribute *attr, char const *buf, size_t size) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + struct usb_device *usb_dev = + interface_to_usbdev(to_usb_interface(dev->parent->parent)); + struct arvo_key_mask temp_buf; + unsigned long key_mask; + int retval; + + retval = strict_strtoul(buf, 10, &key_mask); + if (retval) + return retval; + + temp_buf.command = ARVO_COMMAND_KEY_MASK; + temp_buf.key_mask = key_mask; + + mutex_lock(&arvo->arvo_lock); + retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_KEY_MASK, + &temp_buf, sizeof(struct arvo_key_mask)); + mutex_unlock(&arvo->arvo_lock); + if (retval) + return retval; + + return size; +} + +/* retval is 1-5 on success, < 0 on error */ +static int arvo_get_actual_profile(struct usb_device *usb_dev) +{ + struct arvo_actual_profile temp_buf; + int retval; + + retval = roccat_common_receive(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE, + &temp_buf, sizeof(struct arvo_actual_profile)); + + if (retval) + return retval; + + return temp_buf.actual_profile; +} + +static ssize_t arvo_sysfs_show_actual_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + + return snprintf(buf, PAGE_SIZE, "%d\n", arvo->actual_profile); +} + +static ssize_t arvo_sysfs_set_actual_profile(struct device *dev, + struct device_attribute *attr, char const *buf, size_t size) +{ + struct arvo_device *arvo = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + struct usb_device *usb_dev = + interface_to_usbdev(to_usb_interface(dev->parent->parent)); + struct arvo_actual_profile temp_buf; + unsigned long profile; + int retval; + + retval = strict_strtoul(buf, 10, &profile); + if (retval) + return retval; + + temp_buf.command = ARVO_COMMAND_ACTUAL_PROFILE; + temp_buf.actual_profile = profile; + + mutex_lock(&arvo->arvo_lock); + retval = roccat_common_send(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE, + &temp_buf, sizeof(struct arvo_actual_profile)); + if (!retval) { + arvo->actual_profile = profile; + retval = size; + } + mutex_unlock(&arvo->arvo_lock); + return retval; +} + +static ssize_t arvo_sysfs_write(struct file *fp, + struct kobject *kobj, void const *buf, + loff_t off, size_t count, size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct arvo_device *arvo = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&arvo->arvo_lock); + retval = roccat_common_send(usb_dev, command, buf, real_size); + mutex_unlock(&arvo->arvo_lock); + + return (retval ? retval : real_size); +} + +static ssize_t arvo_sysfs_read(struct file *fp, + struct kobject *kobj, void *buf, loff_t off, + size_t count, size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct arvo_device *arvo = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off >= real_size) + return 0; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&arvo->arvo_lock); + retval = roccat_common_receive(usb_dev, command, buf, real_size); + mutex_unlock(&arvo->arvo_lock); + + return (retval ? retval : real_size); +} + +static ssize_t arvo_sysfs_write_button(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return arvo_sysfs_write(fp, kobj, buf, off, count, + sizeof(struct arvo_button), ARVO_USB_COMMAND_BUTTON); +} + +static ssize_t arvo_sysfs_read_info(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return arvo_sysfs_read(fp, kobj, buf, off, count, + sizeof(struct arvo_info), ARVO_USB_COMMAND_INFO); +} + + +static struct device_attribute arvo_attributes[] = { + __ATTR(mode_key, 0660, + arvo_sysfs_show_mode_key, arvo_sysfs_set_mode_key), + __ATTR(key_mask, 0660, + arvo_sysfs_show_key_mask, arvo_sysfs_set_key_mask), + __ATTR(actual_profile, 0660, + arvo_sysfs_show_actual_profile, + arvo_sysfs_set_actual_profile), + __ATTR_NULL +}; + +static struct bin_attribute arvo_bin_attributes[] = { + { + .attr = { .name = "button", .mode = 0220 }, + .size = sizeof(struct arvo_button), + .write = arvo_sysfs_write_button + }, + { + .attr = { .name = "info", .mode = 0440 }, + .size = sizeof(struct arvo_info), + .read = arvo_sysfs_read_info + }, + __ATTR_NULL +}; + +static int arvo_init_arvo_device_struct(struct usb_device *usb_dev, + struct arvo_device *arvo) +{ + int retval; + + mutex_init(&arvo->arvo_lock); + + retval = arvo_get_actual_profile(usb_dev); + if (retval < 0) + return retval; + arvo->actual_profile = retval; + + return 0; +} + +static int arvo_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 arvo_device *arvo; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_KEYBOARD) { + hid_set_drvdata(hdev, NULL); + return 0; + } + + arvo = kzalloc(sizeof(*arvo), GFP_KERNEL); + if (!arvo) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, arvo); + + retval = arvo_init_arvo_device_struct(usb_dev, arvo); + if (retval) { + hid_err(hdev, "couldn't init struct arvo_device\n"); + goto exit_free; + } + + retval = roccat_connect(arvo_class, hdev, + sizeof(struct arvo_roccat_report)); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + arvo->chrdev_minor = retval; + arvo->roccat_claimed = 1; + } + + return 0; +exit_free: + kfree(arvo); + return retval; +} + +static void arvo_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct arvo_device *arvo; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_KEYBOARD) + return; + + arvo = hid_get_drvdata(hdev); + if (arvo->roccat_claimed) + roccat_disconnect(arvo->chrdev_minor); + kfree(arvo); +} + +static int arvo_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "hw start failed\n"); + goto exit; + } + + retval = arvo_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install keyboard\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void arvo_remove(struct hid_device *hdev) +{ + arvo_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static void arvo_report_to_chrdev(struct arvo_device const *arvo, + u8 const *data) +{ + struct arvo_special_report const *special_report; + struct arvo_roccat_report roccat_report; + + special_report = (struct arvo_special_report const *)data; + + roccat_report.profile = arvo->actual_profile; + roccat_report.button = special_report->event & + ARVO_SPECIAL_REPORT_EVENT_MASK_BUTTON; + if ((special_report->event & ARVO_SPECIAL_REPORT_EVENT_MASK_ACTION) == + ARVO_SPECIAL_REPORT_EVENT_ACTION_PRESS) + roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_PRESS; + else + roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_RELEASE; + + roccat_report_event(arvo->chrdev_minor, + (uint8_t const *)&roccat_report); +} + +static int arvo_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct arvo_device *arvo = hid_get_drvdata(hdev); + + if (size != 3) + return 0; + + if (arvo->roccat_claimed) + arvo_report_to_chrdev(arvo, data); + + return 0; +} + +static const struct hid_device_id arvo_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, arvo_devices); + +static struct hid_driver arvo_driver = { + .name = "arvo", + .id_table = arvo_devices, + .probe = arvo_probe, + .remove = arvo_remove, + .raw_event = arvo_raw_event +}; + +static int __init arvo_init(void) +{ + int retval; + + arvo_class = class_create(THIS_MODULE, "arvo"); + if (IS_ERR(arvo_class)) + return PTR_ERR(arvo_class); + arvo_class->dev_attrs = arvo_attributes; + arvo_class->dev_bin_attrs = arvo_bin_attributes; + + retval = hid_register_driver(&arvo_driver); + if (retval) + class_destroy(arvo_class); + return retval; +} + +static void __exit arvo_exit(void) +{ + hid_unregister_driver(&arvo_driver); + class_destroy(arvo_class); +} + +module_init(arvo_init); +module_exit(arvo_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Arvo driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-arvo.h b/drivers/hid/hid-roccat-arvo.h new file mode 100644 index 0000000..d284a78 --- /dev/null +++ b/drivers/hid/hid-roccat-arvo.h @@ -0,0 +1,98 @@ +#ifndef __HID_ROCCAT_ARVO_H +#define __HID_ROCCAT_ARVO_H + +/* + * Copyright (c) 2011 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> + +struct arvo_mode_key { /* 2 bytes */ + uint8_t command; /* ARVO_COMMAND_MODE_KEY */ + uint8_t state; +} __packed; + +struct arvo_button { + uint8_t unknown[24]; +} __packed; + +struct arvo_info { + uint8_t unknown[8]; +} __packed; + +struct arvo_key_mask { /* 2 bytes */ + uint8_t command; /* ARVO_COMMAND_KEY_MASK */ + uint8_t key_mask; +} __packed; + +/* selected profile is persistent */ +struct arvo_actual_profile { /* 2 bytes */ + uint8_t command; /* ARVO_COMMAND_ACTUAL_PROFILE */ + uint8_t actual_profile; +} __packed; + +enum arvo_commands { + ARVO_COMMAND_MODE_KEY = 0x3, + ARVO_COMMAND_BUTTON = 0x4, + ARVO_COMMAND_INFO = 0x5, + ARVO_COMMAND_KEY_MASK = 0x6, + ARVO_COMMAND_ACTUAL_PROFILE = 0x7, +}; + +enum arvo_usb_commands { + ARVO_USB_COMMAND_MODE_KEY = 0x303, + /* + * read/write + * Read uses both index bytes as profile/key indexes + * Write has index 0, profile/key is determined by payload + */ + ARVO_USB_COMMAND_BUTTON = 0x304, + ARVO_USB_COMMAND_INFO = 0x305, + ARVO_USB_COMMAND_KEY_MASK = 0x306, + ARVO_USB_COMMAND_ACTUAL_PROFILE = 0x307, +}; + +struct arvo_special_report { + uint8_t unknown1; /* always 0x01 */ + uint8_t event; + uint8_t unknown2; /* always 0x70 */ +} __packed; + +enum arvo_special_report_events { + ARVO_SPECIAL_REPORT_EVENT_ACTION_PRESS = 0x10, + ARVO_SPECIAL_REPORT_EVENT_ACTION_RELEASE = 0x0, +}; + +enum arvo_special_report_event_masks { + ARVO_SPECIAL_REPORT_EVENT_MASK_ACTION = 0xf0, + ARVO_SPECIAL_REPORT_EVENT_MASK_BUTTON = 0x0f, +}; + +struct arvo_roccat_report { + uint8_t profile; + uint8_t button; + uint8_t action; +} __packed; + +enum arvo_roccat_report_action { + ARVO_ROCCAT_REPORT_ACTION_RELEASE = 0, + ARVO_ROCCAT_REPORT_ACTION_PRESS = 1, +}; + +struct arvo_device { + int roccat_claimed; + int chrdev_minor; + + struct mutex arvo_lock; + + int actual_profile; +}; + +#endif diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c new file mode 100644 index 0000000..13b1eb0 --- /dev/null +++ b/drivers/hid/hid-roccat-common.c @@ -0,0 +1,62 @@ +/* + * Roccat common functions for device specific drivers + * + * Copyright (c) 2011 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/slab.h> +#include "hid-roccat-common.h" + +int roccat_common_receive(struct usb_device *usb_dev, uint usb_command, + void *data, uint size) +{ + char *buf; + int len; + + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) + 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, + usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT); + + memcpy(data, buf, size); + kfree(buf); + return ((len < 0) ? len : ((len != size) ? -EIO : 0)); +} +EXPORT_SYMBOL_GPL(roccat_common_receive); + +int roccat_common_send(struct usb_device *usb_dev, uint usb_command, + void const *data, uint size) +{ + char *buf; + int len; + + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + memcpy(buf, data, size); + + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT); + + kfree(buf); + return ((len < 0) ? len : ((len != size) ? -EIO : 0)); +} +EXPORT_SYMBOL_GPL(roccat_common_send); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat common driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-common.h b/drivers/hid/hid-roccat-common.h new file mode 100644 index 0000000..fe45fae --- /dev/null +++ b/drivers/hid/hid-roccat-common.h @@ -0,0 +1,23 @@ +#ifndef __HID_ROCCAT_COMMON_H +#define __HID_ROCCAT_COMMON_H + +/* + * Copyright (c) 2011 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/usb.h> +#include <linux/types.h> + +int roccat_common_receive(struct usb_device *usb_dev, uint usb_command, + void *data, uint size); +int roccat_common_send(struct usb_device *usb_dev, uint usb_command, + void const *data, uint size); + +#endif diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index cbd8cc4..a57838d 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -28,11 +28,11 @@ #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 <linux/hid-roccat.h> #include "hid-ids.h" -#include "hid-roccat.h" +#include "hid-roccat-common.h" #include "hid-roccat-kone.h" static uint profile_numbers[5] = {0, 1, 2, 3, 4}; @@ -58,12 +58,8 @@ static void kone_set_settings_checksum(struct kone_settings *settings) */ static int kone_check_write(struct usb_device *usb_dev) { - int len; - unsigned char *data; - - data = kmalloc(1, GFP_KERNEL); - if (!data) - return -ENOMEM; + int retval; + uint8_t data; do { /* @@ -72,56 +68,36 @@ static int kone_check_write(struct usb_device *usb_dev) */ 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; - } + retval = roccat_common_receive(usb_dev, + kone_command_confirm_write, &data, 1); + if (retval) + return retval; /* * value of 3 seems to mean something like * "not finished yet, but it looks good" * So check again after a moment. */ - } while (*data == 3); + } while (data == 3); - if (*data == 1) { /* everything alright */ - kfree(data); + if (data == 1) /* everything alright */ return 0; - } else { /* unknown answer */ - hid_err(usb_dev, "got retval %d when checking write\n", *data); - kfree(data); - return -EIO; - } + + /* unknown answer */ + hid_err(usb_dev, "got retval %d when checking write\n", 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; + return roccat_common_receive(usb_dev, kone_command_settings, buf, + sizeof(struct kone_settings)); } /* @@ -132,22 +108,12 @@ static int kone_get_settings(struct usb_device *usb_dev, 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; + int retval; + retval = roccat_common_send(usb_dev, kone_command_settings, + settings, sizeof(struct kone_settings)); + if (retval) + return retval; + return kone_check_write(usb_dev); } /* @@ -193,7 +159,7 @@ static int kone_set_profile(struct usb_device *usb_dev, 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, + kone_command_profile, number, (void *)profile, sizeof(struct kone_profile), USB_CTRL_SET_TIMEOUT); @@ -213,24 +179,15 @@ static int kone_set_profile(struct usb_device *usb_dev, */ static int kone_get_weight(struct usb_device *usb_dev, int *result) { - int len; - uint8_t *data; + int retval; + uint8_t data; - data = kmalloc(1, GFP_KERNEL); - if (!data) - return -ENOMEM; + retval = roccat_common_receive(usb_dev, kone_command_weight, &data, 1); - 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 (retval) + return retval; - if (len != 1) { - kfree(data); - return -EIO; - } - *result = (int)*data; - kfree(data); + *result = (int)data; return 0; } @@ -241,25 +198,15 @@ static int kone_get_weight(struct usb_device *usb_dev, int *result) */ 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; + int retval; + uint16_t data; - 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); + retval = roccat_common_receive(usb_dev, kone_command_firmware_version, + &data, 2); + if (retval) + return retval; - if (len != 2) { - kfree(data); - return -EIO; - } - *result = le16_to_cpu(*data); - kfree(data); + *result = le16_to_cpu(data); return 0; } @@ -435,23 +382,9 @@ static ssize_t kone_sysfs_show_tcu(struct device *dev, 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); + unsigned char value; + value = number; + return roccat_common_send(usb_dev, kone_command_calibrate, &value, 1); } /* @@ -727,7 +660,8 @@ static int kone_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(kone_class, hdev); + retval = roccat_connect(kone_class, hdev, + sizeof(struct kone_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); /* be tolerant about not getting chrdev */ @@ -827,8 +761,7 @@ static void kone_report_to_chrdev(struct kone_device const *kone, roccat_report.value = event->value; roccat_report.key = 0; roccat_report_event(kone->chrdev_minor, - (uint8_t *)&roccat_report, - sizeof(struct kone_roccat_report)); + (uint8_t *)&roccat_report); break; case kone_mouse_event_call_overlong_macro: if (event->value == kone_keystroke_action_press) { @@ -836,8 +769,7 @@ static void kone_report_to_chrdev(struct kone_device const *kone, roccat_report.value = kone->actual_profile; roccat_report.key = event->macro_key; roccat_report_event(kone->chrdev_minor, - (uint8_t *)&roccat_report, - sizeof(struct kone_roccat_report)); + (uint8_t *)&roccat_report); } break; } @@ -912,8 +844,8 @@ static int __init kone_init(void) static void __exit kone_exit(void) { - class_destroy(kone_class); hid_unregister_driver(&kone_driver); + class_destroy(kone_class); } module_init(kone_init); diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 1608c8d..33eec74 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -19,11 +19,11 @@ #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 <linux/hid-roccat.h> #include "hid-ids.h" -#include "hid-roccat.h" +#include "hid-roccat-common.h" #include "hid-roccat-koneplus.h" static uint profile_numbers[5] = {0, 1, 2, 3, 4}; @@ -39,110 +39,63 @@ static void koneplus_profile_activated(struct koneplus_device *koneplus, static int koneplus_send_control(struct usb_device *usb_dev, uint value, enum koneplus_control_requests request) { - int len; - struct koneplus_control *control; + struct koneplus_control control; if ((request == KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS || request == KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) && value > 4) return -EINVAL; - control = kmalloc(sizeof(struct koneplus_control), GFP_KERNEL); - if (!control) - return -ENOMEM; + control.command = KONEPLUS_COMMAND_CONTROL; + control.value = value; + control.request = request; - control->command = KONEPLUS_COMMAND_CONTROL; - control->value = value; - control->request = request; - - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - KONEPLUS_USB_COMMAND_CONTROL, 0, control, - sizeof(struct koneplus_control), - USB_CTRL_SET_TIMEOUT); - - kfree(control); - - if (len != sizeof(struct koneplus_control)) - return len; - - return 0; -} - -static int koneplus_receive(struct usb_device *usb_dev, uint usb_command, - void *buf, uint size) { - 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, - usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT); - - return (len != size) ? -EIO : 0; + return roccat_common_send(usb_dev, KONEPLUS_USB_COMMAND_CONTROL, + &control, sizeof(struct koneplus_control)); } static int koneplus_receive_control_status(struct usb_device *usb_dev) { int retval; - struct koneplus_control *control; - - control = kmalloc(sizeof(struct koneplus_control), GFP_KERNEL); - if (!control) - return -ENOMEM; + struct koneplus_control control; do { - retval = koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_CONTROL, - control, sizeof(struct koneplus_control)); + retval = roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_CONTROL, + &control, sizeof(struct koneplus_control)); /* check if we get a completely wrong answer */ if (retval) - goto out; + return retval; - if (control->value == KONEPLUS_CONTROL_REQUEST_STATUS_OK) { - retval = 0; - goto out; - } + if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_OK) + return 0; /* indicates that hardware needs some more time to complete action */ - if (control->value == KONEPLUS_CONTROL_REQUEST_STATUS_WAIT) { + if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_WAIT) { msleep(500); /* windows driver uses 1000 */ continue; } /* seems to be critical - replug necessary */ - if (control->value == KONEPLUS_CONTROL_REQUEST_STATUS_OVERLOAD) { - retval = -EINVAL; - goto out; - } - - dev_err(&usb_dev->dev, "koneplus_receive_control_status: " - "unknown response value 0x%x\n", control->value); - retval = -EINVAL; - goto out; + if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_OVERLOAD) + return -EINVAL; + hid_err(usb_dev, "koneplus_receive_control_status: " + "unknown response value 0x%x\n", control.value); + return -EINVAL; } while (1); -out: - kfree(control); - return retval; } static int koneplus_send(struct usb_device *usb_dev, uint command, - void *buf, uint size) { - 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, - command, 0, buf, size, USB_CTRL_SET_TIMEOUT); - - if (len != size) - return -EIO; + void const *buf, uint size) +{ + int retval; - if (koneplus_receive_control_status(usb_dev)) - return -EIO; + retval = roccat_common_send(usb_dev, command, buf, size); + if (retval) + return retval; - return 0; + return koneplus_receive_control_status(usb_dev); } static int koneplus_select_profile(struct usb_device *usb_dev, uint number, @@ -167,7 +120,7 @@ static int koneplus_select_profile(struct usb_device *usb_dev, uint number, static int koneplus_get_info(struct usb_device *usb_dev, struct koneplus_info *buf) { - return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_INFO, + return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_INFO, buf, sizeof(struct koneplus_info)); } @@ -181,7 +134,7 @@ static int koneplus_get_profile_settings(struct usb_device *usb_dev, if (retval) return retval; - return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS, + return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS, buf, sizeof(struct koneplus_profile_settings)); } @@ -189,7 +142,7 @@ static int koneplus_set_profile_settings(struct usb_device *usb_dev, struct koneplus_profile_settings const *settings) { return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_SETTINGS, - (void *)settings, sizeof(struct koneplus_profile_settings)); + settings, sizeof(struct koneplus_profile_settings)); } static int koneplus_get_profile_buttons(struct usb_device *usb_dev, @@ -202,7 +155,7 @@ static int koneplus_get_profile_buttons(struct usb_device *usb_dev, if (retval) return retval; - return koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS, + return roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS, buf, sizeof(struct koneplus_profile_buttons)); } @@ -210,27 +163,19 @@ static int koneplus_set_profile_buttons(struct usb_device *usb_dev, struct koneplus_profile_buttons const *buttons) { return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_PROFILE_BUTTONS, - (void *)buttons, sizeof(struct koneplus_profile_buttons)); + buttons, sizeof(struct koneplus_profile_buttons)); } /* retval is 0-4 on success, < 0 on error */ static int koneplus_get_startup_profile(struct usb_device *usb_dev) { - struct koneplus_startup_profile *buf; + struct koneplus_startup_profile buf; int retval; - buf = kmalloc(sizeof(struct koneplus_startup_profile), GFP_KERNEL); + retval = roccat_common_receive(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE, + &buf, sizeof(struct koneplus_startup_profile)); - retval = koneplus_receive(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE, - buf, sizeof(struct koneplus_startup_profile)); - - if (retval) - goto out; - - retval = buf->startup_profile; -out: - kfree(buf); - return retval; + return retval ? retval : buf.startup_profile; } static int koneplus_set_startup_profile(struct usb_device *usb_dev, @@ -243,7 +188,7 @@ static int koneplus_set_startup_profile(struct usb_device *usb_dev, buf.startup_profile = startup_profile; return koneplus_send(usb_dev, KONEPLUS_USB_COMMAND_STARTUP_PROFILE, - (char *)&buf, sizeof(struct koneplus_profile_buttons)); + &buf, sizeof(struct koneplus_profile_buttons)); } static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj, @@ -256,11 +201,14 @@ static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj, struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); int retval; + if (off >= real_size) + return 0; + if (off != 0 || count != real_size) return -EINVAL; mutex_lock(&koneplus->koneplus_lock); - retval = koneplus_receive(usb_dev, command, buf, real_size); + retval = roccat_common_receive(usb_dev, command, buf, real_size); mutex_unlock(&koneplus->koneplus_lock); if (retval) @@ -283,7 +231,7 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&koneplus->koneplus_lock); - retval = koneplus_send(usb_dev, command, (void *)buf, real_size); + retval = koneplus_send(usb_dev, command, buf, real_size); mutex_unlock(&koneplus->koneplus_lock); if (retval) @@ -347,7 +295,7 @@ static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp, count = sizeof(struct koneplus_profile_settings) - off; mutex_lock(&koneplus->koneplus_lock); - memcpy(buf, ((void const *)&koneplus->profile_settings[*(uint *)(attr->private)]) + off, + memcpy(buf, ((char const *)&koneplus->profile_settings[*(uint *)(attr->private)]) + off, count); mutex_unlock(&koneplus->koneplus_lock); @@ -406,7 +354,7 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp, count = sizeof(struct koneplus_profile_buttons) - off; mutex_lock(&koneplus->koneplus_lock); - memcpy(buf, ((void const *)&koneplus->profile_buttons[*(uint *)(attr->private)]) + off, + memcpy(buf, ((char const *)&koneplus->profile_buttons[*(uint *)(attr->private)]) + off, count); mutex_unlock(&koneplus->koneplus_lock); @@ -512,7 +460,7 @@ static struct device_attribute koneplus_attributes[] = { static struct bin_attribute koneplus_bin_attributes[] = { { - .attr = { .name = "sensor", .mode = 0220 }, + .attr = { .name = "sensor", .mode = 0660 }, .size = sizeof(struct koneplus_sensor), .read = koneplus_sysfs_read_sensor, .write = koneplus_sysfs_write_sensor @@ -609,11 +557,13 @@ static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev, struct koneplus_device *koneplus) { int retval, i; - static uint wait = 70; /* device will freeze with just 60 */ + static uint wait = 100; /* device will freeze with just 60 */ mutex_init(&koneplus->koneplus_lock); koneplus->startup_profile = koneplus_get_startup_profile(usb_dev); + if (koneplus->startup_profile < 0) + return koneplus->startup_profile; msleep(wait); retval = koneplus_get_info(usb_dev, &koneplus->info); @@ -651,21 +601,21 @@ static int koneplus_init_specials(struct hid_device *hdev) koneplus = kzalloc(sizeof(*koneplus), GFP_KERNEL); if (!koneplus) { - dev_err(&hdev->dev, "can't alloc device descriptor\n"); + hid_err(hdev, "can't alloc device descriptor\n"); return -ENOMEM; } hid_set_drvdata(hdev, koneplus); retval = koneplus_init_koneplus_device_struct(usb_dev, koneplus); if (retval) { - dev_err(&hdev->dev, - "couldn't init struct koneplus_device\n"); + hid_err(hdev, "couldn't init struct koneplus_device\n"); goto exit_free; } - retval = roccat_connect(koneplus_class, hdev); + retval = roccat_connect(koneplus_class, hdev, + sizeof(struct koneplus_roccat_report)); if (retval < 0) { - dev_err(&hdev->dev, "couldn't init char dev\n"); + hid_err(hdev, "couldn't init char dev\n"); } else { koneplus->chrdev_minor = retval; koneplus->roccat_claimed = 1; @@ -701,19 +651,19 @@ static int koneplus_probe(struct hid_device *hdev, retval = hid_parse(hdev); if (retval) { - dev_err(&hdev->dev, "parse failed\n"); + hid_err(hdev, "parse failed\n"); goto exit; } retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (retval) { - dev_err(&hdev->dev, "hw start failed\n"); + hid_err(hdev, "hw start failed\n"); goto exit; } retval = koneplus_init_specials(hdev); if (retval) { - dev_err(&hdev->dev, "couldn't install mouse\n"); + hid_err(hdev, "couldn't install mouse\n"); goto exit_stop; } @@ -769,8 +719,7 @@ static void koneplus_report_to_chrdev(struct koneplus_device const *koneplus, roccat_report.data2 = button_report->data2; roccat_report.profile = koneplus->actual_profile + 1; roccat_report_event(koneplus->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct koneplus_roccat_report)); + (uint8_t const *)&roccat_report); } static int koneplus_raw_event(struct hid_device *hdev, @@ -825,8 +774,8 @@ static int __init koneplus_init(void) static void __exit koneplus_exit(void) { - class_destroy(koneplus_class); hid_unregister_driver(&koneplus_driver); + class_destroy(koneplus_class); } module_init(koneplus_init); diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c new file mode 100644 index 0000000..984be2f --- /dev/null +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -0,0 +1,715 @@ +/* + * Roccat Kova[+] driver for Linux + * + * Copyright (c) 2011 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 Kova[+] is a bigger version of the Pyra with two more side buttons. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/hid-roccat.h> +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-kovaplus.h" + +static uint profile_numbers[5] = {0, 1, 2, 3, 4}; + +static struct class *kovaplus_class; + +static uint kovaplus_convert_event_cpi(uint value) +{ + return (value == 7 ? 4 : (value == 4 ? 3 : value)); +} + +static void kovaplus_profile_activated(struct kovaplus_device *kovaplus, + uint new_profile_index) +{ + kovaplus->actual_profile = new_profile_index; + kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level; + kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x; + kovaplus->actual_y_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_y; +} + +static int kovaplus_send_control(struct usb_device *usb_dev, uint value, + enum kovaplus_control_requests request) +{ + int retval; + struct kovaplus_control control; + + if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS || + request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) && + value > 4) + return -EINVAL; + + control.command = KOVAPLUS_COMMAND_CONTROL; + control.value = value; + control.request = request; + + retval = roccat_common_send(usb_dev, KOVAPLUS_USB_COMMAND_CONTROL, + &control, sizeof(struct kovaplus_control)); + + return retval; +} + +static int kovaplus_receive_control_status(struct usb_device *usb_dev) +{ + int retval; + struct kovaplus_control control; + + do { + retval = roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_CONTROL, + &control, sizeof(struct kovaplus_control)); + + /* check if we get a completely wrong answer */ + if (retval) + return retval; + + if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OK) + return 0; + + /* indicates that hardware needs some more time to complete action */ + if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT) { + msleep(500); /* windows driver uses 1000 */ + continue; + } + + /* seems to be critical - replug necessary */ + if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD) + return -EINVAL; + + hid_err(usb_dev, "kovaplus_receive_control_status: " + "unknown response value 0x%x\n", control.value); + return -EINVAL; + } while (1); +} + +static int kovaplus_send(struct usb_device *usb_dev, uint command, + void const *buf, uint size) +{ + int retval; + + retval = roccat_common_send(usb_dev, command, buf, size); + if (retval) + return retval; + + msleep(100); + + return kovaplus_receive_control_status(usb_dev); +} + +static int kovaplus_select_profile(struct usb_device *usb_dev, uint number, + enum kovaplus_control_requests request) +{ + return kovaplus_send_control(usb_dev, number, request); +} + +static int kovaplus_get_info(struct usb_device *usb_dev, + struct kovaplus_info *buf) +{ + return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_INFO, + buf, sizeof(struct kovaplus_info)); +} + +static int kovaplus_get_profile_settings(struct usb_device *usb_dev, + struct kovaplus_profile_settings *buf, uint number) +{ + int retval; + + retval = kovaplus_select_profile(usb_dev, number, + KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS); + if (retval) + return retval; + + return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS, + buf, sizeof(struct kovaplus_profile_settings)); +} + +static int kovaplus_set_profile_settings(struct usb_device *usb_dev, + struct kovaplus_profile_settings const *settings) +{ + return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS, + settings, sizeof(struct kovaplus_profile_settings)); +} + +static int kovaplus_get_profile_buttons(struct usb_device *usb_dev, + struct kovaplus_profile_buttons *buf, int number) +{ + int retval; + + retval = kovaplus_select_profile(usb_dev, number, + KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS); + if (retval) + return retval; + + return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS, + buf, sizeof(struct kovaplus_profile_buttons)); +} + +static int kovaplus_set_profile_buttons(struct usb_device *usb_dev, + struct kovaplus_profile_buttons const *buttons) +{ + return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS, + buttons, sizeof(struct kovaplus_profile_buttons)); +} + +/* retval is 0-4 on success, < 0 on error */ +static int kovaplus_get_actual_profile(struct usb_device *usb_dev) +{ + struct kovaplus_actual_profile buf; + int retval; + + retval = roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE, + &buf, sizeof(struct kovaplus_actual_profile)); + + return retval ? retval : buf.actual_profile; +} + +static int kovaplus_set_actual_profile(struct usb_device *usb_dev, + int new_profile) +{ + struct kovaplus_actual_profile buf; + + buf.command = KOVAPLUS_COMMAND_ACTUAL_PROFILE; + buf.size = sizeof(struct kovaplus_actual_profile); + buf.actual_profile = new_profile; + + return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE, + &buf, sizeof(struct kovaplus_actual_profile)); +} + +static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + + if (off >= sizeof(struct kovaplus_profile_settings)) + return 0; + + if (off + count > sizeof(struct kovaplus_profile_settings)) + count = sizeof(struct kovaplus_profile_settings) - off; + + mutex_lock(&kovaplus->kovaplus_lock); + memcpy(buf, ((char const *)&kovaplus->profile_settings[*(uint *)(attr->private)]) + off, + count); + mutex_unlock(&kovaplus->kovaplus_lock); + + return count; +} + +static ssize_t kovaplus_sysfs_write_profile_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval = 0; + int difference; + int profile_index; + struct kovaplus_profile_settings *profile_settings; + + if (off != 0 || count != sizeof(struct kovaplus_profile_settings)) + return -EINVAL; + + profile_index = ((struct kovaplus_profile_settings const *)buf)->profile_index; + profile_settings = &kovaplus->profile_settings[profile_index]; + + mutex_lock(&kovaplus->kovaplus_lock); + difference = memcmp(buf, profile_settings, + sizeof(struct kovaplus_profile_settings)); + if (difference) { + retval = kovaplus_set_profile_settings(usb_dev, + (struct kovaplus_profile_settings const *)buf); + if (!retval) + memcpy(profile_settings, buf, + sizeof(struct kovaplus_profile_settings)); + } + mutex_unlock(&kovaplus->kovaplus_lock); + + if (retval) + return retval; + + return sizeof(struct kovaplus_profile_settings); +} + +static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + + if (off >= sizeof(struct kovaplus_profile_buttons)) + return 0; + + if (off + count > sizeof(struct kovaplus_profile_buttons)) + count = sizeof(struct kovaplus_profile_buttons) - off; + + mutex_lock(&kovaplus->kovaplus_lock); + memcpy(buf, ((char const *)&kovaplus->profile_buttons[*(uint *)(attr->private)]) + off, + count); + mutex_unlock(&kovaplus->kovaplus_lock); + + return count; +} + +static ssize_t kovaplus_sysfs_write_profile_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval = 0; + int difference; + uint profile_index; + struct kovaplus_profile_buttons *profile_buttons; + + if (off != 0 || count != sizeof(struct kovaplus_profile_buttons)) + return -EINVAL; + + profile_index = ((struct kovaplus_profile_buttons const *)buf)->profile_index; + profile_buttons = &kovaplus->profile_buttons[profile_index]; + + mutex_lock(&kovaplus->kovaplus_lock); + difference = memcmp(buf, profile_buttons, + sizeof(struct kovaplus_profile_buttons)); + if (difference) { + retval = kovaplus_set_profile_buttons(usb_dev, + (struct kovaplus_profile_buttons const *)buf); + if (!retval) + memcpy(profile_buttons, buf, + sizeof(struct kovaplus_profile_buttons)); + } + mutex_unlock(&kovaplus->kovaplus_lock); + + if (retval) + return retval; + + return sizeof(struct kovaplus_profile_buttons); +} + +static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kovaplus_device *kovaplus = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_profile); +} + +static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev, + struct device_attribute *attr, char const *buf, size_t size) +{ + struct kovaplus_device *kovaplus; + struct usb_device *usb_dev; + unsigned long profile; + int retval; + + dev = dev->parent->parent; + kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); + usb_dev = interface_to_usbdev(to_usb_interface(dev)); + + retval = strict_strtoul(buf, 10, &profile); + if (retval) + return retval; + + if (profile >= 5) + return -EINVAL; + + mutex_lock(&kovaplus->kovaplus_lock); + retval = kovaplus_set_actual_profile(usb_dev, profile); + kovaplus->actual_profile = profile; + mutex_unlock(&kovaplus->kovaplus_lock); + if (retval) + return retval; + + return size; +} + +static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kovaplus_device *kovaplus = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi); +} + +static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kovaplus_device *kovaplus = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity); +} + +static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kovaplus_device *kovaplus = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity); +} + +static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kovaplus_device *kovaplus = + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); + return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->info.firmware_version); +} + +static struct device_attribute kovaplus_attributes[] = { + __ATTR(actual_cpi, 0440, + kovaplus_sysfs_show_actual_cpi, NULL), + __ATTR(firmware_version, 0440, + kovaplus_sysfs_show_firmware_version, NULL), + __ATTR(actual_profile, 0660, + kovaplus_sysfs_show_actual_profile, + kovaplus_sysfs_set_actual_profile), + __ATTR(actual_sensitivity_x, 0440, + kovaplus_sysfs_show_actual_sensitivity_x, NULL), + __ATTR(actual_sensitivity_y, 0440, + kovaplus_sysfs_show_actual_sensitivity_y, NULL), + __ATTR_NULL +}; + +static struct bin_attribute kovaplus_bin_attributes[] = { + { + .attr = { .name = "profile_settings", .mode = 0220 }, + .size = sizeof(struct kovaplus_profile_settings), + .write = kovaplus_sysfs_write_profile_settings + }, + { + .attr = { .name = "profile1_settings", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_settings), + .read = kovaplus_sysfs_read_profilex_settings, + .private = &profile_numbers[0] + }, + { + .attr = { .name = "profile2_settings", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_settings), + .read = kovaplus_sysfs_read_profilex_settings, + .private = &profile_numbers[1] + }, + { + .attr = { .name = "profile3_settings", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_settings), + .read = kovaplus_sysfs_read_profilex_settings, + .private = &profile_numbers[2] + }, + { + .attr = { .name = "profile4_settings", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_settings), + .read = kovaplus_sysfs_read_profilex_settings, + .private = &profile_numbers[3] + }, + { + .attr = { .name = "profile5_settings", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_settings), + .read = kovaplus_sysfs_read_profilex_settings, + .private = &profile_numbers[4] + }, + { + .attr = { .name = "profile_buttons", .mode = 0220 }, + .size = sizeof(struct kovaplus_profile_buttons), + .write = kovaplus_sysfs_write_profile_buttons + }, + { + .attr = { .name = "profile1_buttons", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_buttons), + .read = kovaplus_sysfs_read_profilex_buttons, + .private = &profile_numbers[0] + }, + { + .attr = { .name = "profile2_buttons", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_buttons), + .read = kovaplus_sysfs_read_profilex_buttons, + .private = &profile_numbers[1] + }, + { + .attr = { .name = "profile3_buttons", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_buttons), + .read = kovaplus_sysfs_read_profilex_buttons, + .private = &profile_numbers[2] + }, + { + .attr = { .name = "profile4_buttons", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_buttons), + .read = kovaplus_sysfs_read_profilex_buttons, + .private = &profile_numbers[3] + }, + { + .attr = { .name = "profile5_buttons", .mode = 0440 }, + .size = sizeof(struct kovaplus_profile_buttons), + .read = kovaplus_sysfs_read_profilex_buttons, + .private = &profile_numbers[4] + }, + __ATTR_NULL +}; + +static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev, + struct kovaplus_device *kovaplus) +{ + int retval, i; + static uint wait = 70; /* device will freeze with just 60 */ + + mutex_init(&kovaplus->kovaplus_lock); + + retval = kovaplus_get_info(usb_dev, &kovaplus->info); + if (retval) + return retval; + + for (i = 0; i < 5; ++i) { + msleep(wait); + retval = kovaplus_get_profile_settings(usb_dev, + &kovaplus->profile_settings[i], i); + if (retval) + return retval; + + msleep(wait); + retval = kovaplus_get_profile_buttons(usb_dev, + &kovaplus->profile_buttons[i], i); + if (retval) + return retval; + } + + msleep(wait); + retval = kovaplus_get_actual_profile(usb_dev); + if (retval < 0) + return retval; + kovaplus_profile_activated(kovaplus, retval); + + return 0; +} + +static int kovaplus_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 kovaplus_device *kovaplus; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_MOUSE) { + + kovaplus = kzalloc(sizeof(*kovaplus), GFP_KERNEL); + if (!kovaplus) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, kovaplus); + + retval = kovaplus_init_kovaplus_device_struct(usb_dev, kovaplus); + if (retval) { + hid_err(hdev, "couldn't init struct kovaplus_device\n"); + goto exit_free; + } + + retval = roccat_connect(kovaplus_class, hdev, + sizeof(struct kovaplus_roccat_report)); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + kovaplus->chrdev_minor = retval; + kovaplus->roccat_claimed = 1; + } + + } else { + hid_set_drvdata(hdev, NULL); + } + + return 0; +exit_free: + kfree(kovaplus); + return retval; +} + +static void kovaplus_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct kovaplus_device *kovaplus; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_MOUSE) { + kovaplus = hid_get_drvdata(hdev); + if (kovaplus->roccat_claimed) + roccat_disconnect(kovaplus->chrdev_minor); + kfree(kovaplus); + } +} + +static int kovaplus_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "hw start failed\n"); + goto exit; + } + + retval = kovaplus_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install mouse\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void kovaplus_remove(struct hid_device *hdev) +{ + kovaplus_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus, + u8 const *data) +{ + struct kovaplus_mouse_report_button const *button_report; + + if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON) + return; + + button_report = (struct kovaplus_mouse_report_button const *)data; + + switch (button_report->type) { + case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1: + kovaplus_profile_activated(kovaplus, button_report->data1 - 1); + break; + case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI: + kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1); + case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY: + kovaplus->actual_x_sensitivity = button_report->data1; + kovaplus->actual_y_sensitivity = button_report->data2; + } +} + +static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus, + u8 const *data) +{ + struct kovaplus_roccat_report roccat_report; + struct kovaplus_mouse_report_button const *button_report; + + if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON) + return; + + button_report = (struct kovaplus_mouse_report_button const *)data; + + if (button_report->type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2) + return; + + roccat_report.type = button_report->type; + roccat_report.profile = kovaplus->actual_profile + 1; + + if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO || + roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT || + roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH || + roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER) + roccat_report.button = button_report->data1; + else + roccat_report.button = 0; + + if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI) + roccat_report.data1 = kovaplus_convert_event_cpi(button_report->data1); + else + roccat_report.data1 = button_report->data1; + + roccat_report.data2 = button_report->data2; + + roccat_report_event(kovaplus->chrdev_minor, + (uint8_t const *)&roccat_report); +} + +static int kovaplus_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct kovaplus_device *kovaplus = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return 0; + + kovaplus_keep_values_up_to_date(kovaplus, data); + + if (kovaplus->roccat_claimed) + kovaplus_report_to_chrdev(kovaplus, data); + + return 0; +} + +static const struct hid_device_id kovaplus_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, kovaplus_devices); + +static struct hid_driver kovaplus_driver = { + .name = "kovaplus", + .id_table = kovaplus_devices, + .probe = kovaplus_probe, + .remove = kovaplus_remove, + .raw_event = kovaplus_raw_event +}; + +static int __init kovaplus_init(void) +{ + int retval; + + kovaplus_class = class_create(THIS_MODULE, "kovaplus"); + if (IS_ERR(kovaplus_class)) + return PTR_ERR(kovaplus_class); + kovaplus_class->dev_attrs = kovaplus_attributes; + kovaplus_class->dev_bin_attrs = kovaplus_bin_attributes; + + retval = hid_register_driver(&kovaplus_driver); + if (retval) + class_destroy(kovaplus_class); + return retval; +} + +static void __exit kovaplus_exit(void) +{ + hid_unregister_driver(&kovaplus_driver); + class_destroy(kovaplus_class); +} + +module_init(kovaplus_init); +module_exit(kovaplus_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Kova[+] driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h new file mode 100644 index 0000000..ce40607 --- /dev/null +++ b/drivers/hid/hid-roccat-kovaplus.h @@ -0,0 +1,157 @@ +#ifndef __HID_ROCCAT_KOVAPLUS_H +#define __HID_ROCCAT_KOVAPLUS_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> + +struct kovaplus_control { + uint8_t command; /* KOVAPLUS_COMMAND_CONTROL */ + uint8_t value; + uint8_t request; +} __packed; + +enum kovaplus_control_requests { + /* read after write; value = 1 */ + KOVAPLUS_CONTROL_REQUEST_STATUS = 0x0, + /* write; value = profile number range 0-4 */ + KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, + /* write; value = profile number range 0-4 */ + KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20, +}; + +enum kovaplus_control_values { + KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD = 0, /* supposed */ + KOVAPLUS_CONTROL_REQUEST_STATUS_OK = 1, + KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT = 3, /* supposed */ +}; + +struct kovaplus_actual_profile { + uint8_t command; /* KOVAPLUS_COMMAND_ACTUAL_PROFILE */ + uint8_t size; /* always 3 */ + uint8_t actual_profile; /* Range 0-4! */ +} __packed; + +struct kovaplus_profile_settings { + uint8_t command; /* KOVAPLUS_COMMAND_PROFILE_SETTINGS */ + uint8_t size; /* 16 */ + uint8_t profile_index; /* range 0-4 */ + uint8_t unknown1; + uint8_t sensitivity_x; /* range 1-10 */ + uint8_t sensitivity_y; /* range 1-10 */ + uint8_t cpi_levels_enabled; + uint8_t cpi_startup_level; /* range 1-4 */ + uint8_t data[8]; +} __packed; + +struct kovaplus_profile_buttons { + uint8_t command; /* KOVAPLUS_COMMAND_PROFILE_BUTTONS */ + uint8_t size; /* 23 */ + uint8_t profile_index; /* range 0-4 */ + uint8_t data[20]; +} __packed; + +struct kovaplus_info { + uint8_t command; /* KOVAPLUS_COMMAND_INFO */ + uint8_t size; /* 6 */ + uint8_t firmware_version; + uint8_t unknown[3]; +} __packed; + +/* writes 1 on plugin */ +struct kovaplus_a { + uint8_t command; /* KOVAPLUS_COMMAND_A */ + uint8_t size; /* 3 */ + uint8_t unknown; +} __packed; + +enum kovaplus_commands { + KOVAPLUS_COMMAND_CONTROL = 0x4, + KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5, + KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6, + KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7, + KOVAPLUS_COMMAND_INFO = 0x9, + KOVAPLUS_COMMAND_A = 0xa, +}; + +enum kovaplus_usb_commands { + KOVAPLUS_USB_COMMAND_CONTROL = 0x304, + KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE = 0x305, + KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS = 0x306, + KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS = 0x307, + KOVAPLUS_USB_COMMAND_INFO = 0x309, + KOVAPLUS_USB_COMMAND_A = 0x30a, +}; + +enum kovaplus_mouse_report_numbers { + KOVAPLUS_MOUSE_REPORT_NUMBER_MOUSE = 1, + KOVAPLUS_MOUSE_REPORT_NUMBER_AUDIO = 2, + KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON = 3, + KOVAPLUS_MOUSE_REPORT_NUMBER_KBD = 4, +}; + +struct kovaplus_mouse_report_button { + uint8_t report_number; /* KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON */ + uint8_t unknown1; + uint8_t type; + uint8_t data1; + uint8_t data2; +} __packed; + +enum kovaplus_mouse_report_button_types { + /* data1 = profile_number range 1-5; no release event */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1 = 0x20, + /* data1 = profile_number range 1-5; no release event */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2 = 0x30, + /* data1 = button_number range 1-18; data2 = action */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO = 0x40, + /* data1 = button_number range 1-18; data2 = action */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT = 0x50, + /* data1 = button_number range 1-18; data2 = action */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH = 0x60, + /* data1 = button_number range 1-18; data2 = action */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER = 0x80, + /* data1 = 1 = 400, 2 = 800, 4 = 1600, 7 = 3200; no release event */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI = 0xb0, + /* data1 + data2 = sense range 1-10; no release event */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY = 0xc0, + /* data1 = type as in profile_buttons; data2 = action */ + KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MULTIMEDIA = 0xf0, +}; + +enum kovaplus_mouse_report_button_actions { + KOVAPLUS_MOUSE_REPORT_BUTTON_ACTION_PRESS = 0, + KOVAPLUS_MOUSE_REPORT_BUTTON_ACTION_RELEASE = 1, +}; + +struct kovaplus_roccat_report { + uint8_t type; + uint8_t profile; + uint8_t button; + uint8_t data1; + uint8_t data2; +} __packed; + +struct kovaplus_device { + int actual_profile; + int actual_cpi; + int actual_x_sensitivity; + int actual_y_sensitivity; + int roccat_claimed; + int chrdev_minor; + struct mutex kovaplus_lock; + struct kovaplus_info info; + struct kovaplus_profile_settings profile_settings[5]; + struct kovaplus_profile_buttons profile_buttons[5]; +}; + +#endif diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 02c58e0..160f481 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -20,11 +20,11 @@ #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 <linux/hid-roccat.h> #include "hid-ids.h" -#include "hid-roccat.h" +#include "hid-roccat-common.h" #include "hid-roccat-pyra.h" static uint profile_numbers[5] = {0, 1, 2, 3, 4}; @@ -42,7 +42,6 @@ static void profile_activated(struct pyra_device *pyra, static int pyra_send_control(struct usb_device *usb_dev, int value, enum pyra_control_requests request) { - int len; struct pyra_control control; if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || @@ -54,47 +53,31 @@ static int pyra_send_control(struct usb_device *usb_dev, int value, control.value = value; control.request = request; - len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), - USB_REQ_SET_CONFIGURATION, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - PYRA_USB_COMMAND_CONTROL, 0, (char *)&control, - sizeof(struct pyra_control), - USB_CTRL_SET_TIMEOUT); - - if (len != sizeof(struct pyra_control)) - return len; - - return 0; + return roccat_common_send(usb_dev, PYRA_USB_COMMAND_CONTROL, + &control, sizeof(struct pyra_control)); } static int pyra_receive_control_status(struct usb_device *usb_dev) { - int len; + int retval; struct pyra_control control; do { msleep(10); - - len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | - USB_DIR_IN, - PYRA_USB_COMMAND_CONTROL, 0, (char *)&control, - sizeof(struct pyra_control), - USB_CTRL_SET_TIMEOUT); + retval = roccat_common_receive(usb_dev, PYRA_USB_COMMAND_CONTROL, + &control, sizeof(struct pyra_control)); /* requested too early, try again */ - } while (len == -EPROTO); + } while (retval == -EPROTO); - if (len == sizeof(struct pyra_control) && - control.command == PYRA_COMMAND_CONTROL && + if (!retval && control.command == PYRA_COMMAND_CONTROL && control.request == PYRA_CONTROL_REQUEST_STATUS && control.value == 1) - return 0; + return 0; else { hid_err(usb_dev, "receive control status: unknown response 0x%x 0x%x\n", control.request, control.value); - return -EINVAL; + return retval ? retval : -EINVAL; } } @@ -102,125 +85,72 @@ static int pyra_get_profile_settings(struct usb_device *usb_dev, struct pyra_profile_settings *buf, int number) { int retval; - retval = pyra_send_control(usb_dev, number, PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); - if (retval) return retval; - - retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)buf, - sizeof(struct pyra_profile_settings), - USB_CTRL_SET_TIMEOUT); - - if (retval != sizeof(struct pyra_profile_settings)) - return retval; - - return 0; + return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_PROFILE_SETTINGS, + buf, sizeof(struct pyra_profile_settings)); } static int pyra_get_profile_buttons(struct usb_device *usb_dev, struct pyra_profile_buttons *buf, int number) { int retval; - retval = pyra_send_control(usb_dev, number, PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); - if (retval) return retval; - - retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_CLEAR_FEATURE, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buf, - sizeof(struct pyra_profile_buttons), - USB_CTRL_SET_TIMEOUT); - - if (retval != sizeof(struct pyra_profile_buttons)) - return retval; - - return 0; + return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_PROFILE_BUTTONS, + buf, sizeof(struct pyra_profile_buttons)); } static int pyra_get_settings(struct usb_device *usb_dev, struct pyra_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, - PYRA_USB_COMMAND_SETTINGS, 0, buf, - sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT); - if (len != sizeof(struct pyra_settings)) - return -EIO; - return 0; + return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_SETTINGS, + buf, sizeof(struct pyra_settings)); } static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *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, - PYRA_USB_COMMAND_INFO, 0, buf, - sizeof(struct pyra_info), USB_CTRL_SET_TIMEOUT); - if (len != sizeof(struct pyra_info)) - return -EIO; - return 0; + return roccat_common_receive(usb_dev, PYRA_USB_COMMAND_INFO, + buf, sizeof(struct pyra_info)); +} + +static int pyra_send(struct usb_device *usb_dev, uint command, + void const *buf, uint size) +{ + int retval; + retval = roccat_common_send(usb_dev, command, buf, size); + if (retval) + return retval; + return pyra_receive_control_status(usb_dev); } static int pyra_set_profile_settings(struct usb_device *usb_dev, struct pyra_profile_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, - PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)settings, - sizeof(struct pyra_profile_settings), - USB_CTRL_SET_TIMEOUT); - if (len != sizeof(struct pyra_profile_settings)) - return -EIO; - if (pyra_receive_control_status(usb_dev)) - return -EIO; - return 0; + return pyra_send(usb_dev, PYRA_USB_COMMAND_PROFILE_SETTINGS, settings, + sizeof(struct pyra_profile_settings)); } static int pyra_set_profile_buttons(struct usb_device *usb_dev, struct pyra_profile_buttons const *buttons) { - 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, - PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buttons, - sizeof(struct pyra_profile_buttons), - USB_CTRL_SET_TIMEOUT); - if (len != sizeof(struct pyra_profile_buttons)) - return -EIO; - if (pyra_receive_control_status(usb_dev)) - return -EIO; - return 0; + return pyra_send(usb_dev, PYRA_USB_COMMAND_PROFILE_BUTTONS, buttons, + sizeof(struct pyra_profile_buttons)); } static int pyra_set_settings(struct usb_device *usb_dev, struct pyra_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, - PYRA_USB_COMMAND_SETTINGS, 0, (char *)settings, - sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT); - if (len != sizeof(struct pyra_settings)) - return -EIO; - if (pyra_receive_control_status(usb_dev)) - return -EIO; - return 0; + int retval; + retval = roccat_common_send(usb_dev, PYRA_USB_COMMAND_SETTINGS, settings, + sizeof(struct pyra_settings)); + if (retval) + return retval; + return pyra_receive_control_status(usb_dev); } static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, @@ -521,21 +451,16 @@ static struct bin_attribute pyra_bin_attributes[] = { static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, struct pyra_device *pyra) { - struct pyra_info *info; + struct pyra_info info; int retval, i; mutex_init(&pyra->pyra_lock); - info = kmalloc(sizeof(struct pyra_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - retval = pyra_get_info(usb_dev, info); - if (retval) { - kfree(info); + retval = pyra_get_info(usb_dev, &info); + if (retval) return retval; - } - pyra->firmware_version = info->firmware_version; - kfree(info); + + pyra->firmware_version = info.firmware_version; retval = pyra_get_settings(usb_dev, &pyra->settings); if (retval) @@ -581,7 +506,8 @@ static int pyra_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(pyra_class, hdev); + retval = roccat_connect(pyra_class, hdev, + sizeof(struct pyra_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -685,8 +611,7 @@ static void pyra_report_to_chrdev(struct pyra_device const *pyra, roccat_report.value = button_event->data1; roccat_report.key = 0; roccat_report_event(pyra->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct pyra_roccat_report)); + (uint8_t const *)&roccat_report); break; case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: @@ -700,8 +625,7 @@ static void pyra_report_to_chrdev(struct pyra_device const *pyra, */ roccat_report.value = pyra->actual_profile + 1; roccat_report_event(pyra->chrdev_minor, - (uint8_t const *)&roccat_report, - sizeof(struct pyra_roccat_report)); + (uint8_t const *)&roccat_report); } break; } @@ -761,8 +685,8 @@ static int __init pyra_init(void) static void __exit pyra_exit(void) { - class_destroy(pyra_class); hid_unregister_driver(&pyra_driver); + class_destroy(pyra_class); } module_init(pyra_init); diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index a14c579..5666e75 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -26,8 +26,7 @@ #include <linux/cdev.h> #include <linux/poll.h> #include <linux/sched.h> - -#include "hid-roccat.h" +#include <linux/hid-roccat.h> #define ROCCAT_FIRST_MINOR 0 #define ROCCAT_MAX_DEVICES 8 @@ -37,11 +36,11 @@ struct roccat_report { uint8_t *value; - int len; }; struct roccat_device { unsigned int minor; + int report_size; int open; int exist; wait_queue_head_t wait; @@ -123,7 +122,7 @@ static ssize_t roccat_read(struct file *file, char __user *buffer, * If report is larger than requested amount of data, rest of report * is lost! */ - len = report->len > count ? count : report->len; + len = device->report_size > count ? count : device->report_size; if (copy_to_user(buffer, report->value, len)) { retval = -EFAULT; @@ -248,26 +247,25 @@ static int roccat_release(struct inode *inode, struct file *file) * * This is called from interrupt handler. */ -int roccat_report_event(int minor, u8 const *data, int len) +int roccat_report_event(int minor, u8 const *data) { struct roccat_device *device; struct roccat_reader *reader; struct roccat_report *report; uint8_t *new_value; - new_value = kmemdup(data, len, GFP_ATOMIC); + device = devices[minor]; + + new_value = kmemdup(data, device->report_size, GFP_ATOMIC); if (!new_value) return -ENOMEM; - device = devices[minor]; - report = &device->cbuf[device->cbuf_end]; /* passing NULL is safe */ kfree(report->value); report->value = new_value; - report->len = len; device->cbuf_end = (device->cbuf_end + 1) % ROCCAT_CBUF_SIZE; list_for_each_entry(reader, &device->readers, node) { @@ -295,7 +293,7 @@ EXPORT_SYMBOL_GPL(roccat_report_event); * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on * success, a negative error code on failure. */ -int roccat_connect(struct class *klass, struct hid_device *hid) +int roccat_connect(struct class *klass, struct hid_device *hid, int report_size) { unsigned int minor; struct roccat_device *device; @@ -343,6 +341,7 @@ int roccat_connect(struct class *klass, struct hid_device *hid) device->hid = hid; device->exist = 1; device->cbuf_end = 0; + device->report_size = report_size; return minor; } @@ -357,13 +356,16 @@ void roccat_disconnect(int minor) mutex_lock(&devices_lock); device = devices[minor]; - devices[minor] = NULL; mutex_unlock(&devices_lock); device->exist = 0; /* TODO exist maybe not needed */ device_destroy(device->dev->class, MKDEV(roccat_major, minor)); + mutex_lock(&devices_lock); + devices[minor] = NULL; + mutex_unlock(&devices_lock); + if (device->open) { hid_hw_close(device->hid); wake_up_interruptible(&device->wait); @@ -373,6 +375,34 @@ void roccat_disconnect(int minor) } EXPORT_SYMBOL_GPL(roccat_disconnect); +static long roccat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct roccat_device *device; + unsigned int minor = iminor(inode); + long retval = 0; + + mutex_lock(&devices_lock); + + device = devices[minor]; + if (!device) { + retval = -ENODEV; + goto out; + } + + switch (cmd) { + case ROCCATIOCGREPSIZE: + if (put_user(device->report_size, (int __user *)arg)) + retval = -EFAULT; + break; + default: + retval = -ENOTTY; + } +out: + mutex_unlock(&devices_lock); + return retval; +} + static const struct file_operations roccat_ops = { .owner = THIS_MODULE, .read = roccat_read, @@ -380,6 +410,7 @@ static const struct file_operations roccat_ops = { .open = roccat_open, .release = roccat_release, .llseek = noop_llseek, + .unlocked_ioctl = roccat_ioctl, }; static int __init roccat_init(void) diff --git a/drivers/hid/hid-roccat.h b/drivers/hid/hid-roccat.h deleted file mode 100644 index 5784281..0000000 --- a/drivers/hid/hid-roccat.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __HID_ROCCAT_H -#define __HID_ROCCAT_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/hid.h> -#include <linux/types.h> - -#if defined(CONFIG_HID_ROCCAT) || defined(CONFIG_HID_ROCCAT_MODULE) -int roccat_connect(struct class *klass, struct hid_device *hid); -void roccat_disconnect(int minor); -int roccat_report_event(int minor, u8 const *data, int len); -#else -static inline int roccat_connect(struct class *klass, - struct hid_device *hid) { return -1; } -static inline void roccat_disconnect(int minor) {} -static inline int roccat_report_event(int minor, u8 const *data, int len) -{ - return 0; -} -#endif - -#endif diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 68d7b36..93819a0 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -46,6 +46,16 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, return rdesc; } +/* + * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP + * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() + * so we need to override that forcing HID Output Reports on the Control EP. + * + * There is also another issue about HID Output Reports via USB, the Sixaxis + * does not want the report_id as part of the data packet, so we have to + * discard buf[0] when sending the actual control message, even for numbered + * reports, humpf! + */ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { @@ -55,6 +65,12 @@ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, int report_id = buf[0]; int ret; + if (report_type == HID_OUTPUT_REPORT) { + /* Don't send the Report ID */ + buf++; + count--; + } + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), HID_REQ_SET_REPORT, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, @@ -62,6 +78,10 @@ static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, interface->desc.bInterfaceNumber, buf, count, USB_CTRL_SET_TIMEOUT); + /* Count also the Report ID, in case of an Output report. */ + if (ret > 0 && report_type == HID_OUTPUT_REPORT) + ret++; + return ret; } diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 468e87b..54409cb 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -91,7 +91,7 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, ret = -EFAULT; goto out; } - ret += len; + ret = len; kfree(list->buffer[list->tail].value); list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); @@ -102,15 +102,14 @@ out: } /* the first byte is expected to be a report number */ -static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +/* This function is to be called with the minors_lock mutex held */ +static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type) { unsigned int minor = iminor(file->f_path.dentry->d_inode); struct hid_device *dev; __u8 *buf; int ret = 0; - mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { ret = -ENODEV; goto out; @@ -148,14 +147,92 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t goto out_free; } - ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); + ret = dev->hid_output_raw_report(dev, buf, count, report_type); out_free: kfree(buf); out: + return ret; +} + +/* the first byte is expected to be a report number */ +static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + ssize_t ret; + mutex_lock(&minors_lock); + ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT); mutex_unlock(&minors_lock); return ret; } + +/* This function performs a Get_Report transfer over the control endpoint + per section 7.2.1 of the HID specification, version 1.1. The first byte + of buffer is the report number to request, or 0x0 if the defice does not + use numbered reports. The report_type parameter can be HID_FEATURE_REPORT + or HID_INPUT_REPORT. This function is to be called with the minors_lock + mutex held. */ +static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) +{ + unsigned int minor = iminor(file->f_path.dentry->d_inode); + struct hid_device *dev; + __u8 *buf; + int ret = 0, len; + unsigned char report_number; + + dev = hidraw_table[minor]->hid; + + if (!dev->hid_get_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)); + ret = -EINVAL; + goto out; + } + + if (count < 2) { + printk(KERN_WARNING "hidraw: pid %d passed too short report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + /* Read the first byte from the user. This is the report number, + which is passed to dev->hid_get_raw_report(). */ + if (copy_from_user(&report_number, buffer, 1)) { + ret = -EFAULT; + goto out_free; + } + + ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type); + + if (ret < 0) + goto out_free; + + len = (ret < count) ? ret : count; + + if (copy_to_user(buffer, buf, len)) { + ret = -EFAULT; + goto out_free; + } + + ret = len; + +out_free: + kfree(buf); +out: + return ret; +} + static unsigned int hidraw_poll(struct file *file, poll_table *wait) { struct hidraw_list *list = file->private_data; @@ -295,7 +372,24 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, default: { struct hid_device *hid = dev->hid; - if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) { + if (_IOC_TYPE(cmd) != 'H') { + ret = -EINVAL; + break; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) { + int len = _IOC_SIZE(cmd); + ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) { + int len = _IOC_SIZE(cmd); + ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + + /* Begin Read-only ioctls. */ + if (_IOC_DIR(cmd) != _IOC_READ) { ret = -EINVAL; break; } @@ -327,7 +421,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, -EFAULT : len; break; } - } + } ret = -ENOTTY; } @@ -428,12 +522,12 @@ void hidraw_disconnect(struct hid_device *hid) hidraw->exist = 0; + device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); + mutex_lock(&minors_lock); hidraw_table[hidraw->minor] = NULL; mutex_unlock(&minors_lock); - device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); - if (hidraw->open) { hid_hw_close(hid); wake_up_interruptible(&hidraw->wait); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b336dd8..38c261a 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -799,6 +799,40 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) return 0; } +static int usbhid_get_raw_report(struct hid_device *hid, + unsigned char report_number, __u8 *buf, size_t count, + unsigned char report_type) +{ + struct usbhid_device *usbhid = hid->driver_data; + struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_interface *intf = usbhid->intf; + struct usb_host_interface *interface = intf->cur_altsetting; + int skipped_report_id = 0; + int ret; + + /* Byte 0 is the report number. Report data starts at byte 1.*/ + buf[0] = report_number; + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + buf++; + count--; + skipped_report_id = 1; + } + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ((report_type + 1) << 8) | report_number, + interface->desc.bInterfaceNumber, buf, count, + USB_CTRL_SET_TIMEOUT); + + /* count also the report id */ + if (ret > 0 && skipped_report_id) + ret++; + + return ret; +} + static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { @@ -1139,6 +1173,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * usb_set_intfdata(intf, hid); hid->ll_driver = &usb_hid_driver; + hid->hid_get_raw_report = usbhid_get_raw_report; hid->hid_output_raw_report = usbhid_output_raw_report; hid->ff_init = hid_pidff_init; #ifdef CONFIG_USB_HIDDEV |