mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-31 04:01:48 -04:00
Merge branches 'roccat', 'upstream' and 'wiimote' into for-linus
This commit is contained in:
commit
a91f423e59
3523 changed files with 139226 additions and 58439 deletions
|
|
@ -172,6 +172,20 @@ config HID_EZKEY
|
|||
---help---
|
||||
Support for Ezkey BTC 8193 keyboard.
|
||||
|
||||
config HID_HOLTEK
|
||||
tristate "Holtek On Line Grip based game controller support"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Say Y here if you have a Holtek On Line Grip based game controller.
|
||||
|
||||
config HOLTEK_FF
|
||||
bool "Holtek On Line Grip force feedback support"
|
||||
depends on HID_HOLTEK
|
||||
select INPUT_FF_MEMLESS
|
||||
---help---
|
||||
Say Y here if you have a Holtek On Line Grip based game controller
|
||||
and want to have force feedback support for it.
|
||||
|
||||
config HID_KEYTOUCH
|
||||
tristate "Keytouch HID devices"
|
||||
depends on USB_HID
|
||||
|
|
@ -305,6 +319,7 @@ config HID_MULTITOUCH
|
|||
- 3M PCT touch screens
|
||||
- ActionStar dual touch panels
|
||||
- Cando dual touch panels
|
||||
- Chunghwa panels
|
||||
- CVTouch panels
|
||||
- Cypress TrueTouch panels
|
||||
- Elo TouchSystems IntelliTouch Plus panels
|
||||
|
|
@ -321,6 +336,7 @@ config HID_MULTITOUCH
|
|||
- Stantum multitouch panels
|
||||
- Touch International Panels
|
||||
- Unitec Panels
|
||||
- XAT optical touch panels
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
|
|
@ -491,6 +507,12 @@ config HID_SONY
|
|||
---help---
|
||||
Support for Sony PS3 controller.
|
||||
|
||||
config HID_SPEEDLINK
|
||||
tristate "Speedlink VAD Cezanne mouse support"
|
||||
depends on USB_HID
|
||||
---help---
|
||||
Support for Speedlink Vicious and Divine Cezanne mouse.
|
||||
|
||||
config HID_SUNPLUS
|
||||
tristate "Sunplus wireless desktop"
|
||||
depends on USB_HID
|
||||
|
|
@ -564,6 +586,12 @@ config HID_WACOM_POWER_SUPPLY
|
|||
Say Y here if you want to enable power supply status monitoring for
|
||||
Wacom Bluetooth devices.
|
||||
|
||||
config HID_WIIMOTE
|
||||
tristate "Nintendo Wii Remote support"
|
||||
depends on BT_HIDP
|
||||
---help---
|
||||
Support for the Nintendo Wii Remote bluetooth device.
|
||||
|
||||
config HID_ZEROPLUS
|
||||
tristate "Zeroplus based game controller support"
|
||||
depends on USB_HID
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ 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_HOLTEK) += hid-holtekff.o
|
||||
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
|
||||
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
|
||||
obj-$(CONFIG_HID_KYE) += hid-kye.o
|
||||
|
|
@ -63,6 +64,7 @@ obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o
|
|||
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
|
||||
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
|
||||
obj-$(CONFIG_HID_SONY) += hid-sony.o
|
||||
obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o
|
||||
obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
|
||||
obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
|
||||
obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
|
||||
|
|
@ -73,6 +75,7 @@ obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
|
|||
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
|
||||
obj-$(CONFIG_HID_WACOM) += hid-wacom.o
|
||||
obj-$(CONFIG_HID_WALTOP) += hid-waltop.o
|
||||
obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o
|
||||
|
||||
obj-$(CONFIG_USB_HID) += usbhid/
|
||||
obj-$(CONFIG_USB_MOUSE) += usbhid/
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
error = hid_hw_open(hdev);
|
||||
if (error) {
|
||||
dev_err(&hdev->dev, "hw open failed\n");
|
||||
hid_hw_stop(hdev);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1359,6 +1359,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT, USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CVTOUCH, USB_DEVICE_ID_CVTOUCH_SCREEN) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
|
||||
|
|
@ -1387,6 +1388,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, USB_DEVICE_ID_ILITEK_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) },
|
||||
|
|
@ -1422,12 +1424,15 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, USB_DEVICE_ID_CRYSTALTOUCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, USB_DEVICE_ID_CRYSTALTOUCH_DUAL) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
|
||||
|
|
@ -1489,6 +1494,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 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_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0709) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
|
||||
|
|
@ -1497,11 +1503,14 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
|
||||
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -1769,7 +1778,6 @@ static const struct hid_device_id hid_ignore_list[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_JESS_YUREX) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) },
|
||||
|
|
@ -1910,6 +1918,11 @@ static bool hid_ignore(struct hid_device *hdev)
|
|||
hdev->product <= USB_DEVICE_ID_HANWANG_TABLET_LAST)
|
||||
return true;
|
||||
break;
|
||||
case USB_VENDOR_ID_JESS:
|
||||
if (hdev->product == USB_DEVICE_ID_JESS_YUREX &&
|
||||
hdev->type == HID_TYPE_USBNONE)
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdev->type == HID_TYPE_USBMOUSE &&
|
||||
|
|
|
|||
|
|
@ -126,7 +126,12 @@ static int ems_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
goto err;
|
||||
}
|
||||
|
||||
emsff_init(hdev);
|
||||
ret = emsff_init(hdev);
|
||||
if (ret) {
|
||||
dev_err(&hdev->dev, "force feedback init failed\n");
|
||||
hid_hw_stop(hdev);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
|
|
|
|||
240
drivers/hid/hid-holtekff.c
Normal file
240
drivers/hid/hid-holtekff.c
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Force feedback support for Holtek On Line Grip based gamepads
|
||||
*
|
||||
* These include at least a Brazilian "Clone Joypad Super Power Fire"
|
||||
* which uses vendor ID 0x1241 and identifies as "HOLTEK On Line Grip".
|
||||
*
|
||||
* Copyright (c) 2011 Anssi Hannula <anssi.hannula@iki.fi>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/hid.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#ifdef CONFIG_HOLTEK_FF
|
||||
#include "usbhid/usbhid.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Anssi Hannula <[email protected]>");
|
||||
MODULE_DESCRIPTION("Force feedback support for Holtek On Line Grip based devices");
|
||||
|
||||
/*
|
||||
* These commands and parameters are currently known:
|
||||
*
|
||||
* byte 0: command id:
|
||||
* 01 set effect parameters
|
||||
* 02 play specified effect
|
||||
* 03 stop specified effect
|
||||
* 04 stop all effects
|
||||
* 06 stop all effects
|
||||
* (the difference between 04 and 06 isn't known; win driver
|
||||
* sends 06,04 on application init, and 06 otherwise)
|
||||
*
|
||||
* Commands 01 and 02 need to be sent as pairs, i.e. you need to send 01
|
||||
* before each 02.
|
||||
*
|
||||
* The rest of the bytes are parameters. Command 01 takes all of them, and
|
||||
* commands 02,03 take only the effect id.
|
||||
*
|
||||
* byte 1:
|
||||
* bits 0-3: effect id:
|
||||
* 1: very strong rumble
|
||||
* 2: periodic rumble, short intervals
|
||||
* 3: very strong rumble
|
||||
* 4: periodic rumble, long intervals
|
||||
* 5: weak periodic rumble, long intervals
|
||||
* 6: weak periodic rumble, short intervals
|
||||
* 7: periodic rumble, short intervals
|
||||
* 8: strong periodic rumble, short intervals
|
||||
* 9: very strong rumble
|
||||
* a: causes an error
|
||||
* b: very strong periodic rumble, very short intervals
|
||||
* c-f: nothing
|
||||
* bit 6: right (weak) motor enabled
|
||||
* bit 7: left (strong) motor enabled
|
||||
*
|
||||
* bytes 2-3: time in milliseconds, big-endian
|
||||
* bytes 5-6: unknown (win driver seems to use at least 10e0 with effect 1
|
||||
* and 0014 with effect 6)
|
||||
* byte 7:
|
||||
* bits 0-3: effect magnitude
|
||||
*/
|
||||
|
||||
#define HOLTEKFF_MSG_LENGTH 7
|
||||
|
||||
static const u8 start_effect_1[] = { 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
static const u8 stop_all4[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
static const u8 stop_all6[] = { 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
struct holtekff_device {
|
||||
struct hid_field *field;
|
||||
};
|
||||
|
||||
static void holtekff_send(struct holtekff_device *holtekff,
|
||||
struct hid_device *hid,
|
||||
const u8 data[HOLTEKFF_MSG_LENGTH])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HOLTEKFF_MSG_LENGTH; i++) {
|
||||
holtekff->field->value[i] = data[i];
|
||||
}
|
||||
|
||||
dbg_hid("sending %02x %02x %02x %02x %02x %02x %02x\n", data[0],
|
||||
data[1], data[2], data[3], data[4], data[5], data[6]);
|
||||
|
||||
usbhid_submit_report(hid, holtekff->field->report, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
static int holtekff_play(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = input_get_drvdata(dev);
|
||||
struct holtekff_device *holtekff = data;
|
||||
int left, right;
|
||||
/* effect type 1, length 65535 msec */
|
||||
u8 buf[HOLTEKFF_MSG_LENGTH] =
|
||||
{ 0x01, 0x01, 0xff, 0xff, 0x10, 0xe0, 0x00 };
|
||||
|
||||
left = effect->u.rumble.strong_magnitude;
|
||||
right = effect->u.rumble.weak_magnitude;
|
||||
dbg_hid("called with 0x%04x 0x%04x\n", left, right);
|
||||
|
||||
if (!left && !right) {
|
||||
holtekff_send(holtekff, hid, stop_all6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (left)
|
||||
buf[1] |= 0x80;
|
||||
if (right)
|
||||
buf[1] |= 0x40;
|
||||
|
||||
/* The device takes a single magnitude, so we just sum them up. */
|
||||
buf[6] = min(0xf, (left >> 12) + (right >> 12));
|
||||
|
||||
holtekff_send(holtekff, hid, buf);
|
||||
holtekff_send(holtekff, hid, start_effect_1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int holtekff_init(struct hid_device *hid)
|
||||
{
|
||||
struct holtekff_device *holtekff;
|
||||
struct hid_report *report;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
||||
struct hid_input, list);
|
||||
struct list_head *report_list =
|
||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct input_dev *dev = hidinput->input;
|
||||
int error;
|
||||
|
||||
if (list_empty(report_list)) {
|
||||
hid_err(hid, "no output report found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
report = list_entry(report_list->next, struct hid_report, list);
|
||||
|
||||
if (report->maxfield < 1 || report->field[0]->report_count != 7) {
|
||||
hid_err(hid, "unexpected output report layout\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
holtekff = kzalloc(sizeof(*holtekff), GFP_KERNEL);
|
||||
if (!holtekff)
|
||||
return -ENOMEM;
|
||||
|
||||
set_bit(FF_RUMBLE, dev->ffbit);
|
||||
|
||||
holtekff->field = report->field[0];
|
||||
|
||||
/* initialize the same way as win driver does */
|
||||
holtekff_send(holtekff, hid, stop_all4);
|
||||
holtekff_send(holtekff, hid, stop_all6);
|
||||
|
||||
error = input_ff_create_memless(dev, holtekff, holtekff_play);
|
||||
if (error) {
|
||||
kfree(holtekff);
|
||||
return error;
|
||||
}
|
||||
|
||||
hid_info(hid, "Force feedback for Holtek On Line Grip based devices by Anssi Hannula <[email protected]>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int holtekff_init(struct hid_device *hid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int holtek_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "parse failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
holtekff_init(hdev);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct hid_device_id holtek_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, holtek_devices);
|
||||
|
||||
static struct hid_driver holtek_driver = {
|
||||
.name = "holtek",
|
||||
.id_table = holtek_devices,
|
||||
.probe = holtek_probe,
|
||||
};
|
||||
|
||||
static int __init holtek_init(void)
|
||||
{
|
||||
return hid_register_driver(&holtek_driver);
|
||||
}
|
||||
|
||||
static void __exit holtek_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&holtek_driver);
|
||||
}
|
||||
|
||||
module_init(holtek_init);
|
||||
module_exit(holtek_exit);
|
||||
|
||||
|
|
@ -173,6 +173,9 @@
|
|||
#define USB_DEVICE_ID_CHICONY_MULTI_TOUCH 0xb19d
|
||||
#define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618
|
||||
|
||||
#define USB_VENDOR_ID_CHUNGHWAT 0x2247
|
||||
#define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH 0x0001
|
||||
|
||||
#define USB_VENDOR_ID_CIDC 0x1677
|
||||
|
||||
#define USB_VENDOR_ID_CMEDIA 0x0d8c
|
||||
|
|
@ -347,6 +350,9 @@
|
|||
#define USB_VENDOR_ID_ILITEK 0x222a
|
||||
#define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001
|
||||
|
||||
#define USB_VENDOR_ID_HOLTEK 0x1241
|
||||
#define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015
|
||||
|
||||
#define USB_VENDOR_ID_IMATION 0x0718
|
||||
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
|
||||
|
||||
|
|
@ -446,6 +452,7 @@
|
|||
|
||||
#define USB_VENDOR_ID_LUMIO 0x202e
|
||||
#define USB_DEVICE_ID_CRYSTALTOUCH 0x0006
|
||||
#define USB_DEVICE_ID_CRYSTALTOUCH_DUAL 0x0007
|
||||
|
||||
#define USB_VENDOR_ID_MCC 0x09db
|
||||
#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076
|
||||
|
|
@ -468,6 +475,8 @@
|
|||
#define USB_DEVICE_ID_MS_LK6K 0x00f9
|
||||
#define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701
|
||||
#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713
|
||||
#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730
|
||||
#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c
|
||||
|
||||
#define USB_VENDOR_ID_MOJO 0x8282
|
||||
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
|
||||
|
|
@ -491,6 +500,9 @@
|
|||
#define USB_VENDOR_ID_NEXTWINDOW 0x1926
|
||||
#define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN 0x0003
|
||||
|
||||
#define USB_VENDOR_ID_NINTENDO 0x057e
|
||||
#define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306
|
||||
|
||||
#define USB_VENDOR_ID_NTRIG 0x1b96
|
||||
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
|
||||
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003
|
||||
|
|
@ -622,9 +634,11 @@
|
|||
#define USB_VENDOR_ID_UCLOGIC 0x5543
|
||||
#define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042
|
||||
#define USB_DEVICE_ID_UCLOGIC_TABLET_KNA5 0x6001
|
||||
#define USB_DEVICE_ID_UCLOGIC_TABLET_TWA60 0x0064
|
||||
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U 0x0003
|
||||
#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_VENDOR_ID_UNITEC 0x227d
|
||||
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
|
||||
|
|
@ -658,6 +672,12 @@
|
|||
#define USB_VENDOR_ID_WISEGROUP_LTD2 0x6677
|
||||
#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
|
||||
|
||||
#define USB_VENDOR_ID_X_TENSIONS 0x1ae7
|
||||
#define USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE 0x9001
|
||||
|
||||
#define USB_VENDOR_ID_XAT 0x2505
|
||||
#define USB_DEVICE_ID_XAT_CSR 0x0220
|
||||
|
||||
#define USB_VENDOR_ID_YEALINK 0x6993
|
||||
#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,66 @@
|
|||
#define LG_FF3 0x1000
|
||||
#define LG_FF4 0x2000
|
||||
|
||||
/* Size of the original descriptor of the Driving Force Pro wheel */
|
||||
#define DFP_RDESC_ORIG_SIZE 97
|
||||
|
||||
/* Fixed report descriptor for Logitech Driving Force Pro wheel controller
|
||||
*
|
||||
* The original descriptor hides the separate throttle and brake axes in
|
||||
* a custom vendor usage page, providing only a combined value as
|
||||
* GenericDesktop.Y.
|
||||
* This descriptor removes the combined Y axis and instead reports
|
||||
* separate throttle (Y) and brake (RZ).
|
||||
*/
|
||||
static __u8 dfp_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x75, 0x0E, /* Report Size (14), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x26, 0xFF, 0x3F, /* Logical Maximum (16383), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x46, 0xFF, 0x3F, /* Physical Maximum (16383), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x0E, /* Report Count (14), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x45, 0x01, /* Physical Maximum (1), */
|
||||
0x05, 0x09, /* Usage Page (Button), */
|
||||
0x19, 0x01, /* Usage Minimum (01h), */
|
||||
0x29, 0x0E, /* Usage Maximum (0Eh), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x75, 0x04, /* Report Size (4), */
|
||||
0x25, 0x07, /* Logical Maximum (7), */
|
||||
0x46, 0x3B, 0x01, /* Physical Maximum (315), */
|
||||
0x65, 0x14, /* Unit (Degrees), */
|
||||
0x09, 0x39, /* Usage (Hat Switch), */
|
||||
0x81, 0x42, /* Input (Variable, Nullstate), */
|
||||
0x65, 0x00, /* Unit, */
|
||||
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
|
||||
0x46, 0xFF, 0x00, /* Physical Maximum (255), */
|
||||
0x75, 0x08, /* Report Size (8), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x35, /* Usage (Rz), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0xC0, /* End Collection, */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x09, 0x02, /* Usage (02h), */
|
||||
0x95, 0x07, /* Report Count (7), */
|
||||
0x91, 0x02, /* Output (Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Certain Logitech keyboards send in report #3 keys which are far
|
||||
* above the logical maximum described in descriptor. This extends
|
||||
|
|
@ -74,6 +134,18 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
rdesc[47] = 0x95;
|
||||
rdesc[48] = 0x0B;
|
||||
}
|
||||
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
|
||||
if (*rsize == DFP_RDESC_ORIG_SIZE) {
|
||||
hid_info(hdev,
|
||||
"fixing up Logitech Driving Force Pro report descriptor\n");
|
||||
rdesc = dfp_rdesc_fixed;
|
||||
*rsize = sizeof(dfp_rdesc_fixed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
|
|
@ -380,7 +452,7 @@ static const struct hid_device_id lg_devices[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
|
||||
.driver_data = LG_FF },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
|
||||
.driver_data = LG_FF },
|
||||
.driver_data = LG_NOGET | 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 ),
|
||||
|
|
|
|||
|
|
@ -501,17 +501,9 @@ static int magicmouse_probe(struct hid_device *hdev,
|
|||
}
|
||||
report->size = 6;
|
||||
|
||||
/*
|
||||
* The device reponds with 'invalid report id' when feature
|
||||
* report switching it into multitouch mode is sent to it.
|
||||
*
|
||||
* This results in -EIO from the _raw low-level transport callback,
|
||||
* but there seems to be no other way of switching the mode.
|
||||
* Thus the super-ugly hacky success check below.
|
||||
*/
|
||||
ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature),
|
||||
HID_FEATURE_REPORT);
|
||||
if (ret != -EIO) {
|
||||
if (ret != sizeof(feature)) {
|
||||
hid_err(hdev, "unable to request touch data (%d)\n", ret);
|
||||
goto err_stop_hw;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,12 @@
|
|||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define MS_HIDINPUT 0x01
|
||||
#define MS_ERGONOMY 0x02
|
||||
#define MS_PRESENTER 0x04
|
||||
#define MS_RDESC 0x08
|
||||
#define MS_NOGET 0x10
|
||||
#define MS_HIDINPUT 0x01
|
||||
#define MS_ERGONOMY 0x02
|
||||
#define MS_PRESENTER 0x04
|
||||
#define MS_RDESC 0x08
|
||||
#define MS_NOGET 0x10
|
||||
#define MS_DUPLICATE_USAGES 0x20
|
||||
|
||||
/*
|
||||
* Microsoft Wireless Desktop Receiver (Model 1028) has
|
||||
|
|
@ -109,6 +110,18 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ms_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
||||
|
||||
if (quirks & MS_DUPLICATE_USAGES)
|
||||
clear_bit(usage->code, *bit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
|
|
@ -179,8 +192,12 @@ static const struct hid_device_id ms_devices[] = {
|
|||
.driver_data = MS_ERGONOMY | MS_RDESC },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB),
|
||||
.driver_data = MS_PRESENTER },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K),
|
||||
.driver_data = MS_ERGONOMY },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0),
|
||||
.driver_data = MS_NOGET },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
|
||||
.driver_data = MS_DUPLICATE_USAGES },
|
||||
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
|
||||
.driver_data = MS_PRESENTER },
|
||||
|
|
@ -193,6 +210,7 @@ static struct hid_driver ms_driver = {
|
|||
.id_table = ms_devices,
|
||||
.report_fixup = ms_report_fixup,
|
||||
.input_mapping = ms_input_mapping,
|
||||
.input_mapped = ms_input_mapped,
|
||||
.event = ms_event,
|
||||
.probe = ms_probe,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ struct mt_device {
|
|||
struct mt_class *mtclass; /* our mt device class */
|
||||
unsigned last_field_index; /* last field index of the report */
|
||||
unsigned last_slot_field; /* the last field of a slot */
|
||||
int last_mt_collection; /* last known mt-related collection */
|
||||
__s8 inputmode; /* InputMode HID feature, -1 if non-existent */
|
||||
__u8 num_received; /* how many contacts we received */
|
||||
__u8 num_expected; /* expected last contact index */
|
||||
|
|
@ -225,8 +226,10 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
cls->sn_move);
|
||||
/* touchscreen emulation */
|
||||
set_abs(hi->input, ABS_X, field, cls->sn_move);
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
if (td->last_mt_collection == usage->collection_index) {
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
}
|
||||
return 1;
|
||||
case HID_GD_Y:
|
||||
if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
|
||||
|
|
@ -237,8 +240,10 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
cls->sn_move);
|
||||
/* touchscreen emulation */
|
||||
set_abs(hi->input, ABS_Y, field, cls->sn_move);
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
if (td->last_mt_collection == usage->collection_index) {
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -246,31 +251,42 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
case HID_UP_DIGITIZER:
|
||||
switch (usage->hid) {
|
||||
case HID_DG_INRANGE:
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
if (td->last_mt_collection == usage->collection_index) {
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
}
|
||||
return 1;
|
||||
case HID_DG_CONFIDENCE:
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
if (td->last_mt_collection == usage->collection_index) {
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
}
|
||||
return 1;
|
||||
case HID_DG_TIPSWITCH:
|
||||
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
|
||||
input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
if (td->last_mt_collection == usage->collection_index) {
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
}
|
||||
return 1;
|
||||
case HID_DG_CONTACTID:
|
||||
if (!td->maxcontacts)
|
||||
td->maxcontacts = MT_DEFAULT_MAXCONTACT;
|
||||
input_mt_init_slots(hi->input, td->maxcontacts);
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
td->last_mt_collection = usage->collection_index;
|
||||
return 1;
|
||||
case HID_DG_WIDTH:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
EV_ABS, ABS_MT_TOUCH_MAJOR);
|
||||
set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
|
||||
cls->sn_width);
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
if (td->last_mt_collection == usage->collection_index) {
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
}
|
||||
return 1;
|
||||
case HID_DG_HEIGHT:
|
||||
hid_map_usage(hi, usage, bit, max,
|
||||
|
|
@ -279,8 +295,10 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
cls->sn_height);
|
||||
input_set_abs_params(hi->input,
|
||||
ABS_MT_ORIENTATION, 0, 1, 0, 0);
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
if (td->last_mt_collection == usage->collection_index) {
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
}
|
||||
return 1;
|
||||
case HID_DG_TIPPRESSURE:
|
||||
if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
|
||||
|
|
@ -292,16 +310,20 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
/* touchscreen emulation */
|
||||
set_abs(hi->input, ABS_PRESSURE, field,
|
||||
cls->sn_pressure);
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
if (td->last_mt_collection == usage->collection_index) {
|
||||
td->last_slot_field = usage->hid;
|
||||
td->last_field_index = field->index;
|
||||
}
|
||||
return 1;
|
||||
case HID_DG_CONTACTCOUNT:
|
||||
td->last_field_index = field->index;
|
||||
if (td->last_mt_collection == usage->collection_index)
|
||||
td->last_field_index = field->index;
|
||||
return 1;
|
||||
case HID_DG_CONTACTMAX:
|
||||
/* we don't set td->last_slot_field as contactcount and
|
||||
* contact max are global to the report */
|
||||
td->last_field_index = field->index;
|
||||
if (td->last_mt_collection == usage->collection_index)
|
||||
td->last_field_index = field->index;
|
||||
return -1;
|
||||
}
|
||||
/* let hid-input decide for the others */
|
||||
|
|
@ -516,6 +538,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
}
|
||||
td->mtclass = mtclass;
|
||||
td->inputmode = -1;
|
||||
td->last_mt_collection = -1;
|
||||
hid_set_drvdata(hdev, td);
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
|
|
@ -526,9 +549,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
if (ret)
|
||||
goto fail;
|
||||
|
||||
if (!td->maxcontacts)
|
||||
td->maxcontacts = MT_DEFAULT_MAXCONTACT;
|
||||
|
||||
td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot),
|
||||
GFP_KERNEL);
|
||||
if (!td->slots) {
|
||||
|
|
@ -593,6 +613,11 @@ static const struct hid_device_id mt_devices[] = {
|
|||
HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
|
||||
USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) },
|
||||
|
||||
/* Chunghwa Telecom touch panels */
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT,
|
||||
USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) },
|
||||
|
||||
/* CVTouch panels */
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_CVTOUCH,
|
||||
|
|
@ -651,6 +676,9 @@ static const struct hid_device_id mt_devices[] = {
|
|||
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_LUMIO,
|
||||
USB_DEVICE_ID_CRYSTALTOUCH) },
|
||||
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_LUMIO,
|
||||
USB_DEVICE_ID_CRYSTALTOUCH_DUAL) },
|
||||
|
||||
/* MosArt panels */
|
||||
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
|
||||
|
|
@ -681,10 +709,10 @@ static const struct hid_device_id mt_devices[] = {
|
|||
HID_USB_DEVICE(USB_VENDOR_ID_STANTUM,
|
||||
USB_DEVICE_ID_MTP)},
|
||||
{ .driver_data = MT_CLS_CONFIDENCE,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_STANTUM,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM,
|
||||
USB_DEVICE_ID_MTP_STM)},
|
||||
{ .driver_data = MT_CLS_CONFIDENCE,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_STANTUM,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX,
|
||||
USB_DEVICE_ID_MTP_SITRONIX)},
|
||||
|
||||
/* Touch International panels */
|
||||
|
|
@ -699,6 +727,10 @@ static const struct hid_device_id mt_devices[] = {
|
|||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_UNITEC,
|
||||
USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) },
|
||||
/* XAT */
|
||||
{ .driver_data = MT_CLS_DEFAULT,
|
||||
HID_USB_DEVICE(USB_VENDOR_ID_XAT,
|
||||
USB_DEVICE_ID_XAT_CSR) },
|
||||
|
||||
{ }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@ struct pk_device {
|
|||
struct pcmidi_snd *pm; /* pcmidi device context */
|
||||
};
|
||||
|
||||
struct pcmidi_snd;
|
||||
|
||||
struct pcmidi_sustain {
|
||||
unsigned long in_use;
|
||||
struct pcmidi_snd *pm;
|
||||
|
|
@ -242,7 +240,7 @@ drop_note:
|
|||
return;
|
||||
}
|
||||
|
||||
void pcmidi_sustained_note_release(unsigned long data)
|
||||
static void pcmidi_sustained_note_release(unsigned long data)
|
||||
{
|
||||
struct pcmidi_sustain *pms = (struct pcmidi_sustain *)data;
|
||||
|
||||
|
|
@ -250,7 +248,7 @@ void pcmidi_sustained_note_release(unsigned long data)
|
|||
pms->in_use = 0;
|
||||
}
|
||||
|
||||
void init_sustain_timers(struct pcmidi_snd *pm)
|
||||
static void init_sustain_timers(struct pcmidi_snd *pm)
|
||||
{
|
||||
struct pcmidi_sustain *pms;
|
||||
unsigned i;
|
||||
|
|
@ -264,7 +262,7 @@ void init_sustain_timers(struct pcmidi_snd *pm)
|
|||
}
|
||||
}
|
||||
|
||||
void stop_sustain_timers(struct pcmidi_snd *pm)
|
||||
static void stop_sustain_timers(struct pcmidi_snd *pm)
|
||||
{
|
||||
struct pcmidi_sustain *pms;
|
||||
unsigned i;
|
||||
|
|
@ -499,7 +497,7 @@ static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int pcmidi_handle_report(
|
||||
static int pcmidi_handle_report(
|
||||
struct pcmidi_snd *pm, unsigned report_id, u8 *data, int size)
|
||||
{
|
||||
int ret = 0;
|
||||
|
|
@ -518,7 +516,8 @@ int pcmidi_handle_report(
|
|||
return ret;
|
||||
}
|
||||
|
||||
void pcmidi_setup_extra_keys(struct pcmidi_snd *pm, struct input_dev *input)
|
||||
static void pcmidi_setup_extra_keys(
|
||||
struct pcmidi_snd *pm, struct input_dev *input)
|
||||
{
|
||||
/* reassigned functionality for N/A keys
|
||||
MY PICTURES => KEY_WORDPROCESSOR
|
||||
|
|
@ -602,7 +601,7 @@ static struct snd_rawmidi_ops pcmidi_in_ops = {
|
|||
.trigger = pcmidi_in_trigger
|
||||
};
|
||||
|
||||
int pcmidi_snd_initialise(struct pcmidi_snd *pm)
|
||||
static int pcmidi_snd_initialise(struct pcmidi_snd *pm)
|
||||
{
|
||||
static int dev;
|
||||
struct snd_card *card;
|
||||
|
|
@ -720,7 +719,7 @@ fail:
|
|||
return err;
|
||||
}
|
||||
|
||||
int pcmidi_snd_terminate(struct pcmidi_snd *pm)
|
||||
static int pcmidi_snd_terminate(struct pcmidi_snd *pm)
|
||||
{
|
||||
if (pm->card) {
|
||||
stop_sustain_timers(pm);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,12 @@
|
|||
#define SIXAXIS_CONTROLLER_USB (1 << 1)
|
||||
#define SIXAXIS_CONTROLLER_BT (1 << 2)
|
||||
|
||||
static const u8 sixaxis_rdesc_fixup[] = {
|
||||
0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C,
|
||||
0x81, 0x01, 0x75, 0x10, 0x95, 0x04, 0x26, 0xFF,
|
||||
0x03, 0x46, 0xFF, 0x03, 0x09, 0x01, 0x81, 0x02
|
||||
};
|
||||
|
||||
struct sony_sc {
|
||||
unsigned long quirks;
|
||||
};
|
||||
|
|
@ -43,9 +49,37 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
hid_info(hdev, "Fixing up Sony Vaio VGX report descriptor\n");
|
||||
rdesc[55] = 0x06;
|
||||
}
|
||||
|
||||
/* The HID descriptor exposed over BT has a trailing zero byte */
|
||||
if ((((sc->quirks & SIXAXIS_CONTROLLER_USB) && *rsize == 148) ||
|
||||
((sc->quirks & SIXAXIS_CONTROLLER_BT) && *rsize == 149)) &&
|
||||
rdesc[83] == 0x75) {
|
||||
hid_info(hdev, "Fixing up Sony Sixaxis report descriptor\n");
|
||||
memcpy((void *)&rdesc[83], (void *)&sixaxis_rdesc_fixup,
|
||||
sizeof(sixaxis_rdesc_fixup));
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
__u8 *rd, int size)
|
||||
{
|
||||
struct sony_sc *sc = hid_get_drvdata(hdev);
|
||||
|
||||
/* Sixaxis HID report has acclerometers/gyro with MSByte first, this
|
||||
* has to be BYTE_SWAPPED before passing up to joystick interface
|
||||
*/
|
||||
if ((sc->quirks & (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)) &&
|
||||
rd[0] == 0x01 && size == 49) {
|
||||
swap(rd[41], rd[42]);
|
||||
swap(rd[43], rd[44]);
|
||||
swap(rd[45], rd[46]);
|
||||
swap(rd[47], rd[48]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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()
|
||||
|
|
@ -194,6 +228,7 @@ static struct hid_driver sony_driver = {
|
|||
.probe = sony_probe,
|
||||
.remove = sony_remove,
|
||||
.report_fixup = sony_report_fixup,
|
||||
.raw_event = sony_raw_event
|
||||
};
|
||||
|
||||
static int __init sony_init(void)
|
||||
|
|
|
|||
89
drivers/hid/hid-speedlink.c
Normal file
89
drivers/hid/hid-speedlink.c
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* HID driver for Speedlink Vicious and Divine Cezanne (USB mouse).
|
||||
* Fixes "jumpy" cursor and removes nonexistent keyboard LEDS from
|
||||
* the HID descriptor.
|
||||
*
|
||||
* Copyright (c) 2011 Stefan Kriwanek <mail@stefankriwanek.de>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
#include "usbhid/usbhid.h"
|
||||
|
||||
static const struct hid_device_id speedlink_devices[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE)},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int speedlink_input_mapping(struct hid_device *hdev,
|
||||
struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage,
|
||||
unsigned long **bit, int *max)
|
||||
{
|
||||
/*
|
||||
* The Cezanne mouse has a second "keyboard" USB endpoint for it is
|
||||
* able to map keyboard events to the button presses.
|
||||
* It sends a standard keyboard report descriptor, though, whose
|
||||
* LEDs we ignore.
|
||||
*/
|
||||
switch (usage->hid & HID_USAGE_PAGE) {
|
||||
case HID_UP_LED:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int speedlink_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
/* No other conditions due to usage_table. */
|
||||
/* Fix "jumpy" cursor (invalid events sent by device). */
|
||||
if (value == 256)
|
||||
return 1;
|
||||
/* Drop useless distance 0 events (on button clicks etc.) as well */
|
||||
if (value == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(hid, speedlink_devices);
|
||||
|
||||
static const struct hid_usage_id speedlink_grabbed_usages[] = {
|
||||
{ HID_GD_X, EV_REL, 0 },
|
||||
{ HID_GD_Y, EV_REL, 1 },
|
||||
{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
|
||||
};
|
||||
|
||||
static struct hid_driver speedlink_driver = {
|
||||
.name = "speedlink",
|
||||
.id_table = speedlink_devices,
|
||||
.usage_table = speedlink_grabbed_usages,
|
||||
.input_mapping = speedlink_input_mapping,
|
||||
.event = speedlink_event,
|
||||
};
|
||||
|
||||
static int __init speedlink_init(void)
|
||||
{
|
||||
return hid_register_driver(&speedlink_driver);
|
||||
}
|
||||
|
||||
static void __exit speedlink_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&speedlink_driver);
|
||||
}
|
||||
|
||||
module_init(speedlink_init);
|
||||
module_exit(speedlink_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -342,6 +342,193 @@ static __u8 wp8060u_rdesc_fixed[] = {
|
|||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
/*
|
||||
* Original WP1062 report descriptor.
|
||||
*
|
||||
* Only report ID 9 is actually used.
|
||||
*
|
||||
* Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
* Usage (Pen), ; Pen (02h, application collection)
|
||||
* Collection (Application),
|
||||
* Report ID (7),
|
||||
* Usage (Stylus), ; Stylus (20h, logical collection)
|
||||
* Collection (Physical),
|
||||
* Usage (Tip Switch), ; Tip switch (42h, momentary control)
|
||||
* Usage (Barrel Switch), ; Barrel switch (44h, momentary control)
|
||||
* Usage (Eraser), ; Eraser (45h, momentary control)
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (1),
|
||||
* Report Size (1),
|
||||
* Report Count (3),
|
||||
* Input (Variable),
|
||||
* Report Count (3),
|
||||
* Input (Constant, Variable),
|
||||
* Usage (In Range), ; In range (32h, momentary control)
|
||||
* Report Count (1),
|
||||
* Input (Variable),
|
||||
* Report Count (1),
|
||||
* Input (Constant, Variable),
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (X), ; X (30h, dynamic value)
|
||||
* Report Size (16),
|
||||
* Report Count (1),
|
||||
* Push,
|
||||
* Unit Exponent (13),
|
||||
* Unit (Inch),
|
||||
* Physical Minimum (0),
|
||||
* Physical Maximum (10000),
|
||||
* Logical Maximum (20000),
|
||||
* Input (Variable),
|
||||
* Usage (Y), ; Y (31h, dynamic value)
|
||||
* Physical Maximum (6583),
|
||||
* Logical Maximum (13166),
|
||||
* Input (Variable),
|
||||
* Pop,
|
||||
* Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
* Usage (Tip Pressure), ; Tip pressure (30h, dynamic value)
|
||||
* Logical Maximum (1023),
|
||||
* Input (Variable),
|
||||
* Report Size (16),
|
||||
* End Collection,
|
||||
* End Collection,
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (Mouse), ; Mouse (02h, application collection)
|
||||
* Collection (Application),
|
||||
* Report ID (8),
|
||||
* Usage (Pointer), ; Pointer (01h, physical collection)
|
||||
* Collection (Physical),
|
||||
* Usage Page (Button), ; Button (09h)
|
||||
* Usage Minimum (01h),
|
||||
* Usage Maximum (03h),
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (1),
|
||||
* Report Count (3),
|
||||
* Report Size (1),
|
||||
* Input (Variable),
|
||||
* Report Count (5),
|
||||
* Input (Constant),
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (X), ; X (30h, dynamic value)
|
||||
* Usage (Y), ; Y (31h, dynamic value)
|
||||
* Usage (Wheel), ; Wheel (38h, dynamic value)
|
||||
* Usage (00h),
|
||||
* Logical Minimum (-127),
|
||||
* Logical Maximum (127),
|
||||
* Report Size (8),
|
||||
* Report Count (4),
|
||||
* Input (Variable, Relative),
|
||||
* End Collection,
|
||||
* End Collection,
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (Mouse), ; Mouse (02h, application collection)
|
||||
* Collection (Application),
|
||||
* Report ID (9),
|
||||
* Usage (Pointer), ; Pointer (01h, physical collection)
|
||||
* Collection (Physical),
|
||||
* Usage Page (Button), ; Button (09h)
|
||||
* Usage Minimum (01h),
|
||||
* Usage Maximum (03h),
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (1),
|
||||
* Report Count (3),
|
||||
* Report Size (1),
|
||||
* Input (Variable),
|
||||
* Report Count (4),
|
||||
* Input (Constant),
|
||||
* Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
* Usage (In Range), ; In range (32h, momentary control)
|
||||
* Report Count (1),
|
||||
* Input (Variable),
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (X), ; X (30h, dynamic value)
|
||||
* Report Size (16),
|
||||
* Report Count (1),
|
||||
* Push,
|
||||
* Unit Exponent (13),
|
||||
* Unit (Inch),
|
||||
* Physical Minimum (0),
|
||||
* Physical Maximum (10000),
|
||||
* Logical Maximum (20000),
|
||||
* Input (Variable),
|
||||
* Usage (Y), ; Y (31h, dynamic value)
|
||||
* Physical Maximum (6583),
|
||||
* Logical Maximum (13166),
|
||||
* Input (Variable),
|
||||
* Pop,
|
||||
* Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
* Usage (Tip Pressure), ; Tip pressure (30h, dynamic value)
|
||||
* Logical Maximum (1023),
|
||||
* Report Count (1),
|
||||
* Report Size (16),
|
||||
* Input (Variable),
|
||||
* End Collection,
|
||||
* End Collection,
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (00h),
|
||||
* Collection (Application),
|
||||
* Report ID (4),
|
||||
* Logical Minimum (0),
|
||||
* Logical Maximum (255),
|
||||
* Usage (00h),
|
||||
* Report Size (8),
|
||||
* Report Count (3),
|
||||
* Feature (Variable),
|
||||
* End Collection
|
||||
*/
|
||||
|
||||
/* Size of the original descriptor of WP1062 tablet */
|
||||
#define WP1062_RDESC_ORIG_SIZE 254
|
||||
|
||||
/*
|
||||
* Fixed WP1062 report descriptor.
|
||||
*
|
||||
* Removed unused reports, corrected second barrel button usage code, physical
|
||||
* units.
|
||||
*/
|
||||
static __u8 wp1062_rdesc_fixed[] = {
|
||||
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), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x09, 0x44, /* Usage (Barrel Switch), */
|
||||
0x09, 0x46, /* Usage (Tablet Pick), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x04, /* Report Count (4), */
|
||||
0x81, 0x01, /* Input (Constant), */
|
||||
0x09, 0x32, /* Usage (In Range), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x46, 0x10, 0x27, /* Physical Maximum (10000), */
|
||||
0x26, 0x20, 0x4E, /* Logical Maximum (20000), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x46, 0xB7, 0x19, /* Physical Maximum (6583), */
|
||||
0x26, 0x6E, 0x33, /* Logical Maximum (13166), */
|
||||
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 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Original PF1209 report descriptor.
|
||||
*
|
||||
|
|
@ -584,6 +771,12 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
|||
*rsize = sizeof(wp8060u_rdesc_fixed);
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062:
|
||||
if (*rsize == WP1062_RDESC_ORIG_SIZE) {
|
||||
rdesc = wp1062_rdesc_fixed;
|
||||
*rsize = sizeof(wp1062_rdesc_fixed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return rdesc;
|
||||
|
|
@ -598,6 +791,8 @@ static const struct hid_device_id uclogic_devices[] = {
|
|||
USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
|
||||
USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
|
||||
USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, uclogic_devices);
|
||||
|
|
|
|||
489
drivers/hid/hid-wiimote.c
Normal file
489
drivers/hid/hid-wiimote.c
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
/*
|
||||
* HID driver for Nintendo Wiimote devices
|
||||
* Copyright (c) 2011 David Herrmann
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/atomic.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define WIIMOTE_VERSION "0.1"
|
||||
#define WIIMOTE_NAME "Nintendo Wii Remote"
|
||||
#define WIIMOTE_BUFSIZE 32
|
||||
|
||||
struct wiimote_buf {
|
||||
__u8 data[HID_MAX_BUFFER_SIZE];
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct wiimote_state {
|
||||
spinlock_t lock;
|
||||
__u8 flags;
|
||||
};
|
||||
|
||||
struct wiimote_data {
|
||||
atomic_t ready;
|
||||
struct hid_device *hdev;
|
||||
struct input_dev *input;
|
||||
|
||||
spinlock_t qlock;
|
||||
__u8 head;
|
||||
__u8 tail;
|
||||
struct wiimote_buf outq[WIIMOTE_BUFSIZE];
|
||||
struct work_struct worker;
|
||||
|
||||
struct wiimote_state state;
|
||||
};
|
||||
|
||||
#define WIIPROTO_FLAG_LED1 0x01
|
||||
#define WIIPROTO_FLAG_LED2 0x02
|
||||
#define WIIPROTO_FLAG_LED3 0x04
|
||||
#define WIIPROTO_FLAG_LED4 0x08
|
||||
#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
|
||||
WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
|
||||
|
||||
enum wiiproto_reqs {
|
||||
WIIPROTO_REQ_LED = 0x11,
|
||||
WIIPROTO_REQ_DRM_K = 0x30,
|
||||
};
|
||||
|
||||
enum wiiproto_keys {
|
||||
WIIPROTO_KEY_LEFT,
|
||||
WIIPROTO_KEY_RIGHT,
|
||||
WIIPROTO_KEY_UP,
|
||||
WIIPROTO_KEY_DOWN,
|
||||
WIIPROTO_KEY_PLUS,
|
||||
WIIPROTO_KEY_MINUS,
|
||||
WIIPROTO_KEY_ONE,
|
||||
WIIPROTO_KEY_TWO,
|
||||
WIIPROTO_KEY_A,
|
||||
WIIPROTO_KEY_B,
|
||||
WIIPROTO_KEY_HOME,
|
||||
WIIPROTO_KEY_COUNT
|
||||
};
|
||||
|
||||
static __u16 wiiproto_keymap[] = {
|
||||
KEY_LEFT, /* WIIPROTO_KEY_LEFT */
|
||||
KEY_RIGHT, /* WIIPROTO_KEY_RIGHT */
|
||||
KEY_UP, /* WIIPROTO_KEY_UP */
|
||||
KEY_DOWN, /* WIIPROTO_KEY_DOWN */
|
||||
KEY_NEXT, /* WIIPROTO_KEY_PLUS */
|
||||
KEY_PREVIOUS, /* WIIPROTO_KEY_MINUS */
|
||||
BTN_1, /* WIIPROTO_KEY_ONE */
|
||||
BTN_2, /* WIIPROTO_KEY_TWO */
|
||||
BTN_A, /* WIIPROTO_KEY_A */
|
||||
BTN_B, /* WIIPROTO_KEY_B */
|
||||
BTN_MODE, /* WIIPROTO_KEY_HOME */
|
||||
};
|
||||
|
||||
#define dev_to_wii(pdev) hid_get_drvdata(container_of(pdev, struct hid_device, \
|
||||
dev))
|
||||
|
||||
static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
|
||||
size_t count)
|
||||
{
|
||||
__u8 *buf;
|
||||
ssize_t ret;
|
||||
|
||||
if (!hdev->hid_output_raw_report)
|
||||
return -ENODEV;
|
||||
|
||||
buf = kmemdup(buffer, count, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT);
|
||||
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wiimote_worker(struct work_struct *work)
|
||||
{
|
||||
struct wiimote_data *wdata = container_of(work, struct wiimote_data,
|
||||
worker);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wdata->qlock, flags);
|
||||
|
||||
while (wdata->head != wdata->tail) {
|
||||
spin_unlock_irqrestore(&wdata->qlock, flags);
|
||||
wiimote_hid_send(wdata->hdev, wdata->outq[wdata->tail].data,
|
||||
wdata->outq[wdata->tail].size);
|
||||
spin_lock_irqsave(&wdata->qlock, flags);
|
||||
|
||||
wdata->tail = (wdata->tail + 1) % WIIMOTE_BUFSIZE;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&wdata->qlock, flags);
|
||||
}
|
||||
|
||||
static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
|
||||
size_t count)
|
||||
{
|
||||
unsigned long flags;
|
||||
__u8 newhead;
|
||||
|
||||
if (count > HID_MAX_BUFFER_SIZE) {
|
||||
hid_warn(wdata->hdev, "Sending too large output report\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy new request into our output queue and check whether the
|
||||
* queue is full. If it is full, discard this request.
|
||||
* If it is empty we need to start a new worker that will
|
||||
* send out the buffer to the hid device.
|
||||
* If the queue is not empty, then there must be a worker
|
||||
* that is currently sending out our buffer and this worker
|
||||
* will reschedule itself until the queue is empty.
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&wdata->qlock, flags);
|
||||
|
||||
memcpy(wdata->outq[wdata->head].data, buffer, count);
|
||||
wdata->outq[wdata->head].size = count;
|
||||
newhead = (wdata->head + 1) % WIIMOTE_BUFSIZE;
|
||||
|
||||
if (wdata->head == wdata->tail) {
|
||||
wdata->head = newhead;
|
||||
schedule_work(&wdata->worker);
|
||||
} else if (newhead != wdata->tail) {
|
||||
wdata->head = newhead;
|
||||
} else {
|
||||
hid_warn(wdata->hdev, "Output queue is full");
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&wdata->qlock, flags);
|
||||
}
|
||||
|
||||
static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
|
||||
{
|
||||
__u8 cmd[2];
|
||||
|
||||
leds &= WIIPROTO_FLAGS_LEDS;
|
||||
if ((wdata->state.flags & WIIPROTO_FLAGS_LEDS) == leds)
|
||||
return;
|
||||
wdata->state.flags = (wdata->state.flags & ~WIIPROTO_FLAGS_LEDS) | leds;
|
||||
|
||||
cmd[0] = WIIPROTO_REQ_LED;
|
||||
cmd[1] = 0;
|
||||
|
||||
if (leds & WIIPROTO_FLAG_LED1)
|
||||
cmd[1] |= 0x10;
|
||||
if (leds & WIIPROTO_FLAG_LED2)
|
||||
cmd[1] |= 0x20;
|
||||
if (leds & WIIPROTO_FLAG_LED3)
|
||||
cmd[1] |= 0x40;
|
||||
if (leds & WIIPROTO_FLAG_LED4)
|
||||
cmd[1] |= 0x80;
|
||||
|
||||
wiimote_queue(wdata, cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
#define wiifs_led_show_set(num) \
|
||||
static ssize_t wiifs_led_show_##num(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct wiimote_data *wdata = dev_to_wii(dev); \
|
||||
unsigned long flags; \
|
||||
int state; \
|
||||
\
|
||||
if (!atomic_read(&wdata->ready)) \
|
||||
return -EBUSY; \
|
||||
\
|
||||
spin_lock_irqsave(&wdata->state.lock, flags); \
|
||||
state = !!(wdata->state.flags & WIIPROTO_FLAG_LED##num); \
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags); \
|
||||
\
|
||||
return sprintf(buf, "%d\n", state); \
|
||||
} \
|
||||
static ssize_t wiifs_led_set_##num(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t count) \
|
||||
{ \
|
||||
struct wiimote_data *wdata = dev_to_wii(dev); \
|
||||
int tmp = simple_strtoul(buf, NULL, 10); \
|
||||
unsigned long flags; \
|
||||
__u8 state; \
|
||||
\
|
||||
if (!atomic_read(&wdata->ready)) \
|
||||
return -EBUSY; \
|
||||
\
|
||||
spin_lock_irqsave(&wdata->state.lock, flags); \
|
||||
\
|
||||
state = wdata->state.flags; \
|
||||
\
|
||||
if (tmp) \
|
||||
wiiproto_req_leds(wdata, state | WIIPROTO_FLAG_LED##num);\
|
||||
else \
|
||||
wiiproto_req_leds(wdata, state & ~WIIPROTO_FLAG_LED##num);\
|
||||
\
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags); \
|
||||
\
|
||||
return count; \
|
||||
} \
|
||||
static DEVICE_ATTR(led##num, S_IRUGO | S_IWUSR, wiifs_led_show_##num, \
|
||||
wiifs_led_set_##num)
|
||||
|
||||
wiifs_led_show_set(1);
|
||||
wiifs_led_show_set(2);
|
||||
wiifs_led_show_set(3);
|
||||
wiifs_led_show_set(4);
|
||||
|
||||
static int wiimote_input_event(struct input_dev *dev, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
struct wiimote_data *wdata = input_get_drvdata(dev);
|
||||
|
||||
if (!atomic_read(&wdata->ready))
|
||||
return -EBUSY;
|
||||
/* smp_rmb: Make sure wdata->xy is available when wdata->ready is 1 */
|
||||
smp_rmb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
|
||||
{
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT],
|
||||
!!(payload[0] & 0x01));
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_RIGHT],
|
||||
!!(payload[0] & 0x02));
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_DOWN],
|
||||
!!(payload[0] & 0x04));
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_UP],
|
||||
!!(payload[0] & 0x08));
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_PLUS],
|
||||
!!(payload[0] & 0x10));
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_TWO],
|
||||
!!(payload[1] & 0x01));
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_ONE],
|
||||
!!(payload[1] & 0x02));
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_B],
|
||||
!!(payload[1] & 0x04));
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_A],
|
||||
!!(payload[1] & 0x08));
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_MINUS],
|
||||
!!(payload[1] & 0x10));
|
||||
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_HOME],
|
||||
!!(payload[1] & 0x80));
|
||||
input_sync(wdata->input);
|
||||
}
|
||||
|
||||
struct wiiproto_handler {
|
||||
__u8 id;
|
||||
size_t size;
|
||||
void (*func)(struct wiimote_data *wdata, const __u8 *payload);
|
||||
};
|
||||
|
||||
static struct wiiproto_handler handlers[] = {
|
||||
{ .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys },
|
||||
{ .id = 0 }
|
||||
};
|
||||
|
||||
static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *raw_data, int size)
|
||||
{
|
||||
struct wiimote_data *wdata = hid_get_drvdata(hdev);
|
||||
struct wiiproto_handler *h;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
if (!atomic_read(&wdata->ready))
|
||||
return -EBUSY;
|
||||
/* smp_rmb: Make sure wdata->xy is available when wdata->ready is 1 */
|
||||
smp_rmb();
|
||||
|
||||
if (size < 1)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&wdata->state.lock, flags);
|
||||
|
||||
for (i = 0; handlers[i].id; ++i) {
|
||||
h = &handlers[i];
|
||||
if (h->id == raw_data[0] && h->size < size)
|
||||
h->func(wdata, &raw_data[1]);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&wdata->state.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct wiimote_data *wiimote_create(struct hid_device *hdev)
|
||||
{
|
||||
struct wiimote_data *wdata;
|
||||
int i;
|
||||
|
||||
wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
|
||||
if (!wdata)
|
||||
return NULL;
|
||||
|
||||
wdata->input = input_allocate_device();
|
||||
if (!wdata->input) {
|
||||
kfree(wdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wdata->hdev = hdev;
|
||||
hid_set_drvdata(hdev, wdata);
|
||||
|
||||
input_set_drvdata(wdata->input, wdata);
|
||||
wdata->input->event = wiimote_input_event;
|
||||
wdata->input->dev.parent = &wdata->hdev->dev;
|
||||
wdata->input->id.bustype = wdata->hdev->bus;
|
||||
wdata->input->id.vendor = wdata->hdev->vendor;
|
||||
wdata->input->id.product = wdata->hdev->product;
|
||||
wdata->input->id.version = wdata->hdev->version;
|
||||
wdata->input->name = WIIMOTE_NAME;
|
||||
|
||||
set_bit(EV_KEY, wdata->input->evbit);
|
||||
for (i = 0; i < WIIPROTO_KEY_COUNT; ++i)
|
||||
set_bit(wiiproto_keymap[i], wdata->input->keybit);
|
||||
|
||||
spin_lock_init(&wdata->qlock);
|
||||
INIT_WORK(&wdata->worker, wiimote_worker);
|
||||
|
||||
spin_lock_init(&wdata->state.lock);
|
||||
|
||||
return wdata;
|
||||
}
|
||||
|
||||
static void wiimote_destroy(struct wiimote_data *wdata)
|
||||
{
|
||||
kfree(wdata);
|
||||
}
|
||||
|
||||
static int wiimote_hid_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
struct wiimote_data *wdata;
|
||||
int ret;
|
||||
|
||||
wdata = wiimote_create(hdev);
|
||||
if (!wdata) {
|
||||
hid_err(hdev, "Can't alloc device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = device_create_file(&hdev->dev, &dev_attr_led1);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = device_create_file(&hdev->dev, &dev_attr_led2);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = device_create_file(&hdev->dev, &dev_attr_led3);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = device_create_file(&hdev->dev, &dev_attr_led4);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "HID parse failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
|
||||
if (ret) {
|
||||
hid_err(hdev, "HW start failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = input_register_device(wdata->input);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Cannot register input device\n");
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
/* smp_wmb: Write wdata->xy first before wdata->ready is set to 1 */
|
||||
smp_wmb();
|
||||
atomic_set(&wdata->ready, 1);
|
||||
hid_info(hdev, "New device registered\n");
|
||||
|
||||
/* by default set led1 after device initialization */
|
||||
spin_lock_irq(&wdata->state.lock);
|
||||
wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1);
|
||||
spin_unlock_irq(&wdata->state.lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err_stop:
|
||||
hid_hw_stop(hdev);
|
||||
err:
|
||||
input_free_device(wdata->input);
|
||||
device_remove_file(&hdev->dev, &dev_attr_led1);
|
||||
device_remove_file(&hdev->dev, &dev_attr_led2);
|
||||
device_remove_file(&hdev->dev, &dev_attr_led3);
|
||||
device_remove_file(&hdev->dev, &dev_attr_led4);
|
||||
wiimote_destroy(wdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wiimote_hid_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct wiimote_data *wdata = hid_get_drvdata(hdev);
|
||||
|
||||
hid_info(hdev, "Device removed\n");
|
||||
|
||||
device_remove_file(&hdev->dev, &dev_attr_led1);
|
||||
device_remove_file(&hdev->dev, &dev_attr_led2);
|
||||
device_remove_file(&hdev->dev, &dev_attr_led3);
|
||||
device_remove_file(&hdev->dev, &dev_attr_led4);
|
||||
|
||||
hid_hw_stop(hdev);
|
||||
input_unregister_device(wdata->input);
|
||||
|
||||
cancel_work_sync(&wdata->worker);
|
||||
wiimote_destroy(wdata);
|
||||
}
|
||||
|
||||
static const struct hid_device_id wiimote_hid_devices[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
|
||||
USB_DEVICE_ID_NINTENDO_WIIMOTE) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, wiimote_hid_devices);
|
||||
|
||||
static struct hid_driver wiimote_hid_driver = {
|
||||
.name = "wiimote",
|
||||
.id_table = wiimote_hid_devices,
|
||||
.probe = wiimote_hid_probe,
|
||||
.remove = wiimote_hid_remove,
|
||||
.raw_event = wiimote_hid_event,
|
||||
};
|
||||
|
||||
static int __init wiimote_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = hid_register_driver(&wiimote_hid_driver);
|
||||
if (ret)
|
||||
pr_err("Can't register wiimote hid driver\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit wiimote_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&wiimote_hid_driver);
|
||||
}
|
||||
|
||||
module_init(wiimote_init);
|
||||
module_exit(wiimote_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Herrmann <[email protected]>");
|
||||
MODULE_DESCRIPTION(WIIMOTE_NAME " Device Driver");
|
||||
MODULE_VERSION(WIIMOTE_VERSION);
|
||||
|
|
@ -1191,6 +1191,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
|
|||
if (intf->cur_altsetting->desc.bInterfaceProtocol ==
|
||||
USB_INTERFACE_PROTOCOL_MOUSE)
|
||||
hid->type = HID_TYPE_USBMOUSE;
|
||||
else if (intf->cur_altsetting->desc.bInterfaceProtocol == 0)
|
||||
hid->type = HID_TYPE_USBNONE;
|
||||
|
||||
if (dev->manufacturer)
|
||||
strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_KNA5, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWA60, HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U, HID_QUIRK_MULTI_INPUT },
|
||||
{ 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 },
|
||||
|
|
|
|||
|
|
@ -248,12 +248,15 @@ static int hiddev_release(struct inode * inode, struct file * file)
|
|||
usbhid_close(list->hiddev->hid);
|
||||
usbhid_put_power(list->hiddev->hid);
|
||||
} else {
|
||||
mutex_unlock(&list->hiddev->existancelock);
|
||||
kfree(list->hiddev);
|
||||
kfree(list);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(list);
|
||||
mutex_unlock(&list->hiddev->existancelock);
|
||||
kfree(list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -923,10 +926,11 @@ void hiddev_disconnect(struct hid_device *hid)
|
|||
usb_deregister_dev(usbhid->intf, &hiddev_class);
|
||||
|
||||
if (hiddev->open) {
|
||||
mutex_unlock(&hiddev->existancelock);
|
||||
usbhid_close(hiddev->hid);
|
||||
wake_up_interruptible(&hiddev->wait);
|
||||
} else {
|
||||
mutex_unlock(&hiddev->existancelock);
|
||||
kfree(hiddev);
|
||||
}
|
||||
mutex_unlock(&hiddev->existancelock);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue