Commit 3c2c4b73 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull HID subsystem updates from Jiri Kosina:
 "Apart from various driver updates and added support for a number of
  new devices (mostly multitouch ones, but not limited to), there is one
  change that is worth pointing out explicitly: creation of HID device
  groups and proper autoloading of hid-multitouch, implemented by Henrik
  Rydberg."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (50 commits)
  HID: wacom: fix build breakage without CONFIG_LEDS_CLASS
  HID: waltop: Extend barrel button fix
  HID: hyperv: Set the hid drvdata correctly
  HID: wacom: Unify speed setting
  HID: wacom: Add speed setting for Intuos4 WL
  HID: wacom: Move Graphire raport header check.
  HID: uclogic: Add support for UC-Logic TWHL850
  HID: explain the signed/unsigned handling in hid_add_field()
  HID: handle logical min/max signedness properly in parser
  HID: logitech: read all 32 bits of report type bitfield
  HID: wacom: Add LED selector control for Wacom Intuos4 WL
  HID: hid-multitouch: fix wrong protocol detection
  HID: wiimote: Fix IR data parser
  HID: wacom: Add tilt reporting for Intuos4 WL
  HID: multitouch: MT interface matching for Baanto
  HID: hid-multitouch: Only match MT interfaces
  HID: Create a common generic driver
  HID: hid-multitouch: Switch to device groups
  HID: Create a generic device group
  HID: Allow bus wildcard matching
  ...
parents f08b9c2f 99ce58dd
......@@ -9,6 +9,14 @@ Description:
or 0 otherwise. Writing to this file one of these values
switches reporting speed.
What: /sys/class/leds/0005\:056A\:00BD.0001\:selector\:*/
Date: May 2012
Kernel Version: 3.5
Contact: linux-bluetooth@vger.kernel.org
Description:
LED selector for Intuos4 WL. There are 4 leds, but only one LED
can be lit at a time. Max brightness is 127.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/led
Date: August 2011
Contact: linux-input@vger.kernel.org
......
......@@ -32,9 +32,13 @@ config HID
If unsure, say Y.
config HID_BATTERY_STRENGTH
bool
bool "Battery level reporting for HID devices"
depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY
default n
---help---
This option adds support of reporting battery strength (for HID devices
that support this feature) through power_supply class so that userspace
tools, such as upower, can display it.
config HIDRAW
bool "/dev/hidraw raw HID device support"
......@@ -60,6 +64,18 @@ source "drivers/hid/usbhid/Kconfig"
menu "Special HID drivers"
depends on HID
config HID_GENERIC
tristate "Generic HID driver"
depends on HID
default y
---help---
Support for generic HID devices.
To compile this driver as a module, choose M here: the module
will be called hid-generic.
If unsure, say Y.
config HID_A4TECH
tristate "A4 tech mice" if EXPERT
depends on USB_HID
......@@ -92,6 +108,12 @@ config HID_APPLE
Say Y here if you want support for keyboards of Apple iBooks, PowerBooks,
MacBooks, MacBook Pros and Apple Aluminum.
config HID_AUREAL
tristate "Aureal"
depends on USB_HID
---help---
Support for Aureal Cy se W-01RN Remote Controller and other Aureal derived remotes.
config HID_BELKIN
tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT
depends on USB_HID
......@@ -595,16 +617,10 @@ config THRUSTMASTER_FF
config HID_WACOM
tristate "Wacom Bluetooth devices support"
depends on BT_HIDP
---help---
Support for Wacom Graphire Bluetooth tablet.
config HID_WACOM_POWER_SUPPLY
bool "Wacom Bluetooth devices power supply status support"
depends on HID_WACOM
depends on LEDS_CLASS
select POWER_SUPPLY
---help---
Say Y here if you want to enable power supply status monitoring for
Wacom Bluetooth devices.
Support for Wacom Graphire Bluetooth and Intuos4 WL tablets.
config HID_WIIMOTE
tristate "Nintendo Wii Remote support"
......
......@@ -9,6 +9,8 @@ endif
obj-$(CONFIG_HID) += hid.o
obj-$(CONFIG_HID_GENERIC) += hid-generic.o
hid-$(CONFIG_HIDRAW) += hidraw.o
hid-logitech-y := hid-lg.o
......@@ -36,6 +38,7 @@ endif
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
obj-$(CONFIG_HID_ACRUX) += hid-axff.o
obj-$(CONFIG_HID_APPLE) += hid-apple.o
obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
......
......@@ -458,6 +458,9 @@ static const struct hid_device_id apple_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_ISO_KEYBOARD },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
......
/*
* HID driver for Aureal Cy se W-01RN USB_V3.1 devices
*
* Copyright (c) 2010 Franco Catrin <fcatrin@gmail.com>
* Copyright (c) 2010 Ben Cropley <bcropley@internode.on.net>
*
* Based on HID sunplus driver by
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
static __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
if (*rsize >= 54 && rdesc[52] == 0x25 && rdesc[53] == 0x01) {
dev_info(&hdev->dev, "fixing Aureal Cy se W-01RN USB_V3.1 report descriptor.\n");
rdesc[53] = 0x65;
} return rdesc;
}
static const struct hid_device_id aureal_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
{ }
};
MODULE_DEVICE_TABLE(hid, aureal_devices);
static struct hid_driver aureal_driver = {
.name = "aureal",
.id_table = aureal_devices,
.report_fixup = aureal_report_fixup,
};
static int __init aureal_init(void)
{
return hid_register_driver(&aureal_driver);
}
static void __exit aureal_exit(void)
{
hid_unregister_driver(&aureal_driver);
}
module_init(aureal_init);
module_exit(aureal_exit);
MODULE_LICENSE("GPL");
This diff is collapsed.
/*
* HID support for Linux
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2007-2008 Oliver Neukum
* Copyright (c) 2006-2012 Jiri Kosina
* Copyright (c) 2012 Henrik Rydberg
*/
/*
* 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/module.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <linux/hid.h>
static const struct hid_device_id hid_table[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_GENERIC, HID_ANY_ID, HID_ANY_ID) },
{ }
};
MODULE_DEVICE_TABLE(hid, hid_table);
static struct hid_driver hid_generic = {
.name = "hid-generic",
.id_table = hid_table,
};
static int __init hid_init(void)
{
return hid_register_driver(&hid_generic);
}
static void __exit hid_exit(void)
{
hid_unregister_driver(&hid_generic);
}
module_init(hid_init);
module_exit(hid_exit);
MODULE_AUTHOR("Henrik Rydberg");
MODULE_DESCRIPTION("HID generic driver");
MODULE_LICENSE("GPL");
......@@ -430,6 +430,15 @@ static int mousevsc_connect_to_vsp(struct hv_device *device)
return ret;
}
static int mousevsc_hid_parse(struct hid_device *hid)
{
struct hv_device *dev = hid_get_drvdata(hid);
struct mousevsc_dev *input_dev = hv_get_drvdata(dev);
return hid_parse_report(hid, input_dev->report_desc,
input_dev->report_desc_size);
}
static int mousevsc_hid_open(struct hid_device *hid)
{
return 0;
......@@ -449,6 +458,7 @@ static void mousevsc_hid_stop(struct hid_device *hid)
}
static struct hid_ll_driver mousevsc_ll_driver = {
.parse = mousevsc_hid_parse,
.open = mousevsc_hid_open,
.close = mousevsc_hid_close,
.start = mousevsc_hid_start,
......@@ -506,13 +516,14 @@ static int mousevsc_probe(struct hv_device *device,
sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse");
hid_set_drvdata(hid_dev, device);
ret = hid_add_device(hid_dev);
if (ret)
goto probe_err1;
ret = hid_parse_report(hid_dev, input_dev->report_desc,
input_dev->report_desc_size);
ret = hid_parse(hid_dev);
if (ret) {
hid_err(hid_dev, "parse failed\n");
goto probe_err2;
......
......@@ -154,9 +154,15 @@
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
#define USB_DEVICE_ID_ATMEL_MXT_DIGITIZER 0x2118
#define USB_VENDOR_ID_AUREAL 0x0755
#define USB_DEVICE_ID_AUREAL_W01RN 0x2626
#define USB_VENDOR_ID_AVERMEDIA 0x07ca
#define USB_DEVICE_ID_AVER_FM_MR800 0xb800
#define USB_VENDOR_ID_BAANTO 0x2453
#define USB_DEVICE_ID_BAANTO_MT_190W2 0x0100
#define USB_VENDOR_ID_BELKIN 0x050d
#define USB_DEVICE_ID_FLIP_KVM 0x3201
......@@ -726,6 +732,7 @@
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U 0x0004
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064
#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522
#define USB_VENDOR_ID_UNITEC 0x227d
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
......@@ -749,6 +756,7 @@
#define USB_DEVICE_ID_WALTOP_PID_0038 0x0038
#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH 0x0501
#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH 0x0500
#define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET 0x0502
#define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005
......
......@@ -225,7 +225,10 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
* Verify and convert units.
* See HID specification v1.11 6.2.2.7 Global Items for unit decoding
*/
if (code == ABS_X || code == ABS_Y || code == ABS_Z) {
switch (code) {
case ABS_X:
case ABS_Y:
case ABS_Z:
if (field->unit == 0x11) { /* If centimeters */
/* Convert to millimeters */
unit_exponent += 1;
......@@ -239,7 +242,13 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
} else {
return 0;
}
} else if (code == ABS_RX || code == ABS_RY || code == ABS_RZ) {
break;
case ABS_RX:
case ABS_RY:
case ABS_RZ:
case ABS_TILT_X:
case ABS_TILT_Y:
if (field->unit == 0x14) { /* If degrees */
/* Convert to radians */
prev = logical_extents;
......@@ -250,7 +259,9 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
} else if (field->unit != 0x12) { /* If not radians */
return 0;
}
} else {
break;
default:
return 0;
}
......@@ -623,6 +634,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
map_key_clear(BTN_TOOL_RUBBER);
break;
case 0x3d: /* X Tilt */
map_abs_clear(ABS_TILT_X);
break;
case 0x3e: /* Y Tilt */
map_abs_clear(ABS_TILT_Y);
break;
case 0x33: /* Touch */
case 0x42: /* TipSwitch */
case 0x43: /* TipSwitch2 */
......@@ -638,10 +657,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
map_key_clear(BTN_STYLUS2);
break;
case 0x51: /* ContactID */
device->quirks |= HID_QUIRK_MULTITOUCH;
goto unknown;
default: goto unknown;
}
break;
......@@ -1208,13 +1223,6 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
}
if (hid->quirks & HID_QUIRK_MULTITOUCH) {
/* generic hid does not know how to handle multitouch devices */
if (hidinput)
goto out_cleanup;
goto out_unwind;
}
if (hidinput && input_register_device(hidinput->input))
goto out_cleanup;
......
......@@ -109,23 +109,23 @@ static __u8 dfp_rdesc_fixed[] = {
static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
if ((quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
rdesc[84] == 0x8c && rdesc[85] == 0x02) {
hid_info(hdev,
"fixing up Logitech keyboard report descriptor\n");
rdesc[84] = rdesc[89] = 0x4d;
rdesc[85] = rdesc[90] = 0x10;
}
if ((quirks & LG_RDESC_REL_ABS) && *rsize >= 50 &&
if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 50 &&
rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
rdesc[49] == 0x81 && rdesc[50] == 0x06) {
hid_info(hdev,
"fixing up rel/abs in Logitech report descriptor\n");
rdesc[33] = rdesc[50] = 0x02;
}
if ((quirks & LG_FF4) && *rsize >= 101 &&
if ((drv_data->quirks & LG_FF4) && *rsize >= 101 &&
rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
rdesc[47] == 0x05 && rdesc[48] == 0x09) {
hid_info(hdev, "fixing up Logitech Speed Force Wireless button descriptor\n");
......@@ -278,7 +278,7 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
0, 0, 0, 0, 0,183,184,185,186,187,
188,189,190,191,192,193,194, 0, 0, 0
};
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
unsigned int hid = usage->hid;
if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
......@@ -289,7 +289,7 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
lg_dinovo_mapping(hi, usage, bit, max))
return 1;
if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
return 1;
if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
......@@ -299,11 +299,11 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
/* Special handling for Logitech Cordless Desktop */
if (field->application == HID_GD_MOUSE) {
if ((quirks & LG_IGNORE_DOUBLED_WHEEL) &&
if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
(hid == 7 || hid == 8))
return -1;
} else {
if ((quirks & LG_EXPANDED_KEYMAP) &&
if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
hid < ARRAY_SIZE(e_keymap) &&
e_keymap[hid] != 0) {
hid_map_usage(hi, usage, bit, max, EV_KEY,
......@@ -319,13 +319,13 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
(field->flags & HID_MAIN_ITEM_RELATIVE))
field->flags &= ~HID_MAIN_ITEM_RELATIVE;
if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
usage->type == EV_REL || usage->type == EV_ABS))
clear_bit(usage->code, *bit);
......@@ -335,9 +335,9 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
static int lg_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
input_event(field->hidinput->input, usage->type, usage->code,
-value);
return 1;
......@@ -348,13 +348,20 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field,
static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
unsigned long quirks = id->driver_data;
unsigned int connect_mask = HID_CONNECT_DEFAULT;
struct lg_drv_data *drv_data;
int ret;
hid_set_drvdata(hdev, (void *)quirks);
drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
if (!drv_data) {
hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
return -ENOMEM;
}
drv_data->quirks = id->driver_data;
hid_set_drvdata(hdev, (void *)drv_data);
if (quirks & LG_NOGET)
if (drv_data->quirks & LG_NOGET)
hdev->quirks |= HID_QUIRK_NOGET;
ret = hid_parse(hdev);
......@@ -363,7 +370,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_free;
}
if (quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
connect_mask &= ~HID_CONNECT_FF;
ret = hid_hw_start(hdev, connect_mask);
......@@ -392,27 +399,29 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
}
if (quirks & LG_FF)
if (drv_data->quirks & LG_FF)
lgff_init(hdev);
if (quirks & LG_FF2)
if (drv_data->quirks & LG_FF2)
lg2ff_init(hdev);
if (quirks & LG_FF3)
if (drv_data->quirks & LG_FF3)
lg3ff_init(hdev);
if (quirks & LG_FF4)
if (drv_data->quirks & LG_FF4)
lg4ff_init(hdev);
return 0;
err_free:
kfree(drv_data);
return ret;
}
static void lg_remove(struct hid_device *hdev)
{
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
if(quirks & LG_FF4)
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
if (drv_data->quirks & LG_FF4)
lg4ff_deinit(hdev);
hid_hw_stop(hdev);
kfree(drv_data);
}
static const struct hid_device_id lg_devices[] = {
......
#ifndef __HID_LG_H
#define __HID_LG_H
struct lg_drv_data {
unsigned long quirks;
void *device_props; /* Device specific properties */
};
#ifdef CONFIG_LOGITECH_FF
int lgff_init(struct hid_device *hdev);
#else
......
/*
* Force feedback support for Logitech Speed Force Wireless
* Force feedback support for Logitech Gaming Wheels
*
* http://wiibrew.org/wiki/Logitech_USB_steering_wheel
* Including G27, G25, DFP, DFGT, FFEX, Momo, Momo2 &
* Speed Force Wireless (WiiWheel)
*
* Copyright (c) 2010 Simon Wood <simon@mungewell.org>
*/
......@@ -51,20 +52,18 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
static bool list_inited;
struct lg4ff_device_entry {
char *device_id; /* Use name in respective kobject structure's address as the ID */
__u16 range;
__u16 min_range;
__u16 max_range;
__u8 leds;
#ifdef CONFIG_LEDS_CLASS
__u8 led_state;
struct led_classdev *led[5];
#endif
struct list_head list;
void (*set_range)(struct hid_device *hid, u16 range);
};
static struct lg4ff_device_entry device_list;
static const signed short lg4ff_wheel_effects[] = {
FF_CONSTANT,
FF_AUTOCENTER,
......@@ -285,18 +284,20 @@ static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_n
/* Read current range and display it in terminal */
static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lg4ff_device_entry *uninitialized_var(entry);
struct list_head *h;
struct hid_device *hid = to_hid_device(dev);
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
size_t count;
list_for_each(h, &device_list.list) {
entry = list_entry(h, struct lg4ff_device_entry, list);
if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0)
break;
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
hid_err(hid, "Private driver data not found!\n");
return 0;
}
if (h == &device_list.list) {
dbg_hid("Device not found!");
entry = drv_data->device_props;
if (!entry) {
hid_err(hid, "Device properties not found!\n");
return 0;
}
......@@ -308,19 +309,21 @@ static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *att
* according to the type of the wheel */
static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct lg4ff_device_entry *uninitialized_var(entry);
struct list_head *h;
struct hid_device *hid = to_hid_device(dev);
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
__u16 range = simple_strtoul(buf, NULL, 10);
list_for_each(h, &device_list.list) {
entry = list_entry(h, struct lg4ff_device_entry, list);
if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0)
break;
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
hid_err(hid, "Private driver data not found!\n");
return 0;
}
if (h == &device_list.list) {
dbg_hid("Device not found!");
return count;
entry = drv_data->device_props;
if (!entry) {
hid_err(hid, "Device properties not found!\n");
return 0;
}
if (range == 0)
......@@ -336,6 +339,88 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
return count;
}
#ifdef CONFIG_LEDS_CLASS
static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
{
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
report->field[0]->value[0] = 0xf8;
report->field[0]->value[1] = 0x12;
report->field[0]->value[2] = leds;
report->field[0]->value[3] = 0x00;
report->field[0]->value[4] = 0x00;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct device *dev = led_cdev->dev->parent;
struct hid_device *hid = container_of(dev, struct hid_device, dev);
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
struct lg4ff_device_entry *entry;
int i, state = 0;
if (!drv_data) {
hid_err(hid, "Device data not found.");
return;
}
entry = (struct lg4ff_device_entry *)drv_data->device_props;
if (!entry) {
hid_err(hid, "Device properties not found.");
return;
}
for (i = 0; i < 5; i++) {
if (led_cdev != entry->led[i])
continue;
state = (entry->led_state >> i) & 1;
if (value == LED_OFF && state) {
entry->led_state &= ~(1 << i);
lg4ff_set_leds(hid, entry->led_state);
} else if (value != LED_OFF && !state) {
entry->led_state |= 1 << i;
lg4ff_set_leds(hid, entry->led_state);
}
break;
}
}
static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cdev)
{
struct device *dev = led_cdev->dev->parent;
struct hid_device *hid = container_of(dev, struct hid_device, dev);
struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
struct lg4ff_device_entry *entry;
int i, value = 0;
if (!drv_data) {
hid_err(hid, "Device data not found.");
return LED_OFF;
}
entry = (struct lg4ff_device_entry *)drv_data->device_props;
if (!entry) {
hid_err(hid, "Device properties not found.");
return LED_OFF;
}
for (i = 0; i < 5; i++)
if (led_cdev == entry->led[i]) {
value = (entry->led_state >> i) & 1;
break;
}
return value ? LED_FULL : LED_OFF;
}
#endif
int lg4ff_init(struct hid_device *hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
......@@ -344,6 +429,7 @@ int lg4ff_init(struct hid_device *hid)
struct hid_report *report;
struct hid_field *field;
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
struct usb_device_descriptor *udesc;
int error, i, j;
__u16 bcdDevice, rev_maj, rev_min;
......@@ -423,28 +509,24 @@ int lg4ff_init(struct hid_device *hid)
dev->ff->set_autocenter(dev, 0);
}
/* Initialize device_list if this is the first device to handle by lg4ff */
if (!list_inited) {
INIT_LIST_HEAD(&device_list.list);
list_inited = 1;
/* Get private driver data */
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
hid_err(hid, "Cannot add device, private driver data not allocated\n");
return -1;
}
/* Add the device to device_list */
/* Initialize device properties */
entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
if (!entry) {
hid_err(hid, "Cannot add device, insufficient memory.\n");
return -ENOMEM;
}
entry->device_id = kstrdup((&hid->dev)->kobj.name, GFP_KERNEL);
if (!entry->device_id) {
hid_err(hid, "Cannot set device_id, insufficient memory.\n");
kfree(entry);
hid_err(hid, "Cannot add device, insufficient memory to allocate device properties.\n");
return -ENOMEM;
}
drv_data->device_props = entry;
entry->min_range = lg4ff_devices[i].min_range;
entry->max_range = lg4ff_devices[i].max_range;
entry->set_range = lg4ff_devices[i].set_range;
list_add(&entry->list, &device_list.list);
/* Create sysfs interface */
error = device_create_file(&hid->dev, &dev_attr_range);
......@@ -457,32 +539,100 @@ int lg4ff_init(struct hid_device *hid)
if (entry->set_range != NULL)
entry->set_range(hid, entry->range);
hid_info(hid, "Force feedback for Logitech Speed Force Wireless by Simon Wood <simon@mungewell.org>\n");
#ifdef CONFIG_LEDS_CLASS
/* register led subsystem - G27 only */
entry->led_state = 0;
for (j = 0; j < 5; j++)
entry->led[j] = NULL;
if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) {
struct led_classdev *led;
size_t name_sz;
char *name;
lg4ff_set_leds(hid, 0);
name_sz = strlen(dev_name(&hid->dev)) + 8;
for (j = 0; j < 5; j++) {
led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
if (!led) {
hid_err(hid, "can't allocate memory for LED %d\n", j);
goto err;
}
name = (void *)(&led[1]);
snprintf(name, name_sz, "%s::RPM%d", dev_name(&hid->dev), j+1);
led->name = name;
led->brightness = 0;
led->max_brightness = 1;
led->brightness_get = lg4ff_led_get_brightness;
led->brightness_set = lg4ff_led_set_brightness;
entry->led[j] = led;
error = led_classdev_register(&hid->dev, led);
if (error) {
hid_err(hid, "failed to register LED %d. Aborting.\n", j);
err:
/* Deregister LEDs (if any) */
for (j = 0; j < 5; j++) {
led = entry->led[j];
entry->led[j] = NULL;
if (!led)
continue;
led_classdev_unregister(led);
kfree(led);
}
goto out; /* Let the driver continue without LEDs */
}
}
}
out:
#endif
hid_info(hid, "Force feedback support for Logitech Gaming Wheels\n");
return 0;
}
int lg4ff_deinit(struct hid_device *hid)
{
bool found = 0;
struct lg4ff_device_entry *entry;
struct list_head *h, *g;
list_for_each_safe(h, g, &device_list.list) {
entry = list_entry(h, struct lg4ff_device_entry, list);
if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) {
list_del(h);
kfree(entry->device_id);
kfree(entry);
found = 1;
break;
struct lg_drv_data *drv_data;
device_remove_file(&hid->dev, &dev_attr_range);
drv_data = hid_get_drvdata(hid);
if (!drv_data) {
hid_err(hid, "Error while deinitializing device, no private driver data.\n");
return -1;
}
entry = drv_data->device_props;
if (!entry) {
hid_err(hid, "Error while deinitializing device, no device properties data.\n");
return -1;
}
if (!found) {
dbg_hid("Device entry not found!\n");
return -1;
#ifdef CONFIG_LEDS_CLASS
{
int j;
struct led_classdev *led;
/* Deregister LEDs (if any) */
for (j = 0; j < 5; j++) {
led = entry->led[j];
entry->led[j] = NULL;
if (!led)
continue;
led_classdev_unregister(led);
kfree(led);
}
}
#endif
/* Deallocate memory */
kfree(entry);
device_remove_file(&hid->dev, &dev_attr_range);
dbg_hid("Device successfully unregistered\n");
return 0;
}
......@@ -26,6 +26,7 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <asm/unaligned.h>
#include "usbhid/usbhid.h"
#include "hid-ids.h"
#include "hid-logitech-dj.h"
......@@ -155,6 +156,14 @@ static const char media_descriptor[] = {
/* Maximum size of all defined hid reports in bytes (including report id) */
#define MAX_REPORT_SIZE 8
/* Make sure all descriptors are present here */
#define MAX_RDESC_SIZE \
(sizeof(kbd_descriptor) + \
sizeof(mse_descriptor) + \
sizeof(consumer_descriptor) + \
sizeof(syscontrol_descriptor) + \
sizeof(media_descriptor))
/* Number of possible hid report types that can be created by this driver.
*
* Right now, RF report types have the same report types (or report id's)
......@@ -265,8 +274,8 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
goto dj_device_allocate_fail;
}
dj_dev->reports_supported = le32_to_cpu(
dj_report->report_params[DEVICE_PAIRED_RF_REPORT_TYPE]);
dj_dev->reports_supported = get_unaligned_le32(
dj_report->report_params + DEVICE_PAIRED_RF_REPORT_TYPE);
dj_dev->hdev = dj_hiddev;
dj_dev->dj_receiver_dev = djrcv_dev;
dj_dev->device_index = dj_report->device_index;
......@@ -473,9 +482,17 @@ static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
return 0;
}
static void rdcat(char **rdesc, unsigned int *rsize, const char *data, unsigned int size)
{
memcpy(*rdesc + *rsize, data, size);
*rsize += size;
}
static int logi_dj_ll_parse(struct hid_device *hid)
{
struct dj_device *djdev = hid->driver_data;
unsigned int rsize = 0;
char *rdesc;
int retval;
dbg_hid("%s\n", __func__);
......@@ -483,70 +500,38 @@ static int logi_dj_ll_parse(struct hid_device *hid)
djdev->hdev->version = 0x0111;
djdev->hdev->country = 0x00;
rdesc = kmalloc(MAX_RDESC_SIZE, GFP_KERNEL);
if (!rdesc)
return -ENOMEM;
if (djdev->reports_supported & STD_KEYBOARD) {
dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n",
__func__, djdev->reports_supported);
retval = hid_parse_report(hid,
(u8 *) kbd_descriptor,
sizeof(kbd_descriptor));
if (retval) {
dbg_hid("%s: sending a kbd descriptor, hid_parse failed"
" error: %d\n", __func__, retval);
return retval;
}
rdcat(&rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor));
}
if (djdev->reports_supported & STD_MOUSE) {
dbg_hid("%s: sending a mouse descriptor, reports_supported: "
"%x\n", __func__, djdev->reports_supported);
retval = hid_parse_report(hid,
(u8 *) mse_descriptor,
sizeof(mse_descriptor));
if (retval) {
dbg_hid("%s: sending a mouse descriptor, hid_parse "
"failed error: %d\n", __func__, retval);
return retval;
}
rdcat(&rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor));
}
if (djdev->reports_supported & MULTIMEDIA) {
dbg_hid("%s: sending a multimedia report descriptor: %x\n",
__func__, djdev->reports_supported);
retval = hid_parse_report(hid,
(u8 *) consumer_descriptor,
sizeof(consumer_descriptor));
if (retval) {
dbg_hid("%s: sending a consumer_descriptor, hid_parse "
"failed error: %d\n", __func__, retval);
return retval;
}
rdcat(&rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor));
}
if (djdev->reports_supported & POWER_KEYS) {
dbg_hid("%s: sending a power keys report descriptor: %x\n",
__func__, djdev->reports_supported);
retval = hid_parse_report(hid,
(u8 *) syscontrol_descriptor,
sizeof(syscontrol_descriptor));
if (retval) {
dbg_hid("%s: sending a syscontrol_descriptor, "
"hid_parse failed error: %d\n",
__func__, retval);
return retval;
}
rdcat(&rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor));
}
if (djdev->reports_supported & MEDIA_CENTER) {
dbg_hid("%s: sending a media center report descriptor: %x\n",
__func__, djdev->reports_supported);
retval = hid_parse_report(hid,
(u8 *) media_descriptor,
sizeof(media_descriptor));
if (retval) {
dbg_hid("%s: sending a media_descriptor, hid_parse "
"failed error: %d\n", __func__, retval);
return retval;
}
rdcat(&rdesc, &rsize, media_descriptor, sizeof(media_descriptor));
}
if (djdev->reports_supported & KBD_LEDS) {
......@@ -554,7 +539,10 @@ static int logi_dj_ll_parse(struct hid_device *hid)
__func__, djdev->reports_supported);
}
return 0;
retval = hid_parse_report(hid, rdesc, rsize);
kfree(rdesc);
return retval;
}
static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
......
This diff is collapsed.
......@@ -14,6 +14,7 @@
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/usb.h>
#include "hid-ids.h"
......@@ -352,9 +353,125 @@ static __u8 pf1209_rdesc_fixed[] = {
0xC0 /* End Collection */
};
/*
* See TWHL850 description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Wireless_Tablet_TWHL850
*/
/* Size of the original descriptors of TWHL850 tablet */
#define TWHL850_RDESC_ORIG_SIZE0 182
#define TWHL850_RDESC_ORIG_SIZE1 161
#define TWHL850_RDESC_ORIG_SIZE2 92
/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */
static __u8 twhl850_rdesc_fixed0[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x03, /* Report Count (3), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x81, 0x02, /* Input (Variable), */
0x81, 0x03, /* Input (Constant, Variable), */
0x95, 0x01, /* Report Count (1), */
0x09, 0x32, /* Usage (In Range), */
0x81, 0x02, /* Input (Variable), */
0x81, 0x03, /* Input (Constant, Variable), */
0x75, 0x10, /* Report Size (16), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x65, 0x13, /* Unit (Inch), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
0x26, 0x00, 0x7D, /* Logical Maximum (32000), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0x88, 0x13, /* Physical Maximum (5000), */
0x26, 0x20, 0x4E, /* Logical Maximum (20000), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */
static __u8 twhl850_rdesc_fixed1[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x02, /* Usage (Mouse), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x01, /* Report ID (1), */
0x09, 0x01, /* Usage (Pointer), */
0xA0, /* Collection (Physical), */
0x05, 0x09, /* Usage Page (Button), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x03, /* Report Count (3), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x03, /* Usage Maximum (03h), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x03, /* Input (Constant, Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x30, /* Usage (X), */
0x09, 0x31, /* Usage (Y), */
0x16, 0x00, 0x80, /* Logical Minimum (-32768), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x06, /* Input (Variable, Relative), */
0x09, 0x38, /* Usage (Wheel), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x01, /* Report Count (1), */
0x75, 0x08, /* Report Size (8), */
0x81, 0x06, /* Input (Variable, Relative), */
0x81, 0x03, /* Input (Constant, Variable), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */
static __u8 twhl850_rdesc_fixed2[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x06, /* Usage (Keyboard), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x03, /* Report ID (3), */
0x05, 0x07, /* Usage Page (Keyboard), */
0x14, /* Logical Minimum (0), */
0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */
0x29, 0xE7, /* Usage Maximum (KB Right GUI), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x08, /* Report Count (8), */
0x81, 0x02, /* Input (Variable), */
0x18, /* Usage Minimum (None), */
0x29, 0xFF, /* Usage Maximum (FFh), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x06, /* Report Count (6), */
0x80, /* Input, */
0xC0 /* End Collection */
};
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
__u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
switch (hdev->product) {
case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
if (*rsize == PF1209_RDESC_ORIG_SIZE) {
......@@ -386,6 +503,28 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
*rsize = sizeof(wp1062_rdesc_fixed);
}
break;
case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850:
switch (iface_num) {
case 0:
if (*rsize == TWHL850_RDESC_ORIG_SIZE0) {
rdesc = twhl850_rdesc_fixed0;
*rsize = sizeof(twhl850_rdesc_fixed0);
}
break;
case 1:
if (*rsize == TWHL850_RDESC_ORIG_SIZE1) {
rdesc = twhl850_rdesc_fixed1;
*rsize = sizeof(twhl850_rdesc_fixed1);
}
break;
case 2:
if (*rsize == TWHL850_RDESC_ORIG_SIZE2) {
rdesc = twhl850_rdesc_fixed2;
*rsize = sizeof(twhl850_rdesc_fixed2);
}
break;
}
break;
}
return rdesc;
......@@ -402,6 +541,8 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);
......
This diff is collapsed.
This diff is collapsed.
......@@ -769,7 +769,7 @@ static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir,
/*
* Basic IR data is encoded into 3 bytes. The first two bytes are the
* upper 8 bit of the X/Y data, the 3rd byte contains the lower 2 bits
* lower 8 bit of the X/Y data, the 3rd byte contains the upper 2 bits
* of both.
* If data is packed, then the 3rd byte is put first and slightly
* reordered. This allows to interleave packed and non-packed data to
......@@ -778,17 +778,11 @@ static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir,
*/
if (packed) {
x = ir[1] << 2;
y = ir[2] << 2;
x |= ir[0] & 0x3;
y |= (ir[0] >> 2) & 0x3;
x = ir[1] | ((ir[0] & 0x03) << 8);
y = ir[2] | ((ir[0] & 0x0c) << 6);
} else {
x = ir[0] << 2;
y = ir[1] << 2;
x |= (ir[2] >> 4) & 0x3;
y |= (ir[2] >> 6) & 0x3;
x = ir[0] | ((ir[2] & 0x30) << 4);
y = ir[1] | ((ir[2] & 0xc0) << 2);
}
input_report_abs(wdata->ir, xid, x);
......
......@@ -87,11 +87,13 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
len = list->buffer[list->tail].len > count ?
count : list->buffer[list->tail].len;
if (list->buffer[list->tail].value) {
if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
ret = -EFAULT;
goto out;
}
ret = len;
}
kfree(list->buffer[list->tail].value);
list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
......@@ -437,19 +439,24 @@ static const struct file_operations hidraw_ops = {
.llseek = noop_llseek,
};
void hidraw_report_event(struct hid_device *hid, u8 *data, int len)
int hidraw_report_event(struct hid_device *hid, u8 *data, int len)
{
struct hidraw *dev = hid->hidraw;
struct hidraw_list *list;
int ret = 0;
list_for_each_entry(list, &dev->list, node) {
list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC);
if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) {
ret = -ENOMEM;
break;
}
list->buffer[list->head].len = len;
list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
kill_fasync(&list->fasync, SIGIO, POLL_IN);
}
wake_up_interruptible(&dev->wait);
return ret;
}
EXPORT_SYMBOL_GPL(hidraw_report_event);
......
......@@ -28,6 +28,7 @@
#include <linux/input.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/string.h>
#include <linux/usb.h>
......@@ -86,8 +87,13 @@ static int hid_start_in(struct hid_device *hid)
!test_bit(HID_REPORTED_IDLE, &usbhid->iofl) &&
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
if (rc != 0)
if (rc != 0) {
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
if (rc == -ENOSPC)
set_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
} else {
clear_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
}
}
spin_unlock_irqrestore(&usbhid->lock, flags);
return rc;
......@@ -173,8 +179,10 @@ static void hid_io_error(struct hid_device *hid)
if (time_after(jiffies, usbhid->stop_retry)) {
/* Retries failed, so do a port reset */
if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) {
/* Retries failed, so do a port reset unless we lack bandwidth*/
if (test_bit(HID_NO_BANDWIDTH, &usbhid->iofl)
&& !test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) {
schedule_work(&usbhid->reset_work);
goto done;
}
......@@ -749,7 +757,7 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
int usbhid_open(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
int res;
int res = 0;
mutex_lock(&hid_open_mut);
if (!hid->open++) {
......@@ -757,17 +765,27 @@ int usbhid_open(struct hid_device *hid)
/* the device must be awake to reliably request remote wakeup */
if (res < 0) {
hid->open--;
mutex_unlock(&hid_open_mut);
return -EIO;
res = -EIO;
goto done;
}
usbhid->intf->needs_remote_wakeup = 1;
if (hid_start_in(hid))
res = hid_start_in(hid);
if (res) {
if (res != -ENOSPC) {
hid_io_error(hid);
res = 0;
} else {
/* no use opening if resources are insufficient */
hid->open--;
res = -EBUSY;
usbhid->intf->needs_remote_wakeup = 0;
}
}
usb_autopm_put_interface(usbhid->intf);
}
done:
mutex_unlock(&hid_open_mut);
return 0;
return res;
}
void usbhid_close(struct hid_device *hid)
......@@ -1396,7 +1414,34 @@ static int hid_post_reset(struct usb_interface *intf)
struct usb_device *dev = interface_to_usbdev (intf);
struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data;
struct usb_host_interface *interface = intf->cur_altsetting;
int status;
char *rdesc;
/* Fetch and examine the HID report descriptor. If this
* has changed, then rebind. Since usbcore's check of the
* configuration descriptors passed, we already know that
* the size of the HID report descriptor has not changed.
*/
rdesc = kmalloc(hid->rsize, GFP_KERNEL);
if (!rdesc) {
dbg_hid("couldn't allocate rdesc memory (post_reset)\n");
return 1;
}
status = hid_get_class_descriptor(dev,
interface->desc.bInterfaceNumber,
HID_DT_REPORT, rdesc, hid->rsize);
if (status < 0) {
dbg_hid("reading report descriptor failed (post_reset)\n");
kfree(rdesc);
return 1;
}
status = memcmp(rdesc, hid->rdesc, hid->rsize);
kfree(rdesc);
if (status != 0) {
dbg_hid("report descriptor changed\n");
return 1;
}
spin_lock_irq(&usbhid->lock);
clear_bit(HID_RESET_PENDING, &usbhid->iofl);
......@@ -1553,28 +1598,15 @@ static struct usb_driver hid_driver = {
.supports_autosuspend = 1,
};
static const struct hid_device_id hid_usb_table[] = {
{ HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
{ }
};
struct usb_interface *usbhid_find_interface(int minor)
{
return usb_find_interface(&hid_driver, minor);
}
static struct hid_driver hid_usb_driver = {
.name = "generic-usb",
.id_table = hid_usb_table,
};
static int __init hid_init(void)
{
int retval = -ENOMEM;
retval = hid_register_driver(&hid_usb_driver);
if (retval)
goto hid_register_fail;
retval = usbhid_quirks_init(quirks_param);
if (retval)
goto usbhid_quirks_init_fail;
......@@ -1587,8 +1619,6 @@ static int __init hid_init(void)
usb_register_fail:
usbhid_quirks_exit();
usbhid_quirks_init_fail:
hid_unregister_driver(&hid_usb_driver);
hid_register_fail:
return retval;
}
......@@ -1596,7 +1626,6 @@ static void __exit hid_exit(void)
{
usb_deregister(&hid_driver);
usbhid_quirks_exit();
hid_unregister_driver(&hid_usb_driver);
}
module_init(hid_init);
......
......@@ -88,6 +88,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
......
......@@ -34,6 +34,7 @@
#include <linux/hid.h>
#include <linux/hiddev.h>
#include <linux/compat.h>
#include <linux/vmalloc.h>
#include "usbhid.h"
#ifdef CONFIG_USB_DYNAMIC_MINORS
......@@ -250,13 +251,13 @@ static int hiddev_release(struct inode * inode, struct file * file)
} else {
mutex_unlock(&list->hiddev->existancelock);
kfree(list->hiddev);
kfree(list);
vfree(list);
return 0;
}
}
mutex_unlock(&list->hiddev->existancelock);
kfree(list);
vfree(list);
return 0;
}
......@@ -278,7 +279,7 @@ static int hiddev_open(struct inode *inode, struct file *file)
hid = usb_get_intfdata(intf);
hiddev = hid->hiddev;
if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
if (!(list = vzalloc(sizeof(struct hiddev_list))))
return -ENOMEM;
mutex_init(&list->thread_lock);
list->hiddev = hiddev;
......@@ -322,7 +323,7 @@ static int hiddev_open(struct inode *inode, struct file *file)
mutex_unlock(&hiddev->existancelock);
bail:
file->private_data = NULL;
kfree(list);
vfree(list);
return res;
}
......
......@@ -55,6 +55,7 @@ struct usb_interface *usbhid_find_interface(int minor);
#define HID_STARTED 8
#define HID_REPORTED_IDLE 9
#define HID_KEYS_PRESSED 10
#define HID_NO_BANDWIDTH 11
/*
* USB-specific HID struct, to be pointed to
......
......@@ -317,13 +317,18 @@ struct hid_item {
#define HID_QUIRK_BADPAD 0x00000020
#define HID_QUIRK_MULTI_INPUT 0x00000040
#define HID_QUIRK_HIDINPUT_FORCE 0x00000080
#define HID_QUIRK_MULTITOUCH 0x00000100
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000
#define HID_QUIRK_NO_IGNORE 0x40000000
#define HID_QUIRK_NO_INPUT_SYNC 0x80000000
/*
* HID device groups
*/
#define HID_GROUP_GENERIC 0x0001
#define HID_GROUP_MULTITOUCH 0x0002
/*
* This is the global environment of the parser. This information is
* persistent for main-items. The global environment can be saved and
......@@ -467,6 +472,8 @@ struct hid_driver;
struct hid_ll_driver;
struct hid_device { /* device report descriptor */
__u8 *dev_rdesc;
unsigned dev_rsize;
__u8 *rdesc;
unsigned rsize;
struct hid_collection *collection; /* List of HID collections */
......@@ -474,6 +481,7 @@ struct hid_device { /* device report descriptor */
unsigned maxcollection; /* Number of parsed collections */
unsigned maxapplication; /* Number of applications */
__u16 bus; /* BUS ID */
__u16 group; /* Report group */
__u32 vendor; /* Vendor ID */
__u32 product; /* Product ID */
__u32 version; /* HID version */
......@@ -578,12 +586,12 @@ struct hid_descriptor {
struct hid_class_descriptor desc[1];
} __attribute__ ((packed));
#define HID_DEVICE(b, ven, prod) \
.bus = (b), \
.vendor = (ven), .product = (prod)
#define HID_USB_DEVICE(ven, prod) HID_DEVICE(BUS_USB, ven, prod)
#define HID_BLUETOOTH_DEVICE(ven, prod) HID_DEVICE(BUS_BLUETOOTH, ven, prod)
#define HID_DEVICE(b, g, ven, prod) \
.bus = (b), .group = (g), .vendor = (ven), .product = (prod)
#define HID_USB_DEVICE(ven, prod) \
.bus = BUS_USB, .vendor = (ven), .product = (prod)
#define HID_BLUETOOTH_DEVICE(ven, prod) \
.bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod)
#define HID_REPORT_ID(rep) \
.report_type = (rep)
......@@ -735,6 +743,7 @@ void hid_output_report(struct hid_report *report, __u8 *data);
struct hid_device *hid_allocate_device(void);
struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
int hid_open_report(struct hid_device *device);
int hid_check_keys_pressed(struct hid_device *hid);
int hid_connect(struct hid_device *hid, unsigned int connect_mask);
void hid_disconnect(struct hid_device *hid);
......@@ -805,16 +814,7 @@ static inline void hid_map_usage_clear(struct hid_input *hidinput,
*/
static inline int __must_check hid_parse(struct hid_device *hdev)
{
int ret;
if (hdev->status & HID_STAT_PARSED)
return 0;
ret = hdev->ll_driver->parse(hdev);
if (!ret)
hdev->status |= HID_STAT_PARSED;
return ret;
return hid_open_report(hdev);
}
/**
......@@ -896,7 +896,7 @@ static inline int hid_hw_power(struct hid_device *hdev, int level)
return hdev->ll_driver->power ? hdev->ll_driver->power(hdev, level) : 0;
}
void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
int interrupt);
extern int hid_generic_init(void);
......
......@@ -76,13 +76,13 @@ struct hidraw_list {
#ifdef CONFIG_HIDRAW
int hidraw_init(void);
void hidraw_exit(void);
void hidraw_report_event(struct hid_device *, u8 *, int);
int hidraw_report_event(struct hid_device *, u8 *, int);
int hidraw_connect(struct hid_device *);
void hidraw_disconnect(struct hid_device *);
#else
static inline int hidraw_init(void) { return 0; }
static inline void hidraw_exit(void) { }
static inline void hidraw_report_event(struct hid_device *hid, u8 *data, int len) { }
static inline int hidraw_report_event(struct hid_device *hid, u8 *data, int len) { return 0; }
static inline int hidraw_connect(struct hid_device *hid) { return -1; }
static inline void hidraw_disconnect(struct hid_device *hid) { }
#endif
......
......@@ -132,10 +132,12 @@ struct usb_device_id {
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200
#define HID_ANY_ID (~0)
#define HID_BUS_ANY 0xffff
#define HID_GROUP_ANY 0x0000
struct hid_device_id {
__u16 bus;
__u16 pad1;
__u16 group;
__u32 vendor;
__u32 product;
kernel_ulong_t driver_data
......
......@@ -1195,41 +1195,16 @@ int hidp_get_conninfo(struct hidp_conninfo *ci)
return err;
}
static const struct hid_device_id hidp_table[] = {
{ HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) },
{ }
};
static struct hid_driver hidp_driver = {
.name = "generic-bluetooth",
.id_table = hidp_table,
};
static int __init hidp_init(void)
{
int ret;
BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
ret = hid_register_driver(&hidp_driver);
if (ret)
goto err;
ret = hidp_init_sockets();
if (ret)
goto err_drv;
return 0;
err_drv:
hid_unregister_driver(&hidp_driver);
err:
return ret;
return hidp_init_sockets();
}
static void __exit hidp_exit(void)
{
hidp_cleanup_sockets();
hid_unregister_driver(&hidp_driver);
}
module_init(hidp_init);
......
......@@ -336,10 +336,13 @@ static int do_hid_entry(const char *filename,
struct hid_device_id *id, char *alias)
{
id->bus = TO_NATIVE(id->bus);
id->group = TO_NATIVE(id->group);
id->vendor = TO_NATIVE(id->vendor);
id->product = TO_NATIVE(id->product);
sprintf(alias, "hid:b%04X", id->bus);
sprintf(alias, "hid:");
ADD(alias, "b", id->bus != HID_BUS_ANY, id->bus);
ADD(alias, "g", id->group != HID_GROUP_ANY, id->group);
ADD(alias, "v", id->vendor != HID_ANY_ID, id->vendor);
ADD(alias, "p", id->product != HID_ANY_ID, id->product);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment