Commit e1c9b9ff authored by Jiri Kosina's avatar Jiri Kosina

Merge branches 'for-4.5/upstream-fixes', 'for-4.6/cmedia', 'for-4.6/i2c-hid',...

Merge branches 'for-4.5/upstream-fixes', 'for-4.6/cmedia', 'for-4.6/i2c-hid', 'for-4.6/logitech', 'for-4.6/multitouch', 'for-4.6/penmount', 'for-4.6/sony', 'for-4.6/thingm', 'for-4.6/upstream' and 'for-4.6/wacom' into for-linus
...@@ -196,6 +196,12 @@ config HID_PRODIKEYS ...@@ -196,6 +196,12 @@ config HID_PRODIKEYS
multimedia keyboard, but will lack support for the musical keyboard multimedia keyboard, but will lack support for the musical keyboard
and some additional multimedia keys. and some additional multimedia keys.
config HID_CMEDIA
tristate "CMedia CM6533 HID audio jack controls"
depends on HID
---help---
Support for CMedia CM6533 HID audio jack controls.
config HID_CP2112 config HID_CP2112
tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support" tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
depends on USB_HID && I2C && GPIOLIB depends on USB_HID && I2C && GPIOLIB
......
...@@ -29,6 +29,7 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o ...@@ -29,6 +29,7 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o
obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o
obj-$(CONFIG_HID_CP2112) += hid-cp2112.o obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
......
/*
* HID driver for CMedia CM6533 audio jack controls
*
* Copyright (C) 2015 Ben Chen <ben_chen@bizlinktech.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
MODULE_AUTHOR("Ben Chen");
MODULE_DESCRIPTION("CM6533 HID jack controls");
MODULE_LICENSE("GPL");
#define CM6533_JD_TYPE_COUNT 1
#define CM6533_JD_RAWEV_LEN 16
#define CM6533_JD_SFX_OFFSET 8
/*
*
*CM6533 audio jack HID raw events:
*
*Plug in:
*01000600 002083xx 080008c0 10000000
*about 3 seconds later...
*01000a00 002083xx 08000380 10000000
*01000600 002083xx 08000380 10000000
*
*Plug out:
*01000400 002083xx 080008c0 x0000000
*/
static const u8 ji_sfx[] = { 0x08, 0x00, 0x08, 0xc0 };
static const u8 ji_in[] = { 0x01, 0x00, 0x06, 0x00 };
static const u8 ji_out[] = { 0x01, 0x00, 0x04, 0x00 };
static int jack_switch_types[CM6533_JD_TYPE_COUNT] = {
SW_HEADPHONE_INSERT,
};
struct cmhid {
struct input_dev *input_dev;
struct hid_device *hid;
unsigned short switch_map[CM6533_JD_TYPE_COUNT];
};
static void hp_ev(struct hid_device *hid, struct cmhid *cm, int value)
{
input_report_switch(cm->input_dev, SW_HEADPHONE_INSERT, value);
input_sync(cm->input_dev);
}
static int cmhid_raw_event(struct hid_device *hid, struct hid_report *report,
u8 *data, int len)
{
struct cmhid *cm = hid_get_drvdata(hid);
if (len != CM6533_JD_RAWEV_LEN)
goto out;
if (memcmp(data+CM6533_JD_SFX_OFFSET, ji_sfx, sizeof(ji_sfx)))
goto out;
if (!memcmp(data, ji_out, sizeof(ji_out))) {
hp_ev(hid, cm, 0);
goto out;
}
if (!memcmp(data, ji_in, sizeof(ji_in))) {
hp_ev(hid, cm, 1);
goto out;
}
out:
return 0;
}
static int cmhid_input_configured(struct hid_device *hid,
struct hid_input *hidinput)
{
struct input_dev *input_dev = hidinput->input;
struct cmhid *cm = hid_get_drvdata(hid);
int i;
cm->input_dev = input_dev;
memcpy(cm->switch_map, jack_switch_types, sizeof(cm->switch_map));
input_dev->evbit[0] = BIT(EV_SW);
for (i = 0; i < CM6533_JD_TYPE_COUNT; i++)
input_set_capability(cm->input_dev,
EV_SW, jack_switch_types[i]);
return 0;
}
static int cmhid_input_mapping(struct hid_device *hid,
struct hid_input *hi, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max)
{
return -1;
}
static int cmhid_probe(struct hid_device *hid, const struct hid_device_id *id)
{
int ret;
struct cmhid *cm;
cm = kzalloc(sizeof(struct cmhid), GFP_KERNEL);
if (!cm) {
ret = -ENOMEM;
goto allocfail;
}
cm->hid = hid;
hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
hid_set_drvdata(hid, cm);
ret = hid_parse(hid);
if (ret) {
hid_err(hid, "parse failed\n");
goto fail;
}
ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE);
if (ret) {
hid_err(hid, "hw start failed\n");
goto fail;
}
return 0;
fail:
kfree(cm);
allocfail:
return ret;
}
static void cmhid_remove(struct hid_device *hid)
{
struct cmhid *cm = hid_get_drvdata(hid);
hid_hw_stop(hid);
kfree(cm);
}
static const struct hid_device_id cmhid_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
{ }
};
MODULE_DEVICE_TABLE(hid, cmhid_devices);
static struct hid_driver cmhid_driver = {
.name = "cm6533_jd",
.id_table = cmhid_devices,
.raw_event = cmhid_raw_event,
.input_configured = cmhid_input_configured,
.probe = cmhid_probe,
.remove = cmhid_remove,
.input_mapping = cmhid_input_mapping,
};
module_hid_driver(cmhid_driver);
...@@ -1965,6 +1965,7 @@ static const struct hid_device_id hid_have_special_driver[] = { ...@@ -1965,6 +1965,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) },
...@@ -2049,6 +2050,7 @@ static const struct hid_device_id hid_have_special_driver[] = { ...@@ -2049,6 +2050,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) }, { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
...@@ -2097,6 +2099,7 @@ static const struct hid_device_id hid_have_special_driver[] = { ...@@ -2097,6 +2099,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14) }, { HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
{ } { }
}; };
......
...@@ -151,7 +151,7 @@ static inline int drff_init(struct hid_device *hid) ...@@ -151,7 +151,7 @@ static inline int drff_init(struct hid_device *hid)
* descriptor. In any case, it's a wonder it works on Windows. * descriptor. In any case, it's a wonder it works on Windows.
* *
* Usage Page (Desktop), ; Generic desktop controls (01h) * Usage Page (Desktop), ; Generic desktop controls (01h)
* Usage (Joystik), ; Joystik (04h, application collection) * Usage (Joystick), ; Joystick (04h, application collection)
* Collection (Application), * Collection (Application),
* Collection (Logical), * Collection (Logical),
* Report Size (8), * Report Size (8),
...@@ -207,7 +207,7 @@ static inline int drff_init(struct hid_device *hid) ...@@ -207,7 +207,7 @@ static inline int drff_init(struct hid_device *hid)
/* Fixed report descriptor for PID 0x011 joystick */ /* Fixed report descriptor for PID 0x011 joystick */
static __u8 pid0011_rdesc_fixed[] = { static __u8 pid0011_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */ 0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystik), */ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */ 0xA1, 0x02, /* Collection (Logical), */
0x14, /* Logical Minimum (0), */ 0x14, /* Logical Minimum (0), */
......
...@@ -249,6 +249,7 @@ ...@@ -249,6 +249,7 @@
#define USB_VENDOR_ID_CMEDIA 0x0d8c #define USB_VENDOR_ID_CMEDIA 0x0d8c
#define USB_DEVICE_ID_CM109 0x000e #define USB_DEVICE_ID_CM109 0x000e
#define USB_DEVICE_ID_CM6533 0x0022
#define USB_VENDOR_ID_CODEMERCS 0x07c0 #define USB_VENDOR_ID_CODEMERCS 0x07c0
#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 #define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500
...@@ -683,6 +684,7 @@ ...@@ -683,6 +684,7 @@
#define USB_DEVICE_ID_MS_NE7K 0x071d #define USB_DEVICE_ID_MS_NE7K 0x071d
#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730 #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730
#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c
#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3
#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 #define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799
#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 #define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 #define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
...@@ -877,6 +879,9 @@ ...@@ -877,6 +879,9 @@
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002 #define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000 #define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
#define USB_VENDOR_ID_SINO_LITE 0x1345
#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008
#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 #define USB_VENDOR_ID_SOUNDGRAPH 0x15c2
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046
...@@ -1052,7 +1057,7 @@ ...@@ -1052,7 +1057,7 @@
#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */ #define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */
#define USB_VENDOR_ID_MULTIPLE_1781 0x1781 #define USB_VENDOR_ID_MULTIPLE_1781 0x1781
#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD 0x0a8d #define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD 0x0a9d
#define USB_VENDOR_ID_DRACAL_RAPHNET 0x289b #define USB_VENDOR_ID_DRACAL_RAPHNET 0x289b
#define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002 #define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002
......
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
*/ */
static __u8 df_rdesc_fixed[] = { static __u8 df_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */ 0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystik), */ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */ 0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */ 0x95, 0x01, /* Report Count (1), */
...@@ -127,7 +127,7 @@ static __u8 df_rdesc_fixed[] = { ...@@ -127,7 +127,7 @@ static __u8 df_rdesc_fixed[] = {
static __u8 dfp_rdesc_fixed[] = { static __u8 dfp_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */ 0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystik), */ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */ 0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */ 0x95, 0x01, /* Report Count (1), */
...@@ -175,7 +175,7 @@ static __u8 dfp_rdesc_fixed[] = { ...@@ -175,7 +175,7 @@ static __u8 dfp_rdesc_fixed[] = {
static __u8 fv_rdesc_fixed[] = { static __u8 fv_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */ 0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystik), */ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */ 0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */ 0x95, 0x01, /* Report Count (1), */
...@@ -242,7 +242,7 @@ static __u8 fv_rdesc_fixed[] = { ...@@ -242,7 +242,7 @@ static __u8 fv_rdesc_fixed[] = {
static __u8 momo_rdesc_fixed[] = { static __u8 momo_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */ 0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystik), */ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */ 0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */ 0x95, 0x01, /* Report Count (1), */
...@@ -288,7 +288,7 @@ static __u8 momo_rdesc_fixed[] = { ...@@ -288,7 +288,7 @@ static __u8 momo_rdesc_fixed[] = {
static __u8 momo2_rdesc_fixed[] = { static __u8 momo2_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */ 0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystik), */ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */ 0xA1, 0x02, /* Collection (Logical), */
0x95, 0x01, /* Report Count (1), */ 0x95, 0x01, /* Report Count (1), */
......
...@@ -15,13 +15,19 @@ ...@@ -15,13 +15,19 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h> #include <linux/device.h>
#include <linux/input.h>
#include <linux/usb.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/input/mt.h> #include <linux/input/mt.h>
#include <linux/workqueue.h>
#include <linux/atomic.h>
#include <linux/fixp-arith.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "usbhid/usbhid.h"
#include "hid-ids.h" #include "hid-ids.h"
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -773,6 +779,589 @@ static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev, ...@@ -773,6 +779,589 @@ static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
} }
} }
/* -------------------------------------------------------------------------- */
/* 0x8123: Force feedback support */
/* -------------------------------------------------------------------------- */
#define HIDPP_FF_GET_INFO 0x01
#define HIDPP_FF_RESET_ALL 0x11
#define HIDPP_FF_DOWNLOAD_EFFECT 0x21
#define HIDPP_FF_SET_EFFECT_STATE 0x31
#define HIDPP_FF_DESTROY_EFFECT 0x41
#define HIDPP_FF_GET_APERTURE 0x51
#define HIDPP_FF_SET_APERTURE 0x61
#define HIDPP_FF_GET_GLOBAL_GAINS 0x71
#define HIDPP_FF_SET_GLOBAL_GAINS 0x81
#define HIDPP_FF_EFFECT_STATE_GET 0x00
#define HIDPP_FF_EFFECT_STATE_STOP 0x01
#define HIDPP_FF_EFFECT_STATE_PLAY 0x02
#define HIDPP_FF_EFFECT_STATE_PAUSE 0x03
#define HIDPP_FF_EFFECT_CONSTANT 0x00
#define HIDPP_FF_EFFECT_PERIODIC_SINE 0x01
#define HIDPP_FF_EFFECT_PERIODIC_SQUARE 0x02
#define HIDPP_FF_EFFECT_PERIODIC_TRIANGLE 0x03
#define HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHUP 0x04
#define HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHDOWN 0x05
#define HIDPP_FF_EFFECT_SPRING 0x06
#define HIDPP_FF_EFFECT_DAMPER 0x07
#define HIDPP_FF_EFFECT_FRICTION 0x08
#define HIDPP_FF_EFFECT_INERTIA 0x09
#define HIDPP_FF_EFFECT_RAMP 0x0A
#define HIDPP_FF_EFFECT_AUTOSTART 0x80
#define HIDPP_FF_EFFECTID_NONE -1
#define HIDPP_FF_EFFECTID_AUTOCENTER -2
#define HIDPP_FF_MAX_PARAMS 20
#define HIDPP_FF_RESERVED_SLOTS 1
struct hidpp_ff_private_data {
struct hidpp_device *hidpp;
u8 feature_index;
u8 version;
u16 gain;
s16 range;
u8 slot_autocenter;
u8 num_effects;
int *effect_ids;
struct workqueue_struct *wq;
atomic_t workqueue_size;
};
struct hidpp_ff_work_data {
struct work_struct work;
struct hidpp_ff_private_data *data;
int effect_id;
u8 command;
u8 params[HIDPP_FF_MAX_PARAMS];
u8 size;
};
static const signed short hiddpp_ff_effects[] = {
FF_CONSTANT,
FF_PERIODIC,
FF_SINE,
FF_SQUARE,
FF_SAW_UP,
FF_SAW_DOWN,
FF_TRIANGLE,
FF_SPRING,
FF_DAMPER,
FF_AUTOCENTER,
FF_GAIN,
-1
};
static const signed short hiddpp_ff_effects_v2[] = {
FF_RAMP,
FF_FRICTION,
FF_INERTIA,
-1
};
static const u8 HIDPP_FF_CONDITION_CMDS[] = {
HIDPP_FF_EFFECT_SPRING,
HIDPP_FF_EFFECT_FRICTION,
HIDPP_FF_EFFECT_DAMPER,
HIDPP_FF_EFFECT_INERTIA
};
static const char *HIDPP_FF_CONDITION_NAMES[] = {
"spring",
"friction",
"damper",
"inertia"
};
static u8 hidpp_ff_find_effect(struct hidpp_ff_private_data *data, int effect_id)
{
int i;
for (i = 0; i < data->num_effects; i++)
if (data->effect_ids[i] == effect_id)
return i+1;
return 0;
}
static void hidpp_ff_work_handler(struct work_struct *w)
{
struct hidpp_ff_work_data *wd = container_of(w, struct hidpp_ff_work_data, work);
struct hidpp_ff_private_data *data = wd->data;
struct hidpp_report response;
u8 slot;
int ret;
/* add slot number if needed */
switch (wd->effect_id) {
case HIDPP_FF_EFFECTID_AUTOCENTER:
wd->params[0] = data->slot_autocenter;
break;
case HIDPP_FF_EFFECTID_NONE:
/* leave slot as zero */
break;
default:
/* find current slot for effect */
wd->params[0] = hidpp_ff_find_effect(data, wd->effect_id);
break;
}
/* send command and wait for reply */
ret = hidpp_send_fap_command_sync(data->hidpp, data->feature_index,
wd->command, wd->params, wd->size, &response);
if (ret) {
hid_err(data->hidpp->hid_dev, "Failed to send command to device!\n");
goto out;
}
/* parse return data */
switch (wd->command) {
case HIDPP_FF_DOWNLOAD_EFFECT:
slot = response.fap.params[0];
if (slot > 0 && slot <= data->num_effects) {
if (wd->effect_id >= 0)
/* regular effect uploaded */
data->effect_ids[slot-1] = wd->effect_id;
else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
/* autocenter spring uploaded */
data->slot_autocenter = slot;
}
break;
case HIDPP_FF_DESTROY_EFFECT:
if (wd->effect_id >= 0)
/* regular effect destroyed */
data->effect_ids[wd->params[0]-1] = -1;
else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
/* autocenter spring destoyed */
data->slot_autocenter = 0;
break;
case HIDPP_FF_SET_GLOBAL_GAINS:
data->gain = (wd->params[0] << 8) + wd->params[1];
break;
case HIDPP_FF_SET_APERTURE:
data->range = (wd->params[0] << 8) + wd->params[1];
break;
default:
/* no action needed */
break;
}
out:
atomic_dec(&data->workqueue_size);
kfree(wd);
}
static int hidpp_ff_queue_work(struct hidpp_ff_private_data *data, int effect_id, u8 command, u8 *params, u8 size)
{
struct hidpp_ff_work_data *wd = kzalloc(sizeof(*wd), GFP_KERNEL);
int s;
if (!wd)
return -ENOMEM;
INIT_WORK(&wd->work, hidpp_ff_work_handler);
wd->data = data;
wd->effect_id = effect_id;
wd->command = command;
wd->size = size;
memcpy(wd->params, params, size);
atomic_inc(&data->workqueue_size);
queue_work(data->wq, &wd->work);
/* warn about excessive queue size */
s = atomic_read(&data->workqueue_size);
if (s >= 20 && s % 20 == 0)
hid_warn(data->hidpp->hid_dev, "Force feedback command queue contains %d commands, causing substantial delays!", s);
return 0;
}
static int hidpp_ff_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
{
struct hidpp_ff_private_data *data = dev->ff->private;
u8 params[20];
u8 size;
int force;
/* set common parameters */
params[2] = effect->replay.length >> 8;
params[3] = effect->replay.length & 255;
params[4] = effect->replay.delay >> 8;
params[5] = effect->replay.delay & 255;
switch (effect->type) {
case FF_CONSTANT:
force = (effect->u.constant.level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
params[1] = HIDPP_FF_EFFECT_CONSTANT;
params[6] = force >> 8;
params[7] = force & 255;
params[8] = effect->u.constant.envelope.attack_level >> 7;
params[9] = effect->u.constant.envelope.attack_length >> 8;
params[10] = effect->u.constant.envelope.attack_length & 255;
params[11] = effect->u.constant.envelope.fade_level >> 7;
params[12] = effect->u.constant.envelope.fade_length >> 8;
params[13] = effect->u.constant.envelope.fade_length & 255;
size = 14;
dbg_hid("Uploading constant force level=%d in dir %d = %d\n",
effect->u.constant.level,
effect->direction, force);
dbg_hid(" envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
effect->u.constant.envelope.attack_level,
effect->u.constant.envelope.attack_length,
effect->u.constant.envelope.fade_level,
effect->u.constant.envelope.fade_length);
break;
case FF_PERIODIC:
{
switch (effect->u.periodic.waveform) {
case FF_SINE:
params[1] = HIDPP_FF_EFFECT_PERIODIC_SINE;
break;
case FF_SQUARE:
params[1] = HIDPP_FF_EFFECT_PERIODIC_SQUARE;
break;
case FF_SAW_UP:
params[1] = HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHUP;
break;
case FF_SAW_DOWN:
params[1] = HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHDOWN;
break;
case FF_TRIANGLE:
params[1] = HIDPP_FF_EFFECT_PERIODIC_TRIANGLE;
break;
default:
hid_err(data->hidpp->hid_dev, "Unexpected periodic waveform type %i!\n", effect->u.periodic.waveform);
return -EINVAL;
}
force = (effect->u.periodic.magnitude * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
params[6] = effect->u.periodic.magnitude >> 8;
params[7] = effect->u.periodic.magnitude & 255;
params[8] = effect->u.periodic.offset >> 8;
params[9] = effect->u.periodic.offset & 255;
params[10] = effect->u.periodic.period >> 8;
params[11] = effect->u.periodic.period & 255;
params[12] = effect->u.periodic.phase >> 8;
params[13] = effect->u.periodic.phase & 255;
params[14] = effect->u.periodic.envelope.attack_level >> 7;
params[15] = effect->u.periodic.envelope.attack_length >> 8;
params[16] = effect->u.periodic.envelope.attack_length & 255;
params[17] = effect->u.periodic.envelope.fade_level >> 7;
params[18] = effect->u.periodic.envelope.fade_length >> 8;
params[19] = effect->u.periodic.envelope.fade_length & 255;
size = 20;
dbg_hid("Uploading periodic force mag=%d/dir=%d, offset=%d, period=%d ms, phase=%d\n",
effect->u.periodic.magnitude, effect->direction,
effect->u.periodic.offset,
effect->u.periodic.period,
effect->u.periodic.phase);
dbg_hid(" envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
effect->u.periodic.envelope.attack_level,
effect->u.periodic.envelope.attack_length,
effect->u.periodic.envelope.fade_level,
effect->u.periodic.envelope.fade_length);
break;
}
case FF_RAMP:
params[1] = HIDPP_FF_EFFECT_RAMP;
force = (effect->u.ramp.start_level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
params[6] = force >> 8;
params[7] = force & 255;
force = (effect->u.ramp.end_level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
params[8] = force >> 8;
params[9] = force & 255;
params[10] = effect->u.ramp.envelope.attack_level >> 7;
params[11] = effect->u.ramp.envelope.attack_length >> 8;
params[12] = effect->u.ramp.envelope.attack_length & 255;
params[13] = effect->u.ramp.envelope.fade_level >> 7;
params[14] = effect->u.ramp.envelope.fade_length >> 8;
params[15] = effect->u.ramp.envelope.fade_length & 255;
size = 16;
dbg_hid("Uploading ramp force level=%d -> %d in dir %d = %d\n",
effect->u.ramp.start_level,
effect->u.ramp.end_level,
effect->direction, force);
dbg_hid(" envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
effect->u.ramp.envelope.attack_level,
effect->u.ramp.envelope.attack_length,
effect->u.ramp.envelope.fade_level,
effect->u.ramp.envelope.fade_length);
break;
case FF_FRICTION:
case FF_INERTIA:
case FF_SPRING:
case FF_DAMPER:
params[1] = HIDPP_FF_CONDITION_CMDS[effect->type - FF_SPRING];
params[6] = effect->u.condition[0].left_saturation >> 9;
params[7] = (effect->u.condition[0].left_saturation >> 1) & 255;
params[8] = effect->u.condition[0].left_coeff >> 8;
params[9] = effect->u.condition[0].left_coeff & 255;
params[10] = effect->u.condition[0].deadband >> 9;
params[11] = (effect->u.condition[0].deadband >> 1) & 255;
params[12] = effect->u.condition[0].center >> 8;
params[13] = effect->u.condition[0].center & 255;
params[14] = effect->u.condition[0].right_coeff >> 8;
params[15] = effect->u.condition[0].right_coeff & 255;
params[16] = effect->u.condition[0].right_saturation >> 9;
params[17] = (effect->u.condition[0].right_saturation >> 1) & 255;
size = 18;
dbg_hid("Uploading %s force left coeff=%d, left sat=%d, right coeff=%d, right sat=%d\n",
HIDPP_FF_CONDITION_NAMES[effect->type - FF_SPRING],
effect->u.condition[0].left_coeff,
effect->u.condition[0].left_saturation,
effect->u.condition[0].right_coeff,
effect->u.condition[0].right_saturation);
dbg_hid(" deadband=%d, center=%d\n",
effect->u.condition[0].deadband,
effect->u.condition[0].center);
break;
default:
hid_err(data->hidpp->hid_dev, "Unexpected force type %i!\n", effect->type);
return -EINVAL;
}
return hidpp_ff_queue_work(data, effect->id, HIDPP_FF_DOWNLOAD_EFFECT, params, size);
}
static int hidpp_ff_playback(struct input_dev *dev, int effect_id, int value)
{
struct hidpp_ff_private_data *data = dev->ff->private;
u8 params[2];
params[1] = value ? HIDPP_FF_EFFECT_STATE_PLAY : HIDPP_FF_EFFECT_STATE_STOP;
dbg_hid("St%sing playback of effect %d.\n", value?"art":"opp", effect_id);
return hidpp_ff_queue_work(data, effect_id, HIDPP_FF_SET_EFFECT_STATE, params, ARRAY_SIZE(params));
}
static int hidpp_ff_erase_effect(struct input_dev *dev, int effect_id)
{
struct hidpp_ff_private_data *data = dev->ff->private;
u8 slot = 0;
dbg_hid("Erasing effect %d.\n", effect_id);
return hidpp_ff_queue_work(data, effect_id, HIDPP_FF_DESTROY_EFFECT, &slot, 1);
}
static void hidpp_ff_set_autocenter(struct input_dev *dev, u16 magnitude)
{
struct hidpp_ff_private_data *data = dev->ff->private;
u8 params[18];
dbg_hid("Setting autocenter to %d.\n", magnitude);
/* start a standard spring effect */
params[1] = HIDPP_FF_EFFECT_SPRING | HIDPP_FF_EFFECT_AUTOSTART;
/* zero delay and duration */
params[2] = params[3] = params[4] = params[5] = 0;
/* set coeff to 25% of saturation */
params[8] = params[14] = magnitude >> 11;
params[9] = params[15] = (magnitude >> 3) & 255;
params[6] = params[16] = magnitude >> 9;
params[7] = params[17] = (magnitude >> 1) & 255;
/* zero deadband and center */
params[10] = params[11] = params[12] = params[13] = 0;
hidpp_ff_queue_work(data, HIDPP_FF_EFFECTID_AUTOCENTER, HIDPP_FF_DOWNLOAD_EFFECT, params, ARRAY_SIZE(params));
}
static void hidpp_ff_set_gain(struct input_dev *dev, u16 gain)
{
struct hidpp_ff_private_data *data = dev->ff->private;
u8 params[4];
dbg_hid("Setting gain to %d.\n", gain);
params[0] = gain >> 8;
params[1] = gain & 255;
params[2] = 0; /* no boost */
params[3] = 0;
hidpp_ff_queue_work(data, HIDPP_FF_EFFECTID_NONE, HIDPP_FF_SET_GLOBAL_GAINS, params, ARRAY_SIZE(params));
}
static ssize_t hidpp_ff_range_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct hid_device *hid = to_hid_device(dev);
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
struct input_dev *idev = hidinput->input;
struct hidpp_ff_private_data *data = idev->ff->private;
return scnprintf(buf, PAGE_SIZE, "%u\n", data->range);
}
static ssize_t hidpp_ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hid = to_hid_device(dev);
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
struct input_dev *idev = hidinput->input;
struct hidpp_ff_private_data *data = idev->ff->private;
u8 params[2];
int range = simple_strtoul(buf, NULL, 10);
range = clamp(range, 180, 900);
params[0] = range >> 8;
params[1] = range & 0x00FF;
hidpp_ff_queue_work(data, -1, HIDPP_FF_SET_APERTURE, params, ARRAY_SIZE(params));
return count;
}
static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, hidpp_ff_range_show, hidpp_ff_range_store);
static void hidpp_ff_destroy(struct ff_device *ff)
{
struct hidpp_ff_private_data *data = ff->private;
kfree(data->effect_ids);
}
static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
{
struct hid_device *hid = hidpp->hid_dev;
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
struct input_dev *dev = hidinput->input;
const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
struct ff_device *ff;
struct hidpp_report response;
struct hidpp_ff_private_data *data;
int error, j, num_slots;
u8 version;
if (!dev) {
hid_err(hid, "Struct input_dev not set!\n");
return -EINVAL;
}
/* Get firmware release */
version = bcdDevice & 255;
/* Set supported force feedback capabilities */
for (j = 0; hiddpp_ff_effects[j] >= 0; j++)
set_bit(hiddpp_ff_effects[j], dev->ffbit);
if (version > 1)
for (j = 0; hiddpp_ff_effects_v2[j] >= 0; j++)
set_bit(hiddpp_ff_effects_v2[j], dev->ffbit);
/* Read number of slots available in device */
error = hidpp_send_fap_command_sync(hidpp, feature_index,
HIDPP_FF_GET_INFO, NULL, 0, &response);
if (error) {
if (error < 0)
return error;
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, error);
return -EPROTO;
}
num_slots = response.fap.params[0] - HIDPP_FF_RESERVED_SLOTS;
error = input_ff_create(dev, num_slots);
if (error) {
hid_err(dev, "Failed to create FF device!\n");
return error;
}
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->effect_ids = kcalloc(num_slots, sizeof(int), GFP_KERNEL);
if (!data->effect_ids) {
kfree(data);
return -ENOMEM;
}
data->hidpp = hidpp;
data->feature_index = feature_index;
data->version = version;
data->slot_autocenter = 0;
data->num_effects = num_slots;
for (j = 0; j < num_slots; j++)
data->effect_ids[j] = -1;
ff = dev->ff;
ff->private = data;
ff->upload = hidpp_ff_upload_effect;
ff->erase = hidpp_ff_erase_effect;
ff->playback = hidpp_ff_playback;
ff->set_gain = hidpp_ff_set_gain;
ff->set_autocenter = hidpp_ff_set_autocenter;
ff->destroy = hidpp_ff_destroy;
/* reset all forces */
error = hidpp_send_fap_command_sync(hidpp, feature_index,
HIDPP_FF_RESET_ALL, NULL, 0, &response);
/* Read current Range */
error = hidpp_send_fap_command_sync(hidpp, feature_index,
HIDPP_FF_GET_APERTURE, NULL, 0, &response);
if (error)
hid_warn(hidpp->hid_dev, "Failed to read range from device!\n");
data->range = error ? 900 : get_unaligned_be16(&response.fap.params[0]);
/* Create sysfs interface */
error = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range);
if (error)
hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d!\n", error);
/* Read the current gain values */
error = hidpp_send_fap_command_sync(hidpp, feature_index,
HIDPP_FF_GET_GLOBAL_GAINS, NULL, 0, &response);
if (error)
hid_warn(hidpp->hid_dev, "Failed to read gain values from device!\n");
data->gain = error ? 0xffff : get_unaligned_be16(&response.fap.params[0]);
/* ignore boost value at response.fap.params[2] */
/* init the hardware command queue */
data->wq = create_singlethread_workqueue("hidpp-ff-sendqueue");
atomic_set(&data->workqueue_size, 0);
/* initialize with zero autocenter to get wheel in usable state */
hidpp_ff_set_autocenter(dev, 0);
hid_info(hid, "Force feeback support loaded (firmware release %d).\n", version);
return 0;
}
static int hidpp_ff_deinit(struct hid_device *hid)
{
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
struct input_dev *dev = hidinput->input;
struct hidpp_ff_private_data *data;
if (!dev) {
hid_err(hid, "Struct input_dev not found!\n");
return -EINVAL;
}
hid_info(hid, "Unloading HID++ force feedback.\n");
data = dev->ff->private;
if (!data) {
hid_err(hid, "Private data not found!\n");
return -EINVAL;
}
destroy_workqueue(data->wq);
device_remove_file(&hid->dev, &dev_attr_range);
return 0;
}
/* ************************************************************************** */ /* ************************************************************************** */
/* */ /* */
/* Device Support */ /* Device Support */
...@@ -1301,121 +1890,22 @@ static int k400_connect(struct hid_device *hdev, bool connected) ...@@ -1301,121 +1890,22 @@ static int k400_connect(struct hid_device *hdev, bool connected)
#define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123 #define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123
/* Using session ID = 1 */
#define CMD_G920_FORCE_GET_APERTURE 0x51
#define CMD_G920_FORCE_SET_APERTURE 0x61
struct g920_private_data {
u8 force_feature;
u16 range;
};
static ssize_t g920_range_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct hid_device *hid = to_hid_device(dev);
struct hidpp_device *hidpp = hid_get_drvdata(hid);
struct g920_private_data *pdata;
pdata = hidpp->private_data;
if (!pdata) {
hid_err(hid, "Private driver data not found!\n");
return -EINVAL;
}
return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->range);
}
static ssize_t g920_range_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct hid_device *hid = to_hid_device(dev);
struct hidpp_device *hidpp = hid_get_drvdata(hid);
struct g920_private_data *pdata;
struct hidpp_report response;
u8 params[2];
int ret;
u16 range = simple_strtoul(buf, NULL, 10);
pdata = hidpp->private_data;
if (!pdata) {
hid_err(hid, "Private driver data not found!\n");
return -EINVAL;
}
if (range < 180)
range = 180;
else if (range > 900)
range = 900;
params[0] = range >> 8;
params[1] = range & 0x00FF;
ret = hidpp_send_fap_command_sync(hidpp, pdata->force_feature,
CMD_G920_FORCE_SET_APERTURE, params, 2, &response);
if (ret)
return ret;
pdata->range = range;
return count;
}
static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, g920_range_show, g920_range_store);
static int g920_allocate(struct hid_device *hdev)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
struct g920_private_data *pdata;
pdata = devm_kzalloc(&hdev->dev, sizeof(struct g920_private_data),
GFP_KERNEL);
if (!pdata)
return -ENOMEM;
hidpp->private_data = pdata;
return 0;
}
static int g920_get_config(struct hidpp_device *hidpp) static int g920_get_config(struct hidpp_device *hidpp)
{ {
struct g920_private_data *pdata = hidpp->private_data;
struct hidpp_report response;
u8 feature_type; u8 feature_type;
u8 feature_index; u8 feature_index;
int ret; int ret;
pdata = hidpp->private_data;
if (!pdata) {
hid_err(hidpp->hid_dev, "Private driver data not found!\n");
return -EINVAL;
}
/* Find feature and store for later use */ /* Find feature and store for later use */
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK, ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK,
&feature_index, &feature_type); &feature_index, &feature_type);
if (ret) if (ret)
return ret; return ret;
pdata->force_feature = feature_index; ret = hidpp_ff_init(hidpp, feature_index);
/* Read current Range */
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
CMD_G920_FORCE_GET_APERTURE, NULL, 0, &response);
if (ret > 0) {
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
return -EPROTO;
}
if (ret) if (ret)
return ret; hid_warn(hidpp->hid_dev, "Unable to initialize force feedback support, errno %d\n",
ret);
pdata->range = get_unaligned_be16(&response.fap.params[0]);
/* Create sysfs interface */
ret = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range);
if (ret)
hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d\n", ret);
return 0; return 0;
} }
...@@ -1739,10 +2229,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -1739,10 +2229,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
ret = k400_allocate(hdev); ret = k400_allocate(hdev);
if (ret) if (ret)
goto allocate_fail; goto allocate_fail;
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
ret = g920_allocate(hdev);
if (ret)
goto allocate_fail;
} }
INIT_WORK(&hidpp->work, delayed_work_cb); INIT_WORK(&hidpp->work, delayed_work_cb);
...@@ -1825,7 +2311,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -1825,7 +2311,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_hw_open_failed: hid_hw_open_failed:
hid_device_io_stop(hdev); hid_device_io_stop(hdev);
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
device_remove_file(&hdev->dev, &dev_attr_range);
hid_hw_close(hdev); hid_hw_close(hdev);
hid_hw_stop(hdev); hid_hw_stop(hdev);
} }
...@@ -1843,7 +2328,7 @@ static void hidpp_remove(struct hid_device *hdev) ...@@ -1843,7 +2328,7 @@ static void hidpp_remove(struct hid_device *hdev)
struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct hidpp_device *hidpp = hid_get_drvdata(hdev);
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
device_remove_file(&hdev->dev, &dev_attr_range); hidpp_ff_deinit(hdev);
hid_hw_close(hdev); hid_hw_close(hdev);
} }
hid_hw_stop(hdev); hid_hw_stop(hdev);
......
...@@ -286,6 +286,8 @@ static const struct hid_device_id ms_devices[] = { ...@@ -286,6 +286,8 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_HIDINPUT }, .driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER), { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
.driver_data = MS_HIDINPUT }, .driver_data = MS_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD),
.driver_data = MS_ERGONOMY},
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
.driver_data = MS_PRESENTER }, .driver_data = MS_PRESENTER },
......
...@@ -396,6 +396,11 @@ static void mt_feature_mapping(struct hid_device *hdev, ...@@ -396,6 +396,11 @@ static void mt_feature_mapping(struct hid_device *hdev,
td->is_buttonpad = true; td->is_buttonpad = true;
break; break;
case 0xff0000c5:
/* Retrieve the Win8 blob once to enable some devices */
if (usage->usage_index == 0)
mt_get_feature(hdev, field->report);
break;
} }
} }
...@@ -1133,6 +1138,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -1133,6 +1138,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret; return ret;
ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
if (ret)
dev_warn(&hdev->dev, "Cannot allocate sysfs group for %s\n",
hdev->name);
mt_set_maxcontacts(hdev); mt_set_maxcontacts(hdev);
mt_set_input_mode(hdev); mt_set_input_mode(hdev);
...@@ -1145,8 +1153,30 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -1145,8 +1153,30 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static void mt_release_contacts(struct hid_device *hid)
{
struct hid_input *hidinput;
list_for_each_entry(hidinput, &hid->inputs, list) {
struct input_dev *input_dev = hidinput->input;
struct input_mt *mt = input_dev->mt;
int i;
if (mt) {
for (i = 0; i < mt->num_slots; i++) {
input_mt_slot(input_dev, i);
input_mt_report_slot_state(input_dev,
MT_TOOL_FINGER,
false);
}
input_sync(input_dev);
}
}
}
static int mt_reset_resume(struct hid_device *hdev) static int mt_reset_resume(struct hid_device *hdev)
{ {
mt_release_contacts(hdev);
mt_set_maxcontacts(hdev); mt_set_maxcontacts(hdev);
mt_set_input_mode(hdev); mt_set_input_mode(hdev);
return 0; return 0;
......
...@@ -23,8 +23,12 @@ static int penmount_input_mapping(struct hid_device *hdev, ...@@ -23,8 +23,12 @@ static int penmount_input_mapping(struct hid_device *hdev,
struct hid_usage *usage, unsigned long **bit, int *max) struct hid_usage *usage, unsigned long **bit, int *max)
{ {
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) { if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
if (((usage->hid - 1) & HID_USAGE) == 0) {
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
return 1; return 1;
} else {
return -1;
}
} }
return 0; return 0;
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#define MOTION_CONTROLLER_BT BIT(8) #define MOTION_CONTROLLER_BT BIT(8)
#define NAVIGATION_CONTROLLER_USB BIT(9) #define NAVIGATION_CONTROLLER_USB BIT(9)
#define NAVIGATION_CONTROLLER_BT BIT(10) #define NAVIGATION_CONTROLLER_BT BIT(10)
#define SINO_LITE_CONTROLLER BIT(11)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT) #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
...@@ -74,7 +75,7 @@ ...@@ -74,7 +75,7 @@
* axis values. Additionally, the controller only has 20 actual, physical axes * axis values. Additionally, the controller only has 20 actual, physical axes
* so there are several unused axes in between the used ones. * so there are several unused axes in between the used ones.
*/ */
static __u8 sixaxis_rdesc[] = { static u8 sixaxis_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */ 0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystick), */ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
...@@ -152,7 +153,7 @@ static __u8 sixaxis_rdesc[] = { ...@@ -152,7 +153,7 @@ static __u8 sixaxis_rdesc[] = {
}; };
/* PS/3 Motion controller */ /* PS/3 Motion controller */
static __u8 motion_rdesc[] = { static u8 motion_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */ 0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystick), */ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
...@@ -249,9 +250,9 @@ static __u8 motion_rdesc[] = { ...@@ -249,9 +250,9 @@ static __u8 motion_rdesc[] = {
}; };
/* PS/3 Navigation controller */ /* PS/3 Navigation controller */
static __u8 navigation_rdesc[] = { static u8 navigation_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */ 0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystik), */ 0x09, 0x04, /* Usage (Joystick), */
0xA1, 0x01, /* Collection (Application), */ 0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */ 0xA1, 0x02, /* Collection (Logical), */
0x85, 0x01, /* Report ID (1), */ 0x85, 0x01, /* Report ID (1), */
...@@ -809,7 +810,7 @@ static u8 dualshock4_bt_rdesc[] = { ...@@ -809,7 +810,7 @@ static u8 dualshock4_bt_rdesc[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
static __u8 ps3remote_rdesc[] = { static u8 ps3remote_rdesc[] = {
0x05, 0x01, /* GUsagePage Generic Desktop */ 0x05, 0x01, /* GUsagePage Generic Desktop */
0x09, 0x05, /* LUsage 0x05 [Game Pad] */ 0x09, 0x05, /* LUsage 0x05 [Game Pad] */
0xA1, 0x01, /* MCollection Application (mouse, keyboard) */ 0xA1, 0x01, /* MCollection Application (mouse, keyboard) */
...@@ -817,14 +818,18 @@ static __u8 ps3remote_rdesc[] = { ...@@ -817,14 +818,18 @@ static __u8 ps3remote_rdesc[] = {
/* Use collection 1 for joypad buttons */ /* Use collection 1 for joypad buttons */
0xA1, 0x02, /* MCollection Logical (interrelated data) */ 0xA1, 0x02, /* MCollection Logical (interrelated data) */
/* Ignore the 1st byte, maybe it is used for a controller /*
* number but it's not needed for correct operation */ * Ignore the 1st byte, maybe it is used for a controller
* number but it's not needed for correct operation
*/
0x75, 0x08, /* GReportSize 0x08 [8] */ 0x75, 0x08, /* GReportSize 0x08 [8] */
0x95, 0x01, /* GReportCount 0x01 [1] */ 0x95, 0x01, /* GReportCount 0x01 [1] */
0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */ 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
/* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these /*
* buttons multiple keypresses are allowed */ * Bytes from 2nd to 4th are a bitmap for joypad buttons, for these
* buttons multiple keypresses are allowed
*/
0x05, 0x09, /* GUsagePage Button */ 0x05, 0x09, /* GUsagePage Button */
0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */ 0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */
0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */ 0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */
...@@ -849,8 +854,10 @@ static __u8 ps3remote_rdesc[] = { ...@@ -849,8 +854,10 @@ static __u8 ps3remote_rdesc[] = {
0x95, 0x01, /* GReportCount 0x01 [1] */ 0x95, 0x01, /* GReportCount 0x01 [1] */
0x80, /* MInput */ 0x80, /* MInput */
/* Ignore bytes from 6th to 11th, 6th to 10th are always constant at /*
* 0xff and 11th is for press indication */ * Ignore bytes from 6th to 11th, 6th to 10th are always constant at
* 0xff and 11th is for press indication
*/
0x75, 0x08, /* GReportSize 0x08 [8] */ 0x75, 0x08, /* GReportSize 0x08 [8] */
0x95, 0x06, /* GReportCount 0x06 [6] */ 0x95, 0x06, /* GReportCount 0x06 [6] */
0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */ 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
...@@ -943,15 +950,15 @@ static const unsigned int buzz_keymap[] = { ...@@ -943,15 +950,15 @@ static const unsigned int buzz_keymap[] = {
* So, for example, the orange button on the third buzzer is mapped to * So, for example, the orange button on the third buzzer is mapped to
* BTN_TRIGGER_HAPPY14 * BTN_TRIGGER_HAPPY14
*/ */
[ 1] = BTN_TRIGGER_HAPPY1, [1] = BTN_TRIGGER_HAPPY1,
[ 2] = BTN_TRIGGER_HAPPY2, [2] = BTN_TRIGGER_HAPPY2,
[ 3] = BTN_TRIGGER_HAPPY3, [3] = BTN_TRIGGER_HAPPY3,
[ 4] = BTN_TRIGGER_HAPPY4, [4] = BTN_TRIGGER_HAPPY4,
[ 5] = BTN_TRIGGER_HAPPY5, [5] = BTN_TRIGGER_HAPPY5,
[ 6] = BTN_TRIGGER_HAPPY6, [6] = BTN_TRIGGER_HAPPY6,
[ 7] = BTN_TRIGGER_HAPPY7, [7] = BTN_TRIGGER_HAPPY7,
[ 8] = BTN_TRIGGER_HAPPY8, [8] = BTN_TRIGGER_HAPPY8,
[ 9] = BTN_TRIGGER_HAPPY9, [9] = BTN_TRIGGER_HAPPY9,
[10] = BTN_TRIGGER_HAPPY10, [10] = BTN_TRIGGER_HAPPY10,
[11] = BTN_TRIGGER_HAPPY11, [11] = BTN_TRIGGER_HAPPY11,
[12] = BTN_TRIGGER_HAPPY12, [12] = BTN_TRIGGER_HAPPY12,
...@@ -973,33 +980,33 @@ static enum power_supply_property sony_battery_props[] = { ...@@ -973,33 +980,33 @@ static enum power_supply_property sony_battery_props[] = {
}; };
struct sixaxis_led { struct sixaxis_led {
__u8 time_enabled; /* the total time the led is active (0xff means forever) */ u8 time_enabled; /* the total time the led is active (0xff means forever) */
__u8 duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */ u8 duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
__u8 enabled; u8 enabled;
__u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */ u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
__u8 duty_on; /* % of duty_length the led is on (0xff mean 100%) */ u8 duty_on; /* % of duty_length the led is on (0xff mean 100%) */
} __packed; } __packed;
struct sixaxis_rumble { struct sixaxis_rumble {
__u8 padding; u8 padding;
__u8 right_duration; /* Right motor duration (0xff means forever) */ u8 right_duration; /* Right motor duration (0xff means forever) */
__u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */ u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
__u8 left_duration; /* Left motor duration (0xff means forever) */ u8 left_duration; /* Left motor duration (0xff means forever) */
__u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */ u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
} __packed; } __packed;
struct sixaxis_output_report { struct sixaxis_output_report {
__u8 report_id; u8 report_id;
struct sixaxis_rumble rumble; struct sixaxis_rumble rumble;
__u8 padding[4]; u8 padding[4];
__u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */ u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
struct sixaxis_led led[4]; /* LEDx at (4 - x) */ struct sixaxis_led led[4]; /* LEDx at (4 - x) */
struct sixaxis_led _reserved; /* LED5, not actually soldered */ struct sixaxis_led _reserved; /* LED5, not actually soldered */
} __packed; } __packed;
union sixaxis_output_report_01 { union sixaxis_output_report_01 {
struct sixaxis_output_report data; struct sixaxis_output_report data;
__u8 buf[36]; u8 buf[36];
}; };
struct motion_output_report_02 { struct motion_output_report_02 {
...@@ -1028,30 +1035,30 @@ struct sony_sc { ...@@ -1028,30 +1035,30 @@ struct sony_sc {
struct led_classdev *leds[MAX_LEDS]; struct led_classdev *leds[MAX_LEDS];
unsigned long quirks; unsigned long quirks;
struct work_struct state_worker; struct work_struct state_worker;
void(*send_output_report)(struct sony_sc*); void (*send_output_report)(struct sony_sc *);
struct power_supply *battery; struct power_supply *battery;
struct power_supply_desc battery_desc; struct power_supply_desc battery_desc;
int device_id; int device_id;
__u8 *output_report_dmabuf; u8 *output_report_dmabuf;
#ifdef CONFIG_SONY_FF #ifdef CONFIG_SONY_FF
__u8 left; u8 left;
__u8 right; u8 right;
#endif #endif
__u8 mac_address[6]; u8 mac_address[6];
__u8 worker_initialized; u8 worker_initialized;
__u8 cable_state; u8 cable_state;
__u8 battery_charging; u8 battery_charging;
__u8 battery_capacity; u8 battery_capacity;
__u8 led_state[MAX_LEDS]; u8 led_state[MAX_LEDS];
__u8 resume_led_state[MAX_LEDS]; u8 resume_led_state[MAX_LEDS];
__u8 led_delay_on[MAX_LEDS]; u8 led_delay_on[MAX_LEDS];
__u8 led_delay_off[MAX_LEDS]; u8 led_delay_off[MAX_LEDS];
__u8 led_count; u8 led_count;
}; };
static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc, static u8 *sixaxis_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
{ {
*rsize = sizeof(sixaxis_rdesc); *rsize = sizeof(sixaxis_rdesc);
...@@ -1072,7 +1079,7 @@ static u8 *navigation_fixup(struct hid_device *hdev, u8 *rdesc, ...@@ -1072,7 +1079,7 @@ static u8 *navigation_fixup(struct hid_device *hdev, u8 *rdesc,
return navigation_rdesc; return navigation_rdesc;
} }
static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, static u8 *ps3remote_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
{ {
*rsize = sizeof(ps3remote_rdesc); *rsize = sizeof(ps3remote_rdesc);
...@@ -1113,11 +1120,14 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi, ...@@ -1113,11 +1120,14 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
return 1; return 1;
} }
static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
{ {
struct sony_sc *sc = hid_get_drvdata(hdev); struct sony_sc *sc = hid_get_drvdata(hdev);
if (sc->quirks & SINO_LITE_CONTROLLER)
return rdesc;
/* /*
* Some Sony RF receivers wrongly declare the mouse pointer as a * Some Sony RF receivers wrongly declare the mouse pointer as a
* a constant non-data variable. * a constant non-data variable.
...@@ -1164,12 +1174,12 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -1164,12 +1174,12 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
return rdesc; return rdesc;
} }
static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size) static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size)
{ {
static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 }; static const u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
unsigned long flags; unsigned long flags;
int offset; int offset;
__u8 cable_state, battery_capacity, battery_charging; u8 cable_state, battery_capacity, battery_charging;
/* /*
* The sixaxis is charging if the battery value is 0xee * The sixaxis is charging if the battery value is 0xee
...@@ -1184,7 +1194,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size) ...@@ -1184,7 +1194,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
battery_charging = !(rd[offset] & 0x01); battery_charging = !(rd[offset] & 0x01);
cable_state = 1; cable_state = 1;
} else { } else {
__u8 index = rd[offset] <= 5 ? rd[offset] : 5; u8 index = rd[offset] <= 5 ? rd[offset] : 5;
battery_capacity = sixaxis_battery_capacity[index]; battery_capacity = sixaxis_battery_capacity[index];
battery_charging = 0; battery_charging = 0;
cable_state = 0; cable_state = 0;
...@@ -1197,14 +1207,14 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size) ...@@ -1197,14 +1207,14 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
spin_unlock_irqrestore(&sc->lock, flags); spin_unlock_irqrestore(&sc->lock, flags);
} }
static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size) static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
{ {
struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
struct hid_input, list); struct hid_input, list);
struct input_dev *input_dev = hidinput->input; struct input_dev *input_dev = hidinput->input;
unsigned long flags; unsigned long flags;
int n, offset; int n, offset;
__u8 cable_state, battery_capacity, battery_charging; u8 cable_state, battery_capacity, battery_charging;
/* /*
* Battery and touchpad data starts at byte 30 in the USB report and * Battery and touchpad data starts at byte 30 in the USB report and
...@@ -1254,7 +1264,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size) ...@@ -1254,7 +1264,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
* follows the data for the first. * follows the data for the first.
*/ */
for (n = 0; n < 2; n++) { for (n = 0; n < 2; n++) {
__u16 x, y; u16 x, y;
x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8); x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8);
y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4); y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4);
...@@ -1270,7 +1280,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size) ...@@ -1270,7 +1280,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
} }
static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
__u8 *rd, int size) u8 *rd, int size)
{ {
struct sony_sc *sc = hid_get_drvdata(hdev); struct sony_sc *sc = hid_get_drvdata(hdev);
...@@ -1394,7 +1404,7 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) ...@@ -1394,7 +1404,7 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
{ {
const int buf_size = const int buf_size =
max(SIXAXIS_REPORT_0xF2_SIZE, SIXAXIS_REPORT_0xF5_SIZE); max(SIXAXIS_REPORT_0xF2_SIZE, SIXAXIS_REPORT_0xF5_SIZE);
__u8 *buf; u8 *buf;
int ret; int ret;
buf = kmalloc(buf_size, GFP_KERNEL); buf = kmalloc(buf_size, GFP_KERNEL);
...@@ -1420,8 +1430,10 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) ...@@ -1420,8 +1430,10 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
} }
ret = hid_hw_output_report(hdev, buf, 1); ret = hid_hw_output_report(hdev, buf, 1);
if (ret < 0) if (ret < 0) {
hid_err(hdev, "can't set operational mode: step 3\n"); hid_info(hdev, "can't set operational mode: step 3, ignoring\n");
ret = 0;
}
out: out:
kfree(buf); kfree(buf);
...@@ -1431,8 +1443,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) ...@@ -1431,8 +1443,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
static int sixaxis_set_operational_bt(struct hid_device *hdev) static int sixaxis_set_operational_bt(struct hid_device *hdev)
{ {
static const __u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; static const u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
__u8 *buf; u8 *buf;
int ret; int ret;
buf = kmemdup(report, sizeof(report), GFP_KERNEL); buf = kmemdup(report, sizeof(report), GFP_KERNEL);
...@@ -1453,7 +1465,7 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev) ...@@ -1453,7 +1465,7 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
*/ */
static int dualshock4_set_operational_bt(struct hid_device *hdev) static int dualshock4_set_operational_bt(struct hid_device *hdev)
{ {
__u8 *buf; u8 *buf;
int ret; int ret;
buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL); buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL);
...@@ -1470,7 +1482,7 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev) ...@@ -1470,7 +1482,7 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
static void sixaxis_set_leds_from_id(struct sony_sc *sc) static void sixaxis_set_leds_from_id(struct sony_sc *sc)
{ {
static const __u8 sixaxis_leds[10][4] = { static const u8 sixaxis_leds[10][4] = {
{ 0x01, 0x00, 0x00, 0x00 }, { 0x01, 0x00, 0x00, 0x00 },
{ 0x00, 0x01, 0x00, 0x00 }, { 0x00, 0x01, 0x00, 0x00 },
{ 0x00, 0x00, 0x01, 0x00 }, { 0x00, 0x00, 0x01, 0x00 },
...@@ -1497,7 +1509,7 @@ static void sixaxis_set_leds_from_id(struct sony_sc *sc) ...@@ -1497,7 +1509,7 @@ static void sixaxis_set_leds_from_id(struct sony_sc *sc)
static void dualshock4_set_leds_from_id(struct sony_sc *sc) static void dualshock4_set_leds_from_id(struct sony_sc *sc)
{ {
/* The first 4 color/index entries match what the PS4 assigns */ /* The first 4 color/index entries match what the PS4 assigns */
static const __u8 color_code[7][3] = { static const u8 color_code[7][3] = {
/* Blue */ { 0x00, 0x00, 0x01 }, /* Blue */ { 0x00, 0x00, 0x01 },
/* Red */ { 0x01, 0x00, 0x00 }, /* Red */ { 0x01, 0x00, 0x00 },
/* Green */ { 0x00, 0x01, 0x00 }, /* Green */ { 0x00, 0x01, 0x00 },
...@@ -1525,7 +1537,7 @@ static void buzz_set_leds(struct sony_sc *sc) ...@@ -1525,7 +1537,7 @@ static void buzz_set_leds(struct sony_sc *sc)
&hdev->report_enum[HID_OUTPUT_REPORT].report_list; &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report *report = list_entry(report_list->next,
struct hid_report, list); struct hid_report, list);
__s32 *value = report->field[0]->value; s32 *value = report->field[0]->value;
BUILD_BUG_ON(MAX_LEDS < 4); BUILD_BUG_ON(MAX_LEDS < 4);
...@@ -1619,7 +1631,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on, ...@@ -1619,7 +1631,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
struct hid_device *hdev = to_hid_device(dev); struct hid_device *hdev = to_hid_device(dev);
struct sony_sc *drv_data = hid_get_drvdata(hdev); struct sony_sc *drv_data = hid_get_drvdata(hdev);
int n; int n;
__u8 new_on, new_off; u8 new_on, new_off;
if (!drv_data) { if (!drv_data) {
hid_err(hdev, "No device data\n"); hid_err(hdev, "No device data\n");
...@@ -1690,8 +1702,8 @@ static int sony_leds_init(struct sony_sc *sc) ...@@ -1690,8 +1702,8 @@ static int sony_leds_init(struct sony_sc *sc)
const char *name_fmt; const char *name_fmt;
static const char * const ds4_name_str[] = { "red", "green", "blue", static const char * const ds4_name_str[] = { "red", "green", "blue",
"global" }; "global" };
__u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 }; u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
__u8 use_hw_blink[MAX_LEDS] = { 0 }; u8 use_hw_blink[MAX_LEDS] = { 0 };
BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
...@@ -1719,7 +1731,7 @@ static int sony_leds_init(struct sony_sc *sc) ...@@ -1719,7 +1731,7 @@ static int sony_leds_init(struct sony_sc *sc)
name_len = 0; name_len = 0;
name_fmt = "%s:%s"; name_fmt = "%s:%s";
} else if (sc->quirks & NAVIGATION_CONTROLLER) { } else if (sc->quirks & NAVIGATION_CONTROLLER) {
static const __u8 navigation_leds[4] = {0x01, 0x00, 0x00, 0x00}; static const u8 navigation_leds[4] = {0x01, 0x00, 0x00, 0x00};
memcpy(sc->led_state, navigation_leds, sizeof(navigation_leds)); memcpy(sc->led_state, navigation_leds, sizeof(navigation_leds));
sc->led_count = 1; sc->led_count = 1;
...@@ -1796,7 +1808,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc) ...@@ -1796,7 +1808,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
static const union sixaxis_output_report_01 default_report = { static const union sixaxis_output_report_01 default_report = {
.buf = { .buf = {
0x01, 0x01,
0x00, 0xff, 0x00, 0xff, 0x00, 0x01, 0xff, 0x00, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32,
0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32,
...@@ -1842,7 +1854,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc) ...@@ -1842,7 +1854,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
} }
} }
hid_hw_raw_request(sc->hdev, report->report_id, (__u8 *)report, hid_hw_raw_request(sc->hdev, report->report_id, (u8 *)report,
sizeof(struct sixaxis_output_report), sizeof(struct sixaxis_output_report),
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
} }
...@@ -1850,7 +1862,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc) ...@@ -1850,7 +1862,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
static void dualshock4_send_output_report(struct sony_sc *sc) static void dualshock4_send_output_report(struct sony_sc *sc)
{ {
struct hid_device *hdev = sc->hdev; struct hid_device *hdev = sc->hdev;
__u8 *buf = sc->output_report_dmabuf; u8 *buf = sc->output_report_dmabuf;
int offset; int offset;
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
...@@ -1910,7 +1922,7 @@ static void motion_send_output_report(struct sony_sc *sc) ...@@ -1910,7 +1922,7 @@ static void motion_send_output_report(struct sony_sc *sc)
report->rumble = max(sc->right, sc->left); report->rumble = max(sc->right, sc->left);
#endif #endif
hid_hw_output_report(hdev, (__u8 *)report, MOTION_REPORT_0x02_SIZE); hid_hw_output_report(hdev, (u8 *)report, MOTION_REPORT_0x02_SIZE);
} }
static inline void sony_send_output_report(struct sony_sc *sc) static inline void sony_send_output_report(struct sony_sc *sc)
...@@ -1922,6 +1934,7 @@ static inline void sony_send_output_report(struct sony_sc *sc) ...@@ -1922,6 +1934,7 @@ static inline void sony_send_output_report(struct sony_sc *sc)
static void sony_state_worker(struct work_struct *work) static void sony_state_worker(struct work_struct *work)
{ {
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
sc->send_output_report(sc); sc->send_output_report(sc);
} }
...@@ -2142,7 +2155,7 @@ static int sony_get_bt_devaddr(struct sony_sc *sc) ...@@ -2142,7 +2155,7 @@ static int sony_get_bt_devaddr(struct sony_sc *sc)
static int sony_check_add(struct sony_sc *sc) static int sony_check_add(struct sony_sc *sc)
{ {
__u8 *buf = NULL; u8 *buf = NULL;
int n, ret; int n, ret;
if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) || if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
...@@ -2253,7 +2266,7 @@ static void sony_release_device_id(struct sony_sc *sc) ...@@ -2253,7 +2266,7 @@ static void sony_release_device_id(struct sony_sc *sc)
} }
static inline void sony_init_output_report(struct sony_sc *sc, static inline void sony_init_output_report(struct sony_sc *sc,
void(*send_output_report)(struct sony_sc*)) void (*send_output_report)(struct sony_sc *))
{ {
sc->send_output_report = send_output_report; sc->send_output_report = send_output_report;
...@@ -2501,8 +2514,10 @@ static const struct hid_device_id sony_devices[] = { ...@@ -2501,8 +2514,10 @@ static const struct hid_device_id sony_devices[] = {
.driver_data = VAIO_RDESC_CONSTANT }, .driver_data = VAIO_RDESC_CONSTANT },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE), { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE),
.driver_data = VAIO_RDESC_CONSTANT }, .driver_data = VAIO_RDESC_CONSTANT },
/* Wired Buzz Controller. Reported as Sony Hub from its USB ID and as /*
* Logitech joystick from the device descriptor. */ * Wired Buzz Controller. Reported as Sony Hub from its USB ID and as
* Logitech joystick from the device descriptor.
*/
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER), { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER),
.driver_data = BUZZ_CONTROLLER }, .driver_data = BUZZ_CONTROLLER },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER), { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER),
...@@ -2521,6 +2536,9 @@ static const struct hid_device_id sony_devices[] = { ...@@ -2521,6 +2536,9 @@ static const struct hid_device_id sony_devices[] = {
.driver_data = DUALSHOCK4_CONTROLLER_USB }, .driver_data = DUALSHOCK4_CONTROLLER_USB },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER),
.driver_data = DUALSHOCK4_CONTROLLER_BT }, .driver_data = DUALSHOCK4_CONTROLLER_BT },
/* Nyko Core Controller for PS3 */
{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER),
.driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER },
{ } { }
}; };
MODULE_DEVICE_TABLE(hid, sony_devices); MODULE_DEVICE_TABLE(hid, sony_devices);
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/workqueue.h>
#include "hid-ids.h" #include "hid-ids.h"
...@@ -56,7 +55,6 @@ struct thingm_rgb { ...@@ -56,7 +55,6 @@ struct thingm_rgb {
struct thingm_led red; struct thingm_led red;
struct thingm_led green; struct thingm_led green;
struct thingm_led blue; struct thingm_led blue;
struct work_struct work;
u8 num; u8 num;
}; };
...@@ -79,9 +77,13 @@ static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE]) ...@@ -79,9 +77,13 @@ static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
buf[0], buf[1], buf[2], buf[3], buf[4], buf[0], buf[1], buf[2], buf[3], buf[4],
buf[5], buf[6], buf[7], buf[8]); buf[5], buf[6], buf[7], buf[8]);
mutex_lock(&tdev->lock);
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT); HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
mutex_unlock(&tdev->lock);
return ret < 0 ? ret : 0; return ret < 0 ? ret : 0;
} }
...@@ -89,16 +91,31 @@ static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE]) ...@@ -89,16 +91,31 @@ static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
{ {
int ret; int ret;
/*
* A read consists of two operations: sending the read command
* and the actual read from the device. Use the mutex to protect
* the full sequence of both operations.
*/
mutex_lock(&tdev->lock);
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
if (ret < 0)
goto err;
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT); HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret < 0) if (ret < 0)
return ret; goto err;
ret = 0;
hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
buf[0], buf[1], buf[2], buf[3], buf[4], buf[0], buf[1], buf[2], buf[3], buf[4],
buf[5], buf[6], buf[7], buf[8]); buf[5], buf[6], buf[7], buf[8]);
err:
return 0; mutex_unlock(&tdev->lock);
return ret;
} }
static int thingm_version(struct thingm_device *tdev) static int thingm_version(struct thingm_device *tdev)
...@@ -106,10 +123,6 @@ static int thingm_version(struct thingm_device *tdev) ...@@ -106,10 +123,6 @@ static int thingm_version(struct thingm_device *tdev)
u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 }; u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 };
int err; int err;
err = thingm_send(tdev, buf);
if (err)
return err;
err = thingm_recv(tdev, buf); err = thingm_recv(tdev, buf);
if (err) if (err)
return err; return err;
...@@ -131,25 +144,17 @@ static int thingm_write_color(struct thingm_rgb *rgb) ...@@ -131,25 +144,17 @@ static int thingm_write_color(struct thingm_rgb *rgb)
return thingm_send(rgb->tdev, buf); return thingm_send(rgb->tdev, buf);
} }
static void thingm_work(struct work_struct *work) static int thingm_led_set(struct led_classdev *ldev,
{
struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work);
mutex_lock(&rgb->tdev->lock);
if (thingm_write_color(rgb))
hid_err(rgb->tdev->hdev, "failed to write color\n");
mutex_unlock(&rgb->tdev->lock);
}
static void thingm_led_set(struct led_classdev *ldev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct thingm_led *led = container_of(ldev, struct thingm_led, ldev); struct thingm_led *led = container_of(ldev, struct thingm_led, ldev);
int ret;
ret = thingm_write_color(led->rgb);
if (ret)
hid_err(led->rgb->tdev->hdev, "failed to write color\n");
/* the ledclass has already stored the brightness value */ return ret;
schedule_work(&led->rgb->work);
} }
static int thingm_init_rgb(struct thingm_rgb *rgb) static int thingm_init_rgb(struct thingm_rgb *rgb)
...@@ -162,10 +167,11 @@ static int thingm_init_rgb(struct thingm_rgb *rgb) ...@@ -162,10 +167,11 @@ static int thingm_init_rgb(struct thingm_rgb *rgb)
"thingm%d:red:led%d", minor, rgb->num); "thingm%d:red:led%d", minor, rgb->num);
rgb->red.ldev.name = rgb->red.name; rgb->red.ldev.name = rgb->red.name;
rgb->red.ldev.max_brightness = 255; rgb->red.ldev.max_brightness = 255;
rgb->red.ldev.brightness_set = thingm_led_set; rgb->red.ldev.brightness_set_blocking = thingm_led_set;
rgb->red.rgb = rgb; rgb->red.rgb = rgb;
err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->red.ldev); err = devm_led_classdev_register(&rgb->tdev->hdev->dev,
&rgb->red.ldev);
if (err) if (err)
return err; return err;
...@@ -174,46 +180,27 @@ static int thingm_init_rgb(struct thingm_rgb *rgb) ...@@ -174,46 +180,27 @@ static int thingm_init_rgb(struct thingm_rgb *rgb)
"thingm%d:green:led%d", minor, rgb->num); "thingm%d:green:led%d", minor, rgb->num);
rgb->green.ldev.name = rgb->green.name; rgb->green.ldev.name = rgb->green.name;
rgb->green.ldev.max_brightness = 255; rgb->green.ldev.max_brightness = 255;
rgb->green.ldev.brightness_set = thingm_led_set; rgb->green.ldev.brightness_set_blocking = thingm_led_set;
rgb->green.rgb = rgb; rgb->green.rgb = rgb;
err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->green.ldev); err = devm_led_classdev_register(&rgb->tdev->hdev->dev,
&rgb->green.ldev);
if (err) if (err)
goto unregister_red; return err;
/* Register the blue diode */ /* Register the blue diode */
snprintf(rgb->blue.name, sizeof(rgb->blue.name), snprintf(rgb->blue.name, sizeof(rgb->blue.name),
"thingm%d:blue:led%d", minor, rgb->num); "thingm%d:blue:led%d", minor, rgb->num);
rgb->blue.ldev.name = rgb->blue.name; rgb->blue.ldev.name = rgb->blue.name;
rgb->blue.ldev.max_brightness = 255; rgb->blue.ldev.max_brightness = 255;
rgb->blue.ldev.brightness_set = thingm_led_set; rgb->blue.ldev.brightness_set_blocking = thingm_led_set;
rgb->blue.rgb = rgb; rgb->blue.rgb = rgb;
err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev); err = devm_led_classdev_register(&rgb->tdev->hdev->dev,
if (err) &rgb->blue.ldev);
goto unregister_green;
INIT_WORK(&rgb->work, thingm_work);
return 0;
unregister_green:
led_classdev_unregister(&rgb->green.ldev);
unregister_red:
led_classdev_unregister(&rgb->red.ldev);
return err; return err;
} }
static void thingm_remove_rgb(struct thingm_rgb *rgb)
{
led_classdev_unregister(&rgb->red.ldev);
led_classdev_unregister(&rgb->green.ldev);
led_classdev_unregister(&rgb->blue.ldev);
flush_work(&rgb->work);
}
static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
{ {
struct thingm_device *tdev; struct thingm_device *tdev;
...@@ -229,17 +216,13 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -229,17 +216,13 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
err = hid_parse(hdev); err = hid_parse(hdev);
if (err) if (err)
goto error; return err;
err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
if (err)
goto error;
mutex_init(&tdev->lock); mutex_init(&tdev->lock);
err = thingm_version(tdev); err = thingm_version(tdev);
if (err) if (err)
goto stop; return err;
hid_dbg(hdev, "firmware version: %c.%c\n", hid_dbg(hdev, "firmware version: %c.%c\n",
tdev->version.major, tdev->version.minor); tdev->version.major, tdev->version.minor);
...@@ -250,17 +233,18 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -250,17 +233,18 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (!tdev->fwinfo) { if (!tdev->fwinfo) {
hid_err(hdev, "unsupported firmware %c\n", tdev->version.major); hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
err = -ENODEV; return -ENODEV;
goto stop;
} }
tdev->rgb = devm_kzalloc(&hdev->dev, tdev->rgb = devm_kzalloc(&hdev->dev,
sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb, sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb,
GFP_KERNEL); GFP_KERNEL);
if (!tdev->rgb) { if (!tdev->rgb)
err = -ENOMEM; return -ENOMEM;
goto stop;
} err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
if (err)
return err;
for (i = 0; i < tdev->fwinfo->numrgb; ++i) { for (i = 0; i < tdev->fwinfo->numrgb; ++i) {
struct thingm_rgb *rgb = tdev->rgb + i; struct thingm_rgb *rgb = tdev->rgb + i;
...@@ -269,28 +253,12 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -269,28 +253,12 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
rgb->num = tdev->fwinfo->first + i; rgb->num = tdev->fwinfo->first + i;
err = thingm_init_rgb(rgb); err = thingm_init_rgb(rgb);
if (err) { if (err) {
while (--i >= 0) hid_hw_stop(hdev);
thingm_remove_rgb(tdev->rgb + i); return err;
goto stop;
} }
} }
return 0; return 0;
stop:
hid_hw_stop(hdev);
error:
return err;
}
static void thingm_remove(struct hid_device *hdev)
{
struct thingm_device *tdev = hid_get_drvdata(hdev);
int i;
hid_hw_stop(hdev);
for (i = 0; i < tdev->fwinfo->numrgb; ++i)
thingm_remove_rgb(tdev->rgb + i);
} }
static const struct hid_device_id thingm_table[] = { static const struct hid_device_id thingm_table[] = {
...@@ -302,7 +270,6 @@ MODULE_DEVICE_TABLE(hid, thingm_table); ...@@ -302,7 +270,6 @@ MODULE_DEVICE_TABLE(hid, thingm_table);
static struct hid_driver thingm_driver = { static struct hid_driver thingm_driver = {
.name = "thingm", .name = "thingm",
.probe = thingm_probe, .probe = thingm_probe,
.remove = thingm_remove,
.id_table = thingm_table, .id_table = thingm_table,
}; };
......
...@@ -283,17 +283,21 @@ static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType, ...@@ -283,17 +283,21 @@ static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister); u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister); u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength); u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
u16 size;
int args_len;
int index = 0;
i2c_hid_dbg(ihid, "%s\n", __func__);
if (data_len > ihid->bufsize)
return -EINVAL;
/* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */ size = 2 /* size */ +
u16 size = 2 /* size */ +
(reportID ? 1 : 0) /* reportID */ + (reportID ? 1 : 0) /* reportID */ +
data_len /* buf */; data_len /* buf */;
int args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ + args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ +
2 /* dataRegister */ + 2 /* dataRegister */ +
size /* args */; size /* args */;
int index = 0;
i2c_hid_dbg(ihid, "%s\n", __func__);
if (!use_data && maxOutputLength == 0) if (!use_data && maxOutputLength == 0)
return -ENOSYS; return -ENOSYS;
...@@ -1108,13 +1112,30 @@ static int i2c_hid_suspend(struct device *dev) ...@@ -1108,13 +1112,30 @@ static int i2c_hid_suspend(struct device *dev)
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
struct hid_device *hid = ihid->hid; struct hid_device *hid = ihid->hid;
int ret = 0; int ret;
int wake_status; int wake_status;
if (hid->driver && hid->driver->suspend) if (hid->driver && hid->driver->suspend) {
/*
* Wake up the device so that IO issues in
* HID driver's suspend code can succeed.
*/
ret = pm_runtime_resume(dev);
if (ret < 0)
return ret;
ret = hid->driver->suspend(hid, PMSG_SUSPEND); ret = hid->driver->suspend(hid, PMSG_SUSPEND);
if (ret < 0)
return ret;
}
if (!pm_runtime_suspended(dev)) {
/* Save some power */
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
disable_irq(ihid->irq); disable_irq(ihid->irq);
}
if (device_may_wakeup(&client->dev)) { if (device_may_wakeup(&client->dev)) {
wake_status = enable_irq_wake(ihid->irq); wake_status = enable_irq_wake(ihid->irq);
if (!wake_status) if (!wake_status)
...@@ -1124,10 +1145,7 @@ static int i2c_hid_suspend(struct device *dev) ...@@ -1124,10 +1145,7 @@ static int i2c_hid_suspend(struct device *dev)
wake_status); wake_status);
} }
/* Save some power */ return 0;
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
return ret;
} }
static int i2c_hid_resume(struct device *dev) static int i2c_hid_resume(struct device *dev)
...@@ -1138,11 +1156,6 @@ static int i2c_hid_resume(struct device *dev) ...@@ -1138,11 +1156,6 @@ static int i2c_hid_resume(struct device *dev)
struct hid_device *hid = ihid->hid; struct hid_device *hid = ihid->hid;
int wake_status; int wake_status;
enable_irq(ihid->irq);
ret = i2c_hid_hwreset(client);
if (ret)
return ret;
if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) { if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) {
wake_status = disable_irq_wake(ihid->irq); wake_status = disable_irq_wake(ihid->irq);
if (!wake_status) if (!wake_status)
...@@ -1152,6 +1165,16 @@ static int i2c_hid_resume(struct device *dev) ...@@ -1152,6 +1165,16 @@ static int i2c_hid_resume(struct device *dev)
wake_status); wake_status);
} }
/* We'll resume to full power */
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
enable_irq(ihid->irq);
ret = i2c_hid_hwreset(client);
if (ret)
return ret;
if (hid->driver && hid->driver->reset_resume) { if (hid->driver && hid->driver->reset_resume) {
ret = hid->driver->reset_resume(hid); ret = hid->driver->reset_resume(hid);
return ret; return ret;
...@@ -1191,6 +1214,7 @@ static const struct dev_pm_ops i2c_hid_pm = { ...@@ -1191,6 +1214,7 @@ static const struct dev_pm_ops i2c_hid_pm = {
static const struct i2c_device_id i2c_hid_id_table[] = { static const struct i2c_device_id i2c_hid_id_table[] = {
{ "hid", 0 }, { "hid", 0 },
{ "hid-over-i2c", 0 },
{ }, { },
}; };
MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table); MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table);
......
...@@ -1357,6 +1357,9 @@ static void wacom_clean_inputs(struct wacom *wacom) ...@@ -1357,6 +1357,9 @@ static void wacom_clean_inputs(struct wacom *wacom)
wacom->wacom_wac.pen_input = NULL; wacom->wacom_wac.pen_input = NULL;
wacom->wacom_wac.touch_input = NULL; wacom->wacom_wac.touch_input = NULL;
wacom->wacom_wac.pad_input = NULL; wacom->wacom_wac.pad_input = NULL;
wacom->wacom_wac.pen_registered = false;
wacom->wacom_wac.touch_registered = false;
wacom->wacom_wac.pad_registered = false;
wacom_destroy_leds(wacom); wacom_destroy_leds(wacom);
} }
...@@ -1494,123 +1497,6 @@ static void wacom_calculate_res(struct wacom_features *features) ...@@ -1494,123 +1497,6 @@ static void wacom_calculate_res(struct wacom_features *features)
features->unitExpo); features->unitExpo);
} }
static void wacom_wireless_work(struct work_struct *work)
{
struct wacom *wacom = container_of(work, struct wacom, work);
struct usb_device *usbdev = wacom->usbdev;
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct hid_device *hdev1, *hdev2;
struct wacom *wacom1, *wacom2;
struct wacom_wac *wacom_wac1, *wacom_wac2;
int error;
/*
* Regardless if this is a disconnect or a new tablet,
* remove any existing input and battery devices.
*/
wacom_destroy_battery(wacom);
/* Stylus interface */
hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
wacom1 = hid_get_drvdata(hdev1);
wacom_wac1 = &(wacom1->wacom_wac);
wacom_clean_inputs(wacom1);
/* Touch interface */
hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
wacom2 = hid_get_drvdata(hdev2);
wacom_wac2 = &(wacom2->wacom_wac);
wacom_clean_inputs(wacom2);
if (wacom_wac->pid == 0) {
hid_info(wacom->hdev, "wireless tablet disconnected\n");
wacom_wac1->shared->type = 0;
} else {
const struct hid_device_id *id = wacom_ids;
hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
wacom_wac->pid);
while (id->bus) {
if (id->vendor == USB_VENDOR_ID_WACOM &&
id->product == wacom_wac->pid)
break;
id++;
}
if (!id->bus) {
hid_info(wacom->hdev, "ignoring unknown PID.\n");
return;
}
/* Stylus interface */
wacom_wac1->features =
*((struct wacom_features *)id->driver_data);
wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PEN;
wacom_set_default_phy(&wacom_wac1->features);
wacom_calculate_res(&wacom_wac1->features);
snprintf(wacom_wac1->pen_name, WACOM_NAME_MAX, "%s (WL) Pen",
wacom_wac1->features.name);
if (wacom_wac1->features.type < BAMBOO_PEN ||
wacom_wac1->features.type > BAMBOO_PT) {
snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
wacom_wac1->features.name);
wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD;
}
wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max;
wacom_wac1->shared->type = wacom_wac1->features.type;
wacom_wac1->pid = wacom_wac->pid;
error = wacom_allocate_inputs(wacom1) ||
wacom_register_inputs(wacom1);
if (error)
goto fail;
/* Touch interface */
if (wacom_wac1->features.touch_max ||
(wacom_wac1->features.type >= INTUOSHT &&
wacom_wac1->features.type <= BAMBOO_PT)) {
wacom_wac2->features =
*((struct wacom_features *)id->driver_data);
wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
wacom_set_default_phy(&wacom_wac2->features);
wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
wacom_calculate_res(&wacom_wac2->features);
snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX,
"%s (WL) Finger",wacom_wac2->features.name);
if (wacom_wac1->features.touch_max)
wacom_wac2->features.device_type |= WACOM_DEVICETYPE_TOUCH;
if (wacom_wac1->features.type >= INTUOSHT &&
wacom_wac1->features.type <= BAMBOO_PT) {
snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
"%s (WL) Pad",wacom_wac2->features.name);
wacom_wac2->features.device_type |= WACOM_DEVICETYPE_PAD;
}
wacom_wac2->pid = wacom_wac->pid;
error = wacom_allocate_inputs(wacom2) ||
wacom_register_inputs(wacom2);
if (error)
goto fail;
if ((wacom_wac1->features.type == INTUOSHT ||
wacom_wac1->features.type == INTUOSHT2) &&
wacom_wac1->features.touch_max)
wacom_wac->shared->touch_input = wacom_wac2->touch_input;
}
error = wacom_initialize_battery(wacom);
if (error)
goto fail;
}
return;
fail:
wacom_clean_inputs(wacom1);
wacom_clean_inputs(wacom2);
return;
}
void wacom_battery_work(struct work_struct *work) void wacom_battery_work(struct work_struct *work)
{ {
struct wacom *wacom = container_of(work, struct wacom, work); struct wacom *wacom = container_of(work, struct wacom, work);
...@@ -1642,7 +1528,7 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev) ...@@ -1642,7 +1528,7 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev)
return size; return size;
} }
static void wacom_update_name(struct wacom *wacom) static void wacom_update_name(struct wacom *wacom, const char *suffix)
{ {
struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features; struct wacom_features *features = &wacom_wac->features;
...@@ -1678,68 +1564,28 @@ static void wacom_update_name(struct wacom *wacom) ...@@ -1678,68 +1564,28 @@ static void wacom_update_name(struct wacom *wacom)
/* Append the device type to the name */ /* Append the device type to the name */
snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name), snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
"%s Pen", name); "%s%s Pen", name, suffix);
snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name), snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name),
"%s Finger", name); "%s%s Finger", name, suffix);
snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name), snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
"%s Pad", name); "%s%s Pad", name, suffix);
} }
static int wacom_probe(struct hid_device *hdev, static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
const struct hid_device_id *id)
{ {
struct usb_interface *intf = to_usb_interface(hdev->dev.parent); struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct usb_device *dev = interface_to_usbdev(intf); struct wacom_features *features = &wacom_wac->features;
struct wacom *wacom; struct hid_device *hdev = wacom->hdev;
struct wacom_wac *wacom_wac;
struct wacom_features *features;
int error; int error;
unsigned int connect_mask = HID_CONNECT_HIDRAW; unsigned int connect_mask = HID_CONNECT_HIDRAW;
if (!id->driver_data)
return -EINVAL;
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
/* hid-core sets this quirk for the boot interface */
hdev->quirks &= ~HID_QUIRK_NOGET;
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
if (!wacom)
return -ENOMEM;
hid_set_drvdata(hdev, wacom);
wacom->hdev = hdev;
/* ask for the report descriptor to be loaded by HID */
error = hid_parse(hdev);
if (error) {
hid_err(hdev, "parse failed\n");
goto fail_parse;
}
wacom_wac = &wacom->wacom_wac;
wacom_wac->features = *((struct wacom_features *)id->driver_data);
features = &wacom_wac->features;
features->pktlen = wacom_compute_pktlen(hdev); features->pktlen = wacom_compute_pktlen(hdev);
if (features->pktlen > WACOM_PKGLEN_MAX) { if (features->pktlen > WACOM_PKGLEN_MAX)
error = -EINVAL; return -EINVAL;
goto fail_pktlen;
}
if (features->check_for_hid_type && features->hid_type != hdev->type) {
error = -ENODEV;
goto fail_type;
}
wacom->usbdev = dev;
wacom->intf = intf;
mutex_init(&wacom->lock);
INIT_WORK(&wacom->work, wacom_wireless_work);
error = wacom_allocate_inputs(wacom); error = wacom_allocate_inputs(wacom);
if (error) if (error)
goto fail_allocate_inputs; return error;
/* /*
* Bamboo Pad has a generic hid handling for the Pen, and we switch it * Bamboo Pad has a generic hid handling for the Pen, and we switch it
...@@ -1752,7 +1598,7 @@ static int wacom_probe(struct hid_device *hdev, ...@@ -1752,7 +1598,7 @@ static int wacom_probe(struct hid_device *hdev,
} else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) && } else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) &&
(features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) { (features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) {
error = -ENODEV; error = -ENODEV;
goto fail_shared_data; goto fail_allocate_inputs;
} }
} }
...@@ -1772,14 +1618,14 @@ static int wacom_probe(struct hid_device *hdev, ...@@ -1772,14 +1618,14 @@ static int wacom_probe(struct hid_device *hdev,
error ? "Ignoring" : "Assuming pen"); error ? "Ignoring" : "Assuming pen");
if (error) if (error)
goto fail_shared_data; goto fail_parsed;
features->device_type |= WACOM_DEVICETYPE_PEN; features->device_type |= WACOM_DEVICETYPE_PEN;
} }
wacom_calculate_res(features); wacom_calculate_res(features);
wacom_update_name(wacom); wacom_update_name(wacom, wireless ? " (WL)" : "");
error = wacom_add_shared_data(hdev); error = wacom_add_shared_data(hdev);
if (error) if (error)
...@@ -1796,14 +1642,6 @@ static int wacom_probe(struct hid_device *hdev, ...@@ -1796,14 +1642,6 @@ static int wacom_probe(struct hid_device *hdev,
if (error) if (error)
goto fail_register_inputs; goto fail_register_inputs;
if (hdev->bus == BUS_BLUETOOTH) {
error = device_create_file(&hdev->dev, &dev_attr_speed);
if (error)
hid_warn(hdev,
"can't create sysfs speed attribute err: %d\n",
error);
}
if (features->type == HID_GENERIC) if (features->type == HID_GENERIC)
connect_mask |= HID_CONNECT_DRIVER; connect_mask |= HID_CONNECT_DRIVER;
...@@ -1814,8 +1652,10 @@ static int wacom_probe(struct hid_device *hdev, ...@@ -1814,8 +1652,10 @@ static int wacom_probe(struct hid_device *hdev,
goto fail_hw_start; goto fail_hw_start;
} }
if (!wireless) {
/* Note that if query fails it is not a hard failure */ /* Note that if query fails it is not a hard failure */
wacom_query_tablet_data(hdev, features); wacom_query_tablet_data(hdev, features);
}
/* touch only Bamboo doesn't support pen */ /* touch only Bamboo doesn't support pen */
if ((features->type == BAMBOO_TOUCH) && if ((features->type == BAMBOO_TOUCH) &&
...@@ -1844,18 +1684,166 @@ static int wacom_probe(struct hid_device *hdev, ...@@ -1844,18 +1684,166 @@ static int wacom_probe(struct hid_device *hdev,
return 0; return 0;
fail_hw_start: fail_hw_start:
if (hdev->bus == BUS_BLUETOOTH) hid_hw_stop(hdev);
device_remove_file(&hdev->dev, &dev_attr_speed);
fail_register_inputs: fail_register_inputs:
wacom_clean_inputs(wacom); wacom_clean_inputs(wacom);
wacom_destroy_battery(wacom); wacom_destroy_battery(wacom);
fail_battery: fail_battery:
wacom_remove_shared_data(wacom); wacom_remove_shared_data(wacom);
fail_shared_data: fail_shared_data:
wacom_clean_inputs(wacom); fail_parsed:
fail_allocate_inputs: fail_allocate_inputs:
wacom_clean_inputs(wacom);
return error;
}
static void wacom_wireless_work(struct work_struct *work)
{
struct wacom *wacom = container_of(work, struct wacom, work);
struct usb_device *usbdev = wacom->usbdev;
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct hid_device *hdev1, *hdev2;
struct wacom *wacom1, *wacom2;
struct wacom_wac *wacom_wac1, *wacom_wac2;
int error;
/*
* Regardless if this is a disconnect or a new tablet,
* remove any existing input and battery devices.
*/
wacom_destroy_battery(wacom);
/* Stylus interface */
hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
wacom1 = hid_get_drvdata(hdev1);
wacom_wac1 = &(wacom1->wacom_wac);
wacom_clean_inputs(wacom1);
/* Touch interface */
hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
wacom2 = hid_get_drvdata(hdev2);
wacom_wac2 = &(wacom2->wacom_wac);
wacom_clean_inputs(wacom2);
if (wacom_wac->pid == 0) {
hid_info(wacom->hdev, "wireless tablet disconnected\n");
wacom_wac1->shared->type = 0;
} else {
const struct hid_device_id *id = wacom_ids;
hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
wacom_wac->pid);
while (id->bus) {
if (id->vendor == USB_VENDOR_ID_WACOM &&
id->product == wacom_wac->pid)
break;
id++;
}
if (!id->bus) {
hid_info(wacom->hdev, "ignoring unknown PID.\n");
return;
}
/* Stylus interface */
wacom_wac1->features =
*((struct wacom_features *)id->driver_data);
wacom_wac1->pid = wacom_wac->pid;
hid_hw_stop(hdev1);
error = wacom_parse_and_register(wacom1, true);
if (error)
goto fail;
/* Touch interface */
if (wacom_wac1->features.touch_max ||
(wacom_wac1->features.type >= INTUOSHT &&
wacom_wac1->features.type <= BAMBOO_PT)) {
wacom_wac2->features =
*((struct wacom_features *)id->driver_data);
wacom_wac2->pid = wacom_wac->pid;
hid_hw_stop(hdev2);
error = wacom_parse_and_register(wacom2, true);
if (error)
goto fail;
}
error = wacom_initialize_battery(wacom);
if (error)
goto fail;
}
return;
fail:
wacom_clean_inputs(wacom1);
wacom_clean_inputs(wacom2);
return;
}
static int wacom_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct usb_device *dev = interface_to_usbdev(intf);
struct wacom *wacom;
struct wacom_wac *wacom_wac;
struct wacom_features *features;
int error;
if (!id->driver_data)
return -EINVAL;
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
/* hid-core sets this quirk for the boot interface */
hdev->quirks &= ~HID_QUIRK_NOGET;
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
if (!wacom)
return -ENOMEM;
hid_set_drvdata(hdev, wacom);
wacom->hdev = hdev;
wacom_wac = &wacom->wacom_wac;
wacom_wac->features = *((struct wacom_features *)id->driver_data);
features = &wacom_wac->features;
if (features->check_for_hid_type && features->hid_type != hdev->type) {
error = -ENODEV;
goto fail_type;
}
wacom->usbdev = dev;
wacom->intf = intf;
mutex_init(&wacom->lock);
INIT_WORK(&wacom->work, wacom_wireless_work);
/* ask for the report descriptor to be loaded by HID */
error = hid_parse(hdev);
if (error) {
hid_err(hdev, "parse failed\n");
goto fail_parse;
}
error = wacom_parse_and_register(wacom, false);
if (error)
goto fail_parse;
if (hdev->bus == BUS_BLUETOOTH) {
error = device_create_file(&hdev->dev, &dev_attr_speed);
if (error)
hid_warn(hdev,
"can't create sysfs speed attribute err: %d\n",
error);
}
return 0;
fail_type: fail_type:
fail_pktlen:
fail_parse: fail_parse:
kfree(wacom); kfree(wacom);
hid_set_drvdata(hdev, NULL); hid_set_drvdata(hdev, NULL);
...@@ -1865,6 +1853,11 @@ static int wacom_probe(struct hid_device *hdev, ...@@ -1865,6 +1853,11 @@ static int wacom_probe(struct hid_device *hdev,
static void wacom_remove(struct hid_device *hdev) static void wacom_remove(struct hid_device *hdev)
{ {
struct wacom *wacom = hid_get_drvdata(hdev); struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features;
if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR)
hid_hw_close(hdev);
hid_hw_stop(hdev); hid_hw_stop(hdev);
......
...@@ -575,33 +575,16 @@ static int wacom_intuos_pad(struct wacom_wac *wacom) ...@@ -575,33 +575,16 @@ static int wacom_intuos_pad(struct wacom_wac *wacom)
return 1; return 1;
} }
static int wacom_intuos_inout(struct wacom_wac *wacom) static int wacom_intuos_get_tool_type(int tool_id)
{ {
struct wacom_features *features = &wacom->features; int tool_type;
unsigned char *data = wacom->data;
struct input_dev *input = wacom->pen_input;
int idx = 0;
/* tool number */
if (features->type == INTUOS)
idx = data[1] & 0x01;
/* Enter report */
if ((data[1] & 0xfc) == 0xc0) {
/* serial number of the tool */
wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
(data[4] << 20) + (data[5] << 12) +
(data[6] << 4) + (data[7] >> 4);
wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) | switch (tool_id) {
((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12);
switch (wacom->id[idx]) {
case 0x812: /* Inking pen */ case 0x812: /* Inking pen */
case 0x801: /* Intuos3 Inking pen */ case 0x801: /* Intuos3 Inking pen */
case 0x120802: /* Intuos4/5 Inking Pen */ case 0x120802: /* Intuos4/5 Inking Pen */
case 0x012: case 0x012:
wacom->tool[idx] = BTN_TOOL_PENCIL; tool_type = BTN_TOOL_PENCIL;
break; break;
case 0x822: /* Pen */ case 0x822: /* Pen */
...@@ -612,18 +595,19 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) ...@@ -612,18 +595,19 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
case 0x885: /* Intuos3 Marker Pen */ case 0x885: /* Intuos3 Marker Pen */
case 0x802: /* Intuos4/5 13HD/24HD General Pen */ case 0x802: /* Intuos4/5 13HD/24HD General Pen */
case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */ case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
case 0x8e2: /* IntuosHT2 pen */
case 0x022: case 0x022:
case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */ case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */ case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */
case 0x160802: /* Cintiq 13HD Pro Pen */ case 0x160802: /* Cintiq 13HD Pro Pen */
case 0x180802: /* DTH2242 Pen */ case 0x180802: /* DTH2242 Pen */
case 0x100802: /* Intuos4/5 13HD/24HD General Pen */ case 0x100802: /* Intuos4/5 13HD/24HD General Pen */
wacom->tool[idx] = BTN_TOOL_PEN; tool_type = BTN_TOOL_PEN;
break; break;
case 0x832: /* Stroke pen */ case 0x832: /* Stroke pen */
case 0x032: case 0x032:
wacom->tool[idx] = BTN_TOOL_BRUSH; tool_type = BTN_TOOL_BRUSH;
break; break;
case 0x007: /* Mouse 4D and 2D */ case 0x007: /* Mouse 4D and 2D */
...@@ -631,13 +615,13 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) ...@@ -631,13 +615,13 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
case 0x094: case 0x094:
case 0x017: /* Intuos3 2D Mouse */ case 0x017: /* Intuos3 2D Mouse */
case 0x806: /* Intuos4 Mouse */ case 0x806: /* Intuos4 Mouse */
wacom->tool[idx] = BTN_TOOL_MOUSE; tool_type = BTN_TOOL_MOUSE;
break; break;
case 0x096: /* Lens cursor */ case 0x096: /* Lens cursor */
case 0x097: /* Intuos3 Lens cursor */ case 0x097: /* Intuos3 Lens cursor */
case 0x006: /* Intuos4 Lens cursor */ case 0x006: /* Intuos4 Lens cursor */
wacom->tool[idx] = BTN_TOOL_LENS; tool_type = BTN_TOOL_LENS;
break; break;
case 0x82a: /* Eraser */ case 0x82a: /* Eraser */
...@@ -657,7 +641,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) ...@@ -657,7 +641,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */ case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */
case 0x18080a: /* DTH2242 Eraser */ case 0x18080a: /* DTH2242 Eraser */
case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */ case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
wacom->tool[idx] = BTN_TOOL_RUBBER; tool_type = BTN_TOOL_RUBBER;
break; break;
case 0xd12: case 0xd12:
...@@ -666,47 +650,57 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) ...@@ -666,47 +650,57 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
case 0x913: /* Intuos3 Airbrush */ case 0x913: /* Intuos3 Airbrush */
case 0x902: /* Intuos4/5 13HD/24HD Airbrush */ case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */ case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
wacom->tool[idx] = BTN_TOOL_AIRBRUSH; tool_type = BTN_TOOL_AIRBRUSH;
break; break;
default: /* Unknown tool */ default: /* Unknown tool */
wacom->tool[idx] = BTN_TOOL_PEN; tool_type = BTN_TOOL_PEN;
break; break;
} }
return 1; return tool_type;
} }
static int wacom_intuos_inout(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
struct input_dev *input = wacom->pen_input;
int idx = (features->type == INTUOS) ? (data[1] & 0x01) : 0;
if (!(((data[1] & 0xfc) == 0xc0) || /* in prox */
((data[1] & 0xfe) == 0x20) || /* in range */
((data[1] & 0xfe) == 0x80))) /* out prox */
return 0;
/* Enter report */
if ((data[1] & 0xfc) == 0xc0) {
/* serial number of the tool */
wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
(data[4] << 20) + (data[5] << 12) +
(data[6] << 4) + (data[7] >> 4);
wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) |
((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12);
wacom->tool[idx] = wacom_intuos_get_tool_type(wacom->id[idx]);
/*
* don't report events for invalid data
*/
/* older I4 styli don't work with new Cintiqs */
if ((!((wacom->id[idx] >> 20) & 0x01) &&
(features->type == WACOM_21UX2)) ||
/* Only large Intuos support Lense Cursor */
(wacom->tool[idx] == BTN_TOOL_LENS &&
(features->type == INTUOS3 ||
features->type == INTUOS3S ||
features->type == INTUOS4 ||
features->type == INTUOS4S ||
features->type == INTUOS5 ||
features->type == INTUOS5S ||
features->type == INTUOSPM ||
features->type == INTUOSPS)) ||
/* Cintiq doesn't send data when RDY bit isn't set */
(features->type == CINTIQ && !(data[1] & 0x40)))
return 1; return 1;
}
/* in Range */
if ((data[1] & 0xfe) == 0x20) {
if (features->type != INTUOSHT2)
wacom->shared->stylus_in_proximity = true; wacom->shared->stylus_in_proximity = true;
if (wacom->shared->touch_down)
return 1;
/* in Range while exiting */ /* in Range while exiting */
if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) { if (wacom->reporting_data) {
input_report_key(input, BTN_TOUCH, 0); input_report_key(input, BTN_TOUCH, 0);
input_report_abs(input, ABS_PRESSURE, 0); input_report_abs(input, ABS_PRESSURE, 0);
input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max); input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max);
return 2; return 2;
} }
return 1;
}
/* Exit report */ /* Exit report */
if ((data[1] & 0xfe) == 0x80) { if ((data[1] & 0xfe) == 0x80) {
...@@ -750,13 +744,6 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) ...@@ -750,13 +744,6 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
return 2; return 2;
} }
/* don't report other events if we don't know the ID */
if (!wacom->id[idx]) {
/* but reschedule a read of the current tool */
wacom_intuos_schedule_prox_event(wacom);
return 1;
}
return 0; return 0;
} }
...@@ -897,6 +884,36 @@ static int wacom_intuos_general(struct wacom_wac *wacom) ...@@ -897,6 +884,36 @@ static int wacom_intuos_general(struct wacom_wac *wacom)
data[0] != WACOM_REPORT_INTUOS_PEN) data[0] != WACOM_REPORT_INTUOS_PEN)
return 0; return 0;
if (wacom->shared->touch_down)
return 1;
/* don't report events if we don't know the tool ID */
if (!wacom->id[idx]) {
/* but reschedule a read of the current tool */
wacom_intuos_schedule_prox_event(wacom);
return 1;
}
/*
* don't report events for invalid data
*/
/* older I4 styli don't work with new Cintiqs */
if ((!((wacom->id[idx] >> 20) & 0x01) &&
(features->type == WACOM_21UX2)) ||
/* Only large Intuos support Lense Cursor */
(wacom->tool[idx] == BTN_TOOL_LENS &&
(features->type == INTUOS3 ||
features->type == INTUOS3S ||
features->type == INTUOS4 ||
features->type == INTUOS4S ||
features->type == INTUOS5 ||
features->type == INTUOS5S ||
features->type == INTUOSPM ||
features->type == INTUOSPS)) ||
/* Cintiq doesn't send data when RDY bit isn't set */
(features->type == CINTIQ && !(data[1] & 0x40)))
return 1;
x = (be16_to_cpup((__be16 *)&data[2]) << 1) | ((data[9] >> 1) & 1); x = (be16_to_cpup((__be16 *)&data[2]) << 1) | ((data[9] >> 1) & 1);
y = (be16_to_cpup((__be16 *)&data[4]) << 1) | (data[9] & 1); y = (be16_to_cpup((__be16 *)&data[4]) << 1) | (data[9] & 1);
distance = data[9] >> 2; distance = data[9] >> 2;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment