2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-18 18:23:53 +08:00

Merge branches 'acrux', 'logitech', 'multitouch', 'roccat' and 'wiimote' into for-linus

This commit is contained in:
Jiri Kosina 2011-10-25 09:54:16 +02:00
20 changed files with 2476 additions and 131 deletions

View File

@ -0,0 +1,7 @@
What: /sys/module/hid_logitech/drivers/hid:logitech/<dev>/range.
Date: July 2011
KernelVersion: 3.2
Contact: Michal Malý <madcatxster@gmail.com>
Description: Display minimum, maximum and current range of the steering
wheel. Writing a value within min and max boundaries sets the
range of the wheel.

View File

@ -7142,6 +7142,12 @@ L: linux-scsi@vger.kernel.org
S: Maintained
F: drivers/scsi/wd7000.c
WIIMOTE HID DRIVER
M: David Herrmann <dh.herrmann@googlemail.com>
L: linux-input@vger.kernel.org
S: Maintained
F: drivers/hid/hid-wiimote*
WINBOND CIR DRIVER
M: David Härdeman <david@hardeman.nu>
S: Maintained

View File

@ -69,7 +69,7 @@ config HID_ACRUX
Say Y here if you want to enable support for ACRUX game controllers.
config HID_ACRUX_FF
tristate "ACRUX force feedback support"
bool "ACRUX force feedback support"
depends on HID_ACRUX
select INPUT_FF_MEMLESS
---help---
@ -245,6 +245,15 @@ config HID_LOGITECH
---help---
Support for Logitech devices that are not fully compliant with HID standard.
config HID_LOGITECH_DJ
tristate "Logitech Unifying receivers full support"
depends on HID_LOGITECH
default m
---help---
Say Y if you want support for Logitech Unifying receivers and devices.
Unifying receivers are capable of pairing up to 6 Logitech compliant
devices to the same receiver.
config LOGITECH_FF
bool "Logitech force feedback support"
depends on HID_LOGITECH
@ -278,13 +287,21 @@ config LOGIG940_FF
Say Y here if you want to enable force feedback support for Logitech
Flight System G940 devices.
config LOGIWII_FF
bool "Logitech Speed Force Wireless force feedback support"
config LOGIWHEELS_FF
bool "Logitech wheels configuration and force feedback support"
depends on HID_LOGITECH
select INPUT_FF_MEMLESS
default LOGITECH_FF
help
Say Y here if you want to enable force feedback support for Logitech
Speed Force Wireless (Wii) devices.
Say Y here if you want to enable force feedback and range setting
support for following Logitech wheels:
- Logitech Driving Force
- Logitech Driving Force Pro
- Logitech Driving Force GT
- Logitech G25
- Logitech G27
- Logitech MOMO/MOMO 2
- Logitech Formula Force EX
config HID_MAGICMOUSE
tristate "Apple MagicMouse multi-touch support"
@ -328,6 +345,7 @@ config HID_MULTITOUCH
- Hanvon dual touch panels
- Ilitek dual touch panels
- IrTouch Infrared USB panels
- LG Display panels (Dell ST2220Tc)
- Lumio CrystalTouch panels
- MosArt dual-touch panels
- PenMount dual touch panels
@ -590,6 +608,7 @@ config HID_WIIMOTE
tristate "Nintendo Wii Remote support"
depends on BT_HIDP
depends on LEDS_CLASS
select POWER_SUPPLY
---help---
Support for the Nintendo Wii Remote bluetooth device.

View File

@ -21,7 +21,7 @@ endif
ifdef CONFIG_LOGIG940_FF
hid-logitech-y += hid-lg3ff.o
endif
ifdef CONFIG_LOGIWII_FF
ifdef CONFIG_LOGIWHEELS_FF
hid-logitech-y += hid-lg4ff.o
endif
@ -43,6 +43,7 @@ obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
obj-$(CONFIG_HID_KYE) += hid-kye.o
obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o

View File

@ -6,7 +6,7 @@
* Xbox 360 controller.
*
* 1a34:0802 "ACRUX USB GAMEPAD 8116"
* - tested with a EXEQ EQ-PCU-02090 game controller.
* - tested with an EXEQ EQ-PCU-02090 game controller.
*
* Copyright (c) 2010 Sergei Kolzun <x0r@dv-life.ru>
*/
@ -45,7 +45,10 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect
{
struct hid_device *hid = input_get_drvdata(dev);
struct axff_device *axff = data;
struct hid_report *report = axff->report;
int field_count = 0;
int left, right;
int i, j;
left = effect->u.rumble.strong_magnitude;
right = effect->u.rumble.weak_magnitude;
@ -55,10 +58,14 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect
left = left * 0xff / 0xffff;
right = right * 0xff / 0xffff;
axff->report->field[0]->value[0] = left;
axff->report->field[1]->value[0] = right;
axff->report->field[2]->value[0] = left;
axff->report->field[3]->value[0] = right;
for (i = 0; i < report->maxfield; i++) {
for (j = 0; j < report->field[i]->report_count; j++) {
report->field[i]->value[j] =
field_count % 2 ? right : left;
field_count++;
}
}
dbg_hid("running with 0x%02x 0x%02x", left, right);
usbhid_submit_report(hid, axff->report, USB_DIR_OUT);
@ -72,6 +79,8 @@ static int axff_init(struct hid_device *hid)
struct hid_input *hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
int field_count = 0;
int i, j;
int error;
if (list_empty(report_list)) {
@ -80,9 +89,16 @@ static int axff_init(struct hid_device *hid)
}
report = list_first_entry(report_list, struct hid_report, list);
for (i = 0; i < report->maxfield; i++) {
for (j = 0; j < report->field[i]->report_count; j++) {
report->field[i]->value[j] = 0x00;
field_count++;
}
}
if (report->maxfield < 4) {
hid_err(hid, "no fields in the report: %d\n", report->maxfield);
if (field_count < 4) {
hid_err(hid, "not enough fields in the report: %d\n",
field_count);
return -ENODEV;
}
@ -97,13 +113,9 @@ static int axff_init(struct hid_device *hid)
goto err_free_mem;
axff->report = report;
axff->report->field[0]->value[0] = 0x00;
axff->report->field[1]->value[0] = 0x00;
axff->report->field[2]->value[0] = 0x00;
axff->report->field[3]->value[0] = 0x00;
usbhid_submit_report(hid, axff->report, USB_DIR_OUT);
hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun<x0r@dv-life.ru>\n");
hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun <x0r@dv-life.ru>\n");
return 0;

View File

@ -1212,6 +1212,12 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev,
connect_mask & HID_CONNECT_HIDINPUT_FORCE))
hdev->claimed |= HID_CLAIMED_INPUT;
if (hdev->quirks & HID_QUIRK_MULTITOUCH) {
/* this device should be handled by hid-multitouch, skip it */
hdev->quirks &= ~HID_QUIRK_MULTITOUCH;
return -ENODEV;
}
if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect &&
!hdev->hiddev_connect(hdev,
connect_mask & HID_CONNECT_HIDDEV_FORCE))
@ -1391,6 +1397,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_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6650) },
{ 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) },
@ -1399,6 +1406,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LG, USB_DEVICE_ID_LG_MULTITOUCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
@ -1420,8 +1428,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },

View File

@ -351,6 +351,9 @@
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
#define USB_VENDOR_ID_IDEACOM 0x1cb6
#define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650
#define USB_VENDOR_ID_ILITEK 0x222a
#define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001
@ -423,6 +426,9 @@
#define USB_DEVICE_ID_LD_HYBRID 0x2090
#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0
#define USB_VENDOR_ID_LG 0x1fd2
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
#define USB_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
@ -440,6 +446,7 @@
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
#define USB_DEVICE_ID_LOGITECH_DFP_WHEEL 0xc298
#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
#define USB_DEVICE_ID_LOGITECH_DFGT_WHEEL 0xc29a
#define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b
#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
@ -447,6 +454,8 @@
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513
#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER 0xc52b
#define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532
#define USB_DEVICE_ID_SPACETRAVELLER 0xc623
#define USB_DEVICE_ID_SPACENAVIGATOR 0xc626
#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704

View File

@ -474,6 +474,10 @@ 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;
@ -978,6 +982,13 @@ 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;

View File

@ -363,7 +363,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))
if (quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
connect_mask &= ~HID_CONNECT_FF;
ret = hid_hw_start(hdev, connect_mask);
@ -372,7 +372,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_free;
}
if (quirks & LG_FF4) {
/* Setup wireless link with Logitech Wii wheel */
if(hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
@ -405,6 +406,15 @@ err_free:
return ret;
}
static void lg_remove(struct hid_device *hdev)
{
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
if(quirks & LG_FF4)
lg4ff_deinit(hdev);
hid_hw_stop(hdev);
}
static const struct hid_device_id lg_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
.driver_data = LG_RDESC | LG_WIRELESS },
@ -431,7 +441,7 @@ static const struct hid_device_id lg_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
.driver_data = LG_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
.driver_data = LG_NOGET | LG_FF },
.driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
.driver_data = LG_FF2 },
@ -444,15 +454,17 @@ static const struct hid_device_id lg_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
.driver_data = LG_FF },
.driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
.driver_data = LG_FF },
.driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
.driver_data = LG_FF },
.driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
.driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
.driver_data = LG_FF },
.driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
.driver_data = LG_NOGET | LG_FF },
.driver_data = LG_NOGET | LG_FF4 },
{ 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 ),
@ -478,6 +490,7 @@ static struct hid_driver lg_driver = {
.input_mapped = lg_input_mapped,
.event = lg_event,
.probe = lg_probe,
.remove = lg_remove,
};
static int __init lg_init(void)

View File

@ -19,10 +19,12 @@ int lg3ff_init(struct hid_device *hdev);
static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
#endif
#ifdef CONFIG_LOGIWII_FF
#ifdef CONFIG_LOGIWHEELS_FF
int lg4ff_init(struct hid_device *hdev);
int lg4ff_deinit(struct hid_device *hdev);
#else
static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
#endif
#endif

View File

@ -29,19 +29,108 @@
#include "usbhid/usbhid.h"
#include "hid-lg.h"
#include "hid-ids.h"
struct lg4ff_device {
struct hid_report *report;
#define DFGT_REV_MAJ 0x13
#define DFGT_REV_MIN 0x22
#define DFP_REV_MAJ 0x11
#define DFP_REV_MIN 0x06
#define FFEX_REV_MAJ 0x21
#define FFEX_REV_MIN 0x00
#define G25_REV_MAJ 0x12
#define G25_REV_MIN 0x22
#define G27_REV_MAJ 0x12
#define G27_REV_MIN 0x38
#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
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;
struct list_head list;
void (*set_range)(struct hid_device *hid, u16 range);
};
static const signed short ff4_wheel_ac[] = {
static struct lg4ff_device_entry device_list;
static const signed short lg4ff_wheel_effects[] = {
FF_CONSTANT,
FF_AUTOCENTER,
-1
};
static int hid_lg4ff_play(struct input_dev *dev, void *data,
struct ff_effect *effect)
struct lg4ff_wheel {
const __u32 product_id;
const signed short *ff_effects;
const __u16 min_range;
const __u16 max_range;
void (*set_range)(struct hid_device *hid, u16 range);
};
static const struct lg4ff_wheel lg4ff_devices[] = {
{USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
{USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_dfp},
{USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
{USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
{USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL},
{USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}
};
struct lg4ff_native_cmd {
const __u8 cmd_num; /* Number of commands to send */
const __u8 cmd[];
};
struct lg4ff_usb_revision {
const __u16 rev_maj;
const __u16 rev_min;
const struct lg4ff_native_cmd *command;
};
static const struct lg4ff_native_cmd native_dfp = {
1,
{0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
};
static const struct lg4ff_native_cmd native_dfgt = {
2,
{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */
0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* 2nd command */
};
static const struct lg4ff_native_cmd native_g25 = {
1,
{0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
};
static const struct lg4ff_native_cmd native_g27 = {
2,
{0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */
0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* 2nd command */
};
static const struct lg4ff_usb_revision lg4ff_revs[] = {
{DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt}, /* Driving Force GT */
{DFP_REV_MAJ, DFP_REV_MIN, &native_dfp}, /* Driving Force Pro */
{G25_REV_MAJ, G25_REV_MIN, &native_g25}, /* G25 */
{G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */
};
static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
@ -55,13 +144,12 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data,
x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
CLAMP(x);
report->field[0]->value[0] = 0x11; /* Slot 1 */
report->field[0]->value[1] = 0x10;
report->field[0]->value[1] = 0x08;
report->field[0]->value[2] = x;
report->field[0]->value[3] = 0x00;
report->field[0]->value[3] = 0x80;
report->field[0]->value[4] = 0x00;
report->field[0]->value[5] = 0x08;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
dbg_hid("Autocenter, x=0x%02X\n", x);
usbhid_submit_report(hid, report, USB_DIR_OUT);
break;
@ -69,24 +157,184 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data,
return 0;
}
static void hid_lg4ff_set_autocenter(struct input_dev *dev, u16 magnitude)
/* Sends default autocentering command compatible with
* all wheels except Formula Force EX */
static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
{
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
__s32 *value = report->field[0]->value;
*value++ = 0xfe;
*value++ = 0x0d;
*value++ = 0x07;
*value++ = 0x07;
*value++ = (magnitude >> 8) & 0xff;
*value++ = 0x00;
*value = 0x00;
report->field[0]->value[0] = 0xfe;
report->field[0]->value[1] = 0x0d;
report->field[0]->value[2] = magnitude >> 13;
report->field[0]->value[3] = magnitude >> 13;
report->field[0]->value[4] = magnitude >> 8;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
/* Sends autocentering command compatible with Formula Force EX */
static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
{
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
magnitude = magnitude * 90 / 65535;
report->field[0]->value[0] = 0xfe;
report->field[0]->value[1] = 0x03;
report->field[0]->value[2] = magnitude >> 14;
report->field[0]->value[3] = magnitude >> 14;
report->field[0]->value[4] = magnitude;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
/* Sends command to set range compatible with G25/G27/Driving Force GT */
static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
{
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);
dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
report->field[0]->value[0] = 0xf8;
report->field[0]->value[1] = 0x81;
report->field[0]->value[2] = range & 0x00ff;
report->field[0]->value[3] = (range & 0xff00) >> 8;
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);
}
/* Sends commands to set range compatible with Driving Force Pro wheel */
static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
{
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);
int start_left, start_right, full_range;
dbg_hid("Driving Force Pro: setting range to %u\n", range);
/* Prepare "coarse" limit command */
report->field[0]->value[0] = 0xf8;
report->field[0]->value[1] = 0x00; /* Set later */
report->field[0]->value[2] = 0x00;
report->field[0]->value[3] = 0x00;
report->field[0]->value[4] = 0x00;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
if (range > 200) {
report->field[0]->value[1] = 0x03;
full_range = 900;
} else {
report->field[0]->value[1] = 0x02;
full_range = 200;
}
usbhid_submit_report(hid, report, USB_DIR_OUT);
/* Prepare "fine" limit command */
report->field[0]->value[0] = 0x81;
report->field[0]->value[1] = 0x0b;
report->field[0]->value[2] = 0x00;
report->field[0]->value[3] = 0x00;
report->field[0]->value[4] = 0x00;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
if (range == 200 || range == 900) { /* Do not apply any fine limit */
usbhid_submit_report(hid, report, USB_DIR_OUT);
return;
}
/* Construct fine limit command */
start_left = (((full_range - range + 1) * 2047) / full_range);
start_right = 0xfff - start_left;
report->field[0]->value[2] = start_left >> 4;
report->field[0]->value[3] = start_right >> 4;
report->field[0]->value[4] = 0xff;
report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
report->field[0]->value[6] = 0xff;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd)
{
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);
__u8 i, j;
j = 0;
while (j < 7*cmd->cmd_num) {
for (i = 0; i < 7; i++)
report->field[0]->value[i] = cmd->cmd[j++];
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
}
/* 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);
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;
}
if (h == &device_list.list) {
dbg_hid("Device not found!");
return 0;
}
count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->range);
return count;
}
/* Set range to user specified value, call appropriate function
* 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);
__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;
}
if (h == &device_list.list) {
dbg_hid("Device not found!");
return count;
}
if (range == 0)
range = entry->max_range;
/* Check if the wheel supports range setting
* and that the range is within limits for the wheel */
if (entry->set_range != NULL && range >= entry->min_range && range <= entry->max_range) {
entry->set_range(hid, range);
entry->range = range;
}
return count;
}
int lg4ff_init(struct hid_device *hid)
{
@ -95,9 +343,10 @@ int lg4ff_init(struct hid_device *hid)
struct input_dev *dev = hidinput->input;
struct hid_report *report;
struct hid_field *field;
const signed short *ff_bits = ff4_wheel_ac;
int error;
int i;
struct lg4ff_device_entry *entry;
struct usb_device_descriptor *udesc;
int error, i, j;
__u16 bcdDevice, rev_maj, rev_min;
/* Find the report to use */
if (list_empty(report_list)) {
@ -118,18 +367,122 @@ int lg4ff_init(struct hid_device *hid)
return -1;
}
for (i = 0; ff_bits[i] >= 0; i++)
set_bit(ff_bits[i], dev->ffbit);
/* Check what wheel has been connected */
for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
if (hid->product == lg4ff_devices[i].product_id) {
dbg_hid("Found compatible device, product ID %04X\n", lg4ff_devices[i].product_id);
break;
}
}
if (i == ARRAY_SIZE(lg4ff_devices)) {
hid_err(hid, "Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to"
"LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>\n");
return -1;
}
/* Attempt to switch wheel to native mode when applicable */
udesc = &(hid_to_usb_dev(hid)->descriptor);
if (!udesc) {
hid_err(hid, "NULL USB device descriptor\n");
return -1;
}
bcdDevice = le16_to_cpu(udesc->bcdDevice);
rev_maj = bcdDevice >> 8;
rev_min = bcdDevice & 0xff;
if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL) {
dbg_hid("Generic wheel detected, can it do native?\n");
dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min);
for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) {
if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) {
hid_lg4ff_switch_native(hid, lg4ff_revs[j].command);
hid_info(hid, "Switched to native mode\n");
}
}
}
/* Set supported force feedback capabilities */
for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
if (error)
return error;
if (test_bit(FF_AUTOCENTER, dev->ffbit))
dev->ff->set_autocenter = hid_lg4ff_set_autocenter;
/* Check if autocentering is available and
* set the centering force to zero by default */
if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
if(rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
else
dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
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;
}
/* Add the device to device_list */
entry = (struct lg4ff_device_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);
return -ENOMEM;
}
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);
if (error)
return error;
dbg_hid("sysfs interface created\n");
/* Set the maximum range to start with */
entry->range = entry->max_range;
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");
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;
}
}
if (!found) {
dbg_hid("Device entry not found!\n");
return -1;
}
device_remove_file(&hid->dev, &dev_attr_range);
dbg_hid("Device successfully unregistered\n");
return 0;
}

View File

@ -58,12 +58,6 @@ static const signed short ff_joystick_ac[] = {
-1
};
static const signed short ff_wheel[] = {
FF_CONSTANT,
FF_AUTOCENTER,
-1
};
static const struct dev_type devices[] = {
{ 0x046d, 0xc211, ff_rumble },
{ 0x046d, 0xc219, ff_rumble },
@ -71,14 +65,7 @@ static const struct dev_type devices[] = {
{ 0x046d, 0xc286, ff_joystick_ac },
{ 0x046d, 0xc287, ff_joystick_ac },
{ 0x046d, 0xc293, ff_joystick },
{ 0x046d, 0xc294, ff_wheel },
{ 0x046d, 0xc298, ff_wheel },
{ 0x046d, 0xc299, ff_wheel },
{ 0x046d, 0xc29b, ff_wheel },
{ 0x046d, 0xc295, ff_joystick },
{ 0x046d, 0xc298, ff_wheel },
{ 0x046d, 0xc299, ff_wheel },
{ 0x046d, 0xca03, ff_wheel },
};
static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)

View File

@ -0,0 +1,922 @@
/*
* HID driver for Logitech Unifying receivers
*
* Copyright (c) 2011 Logitech
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/usb.h>
#include "usbhid/usbhid.h"
#include "hid-ids.h"
#include "hid-logitech-dj.h"
/* Keyboard descriptor (1) */
static const char kbd_descriptor[] = {
0x05, 0x01, /* USAGE_PAGE (generic Desktop) */
0x09, 0x06, /* USAGE (Keyboard) */
0xA1, 0x01, /* COLLECTION (Application) */
0x85, 0x01, /* REPORT_ID (1) */
0x95, 0x08, /* REPORT_COUNT (8) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0xE0, /* USAGE_MINIMUM (Left Control) */
0x29, 0xE7, /* USAGE_MAXIMUM (Right GUI) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
0x95, 0x05, /* REPORT COUNT (5) */
0x05, 0x08, /* USAGE PAGE (LED page) */
0x19, 0x01, /* USAGE MINIMUM (1) */
0x29, 0x05, /* USAGE MAXIMUM (5) */
0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */
0x95, 0x01, /* REPORT COUNT (1) */
0x75, 0x03, /* REPORT SIZE (3) */
0x91, 0x01, /* OUTPUT (Constant) */
0x95, 0x06, /* REPORT_COUNT (6) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x26, 0xFF, 0x00, /* LOGICAL_MAXIMUM (255) */
0x05, 0x07, /* USAGE_PAGE (Keyboard) */
0x19, 0x00, /* USAGE_MINIMUM (no event) */
0x2A, 0xFF, 0x00, /* USAGE_MAXIMUM (reserved) */
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
0xC0
};
/* Mouse descriptor (2) */
static const char mse_descriptor[] = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x02, /* USAGE (Mouse) */
0xA1, 0x01, /* COLLECTION (Application) */
0x85, 0x02, /* REPORT_ID = 2 */
0x09, 0x01, /* USAGE (pointer) */
0xA1, 0x00, /* COLLECTION (physical) */
0x05, 0x09, /* USAGE_PAGE (buttons) */
0x19, 0x01, /* USAGE_MIN (1) */
0x29, 0x10, /* USAGE_MAX (16) */
0x15, 0x00, /* LOGICAL_MIN (0) */
0x25, 0x01, /* LOGICAL_MAX (1) */
0x95, 0x10, /* REPORT_COUNT (16) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x81, 0x02, /* INPUT (data var abs) */
0x05, 0x01, /* USAGE_PAGE (generic desktop) */
0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */
0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */
0x75, 0x0C, /* REPORT_SIZE (12) */
0x95, 0x02, /* REPORT_COUNT (2) */
0x09, 0x30, /* USAGE (X) */
0x09, 0x31, /* USAGE (Y) */
0x81, 0x06, /* INPUT */
0x15, 0x81, /* LOGICAL_MIN (-127) */
0x25, 0x7F, /* LOGICAL_MAX (127) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x09, 0x38, /* USAGE (wheel) */
0x81, 0x06, /* INPUT */
0x05, 0x0C, /* USAGE_PAGE(consumer) */
0x0A, 0x38, 0x02, /* USAGE(AC Pan) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x81, 0x06, /* INPUT */
0xC0, /* END_COLLECTION */
0xC0, /* END_COLLECTION */
};
/* Consumer Control descriptor (3) */
static const char consumer_descriptor[] = {
0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */
0x09, 0x01, /* USAGE (Consumer Control) */
0xA1, 0x01, /* COLLECTION (Application) */
0x85, 0x03, /* REPORT_ID = 3 */
0x75, 0x10, /* REPORT_SIZE (16) */
0x95, 0x02, /* REPORT_COUNT (2) */
0x15, 0x01, /* LOGICAL_MIN (1) */
0x26, 0x8C, 0x02, /* LOGICAL_MAX (652) */
0x19, 0x01, /* USAGE_MIN (1) */
0x2A, 0x8C, 0x02, /* USAGE_MAX (652) */
0x81, 0x00, /* INPUT (Data Ary Abs) */
0xC0, /* END_COLLECTION */
}; /* */
/* System control descriptor (4) */
static const char syscontrol_descriptor[] = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x80, /* USAGE (System Control) */
0xA1, 0x01, /* COLLECTION (Application) */
0x85, 0x04, /* REPORT_ID = 4 */
0x75, 0x02, /* REPORT_SIZE (2) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x15, 0x01, /* LOGICAL_MIN (1) */
0x25, 0x03, /* LOGICAL_MAX (3) */
0x09, 0x82, /* USAGE (System Sleep) */
0x09, 0x81, /* USAGE (System Power Down) */
0x09, 0x83, /* USAGE (System Wake Up) */
0x81, 0x60, /* INPUT (Data Ary Abs NPrf Null) */
0x75, 0x06, /* REPORT_SIZE (6) */
0x81, 0x03, /* INPUT (Cnst Var Abs) */
0xC0, /* END_COLLECTION */
};
/* Media descriptor (8) */
static const char media_descriptor[] = {
0x06, 0xbc, 0xff, /* Usage Page 0xffbc */
0x09, 0x88, /* Usage 0x0088 */
0xa1, 0x01, /* BeginCollection */
0x85, 0x08, /* Report ID 8 */
0x19, 0x01, /* Usage Min 0x0001 */
0x29, 0xff, /* Usage Max 0x00ff */
0x15, 0x01, /* Logical Min 1 */
0x26, 0xff, 0x00, /* Logical Max 255 */
0x75, 0x08, /* Report Size 8 */
0x95, 0x01, /* Report Count 1 */
0x81, 0x00, /* Input */
0xc0, /* EndCollection */
}; /* */
/* Maximum size of all defined hid reports in bytes (including report id) */
#define MAX_REPORT_SIZE 8
/* 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)
* than the hid report created from those RF reports. In the future
* this doesnt have to be true.
*
* For instance, RF report type 0x01 which has a size of 8 bytes, corresponds
* to hid report id 0x01, this is standard keyboard. Same thing applies to mice
* reports and consumer control, etc. If a new RF report is created, it doesn't
* has to have the same report id as its corresponding hid report, so an
* translation may have to take place for future report types.
*/
#define NUMBER_OF_HID_REPORTS 32
static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
[1] = 8, /* Standard keyboard */
[2] = 8, /* Standard mouse */
[3] = 5, /* Consumer control */
[4] = 2, /* System control */
[8] = 2, /* Media Center */
};
#define LOGITECH_DJ_INTERFACE_NUMBER 0x02
static struct hid_ll_driver logi_dj_ll_driver;
static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
size_t count,
unsigned char report_type);
static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
{
/* Called in delayed work context */
struct dj_device *dj_dev;
unsigned long flags;
spin_lock_irqsave(&djrcv_dev->lock, flags);
dj_dev = djrcv_dev->paired_dj_devices[dj_report->device_index];
djrcv_dev->paired_dj_devices[dj_report->device_index] = NULL;
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
if (dj_dev != NULL) {
hid_destroy_device(dj_dev->hdev);
kfree(dj_dev);
} else {
dev_err(&djrcv_dev->hdev->dev, "%s: can't destroy a NULL device\n",
__func__);
}
}
static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
{
/* Called in delayed work context */
struct hid_device *djrcv_hdev = djrcv_dev->hdev;
struct usb_interface *intf = to_usb_interface(djrcv_hdev->dev.parent);
struct usb_device *usbdev = interface_to_usbdev(intf);
struct hid_device *dj_hiddev;
struct dj_device *dj_dev;
/* Device index goes from 1 to 6, we need 3 bytes to store the
* semicolon, the index, and a null terminator
*/
unsigned char tmpstr[3];
if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
SPFUNCTION_DEVICE_LIST_EMPTY) {
dbg_hid("%s: device list is empty\n", __func__);
return;
}
if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
(dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
dev_err(&djrcv_hdev->dev, "%s: invalid device index:%d\n",
__func__, dj_report->device_index);
return;
}
dj_hiddev = hid_allocate_device();
if (IS_ERR(dj_hiddev)) {
dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
__func__);
return;
}
dj_hiddev->ll_driver = &logi_dj_ll_driver;
dj_hiddev->hid_output_raw_report = logi_dj_output_hidraw_report;
dj_hiddev->dev.parent = &djrcv_hdev->dev;
dj_hiddev->bus = BUS_USB;
dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor);
dj_hiddev->product = le16_to_cpu(usbdev->descriptor.idProduct);
snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
"Logitech Unifying Device. Wireless PID:%02x%02x",
dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB],
dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]);
usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys));
snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index);
strlcat(dj_hiddev->phys, tmpstr, sizeof(dj_hiddev->phys));
dj_dev = kzalloc(sizeof(struct dj_device), GFP_KERNEL);
if (!dj_dev) {
dev_err(&djrcv_hdev->dev, "%s: failed allocating dj_device\n",
__func__);
goto dj_device_allocate_fail;
}
dj_dev->reports_supported = le32_to_cpu(
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;
dj_hiddev->driver_data = dj_dev;
djrcv_dev->paired_dj_devices[dj_report->device_index] = dj_dev;
if (hid_add_device(dj_hiddev)) {
dev_err(&djrcv_hdev->dev, "%s: failed adding dj_device\n",
__func__);
goto hid_add_device_fail;
}
return;
hid_add_device_fail:
djrcv_dev->paired_dj_devices[dj_report->device_index] = NULL;
kfree(dj_dev);
dj_device_allocate_fail:
hid_destroy_device(dj_hiddev);
}
static void delayedwork_callback(struct work_struct *work)
{
struct dj_receiver_dev *djrcv_dev =
container_of(work, struct dj_receiver_dev, work);
struct dj_report dj_report;
unsigned long flags;
int count;
dbg_hid("%s\n", __func__);
spin_lock_irqsave(&djrcv_dev->lock, flags);
count = kfifo_out(&djrcv_dev->notif_fifo, &dj_report,
sizeof(struct dj_report));
if (count != sizeof(struct dj_report)) {
dev_err(&djrcv_dev->hdev->dev, "%s: workitem triggered without "
"notifications available\n", __func__);
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
return;
}
if (!kfifo_is_empty(&djrcv_dev->notif_fifo)) {
if (schedule_work(&djrcv_dev->work) == 0) {
dbg_hid("%s: did not schedule the work item, was "
"already queued\n", __func__);
}
}
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
switch (dj_report.report_type) {
case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
logi_dj_recv_add_djhid_device(djrcv_dev, &dj_report);
break;
case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
break;
default:
dbg_hid("%s: unexpected report type\n", __func__);
}
}
static void logi_dj_recv_queue_notification(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
{
/* We are called from atomic context (tasklet && djrcv->lock held) */
kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
if (schedule_work(&djrcv_dev->work) == 0) {
dbg_hid("%s: did not schedule the work item, was already "
"queued\n", __func__);
}
}
static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
{
/* We are called from atomic context (tasklet && djrcv->lock held) */
unsigned int i;
u8 reportbuffer[MAX_REPORT_SIZE];
struct dj_device *djdev;
djdev = djrcv_dev->paired_dj_devices[dj_report->device_index];
if (!djdev) {
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d\n", dj_report->device_index);
return;
}
memset(reportbuffer, 0, sizeof(reportbuffer));
for (i = 0; i < NUMBER_OF_HID_REPORTS; i++) {
if (djdev->reports_supported & (1 << i)) {
reportbuffer[0] = i;
if (hid_input_report(djdev->hdev,
HID_INPUT_REPORT,
reportbuffer,
hid_reportid_size_map[i], 1)) {
dbg_hid("hid_input_report error sending null "
"report\n");
}
}
}
}
static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
{
/* We are called from atomic context (tasklet && djrcv->lock held) */
struct dj_device *dj_device;
dj_device = djrcv_dev->paired_dj_devices[dj_report->device_index];
if (dj_device == NULL) {
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d\n", dj_report->device_index);
return;
}
if ((dj_report->report_type > ARRAY_SIZE(hid_reportid_size_map) - 1) ||
(hid_reportid_size_map[dj_report->report_type] == 0)) {
dbg_hid("invalid report type:%x\n", dj_report->report_type);
return;
}
if (hid_input_report(dj_device->hdev,
HID_INPUT_REPORT, &dj_report->report_type,
hid_reportid_size_map[dj_report->report_type], 1)) {
dbg_hid("hid_input_report error\n");
}
}
static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
{
struct hid_device *hdev = djrcv_dev->hdev;
int sent_bytes;
if (!hdev->hid_output_raw_report) {
dev_err(&hdev->dev, "%s:"
"hid_output_raw_report is null\n", __func__);
return -ENODEV;
}
sent_bytes = hdev->hid_output_raw_report(hdev, (u8 *) dj_report,
sizeof(struct dj_report),
HID_OUTPUT_REPORT);
return (sent_bytes < 0) ? sent_bytes : 0;
}
static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
{
struct dj_report dj_report;
memset(&dj_report, 0, sizeof(dj_report));
dj_report.report_id = REPORT_ID_DJ_SHORT;
dj_report.device_index = 0xFF;
dj_report.report_type = REPORT_TYPE_CMD_GET_PAIRED_DEVICES;
return logi_dj_recv_send_report(djrcv_dev, &dj_report);
}
static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
unsigned timeout)
{
struct dj_report dj_report;
memset(&dj_report, 0, sizeof(dj_report));
dj_report.report_id = REPORT_ID_DJ_SHORT;
dj_report.device_index = 0xFF;
dj_report.report_type = REPORT_TYPE_CMD_SWITCH;
dj_report.report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x1F;
dj_report.report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout;
return logi_dj_recv_send_report(djrcv_dev, &dj_report);
}
static int logi_dj_ll_open(struct hid_device *hid)
{
dbg_hid("%s:%s\n", __func__, hid->phys);
return 0;
}
static void logi_dj_ll_close(struct hid_device *hid)
{
dbg_hid("%s:%s\n", __func__, hid->phys);
}
static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
size_t count,
unsigned char report_type)
{
/* Called by hid raw to send data */
dbg_hid("%s\n", __func__);
return 0;
}
static int logi_dj_ll_parse(struct hid_device *hid)
{
struct dj_device *djdev = hid->driver_data;
int retval;
dbg_hid("%s\n", __func__);
djdev->hdev->version = 0x0111;
djdev->hdev->country = 0x00;
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
if (djdev->reports_supported & KBD_LEDS) {
dbg_hid("%s: need to send kbd leds report descriptor: %x\n",
__func__, djdev->reports_supported);
}
return 0;
}
static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value)
{
/* Sent by the input layer to handle leds and Force Feedback */
struct hid_device *dj_hiddev = input_get_drvdata(dev);
struct dj_device *dj_dev = dj_hiddev->driver_data;
struct dj_receiver_dev *djrcv_dev =
dev_get_drvdata(dj_hiddev->dev.parent);
struct hid_device *dj_rcv_hiddev = djrcv_dev->hdev;
struct hid_report_enum *output_report_enum;
struct hid_field *field;
struct hid_report *report;
unsigned char data[8];
int offset;
dbg_hid("%s: %s, type:%d | code:%d | value:%d\n",
__func__, dev->phys, type, code, value);
if (type != EV_LED)
return -1;
offset = hidinput_find_field(dj_hiddev, type, code, &field);
if (offset == -1) {
dev_warn(&dev->dev, "event field not found\n");
return -1;
}
hid_set_field(field, offset, value);
hid_output_report(field->report, &data[0]);
output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT];
report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
hid_set_field(report->field[0], 0, dj_dev->device_index);
hid_set_field(report->field[0], 1, REPORT_TYPE_LEDS);
hid_set_field(report->field[0], 2, data[1]);
usbhid_submit_report(dj_rcv_hiddev, report, USB_DIR_OUT);
return 0;
}
static int logi_dj_ll_start(struct hid_device *hid)
{
dbg_hid("%s\n", __func__);
return 0;
}
static void logi_dj_ll_stop(struct hid_device *hid)
{
dbg_hid("%s\n", __func__);
}
static struct hid_ll_driver logi_dj_ll_driver = {
.parse = logi_dj_ll_parse,
.start = logi_dj_ll_start,
.stop = logi_dj_ll_stop,
.open = logi_dj_ll_open,
.close = logi_dj_ll_close,
.hidinput_input_event = logi_dj_ll_input_event,
};
static int logi_dj_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *data,
int size)
{
struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
struct dj_report *dj_report = (struct dj_report *) data;
unsigned long flags;
bool report_processed = false;
dbg_hid("%s, size:%d\n", __func__, size);
/* Here we receive all data coming from iface 2, there are 4 cases:
*
* 1) Data should continue its normal processing i.e. data does not
* come from the DJ collection, in which case we do nothing and
* return 0, so hid-core can continue normal processing (will forward
* to associated hidraw device)
*
* 2) Data is from DJ collection, and is intended for this driver i. e.
* data contains arrival, departure, etc notifications, in which case
* we queue them for delayed processing by the work queue. We return 1
* to hid-core as no further processing is required from it.
*
* 3) Data is from DJ collection, and informs a connection change,
* if the change means rf link loss, then we must send a null report
* to the upper layer to discard potentially pressed keys that may be
* repeated forever by the input layer. Return 1 to hid-core as no
* further processing is required.
*
* 4) Data is from DJ collection and is an actual input event from
* a paired DJ device in which case we forward it to the correct hid
* device (via hid_input_report() ) and return 1 so hid-core does not do
* anything else with it.
*/
spin_lock_irqsave(&djrcv_dev->lock, flags);
if (dj_report->report_id == REPORT_ID_DJ_SHORT) {
switch (dj_report->report_type) {
case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
logi_dj_recv_queue_notification(djrcv_dev, dj_report);
break;
case REPORT_TYPE_NOTIF_CONNECTION_STATUS:
if (dj_report->report_params[CONNECTION_STATUS_PARAM_STATUS] ==
STATUS_LINKLOSS) {
logi_dj_recv_forward_null_report(djrcv_dev, dj_report);
}
break;
default:
logi_dj_recv_forward_report(djrcv_dev, dj_report);
}
report_processed = true;
}
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
return report_processed;
}
static int logi_dj_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct dj_receiver_dev *djrcv_dev;
int retval;
if (is_dj_device((struct dj_device *)hdev->driver_data))
return -ENODEV;
dbg_hid("%s called for ifnum %d\n", __func__,
intf->cur_altsetting->desc.bInterfaceNumber);
/* Ignore interfaces 0 and 1, they will not carry any data, dont create
* any hid_device for them */
if (intf->cur_altsetting->desc.bInterfaceNumber !=
LOGITECH_DJ_INTERFACE_NUMBER) {
dbg_hid("%s: ignoring ifnum %d\n", __func__,
intf->cur_altsetting->desc.bInterfaceNumber);
return -ENODEV;
}
/* Treat interface 2 */
djrcv_dev = kzalloc(sizeof(struct dj_receiver_dev), GFP_KERNEL);
if (!djrcv_dev) {
dev_err(&hdev->dev,
"%s:failed allocating dj_receiver_dev\n", __func__);
return -ENOMEM;
}
djrcv_dev->hdev = hdev;
INIT_WORK(&djrcv_dev->work, delayedwork_callback);
spin_lock_init(&djrcv_dev->lock);
if (kfifo_alloc(&djrcv_dev->notif_fifo,
DJ_MAX_NUMBER_NOTIFICATIONS * sizeof(struct dj_report),
GFP_KERNEL)) {
dev_err(&hdev->dev,
"%s:failed allocating notif_fifo\n", __func__);
kfree(djrcv_dev);
return -ENOMEM;
}
hid_set_drvdata(hdev, djrcv_dev);
/* Call to usbhid to fetch the HID descriptors of interface 2 and
* subsequently call to the hid/hid-core to parse the fetched
* descriptors, this will in turn create the hidraw and hiddev nodes
* for interface 2 of the receiver */
retval = hid_parse(hdev);
if (retval) {
dev_err(&hdev->dev,
"%s:parse of interface 2 failed\n", __func__);
goto hid_parse_fail;
}
/* Starts the usb device and connects to upper interfaces hiddev and
* hidraw */
retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (retval) {
dev_err(&hdev->dev,
"%s:hid_hw_start returned error\n", __func__);
goto hid_hw_start_fail;
}
retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
if (retval < 0) {
dev_err(&hdev->dev,
"%s:logi_dj_recv_switch_to_dj_mode returned error:%d\n",
__func__, retval);
goto switch_to_dj_mode_fail;
}
/* This is enabling the polling urb on the IN endpoint */
retval = hdev->ll_driver->open(hdev);
if (retval < 0) {
dev_err(&hdev->dev, "%s:hdev->ll_driver->open returned "
"error:%d\n", __func__, retval);
goto llopen_failed;
}
retval = logi_dj_recv_query_paired_devices(djrcv_dev);
if (retval < 0) {
dev_err(&hdev->dev, "%s:logi_dj_recv_query_paired_devices "
"error:%d\n", __func__, retval);
goto logi_dj_recv_query_paired_devices_failed;
}
return retval;
logi_dj_recv_query_paired_devices_failed:
hdev->ll_driver->close(hdev);
llopen_failed:
switch_to_dj_mode_fail:
hid_hw_stop(hdev);
hid_hw_start_fail:
hid_parse_fail:
kfifo_free(&djrcv_dev->notif_fifo);
kfree(djrcv_dev);
hid_set_drvdata(hdev, NULL);
return retval;
}
#ifdef CONFIG_PM
static int logi_dj_reset_resume(struct hid_device *hdev)
{
int retval;
struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
if (retval < 0) {
dev_err(&hdev->dev,
"%s:logi_dj_recv_switch_to_dj_mode returned error:%d\n",
__func__, retval);
}
return 0;
}
#endif
static void logi_dj_remove(struct hid_device *hdev)
{
struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
struct dj_device *dj_dev;
int i;
dbg_hid("%s\n", __func__);
cancel_work_sync(&djrcv_dev->work);
hdev->ll_driver->close(hdev);
hid_hw_stop(hdev);
/* I suppose that at this point the only context that can access
* the djrecv_data is this thread as the work item is guaranteed to
* have finished and no more raw_event callbacks should arrive after
* the remove callback was triggered so no locks are put around the
* code below */
for (i = 0; i < (DJ_MAX_PAIRED_DEVICES + DJ_DEVICE_INDEX_MIN); i++) {
dj_dev = djrcv_dev->paired_dj_devices[i];
if (dj_dev != NULL) {
hid_destroy_device(dj_dev->hdev);
kfree(dj_dev);
djrcv_dev->paired_dj_devices[i] = NULL;
}
}
kfifo_free(&djrcv_dev->notif_fifo);
kfree(djrcv_dev);
hid_set_drvdata(hdev, NULL);
}
static int logi_djdevice_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int ret;
struct dj_device *dj_dev = hdev->driver_data;
if (!is_dj_device(dj_dev))
return -ENODEV;
ret = hid_parse(hdev);
if (!ret)
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
return ret;
}
static const struct hid_device_id logi_dj_receivers[] = {
{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)},
{}
};
MODULE_DEVICE_TABLE(hid, logi_dj_receivers);
static struct hid_driver logi_djreceiver_driver = {
.name = "logitech-djreceiver",
.id_table = logi_dj_receivers,
.probe = logi_dj_probe,
.remove = logi_dj_remove,
.raw_event = logi_dj_raw_event,
#ifdef CONFIG_PM
.reset_resume = logi_dj_reset_resume,
#endif
};
static const struct hid_device_id logi_dj_devices[] = {
{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)},
{}
};
static struct hid_driver logi_djdevice_driver = {
.name = "logitech-djdevice",
.id_table = logi_dj_devices,
.probe = logi_djdevice_probe,
};
static int __init logi_dj_init(void)
{
int retval;
dbg_hid("Logitech-DJ:%s\n", __func__);
retval = hid_register_driver(&logi_djreceiver_driver);
if (retval)
return retval;
retval = hid_register_driver(&logi_djdevice_driver);
if (retval)
hid_unregister_driver(&logi_djreceiver_driver);
return retval;
}
static void __exit logi_dj_exit(void)
{
dbg_hid("Logitech-DJ:%s\n", __func__);
hid_unregister_driver(&logi_djdevice_driver);
hid_unregister_driver(&logi_djreceiver_driver);
}
module_init(logi_dj_init);
module_exit(logi_dj_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Logitech");
MODULE_AUTHOR("Nestor Lopez Casado");
MODULE_AUTHOR("nlopezcasad@logitech.com");

View File

@ -0,0 +1,123 @@
#ifndef __HID_LOGITECH_DJ_H
#define __HID_LOGITECH_DJ_H
/*
* HID driver for Logitech Unifying receivers
*
* Copyright (c) 2011 Logitech
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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/kfifo.h>
#define DJ_MAX_PAIRED_DEVICES 6
#define DJ_MAX_NUMBER_NOTIFICATIONS 8
#define DJ_DEVICE_INDEX_MIN 1
#define DJ_DEVICE_INDEX_MAX 6
#define DJREPORT_SHORT_LENGTH 15
#define DJREPORT_LONG_LENGTH 32
#define REPORT_ID_DJ_SHORT 0x20
#define REPORT_ID_DJ_LONG 0x21
#define REPORT_TYPE_RFREPORT_FIRST 0x01
#define REPORT_TYPE_RFREPORT_LAST 0x1F
/* Command Switch to DJ mode */
#define REPORT_TYPE_CMD_SWITCH 0x80
#define CMD_SWITCH_PARAM_DEVBITFIELD 0x00
#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS 0x01
#define TIMEOUT_NO_KEEPALIVE 0x00
/* Command to Get the list of Paired devices */
#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81
/* Device Paired Notification */
#define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41
#define SPFUNCTION_MORE_NOTIF_EXPECTED 0x01
#define SPFUNCTION_DEVICE_LIST_EMPTY 0x02
#define DEVICE_PAIRED_PARAM_SPFUNCTION 0x00
#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB 0x01
#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB 0x02
#define DEVICE_PAIRED_RF_REPORT_TYPE 0x03
/* Device Un-Paired Notification */
#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40
/* Connection Status Notification */
#define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42
#define CONNECTION_STATUS_PARAM_STATUS 0x00
#define STATUS_LINKLOSS 0x01
/* Error Notification */
#define REPORT_TYPE_NOTIF_ERROR 0x7F
#define NOTIF_ERROR_PARAM_ETYPE 0x00
#define ETYPE_KEEPALIVE_TIMEOUT 0x01
/* supported DJ HID && RF report types */
#define REPORT_TYPE_KEYBOARD 0x01
#define REPORT_TYPE_MOUSE 0x02
#define REPORT_TYPE_CONSUMER_CONTROL 0x03
#define REPORT_TYPE_SYSTEM_CONTROL 0x04
#define REPORT_TYPE_MEDIA_CENTER 0x08
#define REPORT_TYPE_LEDS 0x0E
/* RF Report types bitfield */
#define STD_KEYBOARD 0x00000002
#define STD_MOUSE 0x00000004
#define MULTIMEDIA 0x00000008
#define POWER_KEYS 0x00000010
#define MEDIA_CENTER 0x00000100
#define KBD_LEDS 0x00004000
struct dj_report {
u8 report_id;
u8 device_index;
u8 report_type;
u8 report_params[DJREPORT_SHORT_LENGTH - 3];
};
struct dj_receiver_dev {
struct hid_device *hdev;
struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
DJ_DEVICE_INDEX_MIN];
struct work_struct work;
struct kfifo notif_fifo;
spinlock_t lock;
};
struct dj_device {
struct hid_device *hdev;
struct dj_receiver_dev *dj_receiver_dev;
u32 reports_supported;
u8 device_index;
};
/**
* is_dj_device - know if the given dj_device is not the receiver.
* @dj_dev: the dj device to test
*
* This macro tests if a struct dj_device pointer is a device created
* by the bus enumarator.
*/
#define is_dj_device(dj_dev) \
(&(dj_dev)->dj_receiver_dev->hdev->dev == (dj_dev)->hdev->dev.parent)
#endif

View File

@ -47,10 +47,11 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1)
#define MT_QUIRK_CYPRESS (1 << 2)
#define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3)
#define MT_QUIRK_VALID_IS_INRANGE (1 << 4)
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 5)
#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 6)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 7)
#define MT_QUIRK_ALWAYS_VALID (1 << 4)
#define MT_QUIRK_VALID_IS_INRANGE (1 << 5)
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6)
#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 7)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8)
struct mt_slot {
__s32 x, y, p, w, h;
@ -86,11 +87,12 @@ struct mt_class {
/* classes of device behavior */
#define MT_CLS_DEFAULT 0x0001
#define MT_CLS_CONFIDENCE 0x0002
#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0003
#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0004
#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0005
#define MT_CLS_DUAL_NSMU_CONTACTID 0x0006
#define MT_CLS_SERIAL 0x0002
#define MT_CLS_CONFIDENCE 0x0003
#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0004
#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0005
#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0006
#define MT_CLS_DUAL_NSMU_CONTACTID 0x0007
/* vendor specific classes */
#define MT_CLS_3M 0x0101
@ -134,6 +136,8 @@ static int find_slot_from_contactid(struct mt_device *td)
struct mt_class mt_classes[] = {
{ .name = MT_CLS_DEFAULT,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP },
{ .name = MT_CLS_SERIAL,
.quirks = MT_QUIRK_ALWAYS_VALID},
{ .name = MT_CLS_CONFIDENCE,
.quirks = MT_QUIRK_VALID_IS_CONFIDENCE },
{ .name = MT_CLS_CONFIDENCE_MINUS_ONE,
@ -213,6 +217,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct mt_class *cls = td->mtclass;
__s32 quirks = cls->quirks;
/* Only map fields from TouchScreen or TouchPad collections.
* We need to ignore fields that belong to other collections
* such as Mouse that might have the same GenericDesktop usages. */
if (field->application == HID_DG_TOUCHSCREEN)
set_bit(INPUT_PROP_DIRECT, hi->input->propbit);
else if (field->application == HID_DG_TOUCHPAD)
set_bit(INPUT_PROP_POINTER, hi->input->propbit);
else
return 0;
switch (usage->hid & HID_USAGE_PAGE) {
case HID_UP_GENDESK:
@ -277,6 +291,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
td->last_slot_field = usage->hid;
td->last_field_index = field->index;
td->last_mt_collection = usage->collection_index;
hdev->quirks &= ~HID_QUIRK_MULTITOUCH;
return 1;
case HID_DG_WIDTH:
hid_map_usage(hi, usage, bit, max,
@ -435,7 +450,9 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
if (hid->claimed & HID_CLAIMED_INPUT && td->slots) {
switch (usage->hid) {
case HID_DG_INRANGE:
if (quirks & MT_QUIRK_VALID_IS_INRANGE)
if (quirks & MT_QUIRK_ALWAYS_VALID)
td->curvalid = true;
else if (quirks & MT_QUIRK_VALID_IS_INRANGE)
td->curvalid = value;
break;
case HID_DG_TIPSWITCH:
@ -513,12 +530,44 @@ static void mt_set_input_mode(struct hid_device *hdev)
}
}
/* a list of devices for which there is a specialized multitouch driver */
static const struct hid_device_id mt_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, 0x0001) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, 0x0006) },
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
{ }
};
static bool mt_match_one_id(struct hid_device *hdev,
const struct hid_device_id *id)
{
return id->bus == hdev->bus &&
(id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) &&
(id->product == HID_ANY_ID || id->product == hdev->product);
}
static const struct hid_device_id *mt_match_id(struct hid_device *hdev,
const struct hid_device_id *id)
{
for (; id->bus; id++)
if (mt_match_one_id(hdev, id))
return id;
return NULL;
}
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret, i;
struct mt_device *td;
struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
if (mt_match_id(hdev, mt_have_special_driver))
return -ENODEV;
for (i = 0; mt_classes[i].name ; i++) {
if (id->driver_data == mt_classes[i].name) {
mtclass = &(mt_classes[i]);
@ -526,10 +575,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
}
/* This allows the driver to correctly support devices
* that emit events over several HID messages.
*/
hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
td = kzalloc(sizeof(struct mt_device), GFP_KERNEL);
if (!td) {
@ -545,10 +590,16 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret != 0)
goto fail;
hdev->quirks |= HID_QUIRK_MULTITOUCH;
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret)
goto fail;
/* This allows the driver to correctly support devices
* that emit events over several HID messages.
*/
hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot),
GFP_KERNEL);
if (!td->slots) {
@ -662,6 +713,11 @@ static const struct hid_device_id mt_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH,
USB_DEVICE_ID_GOODTOUCH_000f) },
/* Ideacom panel */
{ .driver_data = MT_CLS_SERIAL,
HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM,
USB_DEVICE_ID_IDEACOM_IDC6650) },
/* Ilitek dual touch panel */
{ .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_ILITEK,
@ -672,6 +728,11 @@ static const struct hid_device_id mt_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS,
USB_DEVICE_ID_IRTOUCH_INFRARED_USB) },
/* LG Display panels */
{ .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_LG,
USB_DEVICE_ID_LG_MULTITOUCH) },
/* Lumio panels */
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
HID_USB_DEVICE(USB_VENDOR_ID_LUMIO,
@ -732,6 +793,10 @@ static const struct hid_device_id mt_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_XAT,
USB_DEVICE_ID_XAT_CSR) },
/* Rest of the world */
{ .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
{ }
};
MODULE_DEVICE_TABLE(hid, mt_devices);

View File

@ -37,6 +37,21 @@
static uint profile_numbers[5] = {0, 1, 2, 3, 4};
static void kone_profile_activated(struct kone_device *kone, uint new_profile)
{
kone->actual_profile = new_profile;
kone->actual_dpi = kone->profiles[new_profile - 1].startup_dpi;
}
static void kone_profile_report(struct kone_device *kone, uint new_profile)
{
struct kone_roccat_report roccat_report;
roccat_report.event = kone_mouse_event_switch_profile;
roccat_report.value = new_profile;
roccat_report.key = 0;
roccat_report_event(kone->chrdev_minor, (uint8_t *)&roccat_report);
}
static int kone_receive(struct usb_device *usb_dev, uint usb_command,
void *data, uint size)
{
@ -283,7 +298,7 @@ static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj,
container_of(kobj, struct device, kobj)->parent->parent;
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval = 0, difference;
int retval = 0, difference, old_profile;
/* I need to get my data in one piece */
if (off != 0 || count != sizeof(struct kone_settings))
@ -294,22 +309,21 @@ static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj,
if (difference) {
retval = kone_set_settings(usb_dev,
(struct kone_settings const *)buf);
if (!retval)
memcpy(&kone->settings, buf,
sizeof(struct kone_settings));
if (retval) {
mutex_unlock(&kone->kone_lock);
return retval;
}
old_profile = kone->settings.startup_profile;
memcpy(&kone->settings, buf, sizeof(struct kone_settings));
kone_profile_activated(kone, kone->settings.startup_profile);
if (kone->settings.startup_profile != old_profile)
kone_profile_report(kone, kone->settings.startup_profile);
}
mutex_unlock(&kone->kone_lock);
if (retval)
return retval;
/*
* If we get here, treat settings as okay and update actual values
* according to startup_profile
*/
kone->actual_profile = kone->settings.startup_profile;
kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi;
return sizeof(struct kone_settings);
}
@ -501,6 +515,8 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev,
goto exit_no_settings;
goto exit_unlock;
}
/* calibration resets profile */
kone_profile_activated(kone, kone->settings.startup_profile);
}
retval = size;
@ -544,16 +560,16 @@ static ssize_t kone_sysfs_set_startup_profile(struct device *dev,
kone_set_settings_checksum(&kone->settings);
retval = kone_set_settings(usb_dev, &kone->settings);
mutex_unlock(&kone->kone_lock);
if (retval)
if (retval) {
mutex_unlock(&kone->kone_lock);
return retval;
}
/* changing the startup profile immediately activates this profile */
kone->actual_profile = new_startup_profile;
kone->actual_dpi = kone->profiles[kone->actual_profile - 1].startup_dpi;
kone_profile_activated(kone, new_startup_profile);
kone_profile_report(kone, new_startup_profile);
mutex_unlock(&kone->kone_lock);
return size;
}
@ -665,8 +681,7 @@ static int kone_init_kone_device_struct(struct usb_device *usb_dev,
if (retval)
return retval;
kone->actual_profile = kone->settings.startup_profile;
kone->actual_dpi = kone->profiles[kone->actual_profile].startup_dpi;
kone_profile_activated(kone, kone->settings.startup_profile);
return 0;
}
@ -776,10 +791,10 @@ static void kone_keep_values_up_to_date(struct kone_device *kone,
{
switch (event->event) {
case kone_mouse_event_switch_profile:
kone->actual_dpi = kone->profiles[event->value - 1].
startup_dpi;
case kone_mouse_event_osd_profile:
kone->actual_profile = event->value;
kone->actual_dpi = kone->profiles[kone->actual_profile - 1].
startup_dpi;
break;
case kone_mouse_event_switch_dpi:
case kone_mouse_event_osd_dpi:

View File

@ -323,6 +323,7 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
struct usb_device *usb_dev;
unsigned long profile;
int retval;
struct kovaplus_roccat_report roccat_report;
dev = dev->parent->parent;
kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
@ -337,10 +338,22 @@ static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
mutex_lock(&kovaplus->kovaplus_lock);
retval = kovaplus_set_actual_profile(usb_dev, profile);
kovaplus_profile_activated(kovaplus, profile);
mutex_unlock(&kovaplus->kovaplus_lock);
if (retval)
if (retval) {
mutex_unlock(&kovaplus->kovaplus_lock);
return retval;
}
kovaplus_profile_activated(kovaplus, profile);
roccat_report.type = KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1;
roccat_report.profile = profile + 1;
roccat_report.button = 0;
roccat_report.data1 = profile + 1;
roccat_report.data2 = 0;
roccat_report_event(kovaplus->chrdev_minor,
(uint8_t const *)&roccat_report);
mutex_unlock(&kovaplus->kovaplus_lock);
return size;
}

View File

@ -298,6 +298,7 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp,
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval = 0;
int difference;
struct pyra_roccat_report roccat_report;
if (off != 0 || count != sizeof(struct pyra_settings))
return -EINVAL;
@ -307,17 +308,23 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp,
if (difference) {
retval = pyra_set_settings(usb_dev,
(struct pyra_settings const *)buf);
if (!retval)
memcpy(&pyra->settings, buf,
sizeof(struct pyra_settings));
if (retval) {
mutex_unlock(&pyra->pyra_lock);
return retval;
}
memcpy(&pyra->settings, buf,
sizeof(struct pyra_settings));
profile_activated(pyra, pyra->settings.startup_profile);
roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2;
roccat_report.value = pyra->settings.startup_profile + 1;
roccat_report.key = 0;
roccat_report_event(pyra->chrdev_minor,
(uint8_t const *)&roccat_report);
}
mutex_unlock(&pyra->pyra_lock);
if (retval)
return retval;
profile_activated(pyra, pyra->settings.startup_profile);
return sizeof(struct pyra_settings);
}

View File

@ -10,15 +10,18 @@
* any later version.
*/
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/power_supply.h>
#include <linux/spinlock.h>
#include "hid-ids.h"
#define WIIMOTE_VERSION "0.1"
#define WIIMOTE_VERSION "0.2"
#define WIIMOTE_NAME "Nintendo Wii Remote"
#define WIIMOTE_BUFSIZE 32
@ -30,12 +33,26 @@ struct wiimote_buf {
struct wiimote_state {
spinlock_t lock;
__u8 flags;
__u8 accel_split[2];
/* synchronous cmd requests */
struct mutex sync;
struct completion ready;
int cmd;
__u32 opt;
/* results of synchronous requests */
__u8 cmd_battery;
__u8 cmd_err;
};
struct wiimote_data {
struct hid_device *hdev;
struct input_dev *input;
struct led_classdev *leds[4];
struct input_dev *accel;
struct input_dev *ir;
struct power_supply battery;
spinlock_t qlock;
__u8 head;
@ -46,23 +63,47 @@ struct wiimote_data {
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_FLAG_LED1 0x01
#define WIIPROTO_FLAG_LED2 0x02
#define WIIPROTO_FLAG_LED3 0x04
#define WIIPROTO_FLAG_LED4 0x08
#define WIIPROTO_FLAG_RUMBLE 0x10
#define WIIPROTO_FLAG_ACCEL 0x20
#define WIIPROTO_FLAG_IR_BASIC 0x40
#define WIIPROTO_FLAG_IR_EXT 0x80
#define WIIPROTO_FLAG_IR_FULL 0xc0 /* IR_BASIC | IR_EXT */
#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \
WIIPROTO_FLAG_IR_FULL)
/* return flag for led \num */
#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1))
enum wiiproto_reqs {
WIIPROTO_REQ_NULL = 0x0,
WIIPROTO_REQ_RUMBLE = 0x10,
WIIPROTO_REQ_LED = 0x11,
WIIPROTO_REQ_DRM = 0x12,
WIIPROTO_REQ_IR1 = 0x13,
WIIPROTO_REQ_SREQ = 0x15,
WIIPROTO_REQ_WMEM = 0x16,
WIIPROTO_REQ_RMEM = 0x17,
WIIPROTO_REQ_IR2 = 0x1a,
WIIPROTO_REQ_STATUS = 0x20,
WIIPROTO_REQ_DATA = 0x21,
WIIPROTO_REQ_RETURN = 0x22,
WIIPROTO_REQ_DRM_K = 0x30,
WIIPROTO_REQ_DRM_KA = 0x31,
WIIPROTO_REQ_DRM_KE = 0x32,
WIIPROTO_REQ_DRM_KAI = 0x33,
WIIPROTO_REQ_DRM_KEE = 0x34,
WIIPROTO_REQ_DRM_KAE = 0x35,
WIIPROTO_REQ_DRM_KIE = 0x36,
WIIPROTO_REQ_DRM_KAIE = 0x37,
WIIPROTO_REQ_DRM_E = 0x3d,
WIIPROTO_REQ_DRM_SKAI1 = 0x3e,
WIIPROTO_REQ_DRM_SKAI2 = 0x3f,
};
enum wiiproto_keys {
@ -94,6 +135,56 @@ static __u16 wiiproto_keymap[] = {
BTN_MODE, /* WIIPROTO_KEY_HOME */
};
static enum power_supply_property wiimote_battery_props[] = {
POWER_SUPPLY_PROP_CAPACITY
};
/* requires the state.lock spinlock to be held */
static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd,
__u32 opt)
{
return wdata->state.cmd == cmd && wdata->state.opt == opt;
}
/* requires the state.lock spinlock to be held */
static inline void wiimote_cmd_complete(struct wiimote_data *wdata)
{
wdata->state.cmd = WIIPROTO_REQ_NULL;
complete(&wdata->state.ready);
}
static inline int wiimote_cmd_acquire(struct wiimote_data *wdata)
{
return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0;
}
/* requires the state.lock spinlock to be held */
static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd,
__u32 opt)
{
INIT_COMPLETION(wdata->state.ready);
wdata->state.cmd = cmd;
wdata->state.opt = opt;
}
static inline void wiimote_cmd_release(struct wiimote_data *wdata)
{
mutex_unlock(&wdata->state.sync);
}
static inline int wiimote_cmd_wait(struct wiimote_data *wdata)
{
int ret;
ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ);
if (ret < 0)
return -ERESTARTSYS;
else if (ret == 0)
return -EIO;
else
return 0;
}
static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
size_t count)
{
@ -172,6 +263,39 @@ static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
spin_unlock_irqrestore(&wdata->qlock, flags);
}
/*
* This sets the rumble bit on the given output report if rumble is
* currently enabled.
* \cmd1 must point to the second byte in the output report => &cmd[1]
* This must be called on nearly every output report before passing it
* into the output queue!
*/
static inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1)
{
if (wdata->state.flags & WIIPROTO_FLAG_RUMBLE)
*cmd1 |= 0x01;
}
static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble)
{
__u8 cmd[2];
rumble = !!rumble;
if (rumble == !!(wdata->state.flags & WIIPROTO_FLAG_RUMBLE))
return;
if (rumble)
wdata->state.flags |= WIIPROTO_FLAG_RUMBLE;
else
wdata->state.flags &= ~WIIPROTO_FLAG_RUMBLE;
cmd[0] = WIIPROTO_REQ_RUMBLE;
cmd[1] = 0;
wiiproto_keep_rumble(wdata, &cmd[1]);
wiimote_queue(wdata, cmd, sizeof(cmd));
}
static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
{
__u8 cmd[2];
@ -193,6 +317,7 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
if (leds & WIIPROTO_FLAG_LED4)
cmd[1] |= 0x80;
wiiproto_keep_rumble(wdata, &cmd[1]);
wiimote_queue(wdata, cmd, sizeof(cmd));
}
@ -203,7 +328,23 @@ static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
*/
static __u8 select_drm(struct wiimote_data *wdata)
{
return WIIPROTO_REQ_DRM_K;
__u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR;
if (ir == WIIPROTO_FLAG_IR_BASIC) {
if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
return WIIPROTO_REQ_DRM_KAIE;
else
return WIIPROTO_REQ_DRM_KIE;
} else if (ir == WIIPROTO_FLAG_IR_EXT) {
return WIIPROTO_REQ_DRM_KAI;
} else if (ir == WIIPROTO_FLAG_IR_FULL) {
return WIIPROTO_REQ_DRM_SKAI1;
} else {
if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
return WIIPROTO_REQ_DRM_KA;
else
return WIIPROTO_REQ_DRM_K;
}
}
static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
@ -217,9 +358,256 @@ static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
cmd[1] = 0;
cmd[2] = drm;
wiiproto_keep_rumble(wdata, &cmd[1]);
wiimote_queue(wdata, cmd, sizeof(cmd));
}
static void wiiproto_req_status(struct wiimote_data *wdata)
{
__u8 cmd[2];
cmd[0] = WIIPROTO_REQ_SREQ;
cmd[1] = 0;
wiiproto_keep_rumble(wdata, &cmd[1]);
wiimote_queue(wdata, cmd, sizeof(cmd));
}
static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel)
{
accel = !!accel;
if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
return;
if (accel)
wdata->state.flags |= WIIPROTO_FLAG_ACCEL;
else
wdata->state.flags &= ~WIIPROTO_FLAG_ACCEL;
wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
}
static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags)
{
__u8 cmd[2];
cmd[0] = WIIPROTO_REQ_IR1;
cmd[1] = flags;
wiiproto_keep_rumble(wdata, &cmd[1]);
wiimote_queue(wdata, cmd, sizeof(cmd));
}
static void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags)
{
__u8 cmd[2];
cmd[0] = WIIPROTO_REQ_IR2;
cmd[1] = flags;
wiiproto_keep_rumble(wdata, &cmd[1]);
wiimote_queue(wdata, cmd, sizeof(cmd));
}
#define wiiproto_req_wreg(wdata, os, buf, sz) \
wiiproto_req_wmem((wdata), false, (os), (buf), (sz))
#define wiiproto_req_weeprom(wdata, os, buf, sz) \
wiiproto_req_wmem((wdata), true, (os), (buf), (sz))
static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom,
__u32 offset, const __u8 *buf, __u8 size)
{
__u8 cmd[22];
if (size > 16 || size == 0) {
hid_warn(wdata->hdev, "Invalid length %d wmem request\n", size);
return;
}
memset(cmd, 0, sizeof(cmd));
cmd[0] = WIIPROTO_REQ_WMEM;
cmd[2] = (offset >> 16) & 0xff;
cmd[3] = (offset >> 8) & 0xff;
cmd[4] = offset & 0xff;
cmd[5] = size;
memcpy(&cmd[6], buf, size);
if (!eeprom)
cmd[1] |= 0x04;
wiiproto_keep_rumble(wdata, &cmd[1]);
wiimote_queue(wdata, cmd, sizeof(cmd));
}
/* requries the cmd-mutex to be held */
static int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
const __u8 *wmem, __u8 size)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&wdata->state.lock, flags);
wiimote_cmd_set(wdata, WIIPROTO_REQ_WMEM, 0);
wiiproto_req_wreg(wdata, offset, wmem, size);
spin_unlock_irqrestore(&wdata->state.lock, flags);
ret = wiimote_cmd_wait(wdata);
if (!ret && wdata->state.cmd_err)
ret = -EIO;
return ret;
}
static int wiimote_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct wiimote_data *wdata = container_of(psy,
struct wiimote_data, battery);
int ret = 0, state;
unsigned long flags;
ret = wiimote_cmd_acquire(wdata);
if (ret)
return ret;
spin_lock_irqsave(&wdata->state.lock, flags);
wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0);
wiiproto_req_status(wdata);
spin_unlock_irqrestore(&wdata->state.lock, flags);
ret = wiimote_cmd_wait(wdata);
state = wdata->state.cmd_battery;
wiimote_cmd_release(wdata);
if (ret)
return ret;
switch (psp) {
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = state * 100 / 255;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode)
{
int ret;
unsigned long flags;
__u8 format = 0;
static const __u8 data_enable[] = { 0x01 };
static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01,
0x00, 0xaa, 0x00, 0x64 };
static const __u8 data_sens2[] = { 0x63, 0x03 };
static const __u8 data_fin[] = { 0x08 };
spin_lock_irqsave(&wdata->state.lock, flags);
if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) {
spin_unlock_irqrestore(&wdata->state.lock, flags);
return 0;
}
if (mode == 0) {
wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
wiiproto_req_ir1(wdata, 0);
wiiproto_req_ir2(wdata, 0);
wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
spin_unlock_irqrestore(&wdata->state.lock, flags);
return 0;
}
spin_unlock_irqrestore(&wdata->state.lock, flags);
ret = wiimote_cmd_acquire(wdata);
if (ret)
return ret;
/* send PIXEL CLOCK ENABLE cmd first */
spin_lock_irqsave(&wdata->state.lock, flags);
wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0);
wiiproto_req_ir1(wdata, 0x06);
spin_unlock_irqrestore(&wdata->state.lock, flags);
ret = wiimote_cmd_wait(wdata);
if (ret)
goto unlock;
if (wdata->state.cmd_err) {
ret = -EIO;
goto unlock;
}
/* enable IR LOGIC */
spin_lock_irqsave(&wdata->state.lock, flags);
wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0);
wiiproto_req_ir2(wdata, 0x06);
spin_unlock_irqrestore(&wdata->state.lock, flags);
ret = wiimote_cmd_wait(wdata);
if (ret)
goto unlock;
if (wdata->state.cmd_err) {
ret = -EIO;
goto unlock;
}
/* enable IR cam but do not make it send data, yet */
ret = wiimote_cmd_write(wdata, 0xb00030, data_enable,
sizeof(data_enable));
if (ret)
goto unlock;
/* write first sensitivity block */
ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1,
sizeof(data_sens1));
if (ret)
goto unlock;
/* write second sensitivity block */
ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2,
sizeof(data_sens2));
if (ret)
goto unlock;
/* put IR cam into desired state */
switch (mode) {
case WIIPROTO_FLAG_IR_FULL:
format = 5;
break;
case WIIPROTO_FLAG_IR_EXT:
format = 3;
break;
case WIIPROTO_FLAG_IR_BASIC:
format = 1;
break;
}
ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format));
if (ret)
goto unlock;
/* make IR cam send data */
ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin));
if (ret)
goto unlock;
/* request new DRM mode compatible to IR mode */
spin_lock_irqsave(&wdata->state.lock, flags);
wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
wdata->state.flags |= mode & WIIPROTO_FLAGS_IR;
wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
spin_unlock_irqrestore(&wdata->state.lock, flags);
unlock:
wiimote_cmd_release(wdata);
return ret;
}
static enum led_brightness wiimote_leds_get(struct led_classdev *led_dev)
{
struct wiimote_data *wdata;
@ -268,9 +656,28 @@ static void wiimote_leds_set(struct led_classdev *led_dev,
}
}
static int wiimote_input_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value)
static int wiimote_ff_play(struct input_dev *dev, void *data,
struct ff_effect *eff)
{
struct wiimote_data *wdata = input_get_drvdata(dev);
__u8 value;
unsigned long flags;
/*
* The wiimote supports only a single rumble motor so if any magnitude
* is set to non-zero then we start the rumble motor. If both are set to
* zero, we stop the rumble motor.
*/
if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
value = 1;
else
value = 0;
spin_lock_irqsave(&wdata->state.lock, flags);
wiiproto_req_rumble(wdata, value);
spin_unlock_irqrestore(&wdata->state.lock, flags);
return 0;
}
@ -288,6 +695,61 @@ static void wiimote_input_close(struct input_dev *dev)
hid_hw_close(wdata->hdev);
}
static int wiimote_accel_open(struct input_dev *dev)
{
struct wiimote_data *wdata = input_get_drvdata(dev);
int ret;
unsigned long flags;
ret = hid_hw_open(wdata->hdev);
if (ret)
return ret;
spin_lock_irqsave(&wdata->state.lock, flags);
wiiproto_req_accel(wdata, true);
spin_unlock_irqrestore(&wdata->state.lock, flags);
return 0;
}
static void wiimote_accel_close(struct input_dev *dev)
{
struct wiimote_data *wdata = input_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&wdata->state.lock, flags);
wiiproto_req_accel(wdata, false);
spin_unlock_irqrestore(&wdata->state.lock, flags);
hid_hw_close(wdata->hdev);
}
static int wiimote_ir_open(struct input_dev *dev)
{
struct wiimote_data *wdata = input_get_drvdata(dev);
int ret;
ret = hid_hw_open(wdata->hdev);
if (ret)
return ret;
ret = wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC);
if (ret) {
hid_hw_close(wdata->hdev);
return ret;
}
return 0;
}
static void wiimote_ir_close(struct input_dev *dev)
{
struct wiimote_data *wdata = input_get_drvdata(dev);
wiimote_init_ir(wdata, 0);
hid_hw_close(wdata->hdev);
}
static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
{
input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT],
@ -315,12 +777,100 @@ static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
input_sync(wdata->input);
}
static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
{
__u16 x, y, z;
if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
return;
/*
* payload is: BB BB XX YY ZZ
* Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ
* contain the upper 8 bits of each value. The lower 2 bits are
* contained in the buttons data BB BB.
* Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the
* X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y
* accel value and bit 6 is the second bit of the Z value.
* The first bit of Y and Z values is not available and always set to 0.
* 0x200 is returned on no movement.
*/
x = payload[2] << 2;
y = payload[3] << 2;
z = payload[4] << 2;
x |= (payload[0] >> 5) & 0x3;
y |= (payload[1] >> 4) & 0x2;
z |= (payload[1] >> 5) & 0x2;
input_report_abs(wdata->accel, ABS_RX, x - 0x200);
input_report_abs(wdata->accel, ABS_RY, y - 0x200);
input_report_abs(wdata->accel, ABS_RZ, z - 0x200);
input_sync(wdata->accel);
}
#define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
ABS_HAT0X, ABS_HAT0Y)
#define ir_to_input1(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
ABS_HAT1X, ABS_HAT1Y)
#define ir_to_input2(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
ABS_HAT2X, ABS_HAT2Y)
#define ir_to_input3(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
ABS_HAT3X, ABS_HAT3Y)
static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir,
bool packed, __u8 xid, __u8 yid)
{
__u16 x, y;
if (!(wdata->state.flags & WIIPROTO_FLAGS_IR))
return;
/*
* 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
* 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
* have two IR sets in 5 bytes instead of 6.
* The resulting 10bit X/Y values are passed to the ABS_HATXY input dev.
*/
if (packed) {
x = ir[1] << 2;
y = ir[2] << 2;
x |= ir[0] & 0x3;
y |= (ir[0] >> 2) & 0x3;
} else {
x = ir[0] << 2;
y = ir[1] << 2;
x |= (ir[2] >> 4) & 0x3;
y |= (ir[2] >> 6) & 0x3;
}
input_report_abs(wdata->ir, xid, x);
input_report_abs(wdata->ir, yid, y);
}
static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
{
handler_keys(wdata, payload);
/* on status reports the drm is reset so we need to resend the drm */
wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) {
wdata->state.cmd_battery = payload[5];
wiimote_cmd_complete(wdata);
}
}
static void handler_data(struct wiimote_data *wdata, const __u8 *payload)
{
handler_keys(wdata, payload);
}
static void handler_return(struct wiimote_data *wdata, const __u8 *payload)
@ -330,9 +880,105 @@ static void handler_return(struct wiimote_data *wdata, const __u8 *payload)
handler_keys(wdata, payload);
if (err)
if (wiimote_cmd_pending(wdata, cmd, 0)) {
wdata->state.cmd_err = err;
wiimote_cmd_complete(wdata);
} else if (err) {
hid_warn(wdata->hdev, "Remote error %hhu on req %hhu\n", err,
cmd);
}
}
static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload)
{
handler_keys(wdata, payload);
handler_accel(wdata, payload);
}
static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload)
{
handler_keys(wdata, payload);
}
static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
{
handler_keys(wdata, payload);
handler_accel(wdata, payload);
ir_to_input0(wdata, &payload[5], false);
ir_to_input1(wdata, &payload[8], false);
ir_to_input2(wdata, &payload[11], false);
ir_to_input3(wdata, &payload[14], false);
input_sync(wdata->ir);
}
static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload)
{
handler_keys(wdata, payload);
}
static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
{
handler_keys(wdata, payload);
ir_to_input0(wdata, &payload[2], false);
ir_to_input1(wdata, &payload[4], true);
ir_to_input2(wdata, &payload[7], false);
ir_to_input3(wdata, &payload[9], true);
input_sync(wdata->ir);
}
static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload)
{
handler_keys(wdata, payload);
handler_accel(wdata, payload);
}
static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
{
handler_keys(wdata, payload);
handler_accel(wdata, payload);
ir_to_input0(wdata, &payload[5], false);
ir_to_input1(wdata, &payload[7], true);
ir_to_input2(wdata, &payload[10], false);
ir_to_input3(wdata, &payload[12], true);
input_sync(wdata->ir);
}
static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload)
{
}
static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
{
handler_keys(wdata, payload);
wdata->state.accel_split[0] = payload[2];
wdata->state.accel_split[1] = (payload[0] >> 1) & (0x10 | 0x20);
wdata->state.accel_split[1] |= (payload[1] << 1) & (0x40 | 0x80);
ir_to_input0(wdata, &payload[3], false);
ir_to_input1(wdata, &payload[12], false);
input_sync(wdata->ir);
}
static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload)
{
__u8 buf[5];
handler_keys(wdata, payload);
wdata->state.accel_split[1] |= (payload[0] >> 5) & (0x01 | 0x02);
wdata->state.accel_split[1] |= (payload[1] >> 3) & (0x04 | 0x08);
buf[0] = 0;
buf[1] = 0;
buf[2] = wdata->state.accel_split[0];
buf[3] = payload[2];
buf[4] = wdata->state.accel_split[1];
handler_accel(wdata, buf);
ir_to_input2(wdata, &payload[3], false);
ir_to_input3(wdata, &payload[12], false);
input_sync(wdata->ir);
}
struct wiiproto_handler {
@ -343,8 +989,19 @@ struct wiiproto_handler {
static struct wiiproto_handler handlers[] = {
{ .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status },
{ .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data },
{ .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return },
{ .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys },
{ .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA },
{ .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE },
{ .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI },
{ .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE },
{ .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE },
{ .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE },
{ .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE },
{ .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E },
{ .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 },
{ .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 },
{ .id = 0 }
};
@ -355,6 +1012,7 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
struct wiiproto_handler *h;
int i;
unsigned long flags;
bool handled = false;
if (size < 1)
return -EINVAL;
@ -363,10 +1021,16 @@ static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
for (i = 0; handlers[i].id; ++i) {
h = &handlers[i];
if (h->id == raw_data[0] && h->size < size)
if (h->id == raw_data[0] && h->size < size) {
h->func(wdata, &raw_data[1]);
handled = true;
}
}
if (!handled)
hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0],
size);
spin_unlock_irqrestore(&wdata->state.lock, flags);
return 0;
@ -434,16 +1098,13 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
return NULL;
wdata->input = input_allocate_device();
if (!wdata->input) {
kfree(wdata);
return NULL;
}
if (!wdata->input)
goto err;
wdata->hdev = hdev;
hid_set_drvdata(hdev, wdata);
input_set_drvdata(wdata->input, wdata);
wdata->input->event = wiimote_input_event;
wdata->input->open = wiimote_input_open;
wdata->input->close = wiimote_input_close;
wdata->input->dev.parent = &wdata->hdev->dev;
@ -457,18 +1118,89 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
for (i = 0; i < WIIPROTO_KEY_COUNT; ++i)
set_bit(wiiproto_keymap[i], wdata->input->keybit);
set_bit(FF_RUMBLE, wdata->input->ffbit);
if (input_ff_create_memless(wdata->input, NULL, wiimote_ff_play))
goto err_input;
wdata->accel = input_allocate_device();
if (!wdata->accel)
goto err_input;
input_set_drvdata(wdata->accel, wdata);
wdata->accel->open = wiimote_accel_open;
wdata->accel->close = wiimote_accel_close;
wdata->accel->dev.parent = &wdata->hdev->dev;
wdata->accel->id.bustype = wdata->hdev->bus;
wdata->accel->id.vendor = wdata->hdev->vendor;
wdata->accel->id.product = wdata->hdev->product;
wdata->accel->id.version = wdata->hdev->version;
wdata->accel->name = WIIMOTE_NAME " Accelerometer";
set_bit(EV_ABS, wdata->accel->evbit);
set_bit(ABS_RX, wdata->accel->absbit);
set_bit(ABS_RY, wdata->accel->absbit);
set_bit(ABS_RZ, wdata->accel->absbit);
input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4);
input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4);
input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4);
wdata->ir = input_allocate_device();
if (!wdata->ir)
goto err_ir;
input_set_drvdata(wdata->ir, wdata);
wdata->ir->open = wiimote_ir_open;
wdata->ir->close = wiimote_ir_close;
wdata->ir->dev.parent = &wdata->hdev->dev;
wdata->ir->id.bustype = wdata->hdev->bus;
wdata->ir->id.vendor = wdata->hdev->vendor;
wdata->ir->id.product = wdata->hdev->product;
wdata->ir->id.version = wdata->hdev->version;
wdata->ir->name = WIIMOTE_NAME " IR";
set_bit(EV_ABS, wdata->ir->evbit);
set_bit(ABS_HAT0X, wdata->ir->absbit);
set_bit(ABS_HAT0Y, wdata->ir->absbit);
set_bit(ABS_HAT1X, wdata->ir->absbit);
set_bit(ABS_HAT1Y, wdata->ir->absbit);
set_bit(ABS_HAT2X, wdata->ir->absbit);
set_bit(ABS_HAT2Y, wdata->ir->absbit);
set_bit(ABS_HAT3X, wdata->ir->absbit);
set_bit(ABS_HAT3Y, wdata->ir->absbit);
input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4);
input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4);
input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4);
input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4);
input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4);
input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4);
input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4);
input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4);
spin_lock_init(&wdata->qlock);
INIT_WORK(&wdata->worker, wiimote_worker);
spin_lock_init(&wdata->state.lock);
init_completion(&wdata->state.ready);
mutex_init(&wdata->state.sync);
return wdata;
err_ir:
input_free_device(wdata->accel);
err_input:
input_free_device(wdata->input);
err:
kfree(wdata);
return NULL;
}
static void wiimote_destroy(struct wiimote_data *wdata)
{
wiimote_leds_destroy(wdata);
power_supply_unregister(&wdata->battery);
input_unregister_device(wdata->accel);
input_unregister_device(wdata->ir);
input_unregister_device(wdata->input);
cancel_work_sync(&wdata->worker);
hid_hw_stop(wdata->hdev);
@ -500,12 +1232,37 @@ static int wiimote_hid_probe(struct hid_device *hdev,
goto err;
}
ret = input_register_device(wdata->input);
ret = input_register_device(wdata->accel);
if (ret) {
hid_err(hdev, "Cannot register input device\n");
goto err_stop;
}
ret = input_register_device(wdata->ir);
if (ret) {
hid_err(hdev, "Cannot register input device\n");
goto err_ir;
}
ret = input_register_device(wdata->input);
if (ret) {
hid_err(hdev, "Cannot register input device\n");
goto err_input;
}
wdata->battery.properties = wiimote_battery_props;
wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props);
wdata->battery.get_property = wiimote_battery_get_property;
wdata->battery.name = "wiimote_battery";
wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
wdata->battery.use_for_apm = 0;
ret = power_supply_register(&wdata->hdev->dev, &wdata->battery);
if (ret) {
hid_err(hdev, "Cannot register battery device\n");
goto err_battery;
}
ret = wiimote_leds_create(wdata);
if (ret)
goto err_free;
@ -523,9 +1280,20 @@ err_free:
wiimote_destroy(wdata);
return ret;
err_battery:
input_unregister_device(wdata->input);
wdata->input = NULL;
err_input:
input_unregister_device(wdata->ir);
wdata->ir = NULL;
err_ir:
input_unregister_device(wdata->accel);
wdata->accel = NULL;
err_stop:
hid_hw_stop(hdev);
err:
input_free_device(wdata->ir);
input_free_device(wdata->accel);
input_free_device(wdata->input);
kfree(wdata);
return ret;

View File

@ -312,6 +312,7 @@ 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