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
This commit is contained in:
parent
c14022bfd2
ad8ddc5755
3b654288b1
af2e628d6b
d3e69b9a04
5f66872cbd
1adf904e90
1d1b564ff8
f9a82c2054
f620516178
commit
e1c9b9ff24
|
@ -196,6 +196,12 @@ config HID_PRODIKEYS
|
|||
multimedia keyboard, but will lack support for the musical keyboard
|
||||
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
|
||||
tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
|
||||
depends on USB_HID && I2C && GPIOLIB
|
||||
|
|
|
@ -29,6 +29,7 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
|
|||
obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o
|
||||
obj-$(CONFIG_HID_CHERRY) += hid-cherry.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_CP2112) += hid-cp2112.o
|
||||
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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[] = {
|
|||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_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_MS_NE4K) },
|
||||
{ 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[] = {
|
|||
{ 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_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_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
|
||||
{ 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[] = {
|
|||
{ 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_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)
|
|||
* descriptor. In any case, it's a wonder it works on Windows.
|
||||
*
|
||||
* Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
* Usage (Joystik), ; Joystik (04h, application collection)
|
||||
* Usage (Joystick), ; Joystick (04h, application collection)
|
||||
* Collection (Application),
|
||||
* Collection (Logical),
|
||||
* Report Size (8),
|
||||
|
@ -207,7 +207,7 @@ static inline int drff_init(struct hid_device *hid)
|
|||
/* Fixed report descriptor for PID 0x011 joystick */
|
||||
static __u8 pid0011_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
|
|
|
@ -249,6 +249,7 @@
|
|||
|
||||
#define USB_VENDOR_ID_CMEDIA 0x0d8c
|
||||
#define USB_DEVICE_ID_CM109 0x000e
|
||||
#define USB_DEVICE_ID_CM6533 0x0022
|
||||
|
||||
#define USB_VENDOR_ID_CODEMERCS 0x07c0
|
||||
#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500
|
||||
|
@ -683,6 +684,7 @@
|
|||
#define USB_DEVICE_ID_MS_NE7K 0x071d
|
||||
#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730
|
||||
#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_TOUCH_COVER_2 0x07a7
|
||||
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
|
||||
|
@ -877,6 +879,9 @@
|
|||
#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002
|
||||
#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_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
|
||||
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046
|
||||
|
@ -1052,7 +1057,7 @@
|
|||
#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */
|
||||
|
||||
#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_DEVICE_ID_RAPHNET_2NES2SNES 0x0002
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
*/
|
||||
static __u8 df_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
|
@ -127,7 +127,7 @@ static __u8 df_rdesc_fixed[] = {
|
|||
|
||||
static __u8 dfp_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
|
@ -175,7 +175,7 @@ static __u8 dfp_rdesc_fixed[] = {
|
|||
|
||||
static __u8 fv_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
|
@ -242,7 +242,7 @@ static __u8 fv_rdesc_fixed[] = {
|
|||
|
||||
static __u8 momo_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
|
@ -288,7 +288,7 @@ static __u8 momo_rdesc_fixed[] = {
|
|||
|
||||
static __u8 momo2_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
|
|
|
@ -15,13 +15,19 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/fixp-arith.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "usbhid/usbhid.h"
|
||||
#include "hid-ids.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -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 */
|
||||
|
@ -1301,121 +1890,22 @@ static int k400_connect(struct hid_device *hdev, bool connected)
|
|||
|
||||
#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)
|
||||
{
|
||||
struct g920_private_data *pdata = hidpp->private_data;
|
||||
struct hidpp_report response;
|
||||
u8 feature_type;
|
||||
u8 feature_index;
|
||||
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 */
|
||||
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK,
|
||||
&feature_index, &feature_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pdata->force_feature = 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;
|
||||
}
|
||||
ret = hidpp_ff_init(hidpp, feature_index);
|
||||
if (ret)
|
||||
return 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);
|
||||
hid_warn(hidpp->hid_dev, "Unable to initialize force feedback support, errno %d\n",
|
||||
ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1739,10 +2229,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
ret = k400_allocate(hdev);
|
||||
if (ret)
|
||||
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);
|
||||
|
@ -1825,7 +2311,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
hid_hw_open_failed:
|
||||
hid_device_io_stop(hdev);
|
||||
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
|
||||
device_remove_file(&hdev->dev, &dev_attr_range);
|
||||
hid_hw_close(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
@ -1843,7 +2328,7 @@ static void hidpp_remove(struct hid_device *hdev)
|
|||
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||
|
||||
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_stop(hdev);
|
||||
|
|
|
@ -286,6 +286,8 @@ static const struct hid_device_id ms_devices[] = {
|
|||
.driver_data = MS_HIDINPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
|
||||
.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),
|
||||
.driver_data = MS_PRESENTER },
|
||||
|
|
|
@ -396,6 +396,11 @@ static void mt_feature_mapping(struct hid_device *hdev,
|
|||
td->is_buttonpad = true;
|
||||
|
||||
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)
|
|||
return ret;
|
||||
|
||||
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_input_mode(hdev);
|
||||
|
@ -1145,8 +1153,30 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
mt_release_contacts(hdev);
|
||||
mt_set_maxcontacts(hdev);
|
||||
mt_set_input_mode(hdev);
|
||||
return 0;
|
||||
|
|
|
@ -23,8 +23,12 @@ static int penmount_input_mapping(struct hid_device *hdev,
|
|||
struct hid_usage *usage, unsigned long **bit, int *max)
|
||||
{
|
||||
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
|
||||
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
|
||||
return 1;
|
||||
if (((usage->hid - 1) & HID_USAGE) == 0) {
|
||||
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#define MOTION_CONTROLLER_BT BIT(8)
|
||||
#define NAVIGATION_CONTROLLER_USB BIT(9)
|
||||
#define NAVIGATION_CONTROLLER_BT BIT(10)
|
||||
#define SINO_LITE_CONTROLLER BIT(11)
|
||||
|
||||
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
|
||||
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
|
||||
|
@ -74,7 +75,7 @@
|
|||
* axis values. Additionally, the controller only has 20 actual, physical axes
|
||||
* so there are several unused axes in between the used ones.
|
||||
*/
|
||||
static __u8 sixaxis_rdesc[] = {
|
||||
static u8 sixaxis_rdesc[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
|
@ -152,7 +153,7 @@ static __u8 sixaxis_rdesc[] = {
|
|||
};
|
||||
|
||||
/* PS/3 Motion controller */
|
||||
static __u8 motion_rdesc[] = {
|
||||
static u8 motion_rdesc[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
|
@ -249,9 +250,9 @@ static __u8 motion_rdesc[] = {
|
|||
};
|
||||
|
||||
/* PS/3 Navigation controller */
|
||||
static __u8 navigation_rdesc[] = {
|
||||
static u8 navigation_rdesc[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0xA1, 0x02, /* Collection (Logical), */
|
||||
0x85, 0x01, /* Report ID (1), */
|
||||
|
@ -809,7 +810,7 @@ static u8 dualshock4_bt_rdesc[] = {
|
|||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 ps3remote_rdesc[] = {
|
||||
static u8 ps3remote_rdesc[] = {
|
||||
0x05, 0x01, /* GUsagePage Generic Desktop */
|
||||
0x09, 0x05, /* LUsage 0x05 [Game Pad] */
|
||||
0xA1, 0x01, /* MCollection Application (mouse, keyboard) */
|
||||
|
@ -817,14 +818,18 @@ static __u8 ps3remote_rdesc[] = {
|
|||
/* Use collection 1 for joypad buttons */
|
||||
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] */
|
||||
0x95, 0x01, /* GReportCount 0x01 [1] */
|
||||
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 */
|
||||
0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */
|
||||
0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */
|
||||
|
@ -849,8 +854,10 @@ static __u8 ps3remote_rdesc[] = {
|
|||
0x95, 0x01, /* GReportCount 0x01 [1] */
|
||||
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] */
|
||||
0x95, 0x06, /* GReportCount 0x06 [6] */
|
||||
0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
|
||||
|
@ -929,7 +936,7 @@ static const unsigned int buzz_keymap[] = {
|
|||
/*
|
||||
* The controller has 4 remote buzzers, each with one LED and 5
|
||||
* buttons.
|
||||
*
|
||||
*
|
||||
* We use the mapping chosen by the controller, which is:
|
||||
*
|
||||
* Key Offset
|
||||
|
@ -943,15 +950,15 @@ static const unsigned int buzz_keymap[] = {
|
|||
* So, for example, the orange button on the third buzzer is mapped to
|
||||
* BTN_TRIGGER_HAPPY14
|
||||
*/
|
||||
[ 1] = BTN_TRIGGER_HAPPY1,
|
||||
[ 2] = BTN_TRIGGER_HAPPY2,
|
||||
[ 3] = BTN_TRIGGER_HAPPY3,
|
||||
[ 4] = BTN_TRIGGER_HAPPY4,
|
||||
[ 5] = BTN_TRIGGER_HAPPY5,
|
||||
[ 6] = BTN_TRIGGER_HAPPY6,
|
||||
[ 7] = BTN_TRIGGER_HAPPY7,
|
||||
[ 8] = BTN_TRIGGER_HAPPY8,
|
||||
[ 9] = BTN_TRIGGER_HAPPY9,
|
||||
[1] = BTN_TRIGGER_HAPPY1,
|
||||
[2] = BTN_TRIGGER_HAPPY2,
|
||||
[3] = BTN_TRIGGER_HAPPY3,
|
||||
[4] = BTN_TRIGGER_HAPPY4,
|
||||
[5] = BTN_TRIGGER_HAPPY5,
|
||||
[6] = BTN_TRIGGER_HAPPY6,
|
||||
[7] = BTN_TRIGGER_HAPPY7,
|
||||
[8] = BTN_TRIGGER_HAPPY8,
|
||||
[9] = BTN_TRIGGER_HAPPY9,
|
||||
[10] = BTN_TRIGGER_HAPPY10,
|
||||
[11] = BTN_TRIGGER_HAPPY11,
|
||||
[12] = BTN_TRIGGER_HAPPY12,
|
||||
|
@ -973,33 +980,33 @@ static enum power_supply_property sony_battery_props[] = {
|
|||
};
|
||||
|
||||
struct sixaxis_led {
|
||||
__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 enabled;
|
||||
__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 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 enabled;
|
||||
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%) */
|
||||
} __packed;
|
||||
|
||||
struct sixaxis_rumble {
|
||||
__u8 padding;
|
||||
__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 left_duration; /* Left motor duration (0xff means forever) */
|
||||
__u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
|
||||
u8 padding;
|
||||
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 left_duration; /* Left motor duration (0xff means forever) */
|
||||
u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
|
||||
} __packed;
|
||||
|
||||
struct sixaxis_output_report {
|
||||
__u8 report_id;
|
||||
u8 report_id;
|
||||
struct sixaxis_rumble rumble;
|
||||
__u8 padding[4];
|
||||
__u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
|
||||
u8 padding[4];
|
||||
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 _reserved; /* LED5, not actually soldered */
|
||||
} __packed;
|
||||
|
||||
union sixaxis_output_report_01 {
|
||||
struct sixaxis_output_report data;
|
||||
__u8 buf[36];
|
||||
u8 buf[36];
|
||||
};
|
||||
|
||||
struct motion_output_report_02 {
|
||||
|
@ -1028,30 +1035,30 @@ struct sony_sc {
|
|||
struct led_classdev *leds[MAX_LEDS];
|
||||
unsigned long quirks;
|
||||
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_desc battery_desc;
|
||||
int device_id;
|
||||
__u8 *output_report_dmabuf;
|
||||
u8 *output_report_dmabuf;
|
||||
|
||||
#ifdef CONFIG_SONY_FF
|
||||
__u8 left;
|
||||
__u8 right;
|
||||
u8 left;
|
||||
u8 right;
|
||||
#endif
|
||||
|
||||
__u8 mac_address[6];
|
||||
__u8 worker_initialized;
|
||||
__u8 cable_state;
|
||||
__u8 battery_charging;
|
||||
__u8 battery_capacity;
|
||||
__u8 led_state[MAX_LEDS];
|
||||
__u8 resume_led_state[MAX_LEDS];
|
||||
__u8 led_delay_on[MAX_LEDS];
|
||||
__u8 led_delay_off[MAX_LEDS];
|
||||
__u8 led_count;
|
||||
u8 mac_address[6];
|
||||
u8 worker_initialized;
|
||||
u8 cable_state;
|
||||
u8 battery_charging;
|
||||
u8 battery_capacity;
|
||||
u8 led_state[MAX_LEDS];
|
||||
u8 resume_led_state[MAX_LEDS];
|
||||
u8 led_delay_on[MAX_LEDS];
|
||||
u8 led_delay_off[MAX_LEDS];
|
||||
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)
|
||||
{
|
||||
*rsize = sizeof(sixaxis_rdesc);
|
||||
|
@ -1072,7 +1079,7 @@ static u8 *navigation_fixup(struct hid_device *hdev, u8 *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)
|
||||
{
|
||||
*rsize = sizeof(ps3remote_rdesc);
|
||||
|
@ -1113,11 +1120,14 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
|
|||
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)
|
||||
{
|
||||
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
|
||||
* a constant non-data variable.
|
||||
|
@ -1164,12 +1174,12 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *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;
|
||||
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
|
||||
|
@ -1184,7 +1194,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
|
|||
battery_charging = !(rd[offset] & 0x01);
|
||||
cable_state = 1;
|
||||
} else {
|
||||
__u8 index = rd[offset] <= 5 ? rd[offset] : 5;
|
||||
u8 index = rd[offset] <= 5 ? rd[offset] : 5;
|
||||
battery_capacity = sixaxis_battery_capacity[index];
|
||||
battery_charging = 0;
|
||||
cable_state = 0;
|
||||
|
@ -1197,14 +1207,14 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
|
|||
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, list);
|
||||
struct input_dev *input_dev = hidinput->input;
|
||||
unsigned long flags;
|
||||
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
|
||||
|
@ -1254,7 +1264,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
|
|||
* follows the data for the first.
|
||||
*/
|
||||
for (n = 0; n < 2; n++) {
|
||||
__u16 x, y;
|
||||
u16 x, y;
|
||||
|
||||
x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8);
|
||||
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)
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -1394,7 +1404,7 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
|
|||
{
|
||||
const int buf_size =
|
||||
max(SIXAXIS_REPORT_0xF2_SIZE, SIXAXIS_REPORT_0xF5_SIZE);
|
||||
__u8 *buf;
|
||||
u8 *buf;
|
||||
int ret;
|
||||
|
||||
buf = kmalloc(buf_size, GFP_KERNEL);
|
||||
|
@ -1420,8 +1430,10 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
|
|||
}
|
||||
|
||||
ret = hid_hw_output_report(hdev, buf, 1);
|
||||
if (ret < 0)
|
||||
hid_err(hdev, "can't set operational mode: step 3\n");
|
||||
if (ret < 0) {
|
||||
hid_info(hdev, "can't set operational mode: step 3, ignoring\n");
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
|
@ -1431,8 +1443,8 @@ out:
|
|||
|
||||
static int sixaxis_set_operational_bt(struct hid_device *hdev)
|
||||
{
|
||||
static const __u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
|
||||
__u8 *buf;
|
||||
static const u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
|
||||
u8 *buf;
|
||||
int ret;
|
||||
|
||||
buf = kmemdup(report, sizeof(report), GFP_KERNEL);
|
||||
|
@ -1453,7 +1465,7 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
|
|||
*/
|
||||
static int dualshock4_set_operational_bt(struct hid_device *hdev)
|
||||
{
|
||||
__u8 *buf;
|
||||
u8 *buf;
|
||||
int ret;
|
||||
|
||||
buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL);
|
||||
|
@ -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 const __u8 sixaxis_leds[10][4] = {
|
||||
static const u8 sixaxis_leds[10][4] = {
|
||||
{ 0x01, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x01, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x01, 0x00 },
|
||||
|
@ -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)
|
||||
{
|
||||
/* 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 },
|
||||
/* Red */ { 0x01, 0x00, 0x00 },
|
||||
/* Green */ { 0x00, 0x01, 0x00 },
|
||||
|
@ -1525,7 +1537,7 @@ static void buzz_set_leds(struct sony_sc *sc)
|
|||
&hdev->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct hid_report *report = list_entry(report_list->next,
|
||||
struct hid_report, list);
|
||||
__s32 *value = report->field[0]->value;
|
||||
s32 *value = report->field[0]->value;
|
||||
|
||||
BUILD_BUG_ON(MAX_LEDS < 4);
|
||||
|
||||
|
@ -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 sony_sc *drv_data = hid_get_drvdata(hdev);
|
||||
int n;
|
||||
__u8 new_on, new_off;
|
||||
u8 new_on, new_off;
|
||||
|
||||
if (!drv_data) {
|
||||
hid_err(hdev, "No device data\n");
|
||||
|
@ -1690,8 +1702,8 @@ static int sony_leds_init(struct sony_sc *sc)
|
|||
const char *name_fmt;
|
||||
static const char * const ds4_name_str[] = { "red", "green", "blue",
|
||||
"global" };
|
||||
__u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
|
||||
__u8 use_hw_blink[MAX_LEDS] = { 0 };
|
||||
u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
|
||||
u8 use_hw_blink[MAX_LEDS] = { 0 };
|
||||
|
||||
BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
|
||||
|
||||
|
@ -1719,7 +1731,7 @@ static int sony_leds_init(struct sony_sc *sc)
|
|||
name_len = 0;
|
||||
name_fmt = "%s:%s";
|
||||
} 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));
|
||||
sc->led_count = 1;
|
||||
|
@ -1796,7 +1808,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
|
|||
static const union sixaxis_output_report_01 default_report = {
|
||||
.buf = {
|
||||
0x01,
|
||||
0x00, 0xff, 0x00, 0xff, 0x00,
|
||||
0x01, 0xff, 0x00, 0xff, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
0xff, 0x27, 0x10, 0x00, 0x32,
|
||||
|
@ -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),
|
||||
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
@ -1850,7 +1862,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
|
|||
static void dualshock4_send_output_report(struct sony_sc *sc)
|
||||
{
|
||||
struct hid_device *hdev = sc->hdev;
|
||||
__u8 *buf = sc->output_report_dmabuf;
|
||||
u8 *buf = sc->output_report_dmabuf;
|
||||
int offset;
|
||||
|
||||
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
|
||||
|
@ -1910,7 +1922,7 @@ static void motion_send_output_report(struct sony_sc *sc)
|
|||
report->rumble = max(sc->right, sc->left);
|
||||
#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)
|
||||
|
@ -1922,6 +1934,7 @@ static inline void sony_send_output_report(struct sony_sc *sc)
|
|||
static void sony_state_worker(struct work_struct *work)
|
||||
{
|
||||
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
|
||||
|
||||
sc->send_output_report(sc);
|
||||
}
|
||||
|
||||
|
@ -2142,7 +2155,7 @@ static int sony_get_bt_devaddr(struct sony_sc *sc)
|
|||
|
||||
static int sony_check_add(struct sony_sc *sc)
|
||||
{
|
||||
__u8 *buf = NULL;
|
||||
u8 *buf = NULL;
|
||||
int n, ret;
|
||||
|
||||
if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
|
||||
|
@ -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,
|
||||
void(*send_output_report)(struct sony_sc*))
|
||||
void (*send_output_report)(struct sony_sc *))
|
||||
{
|
||||
sc->send_output_report = send_output_report;
|
||||
|
||||
|
@ -2441,7 +2454,7 @@ static int sony_suspend(struct hid_device *hdev, pm_message_t message)
|
|||
/*
|
||||
* On suspend save the current LED state,
|
||||
* stop running force-feedback and blank the LEDS.
|
||||
*/
|
||||
*/
|
||||
if (SONY_LED_SUPPORT || SONY_FF_SUPPORT) {
|
||||
struct sony_sc *sc = hid_get_drvdata(hdev);
|
||||
|
||||
|
@ -2501,8 +2514,10 @@ static const struct hid_device_id sony_devices[] = {
|
|||
.driver_data = VAIO_RDESC_CONSTANT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE),
|
||||
.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),
|
||||
.driver_data = 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[] = {
|
|||
.driver_data = DUALSHOCK4_CONTROLLER_USB },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER),
|
||||
.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);
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
|
@ -56,7 +55,6 @@ struct thingm_rgb {
|
|||
struct thingm_led red;
|
||||
struct thingm_led green;
|
||||
struct thingm_led blue;
|
||||
struct work_struct work;
|
||||
u8 num;
|
||||
};
|
||||
|
||||
|
@ -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[5], buf[6], buf[7], buf[8]);
|
||||
|
||||
mutex_lock(&tdev->lock);
|
||||
|
||||
ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
|
||||
mutex_unlock(&tdev->lock);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
|
@ -89,16 +91,31 @@ static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
|
|||
{
|
||||
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,
|
||||
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err;
|
||||
|
||||
ret = 0;
|
||||
|
||||
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[5], buf[6], buf[7], buf[8]);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
mutex_unlock(&tdev->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 };
|
||||
int err;
|
||||
|
||||
err = thingm_send(tdev, buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = thingm_recv(tdev, buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -131,25 +144,17 @@ static int thingm_write_color(struct thingm_rgb *rgb)
|
|||
return thingm_send(rgb->tdev, buf);
|
||||
}
|
||||
|
||||
static void thingm_work(struct work_struct *work)
|
||||
{
|
||||
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)
|
||||
static int thingm_led_set(struct led_classdev *ldev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct thingm_led *led = container_of(ldev, struct thingm_led, ldev);
|
||||
int ret;
|
||||
|
||||
/* the ledclass has already stored the brightness value */
|
||||
schedule_work(&led->rgb->work);
|
||||
ret = thingm_write_color(led->rgb);
|
||||
if (ret)
|
||||
hid_err(led->rgb->tdev->hdev, "failed to write color\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
rgb->red.ldev.name = rgb->red.name;
|
||||
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;
|
||||
|
||||
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)
|
||||
return err;
|
||||
|
||||
|
@ -174,46 +180,27 @@ static int thingm_init_rgb(struct thingm_rgb *rgb)
|
|||
"thingm%d:green:led%d", minor, rgb->num);
|
||||
rgb->green.ldev.name = rgb->green.name;
|
||||
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;
|
||||
|
||||
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)
|
||||
goto unregister_red;
|
||||
return err;
|
||||
|
||||
/* Register the blue diode */
|
||||
snprintf(rgb->blue.name, sizeof(rgb->blue.name),
|
||||
"thingm%d:blue:led%d", minor, rgb->num);
|
||||
rgb->blue.ldev.name = rgb->blue.name;
|
||||
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;
|
||||
|
||||
err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev);
|
||||
if (err)
|
||||
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);
|
||||
|
||||
err = devm_led_classdev_register(&rgb->tdev->hdev->dev,
|
||||
&rgb->blue.ldev);
|
||||
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)
|
||||
{
|
||||
struct thingm_device *tdev;
|
||||
|
@ -229,17 +216,13 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
|
||||
err = hid_parse(hdev);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
|
||||
if (err)
|
||||
goto error;
|
||||
return err;
|
||||
|
||||
mutex_init(&tdev->lock);
|
||||
|
||||
err = thingm_version(tdev);
|
||||
if (err)
|
||||
goto stop;
|
||||
return err;
|
||||
|
||||
hid_dbg(hdev, "firmware version: %c.%c\n",
|
||||
tdev->version.major, tdev->version.minor);
|
||||
|
@ -250,17 +233,18 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
|
||||
if (!tdev->fwinfo) {
|
||||
hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
|
||||
err = -ENODEV;
|
||||
goto stop;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tdev->rgb = devm_kzalloc(&hdev->dev,
|
||||
sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb,
|
||||
GFP_KERNEL);
|
||||
if (!tdev->rgb) {
|
||||
err = -ENOMEM;
|
||||
goto stop;
|
||||
}
|
||||
if (!tdev->rgb)
|
||||
return -ENOMEM;
|
||||
|
||||
err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < tdev->fwinfo->numrgb; ++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)
|
|||
rgb->num = tdev->fwinfo->first + i;
|
||||
err = thingm_init_rgb(rgb);
|
||||
if (err) {
|
||||
while (--i >= 0)
|
||||
thingm_remove_rgb(tdev->rgb + i);
|
||||
goto stop;
|
||||
hid_hw_stop(hdev);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
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[] = {
|
||||
|
@ -302,7 +270,6 @@ MODULE_DEVICE_TABLE(hid, thingm_table);
|
|||
static struct hid_driver thingm_driver = {
|
||||
.name = "thingm",
|
||||
.probe = thingm_probe,
|
||||
.remove = thingm_remove,
|
||||
.id_table = thingm_table,
|
||||
};
|
||||
|
||||
|
|
|
@ -283,18 +283,22 @@ static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
|
|||
u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
|
||||
u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
|
||||
u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
|
||||
|
||||
/* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */
|
||||
u16 size = 2 /* size */ +
|
||||
(reportID ? 1 : 0) /* reportID */ +
|
||||
data_len /* buf */;
|
||||
int args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ +
|
||||
2 /* dataRegister */ +
|
||||
size /* args */;
|
||||
u16 size;
|
||||
int args_len;
|
||||
int index = 0;
|
||||
|
||||
i2c_hid_dbg(ihid, "%s\n", __func__);
|
||||
|
||||
if (data_len > ihid->bufsize)
|
||||
return -EINVAL;
|
||||
|
||||
size = 2 /* size */ +
|
||||
(reportID ? 1 : 0) /* reportID */ +
|
||||
data_len /* buf */;
|
||||
args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ +
|
||||
2 /* dataRegister */ +
|
||||
size /* args */;
|
||||
|
||||
if (!use_data && maxOutputLength == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
|
@ -1108,13 +1112,30 @@ static int i2c_hid_suspend(struct device *dev)
|
|||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||
struct hid_device *hid = ihid->hid;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
int wake_status;
|
||||
|
||||
if (hid->driver && hid->driver->suspend)
|
||||
ret = hid->driver->suspend(hid, PMSG_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);
|
||||
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)) {
|
||||
wake_status = enable_irq_wake(ihid->irq);
|
||||
if (!wake_status)
|
||||
|
@ -1124,10 +1145,7 @@ static int i2c_hid_suspend(struct device *dev)
|
|||
wake_status);
|
||||
}
|
||||
|
||||
/* Save some power */
|
||||
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
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) {
|
||||
wake_status = disable_irq_wake(ihid->irq);
|
||||
if (!wake_status)
|
||||
|
@ -1152,6 +1165,16 @@ static int i2c_hid_resume(struct device *dev)
|
|||
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) {
|
||||
ret = hid->driver->reset_resume(hid);
|
||||
return ret;
|
||||
|
@ -1191,6 +1214,7 @@ static const struct dev_pm_ops i2c_hid_pm = {
|
|||
|
||||
static const struct i2c_device_id i2c_hid_id_table[] = {
|
||||
{ "hid", 0 },
|
||||
{ "hid-over-i2c", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table);
|
||||
|
|
|
@ -1357,6 +1357,9 @@ static void wacom_clean_inputs(struct wacom *wacom)
|
|||
wacom->wacom_wac.pen_input = NULL;
|
||||
wacom->wacom_wac.touch_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);
|
||||
}
|
||||
|
||||
|
@ -1494,6 +1497,206 @@ static void wacom_calculate_res(struct wacom_features *features)
|
|||
features->unitExpo);
|
||||
}
|
||||
|
||||
void wacom_battery_work(struct work_struct *work)
|
||||
{
|
||||
struct wacom *wacom = container_of(work, struct wacom, work);
|
||||
|
||||
if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
|
||||
!wacom->battery) {
|
||||
wacom_initialize_battery(wacom);
|
||||
}
|
||||
else if (!(wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
|
||||
wacom->battery) {
|
||||
wacom_destroy_battery(wacom);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t wacom_compute_pktlen(struct hid_device *hdev)
|
||||
{
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_report *report;
|
||||
size_t size = 0;
|
||||
|
||||
report_enum = hdev->report_enum + HID_INPUT_REPORT;
|
||||
|
||||
list_for_each_entry(report, &report_enum->report_list, list) {
|
||||
size_t report_size = hid_report_len(report);
|
||||
if (report_size > size)
|
||||
size = report_size;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void wacom_update_name(struct wacom *wacom, const char *suffix)
|
||||
{
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct wacom_features *features = &wacom_wac->features;
|
||||
char name[WACOM_NAME_MAX];
|
||||
|
||||
/* Generic devices name unspecified */
|
||||
if ((features->type == HID_GENERIC) && !strcmp("Wacom HID", features->name)) {
|
||||
if (strstr(wacom->hdev->name, "Wacom") ||
|
||||
strstr(wacom->hdev->name, "wacom") ||
|
||||
strstr(wacom->hdev->name, "WACOM")) {
|
||||
/* name is in HID descriptor, use it */
|
||||
strlcpy(name, wacom->hdev->name, sizeof(name));
|
||||
|
||||
/* strip out excess whitespaces */
|
||||
while (1) {
|
||||
char *gap = strstr(name, " ");
|
||||
if (gap == NULL)
|
||||
break;
|
||||
/* shift everything including the terminator */
|
||||
memmove(gap, gap+1, strlen(gap));
|
||||
}
|
||||
/* get rid of trailing whitespace */
|
||||
if (name[strlen(name)-1] == ' ')
|
||||
name[strlen(name)-1] = '\0';
|
||||
} else {
|
||||
/* no meaningful name retrieved. use product ID */
|
||||
snprintf(name, sizeof(name),
|
||||
"%s %X", features->name, wacom->hdev->product);
|
||||
}
|
||||
} else {
|
||||
strlcpy(name, features->name, sizeof(name));
|
||||
}
|
||||
|
||||
/* Append the device type to the name */
|
||||
snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
|
||||
"%s%s Pen", name, suffix);
|
||||
snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name),
|
||||
"%s%s Finger", name, suffix);
|
||||
snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
|
||||
"%s%s Pad", name, suffix);
|
||||
}
|
||||
|
||||
static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
|
||||
{
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct wacom_features *features = &wacom_wac->features;
|
||||
struct hid_device *hdev = wacom->hdev;
|
||||
int error;
|
||||
unsigned int connect_mask = HID_CONNECT_HIDRAW;
|
||||
|
||||
features->pktlen = wacom_compute_pktlen(hdev);
|
||||
if (features->pktlen > WACOM_PKGLEN_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
error = wacom_allocate_inputs(wacom);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Bamboo Pad has a generic hid handling for the Pen, and we switch it
|
||||
* into debug mode for the touch part.
|
||||
* We ignore the other interfaces.
|
||||
*/
|
||||
if (features->type == BAMBOO_PAD) {
|
||||
if (features->pktlen == WACOM_PKGLEN_PENABLED) {
|
||||
features->type = HID_GENERIC;
|
||||
} else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) &&
|
||||
(features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) {
|
||||
error = -ENODEV;
|
||||
goto fail_allocate_inputs;
|
||||
}
|
||||
}
|
||||
|
||||
/* set the default size in case we do not get them from hid */
|
||||
wacom_set_default_phy(features);
|
||||
|
||||
/* Retrieve the physical and logical size for touch devices */
|
||||
wacom_retrieve_hid_descriptor(hdev, features);
|
||||
wacom_setup_device_quirks(wacom);
|
||||
|
||||
if (features->device_type == WACOM_DEVICETYPE_NONE &&
|
||||
features->type != WIRELESS) {
|
||||
error = features->type == HID_GENERIC ? -ENODEV : 0;
|
||||
|
||||
dev_warn(&hdev->dev, "Unknown device_type for '%s'. %s.",
|
||||
hdev->name,
|
||||
error ? "Ignoring" : "Assuming pen");
|
||||
|
||||
if (error)
|
||||
goto fail_parsed;
|
||||
|
||||
features->device_type |= WACOM_DEVICETYPE_PEN;
|
||||
}
|
||||
|
||||
wacom_calculate_res(features);
|
||||
|
||||
wacom_update_name(wacom, wireless ? " (WL)" : "");
|
||||
|
||||
error = wacom_add_shared_data(hdev);
|
||||
if (error)
|
||||
goto fail_shared_data;
|
||||
|
||||
if (!(features->device_type & WACOM_DEVICETYPE_WL_MONITOR) &&
|
||||
(features->quirks & WACOM_QUIRK_BATTERY)) {
|
||||
error = wacom_initialize_battery(wacom);
|
||||
if (error)
|
||||
goto fail_battery;
|
||||
}
|
||||
|
||||
error = wacom_register_inputs(wacom);
|
||||
if (error)
|
||||
goto fail_register_inputs;
|
||||
|
||||
if (features->type == HID_GENERIC)
|
||||
connect_mask |= HID_CONNECT_DRIVER;
|
||||
|
||||
/* Regular HID work starts now */
|
||||
error = hid_hw_start(hdev, connect_mask);
|
||||
if (error) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
goto fail_hw_start;
|
||||
}
|
||||
|
||||
if (!wireless) {
|
||||
/* Note that if query fails it is not a hard failure */
|
||||
wacom_query_tablet_data(hdev, features);
|
||||
}
|
||||
|
||||
/* touch only Bamboo doesn't support pen */
|
||||
if ((features->type == BAMBOO_TOUCH) &&
|
||||
(features->device_type & WACOM_DEVICETYPE_PEN)) {
|
||||
error = -ENODEV;
|
||||
goto fail_hw_start;
|
||||
}
|
||||
|
||||
/* pen only Bamboo neither support touch nor pad */
|
||||
if ((features->type == BAMBOO_PEN) &&
|
||||
((features->device_type & WACOM_DEVICETYPE_TOUCH) ||
|
||||
(features->device_type & WACOM_DEVICETYPE_PAD))) {
|
||||
error = -ENODEV;
|
||||
goto fail_hw_start;
|
||||
}
|
||||
|
||||
if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR)
|
||||
error = hid_hw_open(hdev);
|
||||
|
||||
if ((wacom_wac->features.type == INTUOSHT ||
|
||||
wacom_wac->features.type == INTUOSHT2) &&
|
||||
(wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)) {
|
||||
wacom_wac->shared->touch_input = wacom_wac->touch_input;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_hw_start:
|
||||
hid_hw_stop(hdev);
|
||||
fail_register_inputs:
|
||||
wacom_clean_inputs(wacom);
|
||||
wacom_destroy_battery(wacom);
|
||||
fail_battery:
|
||||
wacom_remove_shared_data(wacom);
|
||||
fail_shared_data:
|
||||
fail_parsed:
|
||||
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);
|
||||
|
@ -1547,22 +1750,10 @@ static void wacom_wireless_work(struct work_struct *work)
|
|||
/* 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);
|
||||
hid_hw_stop(hdev1);
|
||||
error = wacom_parse_and_register(wacom1, true);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
|
@ -1572,30 +1763,11 @@ static void wacom_wireless_work(struct work_struct *work)
|
|||
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);
|
||||
hid_hw_stop(hdev2);
|
||||
error = wacom_parse_and_register(wacom2, true);
|
||||
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);
|
||||
|
@ -1611,80 +1783,6 @@ fail:
|
|||
return;
|
||||
}
|
||||
|
||||
void wacom_battery_work(struct work_struct *work)
|
||||
{
|
||||
struct wacom *wacom = container_of(work, struct wacom, work);
|
||||
|
||||
if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
|
||||
!wacom->battery) {
|
||||
wacom_initialize_battery(wacom);
|
||||
}
|
||||
else if (!(wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
|
||||
wacom->battery) {
|
||||
wacom_destroy_battery(wacom);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t wacom_compute_pktlen(struct hid_device *hdev)
|
||||
{
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_report *report;
|
||||
size_t size = 0;
|
||||
|
||||
report_enum = hdev->report_enum + HID_INPUT_REPORT;
|
||||
|
||||
list_for_each_entry(report, &report_enum->report_list, list) {
|
||||
size_t report_size = hid_report_len(report);
|
||||
if (report_size > size)
|
||||
size = report_size;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void wacom_update_name(struct wacom *wacom)
|
||||
{
|
||||
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
|
||||
struct wacom_features *features = &wacom_wac->features;
|
||||
char name[WACOM_NAME_MAX];
|
||||
|
||||
/* Generic devices name unspecified */
|
||||
if ((features->type == HID_GENERIC) && !strcmp("Wacom HID", features->name)) {
|
||||
if (strstr(wacom->hdev->name, "Wacom") ||
|
||||
strstr(wacom->hdev->name, "wacom") ||
|
||||
strstr(wacom->hdev->name, "WACOM")) {
|
||||
/* name is in HID descriptor, use it */
|
||||
strlcpy(name, wacom->hdev->name, sizeof(name));
|
||||
|
||||
/* strip out excess whitespaces */
|
||||
while (1) {
|
||||
char *gap = strstr(name, " ");
|
||||
if (gap == NULL)
|
||||
break;
|
||||
/* shift everything including the terminator */
|
||||
memmove(gap, gap+1, strlen(gap));
|
||||
}
|
||||
/* get rid of trailing whitespace */
|
||||
if (name[strlen(name)-1] == ' ')
|
||||
name[strlen(name)-1] = '\0';
|
||||
} else {
|
||||
/* no meaningful name retrieved. use product ID */
|
||||
snprintf(name, sizeof(name),
|
||||
"%s %X", features->name, wacom->hdev->product);
|
||||
}
|
||||
} else {
|
||||
strlcpy(name, features->name, sizeof(name));
|
||||
}
|
||||
|
||||
/* Append the device type to the name */
|
||||
snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
|
||||
"%s Pen", name);
|
||||
snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name),
|
||||
"%s Finger", name);
|
||||
snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
|
||||
"%s Pad", name);
|
||||
}
|
||||
|
||||
static int wacom_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
|
@ -1694,7 +1792,6 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
struct wacom_wac *wacom_wac;
|
||||
struct wacom_features *features;
|
||||
int error;
|
||||
unsigned int connect_mask = HID_CONNECT_HIDRAW;
|
||||
|
||||
if (!id->driver_data)
|
||||
return -EINVAL;
|
||||
|
@ -1711,21 +1808,9 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
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);
|
||||
if (features->pktlen > WACOM_PKGLEN_MAX) {
|
||||
error = -EINVAL;
|
||||
goto fail_pktlen;
|
||||
}
|
||||
|
||||
if (features->check_for_hid_type && features->hid_type != hdev->type) {
|
||||
error = -ENODEV;
|
||||
|
@ -1737,64 +1822,16 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
mutex_init(&wacom->lock);
|
||||
INIT_WORK(&wacom->work, wacom_wireless_work);
|
||||
|
||||
error = wacom_allocate_inputs(wacom);
|
||||
if (error)
|
||||
goto fail_allocate_inputs;
|
||||
|
||||
/*
|
||||
* Bamboo Pad has a generic hid handling for the Pen, and we switch it
|
||||
* into debug mode for the touch part.
|
||||
* We ignore the other interfaces.
|
||||
*/
|
||||
if (features->type == BAMBOO_PAD) {
|
||||
if (features->pktlen == WACOM_PKGLEN_PENABLED) {
|
||||
features->type = HID_GENERIC;
|
||||
} else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) &&
|
||||
(features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) {
|
||||
error = -ENODEV;
|
||||
goto fail_shared_data;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* set the default size in case we do not get them from hid */
|
||||
wacom_set_default_phy(features);
|
||||
|
||||
/* Retrieve the physical and logical size for touch devices */
|
||||
wacom_retrieve_hid_descriptor(hdev, features);
|
||||
wacom_setup_device_quirks(wacom);
|
||||
|
||||
if (features->device_type == WACOM_DEVICETYPE_NONE &&
|
||||
features->type != WIRELESS) {
|
||||
error = features->type == HID_GENERIC ? -ENODEV : 0;
|
||||
|
||||
dev_warn(&hdev->dev, "Unknown device_type for '%s'. %s.",
|
||||
hdev->name,
|
||||
error ? "Ignoring" : "Assuming pen");
|
||||
|
||||
if (error)
|
||||
goto fail_shared_data;
|
||||
|
||||
features->device_type |= WACOM_DEVICETYPE_PEN;
|
||||
}
|
||||
|
||||
wacom_calculate_res(features);
|
||||
|
||||
wacom_update_name(wacom);
|
||||
|
||||
error = wacom_add_shared_data(hdev);
|
||||
error = wacom_parse_and_register(wacom, false);
|
||||
if (error)
|
||||
goto fail_shared_data;
|
||||
|
||||
if (!(features->device_type & WACOM_DEVICETYPE_WL_MONITOR) &&
|
||||
(features->quirks & WACOM_QUIRK_BATTERY)) {
|
||||
error = wacom_initialize_battery(wacom);
|
||||
if (error)
|
||||
goto fail_battery;
|
||||
}
|
||||
|
||||
error = wacom_register_inputs(wacom);
|
||||
if (error)
|
||||
goto fail_register_inputs;
|
||||
goto fail_parse;
|
||||
|
||||
if (hdev->bus == BUS_BLUETOOTH) {
|
||||
error = device_create_file(&hdev->dev, &dev_attr_speed);
|
||||
|
@ -1804,58 +1841,9 @@ static int wacom_probe(struct hid_device *hdev,
|
|||
error);
|
||||
}
|
||||
|
||||
if (features->type == HID_GENERIC)
|
||||
connect_mask |= HID_CONNECT_DRIVER;
|
||||
|
||||
/* Regular HID work starts now */
|
||||
error = hid_hw_start(hdev, connect_mask);
|
||||
if (error) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
goto fail_hw_start;
|
||||
}
|
||||
|
||||
/* Note that if query fails it is not a hard failure */
|
||||
wacom_query_tablet_data(hdev, features);
|
||||
|
||||
/* touch only Bamboo doesn't support pen */
|
||||
if ((features->type == BAMBOO_TOUCH) &&
|
||||
(features->device_type & WACOM_DEVICETYPE_PEN)) {
|
||||
error = -ENODEV;
|
||||
goto fail_hw_start;
|
||||
}
|
||||
|
||||
/* pen only Bamboo neither support touch nor pad */
|
||||
if ((features->type == BAMBOO_PEN) &&
|
||||
((features->device_type & WACOM_DEVICETYPE_TOUCH) ||
|
||||
(features->device_type & WACOM_DEVICETYPE_PAD))) {
|
||||
error = -ENODEV;
|
||||
goto fail_hw_start;
|
||||
}
|
||||
|
||||
if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR)
|
||||
error = hid_hw_open(hdev);
|
||||
|
||||
if ((wacom_wac->features.type == INTUOSHT ||
|
||||
wacom_wac->features.type == INTUOSHT2) &&
|
||||
(wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)) {
|
||||
wacom_wac->shared->touch_input = wacom_wac->touch_input;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_hw_start:
|
||||
if (hdev->bus == BUS_BLUETOOTH)
|
||||
device_remove_file(&hdev->dev, &dev_attr_speed);
|
||||
fail_register_inputs:
|
||||
wacom_clean_inputs(wacom);
|
||||
wacom_destroy_battery(wacom);
|
||||
fail_battery:
|
||||
wacom_remove_shared_data(wacom);
|
||||
fail_shared_data:
|
||||
wacom_clean_inputs(wacom);
|
||||
fail_allocate_inputs:
|
||||
fail_type:
|
||||
fail_pktlen:
|
||||
fail_parse:
|
||||
kfree(wacom);
|
||||
hid_set_drvdata(hdev, NULL);
|
||||
|
@ -1865,6 +1853,11 @@ fail_parse:
|
|||
static void wacom_remove(struct hid_device *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);
|
||||
|
||||
|
|
|
@ -575,16 +575,102 @@ static int wacom_intuos_pad(struct wacom_wac *wacom)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_intuos_get_tool_type(int tool_id)
|
||||
{
|
||||
int tool_type;
|
||||
|
||||
switch (tool_id) {
|
||||
case 0x812: /* Inking pen */
|
||||
case 0x801: /* Intuos3 Inking pen */
|
||||
case 0x120802: /* Intuos4/5 Inking Pen */
|
||||
case 0x012:
|
||||
tool_type = BTN_TOOL_PENCIL;
|
||||
break;
|
||||
|
||||
case 0x822: /* Pen */
|
||||
case 0x842:
|
||||
case 0x852:
|
||||
case 0x823: /* Intuos3 Grip Pen */
|
||||
case 0x813: /* Intuos3 Classic Pen */
|
||||
case 0x885: /* Intuos3 Marker Pen */
|
||||
case 0x802: /* Intuos4/5 13HD/24HD General Pen */
|
||||
case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
|
||||
case 0x8e2: /* IntuosHT2 pen */
|
||||
case 0x022:
|
||||
case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
|
||||
case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */
|
||||
case 0x160802: /* Cintiq 13HD Pro Pen */
|
||||
case 0x180802: /* DTH2242 Pen */
|
||||
case 0x100802: /* Intuos4/5 13HD/24HD General Pen */
|
||||
tool_type = BTN_TOOL_PEN;
|
||||
break;
|
||||
|
||||
case 0x832: /* Stroke pen */
|
||||
case 0x032:
|
||||
tool_type = BTN_TOOL_BRUSH;
|
||||
break;
|
||||
|
||||
case 0x007: /* Mouse 4D and 2D */
|
||||
case 0x09c:
|
||||
case 0x094:
|
||||
case 0x017: /* Intuos3 2D Mouse */
|
||||
case 0x806: /* Intuos4 Mouse */
|
||||
tool_type = BTN_TOOL_MOUSE;
|
||||
break;
|
||||
|
||||
case 0x096: /* Lens cursor */
|
||||
case 0x097: /* Intuos3 Lens cursor */
|
||||
case 0x006: /* Intuos4 Lens cursor */
|
||||
tool_type = BTN_TOOL_LENS;
|
||||
break;
|
||||
|
||||
case 0x82a: /* Eraser */
|
||||
case 0x85a:
|
||||
case 0x91a:
|
||||
case 0xd1a:
|
||||
case 0x0fa:
|
||||
case 0x82b: /* Intuos3 Grip Pen Eraser */
|
||||
case 0x81b: /* Intuos3 Classic Pen Eraser */
|
||||
case 0x91b: /* Intuos3 Airbrush Eraser */
|
||||
case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */
|
||||
case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */
|
||||
case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
|
||||
case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
|
||||
case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
|
||||
case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
|
||||
case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */
|
||||
case 0x18080a: /* DTH2242 Eraser */
|
||||
case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
|
||||
tool_type = BTN_TOOL_RUBBER;
|
||||
break;
|
||||
|
||||
case 0xd12:
|
||||
case 0x912:
|
||||
case 0x112:
|
||||
case 0x913: /* Intuos3 Airbrush */
|
||||
case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
|
||||
case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
|
||||
tool_type = BTN_TOOL_AIRBRUSH;
|
||||
break;
|
||||
|
||||
default: /* Unknown tool */
|
||||
tool_type = BTN_TOOL_PEN;
|
||||
break;
|
||||
}
|
||||
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 = 0;
|
||||
int idx = (features->type == INTUOS) ? (data[1] & 0x01) : 0;
|
||||
|
||||
/* tool number */
|
||||
if (features->type == INTUOS)
|
||||
idx = data[1] & 0x01;
|
||||
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) {
|
||||
|
@ -596,116 +682,24 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
|
|||
wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) |
|
||||
((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12);
|
||||
|
||||
switch (wacom->id[idx]) {
|
||||
case 0x812: /* Inking pen */
|
||||
case 0x801: /* Intuos3 Inking pen */
|
||||
case 0x120802: /* Intuos4/5 Inking Pen */
|
||||
case 0x012:
|
||||
wacom->tool[idx] = BTN_TOOL_PENCIL;
|
||||
break;
|
||||
wacom->tool[idx] = wacom_intuos_get_tool_type(wacom->id[idx]);
|
||||
|
||||
case 0x822: /* Pen */
|
||||
case 0x842:
|
||||
case 0x852:
|
||||
case 0x823: /* Intuos3 Grip Pen */
|
||||
case 0x813: /* Intuos3 Classic Pen */
|
||||
case 0x885: /* Intuos3 Marker Pen */
|
||||
case 0x802: /* Intuos4/5 13HD/24HD General Pen */
|
||||
case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
|
||||
case 0x022:
|
||||
case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
|
||||
case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */
|
||||
case 0x160802: /* Cintiq 13HD Pro Pen */
|
||||
case 0x180802: /* DTH2242 Pen */
|
||||
case 0x100802: /* Intuos4/5 13HD/24HD General Pen */
|
||||
wacom->tool[idx] = BTN_TOOL_PEN;
|
||||
break;
|
||||
|
||||
case 0x832: /* Stroke pen */
|
||||
case 0x032:
|
||||
wacom->tool[idx] = BTN_TOOL_BRUSH;
|
||||
break;
|
||||
|
||||
case 0x007: /* Mouse 4D and 2D */
|
||||
case 0x09c:
|
||||
case 0x094:
|
||||
case 0x017: /* Intuos3 2D Mouse */
|
||||
case 0x806: /* Intuos4 Mouse */
|
||||
wacom->tool[idx] = BTN_TOOL_MOUSE;
|
||||
break;
|
||||
|
||||
case 0x096: /* Lens cursor */
|
||||
case 0x097: /* Intuos3 Lens cursor */
|
||||
case 0x006: /* Intuos4 Lens cursor */
|
||||
wacom->tool[idx] = BTN_TOOL_LENS;
|
||||
break;
|
||||
|
||||
case 0x82a: /* Eraser */
|
||||
case 0x85a:
|
||||
case 0x91a:
|
||||
case 0xd1a:
|
||||
case 0x0fa:
|
||||
case 0x82b: /* Intuos3 Grip Pen Eraser */
|
||||
case 0x81b: /* Intuos3 Classic Pen Eraser */
|
||||
case 0x91b: /* Intuos3 Airbrush Eraser */
|
||||
case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */
|
||||
case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */
|
||||
case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
|
||||
case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
|
||||
case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
|
||||
case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
|
||||
case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */
|
||||
case 0x18080a: /* DTH2242 Eraser */
|
||||
case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
|
||||
wacom->tool[idx] = BTN_TOOL_RUBBER;
|
||||
break;
|
||||
|
||||
case 0xd12:
|
||||
case 0x912:
|
||||
case 0x112:
|
||||
case 0x913: /* Intuos3 Airbrush */
|
||||
case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
|
||||
case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
|
||||
wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
|
||||
break;
|
||||
|
||||
default: /* Unknown tool */
|
||||
wacom->tool[idx] = BTN_TOOL_PEN;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
/* 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)
|
||||
/* in Range while exiting */
|
||||
if (wacom->reporting_data) {
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
input_report_abs(input, ABS_PRESSURE, 0);
|
||||
input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max);
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
|
||||
/* in Range while exiting */
|
||||
if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) {
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
input_report_abs(input, ABS_PRESSURE, 0);
|
||||
input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Exit report */
|
||||
|
@ -750,13 +744,6 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -897,6 +884,36 @@ static int wacom_intuos_general(struct wacom_wac *wacom)
|
|||
data[0] != WACOM_REPORT_INTUOS_PEN)
|
||||
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);
|
||||
y = (be16_to_cpup((__be16 *)&data[4]) << 1) | (data[9] & 1);
|
||||
distance = data[9] >> 2;
|
||||
|
|
Loading…
Reference in New Issue