Commit 019e129f authored by Jiri Kosina's avatar Jiri Kosina

Merge branches 'for-3.19/hid-report-len', 'for-3.19/i2c-hid',...

Merge branches 'for-3.19/hid-report-len', 'for-3.19/i2c-hid', 'for-3.19/lenovo', 'for-3.19/logitech', 'for-3.19/microsoft', 'for-3.19/plantronics', 'for-3.19/rmi', 'for-3.19/sony' and 'for-3.19/wacom' into for-linus
......@@ -371,6 +371,7 @@ config HID_LOGITECH_DJ
tristate "Logitech Unifying receivers full support"
depends on HIDRAW
depends on HID_LOGITECH
select HID_LOGITECH_HIDPP
---help---
Say Y if you want support for Logitech Unifying receivers and devices.
Unifying receivers are capable of pairing up to 6 Logitech compliant
......@@ -378,6 +379,17 @@ config HID_LOGITECH_DJ
generic USB_HID driver and all incoming events will be multiplexed
into a single mouse and a single keyboard device.
config HID_LOGITECH_HIDPP
tristate "Logitech HID++ devices support"
depends on HID_LOGITECH
---help---
Support for Logitech devices relyingon the HID++ Logitech specification
Say Y if you want support for Logitech devices relying on the HID++
specification. Such devices are the various Logitech Touchpads (T650,
T651, TK820), some mice (Zone Touch mouse), or even keyboards (Solar
Keayboard).
config LOGITECH_FF
bool "Logitech force feedback support"
depends on HID_LOGITECH
......@@ -613,6 +625,13 @@ config HID_PICOLCD_CIR
---help---
Provide access to PicoLCD's CIR interface via remote control (LIRC).
config HID_PLANTRONICS
tristate "Plantronics USB HID Driver"
default !EXPERT
depends on HID
---help---
Provides HID support for Plantronics telephony devices.
config HID_PRIMAX
tristate "Primax non-fully HID-compliant devices"
depends on HID
......
......@@ -63,6 +63,7 @@ obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
......@@ -94,6 +95,7 @@ ifdef CONFIG_DEBUG_FS
hid-picolcd-y += hid-picolcd_debugfs.o
endif
obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o
obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
......
......@@ -702,6 +702,11 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
if (((parser->global.usage_page << 16) == HID_UP_SENSOR) &&
type == HID_COLLECTION_PHYSICAL)
hid->group = HID_GROUP_SENSOR_HUB;
if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 &&
hid->group == HID_GROUP_MULTITOUCH)
hid->group = HID_GROUP_GENERIC;
}
static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
......@@ -1277,12 +1282,6 @@ void hid_output_report(struct hid_report *report, __u8 *data)
}
EXPORT_SYMBOL_GPL(hid_output_report);
static int hid_report_len(struct hid_report *report)
{
/* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */
return ((report->size - 1) >> 3) + 1 + (report->id > 0);
}
/*
* Allocator for buffer that is going to be passed to hid_output_report()
*/
......@@ -1819,6 +1818,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
......@@ -1859,6 +1859,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
......@@ -1884,6 +1885,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_6000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
#if IS_ENABLED(CONFIG_HID_ROCCAT)
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
......@@ -1912,6 +1914,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
......
......@@ -579,6 +579,7 @@
#define USB_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
#define USB_DEVICE_ID_LOGITECH_T651 0xb00c
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
......@@ -651,6 +652,7 @@
#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
#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07dc
#define USB_VENDOR_ID_MOJO 0x8282
#define USB_DEVICE_ID_RETRO_ADAPTER 0x3201
......@@ -718,6 +720,8 @@
#define USB_DEVICE_ID_ORTEK_PKB1700 0x1700
#define USB_DEVICE_ID_ORTEK_WKB2000 0x2000
#define USB_VENDOR_ID_PLANTRONICS 0x047f
#define USB_VENDOR_ID_PANASONIC 0x04da
#define USB_DEVICE_ID_PANABOARD_UBT780 0x1044
#define USB_DEVICE_ID_PANABOARD_UBT880 0x104d
......@@ -815,6 +819,9 @@
#define USB_VENDOR_ID_SKYCABLE 0x1223
#define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07
#define USB_VENDOR_ID_SMK 0x0609
#define USB_DEVICE_ID_SMK_PS3_BDREMOTE 0x0306
#define USB_VENDOR_ID_SONY 0x054c
#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b
#define USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE 0x0374
......@@ -933,6 +940,9 @@
#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006
#define USB_VENDOR_ID_VTL 0x0306
#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f
#define USB_VENDOR_ID_WACOM 0x056a
#define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81
#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH 0x00BD
......
......@@ -1215,7 +1215,7 @@ static void hidinput_led_worker(struct work_struct *work)
return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT);
/* fall back to generic raw-output-report */
len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
len = hid_report_len(report);
buf = hid_alloc_report_buf(report, GFP_KERNEL);
if (!buf)
return;
......
......@@ -62,7 +62,6 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
/* HID_UP_LNVENDOR = USB, HID_UP_MSVENDOR = BT */
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR ||
(usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) {
set_bit(EV_REP, hi->input->evbit);
switch (usage->hid & HID_USAGE) {
case 0x00f1: /* Fn-F4: Mic mute */
map_key_clear(KEY_MICMUTE);
......@@ -85,13 +84,13 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
case 0x00f8: /* Fn-F11: View open applications (3 boxes) */
map_key_clear(KEY_SCALE);
return 1;
case 0x00fa: /* Fn-Esc: Fn-lock toggle */
map_key_clear(KEY_FN_ESC);
return 1;
case 0x00fb: /* Fn-F12: Open My computer (6 boxes) USB-only */
case 0x00f9: /* Fn-F12: Open My computer (6 boxes) USB-only */
/* NB: This mapping is invented in raw_event below */
map_key_clear(KEY_FILE);
return 1;
case 0x00fa: /* Fn-Esc: Fn-lock toggle */
map_key_clear(KEY_FN_ESC);
return 1;
}
}
......@@ -207,8 +206,8 @@ static int lenovo_raw_event(struct hid_device *hdev,
&& data[0] == 0x15
&& data[1] == 0x94
&& data[2] == 0x01)) {
data[1] = 0x0;
data[2] = 0x4;
data[1] = 0x00;
data[2] = 0x01;
}
return 0;
......
......@@ -26,9 +26,104 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/kfifo.h>
#include <asm/unaligned.h>
#include "hid-ids.h"
#include "hid-logitech-dj.h"
#define DJ_MAX_PAIRED_DEVICES 6
#define DJ_MAX_NUMBER_NOTIFICATIONS 8
#define DJ_RECEIVER_INDEX 0
#define DJ_DEVICE_INDEX_MIN 1
#define DJ_DEVICE_INDEX_MAX 6
#define DJREPORT_SHORT_LENGTH 15
#define DJREPORT_LONG_LENGTH 32
#define REPORT_ID_DJ_SHORT 0x20
#define REPORT_ID_DJ_LONG 0x21
#define REPORT_ID_HIDPP_SHORT 0x10
#define REPORT_ID_HIDPP_LONG 0x11
#define HIDPP_REPORT_SHORT_LENGTH 7
#define HIDPP_REPORT_LONG_LENGTH 20
#define HIDPP_RECEIVER_INDEX 0xff
#define REPORT_TYPE_RFREPORT_FIRST 0x01
#define REPORT_TYPE_RFREPORT_LAST 0x1F
/* Command Switch to DJ mode */
#define REPORT_TYPE_CMD_SWITCH 0x80
#define CMD_SWITCH_PARAM_DEVBITFIELD 0x00
#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS 0x01
#define TIMEOUT_NO_KEEPALIVE 0x00
/* Command to Get the list of Paired devices */
#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81
/* Device Paired Notification */
#define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41
#define SPFUNCTION_MORE_NOTIF_EXPECTED 0x01
#define SPFUNCTION_DEVICE_LIST_EMPTY 0x02
#define DEVICE_PAIRED_PARAM_SPFUNCTION 0x00
#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB 0x01
#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB 0x02
#define DEVICE_PAIRED_RF_REPORT_TYPE 0x03
/* Device Un-Paired Notification */
#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40
/* Connection Status Notification */
#define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42
#define CONNECTION_STATUS_PARAM_STATUS 0x00
#define STATUS_LINKLOSS 0x01
/* Error Notification */
#define REPORT_TYPE_NOTIF_ERROR 0x7F
#define NOTIF_ERROR_PARAM_ETYPE 0x00
#define ETYPE_KEEPALIVE_TIMEOUT 0x01
/* supported DJ HID && RF report types */
#define REPORT_TYPE_KEYBOARD 0x01
#define REPORT_TYPE_MOUSE 0x02
#define REPORT_TYPE_CONSUMER_CONTROL 0x03
#define REPORT_TYPE_SYSTEM_CONTROL 0x04
#define REPORT_TYPE_MEDIA_CENTER 0x08
#define REPORT_TYPE_LEDS 0x0E
/* RF Report types bitfield */
#define STD_KEYBOARD 0x00000002
#define STD_MOUSE 0x00000004
#define MULTIMEDIA 0x00000008
#define POWER_KEYS 0x00000010
#define MEDIA_CENTER 0x00000100
#define KBD_LEDS 0x00004000
struct dj_report {
u8 report_id;
u8 device_index;
u8 report_type;
u8 report_params[DJREPORT_SHORT_LENGTH - 3];
};
struct dj_receiver_dev {
struct hid_device *hdev;
struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
DJ_DEVICE_INDEX_MIN];
struct work_struct work;
struct kfifo notif_fifo;
spinlock_t lock;
bool querying_devices;
};
struct dj_device {
struct hid_device *hdev;
struct dj_receiver_dev *dj_receiver_dev;
u32 reports_supported;
u8 device_index;
};
/* Keyboard descriptor (1) */
static const char kbd_descriptor[] = {
......@@ -156,6 +251,57 @@ static const char media_descriptor[] = {
0xc0, /* EndCollection */
}; /* */
/* HIDPP descriptor */
static const char hidpp_descriptor[] = {
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
0x09, 0x01, /* Usage (Vendor Usage 1) */
0xa1, 0x01, /* Collection (Application) */
0x85, 0x10, /* Report ID (16) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x06, /* Report Count (6) */
0x15, 0x00, /* Logical Minimum (0) */
0x26, 0xff, 0x00, /* Logical Maximum (255) */
0x09, 0x01, /* Usage (Vendor Usage 1) */
0x81, 0x00, /* Input (Data,Arr,Abs) */
0x09, 0x01, /* Usage (Vendor Usage 1) */
0x91, 0x00, /* Output (Data,Arr,Abs) */
0xc0, /* End Collection */
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
0x09, 0x02, /* Usage (Vendor Usage 2) */
0xa1, 0x01, /* Collection (Application) */
0x85, 0x11, /* Report ID (17) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x13, /* Report Count (19) */
0x15, 0x00, /* Logical Minimum (0) */
0x26, 0xff, 0x00, /* Logical Maximum (255) */
0x09, 0x02, /* Usage (Vendor Usage 2) */
0x81, 0x00, /* Input (Data,Arr,Abs) */
0x09, 0x02, /* Usage (Vendor Usage 2) */
0x91, 0x00, /* Output (Data,Arr,Abs) */
0xc0, /* End Collection */
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
0x09, 0x04, /* Usage (Vendor Usage 0x04) */
0xa1, 0x01, /* Collection (Application) */
0x85, 0x20, /* Report ID (32) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x0e, /* Report Count (14) */
0x15, 0x00, /* Logical Minimum (0) */
0x26, 0xff, 0x00, /* Logical Maximum (255) */
0x09, 0x41, /* Usage (Vendor Usage 0x41) */
0x81, 0x00, /* Input (Data,Arr,Abs) */
0x09, 0x41, /* Usage (Vendor Usage 0x41) */
0x91, 0x00, /* Output (Data,Arr,Abs) */
0x85, 0x21, /* Report ID (33) */
0x95, 0x1f, /* Report Count (31) */
0x15, 0x00, /* Logical Minimum (0) */
0x26, 0xff, 0x00, /* Logical Maximum (255) */
0x09, 0x42, /* Usage (Vendor Usage 0x42) */
0x81, 0x00, /* Input (Data,Arr,Abs) */
0x09, 0x42, /* Usage (Vendor Usage 0x42) */
0x91, 0x00, /* Output (Data,Arr,Abs) */
0xc0, /* End Collection */
};
/* Maximum size of all defined hid reports in bytes (including report id) */
#define MAX_REPORT_SIZE 8
......@@ -165,7 +311,8 @@ static const char media_descriptor[] = {
sizeof(mse_descriptor) + \
sizeof(consumer_descriptor) + \
sizeof(syscontrol_descriptor) + \
sizeof(media_descriptor))
sizeof(media_descriptor) + \
sizeof(hidpp_descriptor))
/* Number of possible hid report types that can be created by this driver.
*
......@@ -256,11 +403,15 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
dj_hiddev->dev.parent = &djrcv_hdev->dev;
dj_hiddev->bus = BUS_USB;
dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor);
dj_hiddev->product = le16_to_cpu(usbdev->descriptor.idProduct);
dj_hiddev->product =
(dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB]
<< 8) |
dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB];
snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
"Logitech Unifying Device. Wireless PID:%02x%02x",
dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB],
dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]);
"Logitech Unifying Device. Wireless PID:%04x",
dj_hiddev->product);
dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE;
usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys));
snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index);
......@@ -422,6 +573,13 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
}
}
static void logi_dj_recv_forward_hidpp(struct dj_device *dj_dev, u8 *data,
int size)
{
/* We are called from atomic context (tasklet && djrcv->lock held) */
if (hid_input_report(dj_dev->hdev, HID_INPUT_REPORT, data, size, 1))
dbg_hid("hid_input_report error\n");
}
static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
......@@ -472,7 +630,9 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
unsigned timeout)
{
struct hid_device *hdev = djrcv_dev->hdev;
struct dj_report *dj_report;
u8 *buf;
int retval;
dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
......@@ -484,7 +644,6 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
dj_report->report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x3F;
dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout;
retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
kfree(dj_report);
/*
* Ugly sleep to work around a USB 3.0 bug when the receiver is still
......@@ -493,6 +652,30 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
*/
msleep(50);
/*
* Magical bits to set up hidpp notifications when the dj devices
* are connected/disconnected.
*
* We can reuse dj_report because HIDPP_REPORT_SHORT_LENGTH is smaller
* than DJREPORT_SHORT_LENGTH.
*/
buf = (u8 *)dj_report;
memset(buf, 0, HIDPP_REPORT_SHORT_LENGTH);
buf[0] = REPORT_ID_HIDPP_SHORT;
buf[1] = 0xFF;
buf[2] = 0x80;
buf[3] = 0x00;
buf[4] = 0x00;
buf[5] = 0x09;
buf[6] = 0x00;
hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf,
HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT,
HID_REQ_SET_REPORT);
kfree(dj_report);
return retval;
}
......@@ -509,6 +692,9 @@ static void logi_dj_ll_close(struct hid_device *hid)
dbg_hid("%s:%s\n", __func__, hid->phys);
}
static u8 unifying_name_query[] = {0x10, 0xff, 0x83, 0xb5, 0x40, 0x00, 0x00};
static u8 unifying_name_answer[] = {0x11, 0xff, 0x83, 0xb5};
static int logi_dj_ll_raw_request(struct hid_device *hid,
unsigned char reportnum, __u8 *buf,
size_t count, unsigned char report_type,
......@@ -519,6 +705,22 @@ static int logi_dj_ll_raw_request(struct hid_device *hid,
u8 *out_buf;
int ret;
if ((buf[0] == REPORT_ID_HIDPP_SHORT) ||
(buf[0] == REPORT_ID_HIDPP_LONG)) {
if (count < 2)
return -EINVAL;
/* special case where we should not overwrite
* the device_index */
if (count == 7 && !memcmp(buf, unifying_name_query,
sizeof(unifying_name_query)))
buf[4] |= djdev->device_index - 1;
else
buf[1] = djdev->device_index;
return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf,
count, report_type, reqtype);
}
if (buf[0] != REPORT_TYPE_LEDS)
return -EINVAL;
......@@ -597,6 +799,8 @@ static int logi_dj_ll_parse(struct hid_device *hid)
__func__, djdev->reports_supported);
}
rdcat(rdesc, &rsize, hidpp_descriptor, sizeof(hidpp_descriptor));
retval = hid_parse_report(hid, rdesc, rsize);
kfree(rdesc);
......@@ -624,8 +828,7 @@ static struct hid_ll_driver logi_dj_ll_driver = {
.raw_request = logi_dj_ll_raw_request,
};
static int logi_dj_raw_event(struct hid_device *hdev,
static int logi_dj_dj_event(struct hid_device *hdev,
struct hid_report *report, u8 *data,
int size)
{
......@@ -633,36 +836,24 @@ static int logi_dj_raw_event(struct hid_device *hdev,
struct dj_report *dj_report = (struct dj_report *) data;
unsigned long flags;
dbg_hid("%s, size:%d\n", __func__, size);
/* Here we receive all data coming from iface 2, there are 4 cases:
*
* 1) Data should continue its normal processing i.e. data does not
* come from the DJ collection, in which case we do nothing and
* return 0, so hid-core can continue normal processing (will forward
* to associated hidraw device)
/*
* Here we receive all data coming from iface 2, there are 3 cases:
*
* 2) Data is from DJ collection, and is intended for this driver i. e.
* data contains arrival, departure, etc notifications, in which case
* we queue them for delayed processing by the work queue. We return 1
* to hid-core as no further processing is required from it.
* 1) Data is intended for this driver i. e. data contains arrival,
* departure, etc notifications, in which case we queue them for delayed
* processing by the work queue. We return 1 to hid-core as no further
* processing is required from it.
*
* 3) Data is from DJ collection, and informs a connection change,
* if the change means rf link loss, then we must send a null report
* to the upper layer to discard potentially pressed keys that may be
* repeated forever by the input layer. Return 1 to hid-core as no
* further processing is required.
* 2) Data informs a connection change, if the change means rf link
* loss, then we must send a null report to the upper layer to discard
* potentially pressed keys that may be repeated forever by the input
* layer. Return 1 to hid-core as no further processing is required.
*
* 4) Data is from DJ collection and is an actual input event from
* a paired DJ device in which case we forward it to the correct hid
* device (via hid_input_report() ) and return 1 so hid-core does not do
* anything else with it.
* 3) Data is an actual input event from a paired DJ device in which
* case we forward it to the correct hid device (via hid_input_report()
* ) and return 1 so hid-core does not anything else with it.
*/
/* case 1) */
if (data[0] != REPORT_ID_DJ_SHORT)
return false;
if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
(dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
/*
......@@ -707,6 +898,80 @@ static int logi_dj_raw_event(struct hid_device *hdev,
return true;
}
static int logi_dj_hidpp_event(struct hid_device *hdev,
struct hid_report *report, u8 *data,
int size)
{
struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
struct dj_report *dj_report = (struct dj_report *) data;
unsigned long flags;
u8 device_index = dj_report->device_index;
if (device_index == HIDPP_RECEIVER_INDEX) {
/* special case were the device wants to know its unifying
* name */
if (size == HIDPP_REPORT_LONG_LENGTH &&
!memcmp(data, unifying_name_answer,
sizeof(unifying_name_answer)) &&
((data[4] & 0xF0) == 0x40))
device_index = (data[4] & 0x0F) + 1;
else
return false;
}
/*
* Data is from the HID++ collection, in this case, we forward the
* data to the corresponding child dj device and return 0 to hid-core
* so he data also goes to the hidraw device of the receiver. This
* allows a user space application to implement the full HID++ routing
* via the receiver.
*/
if ((device_index < DJ_DEVICE_INDEX_MIN) ||
(device_index > DJ_DEVICE_INDEX_MAX)) {
/*
* Device index is wrong, bail out.
* This driver can ignore safely the receiver notifications,
* so ignore those reports too.
*/
dev_err(&hdev->dev, "%s: invalid device index:%d\n",
__func__, dj_report->device_index);
return false;
}
spin_lock_irqsave(&djrcv_dev->lock, flags);
if (!djrcv_dev->paired_dj_devices[device_index])
/* received an event for an unknown device, bail out */
goto out;
logi_dj_recv_forward_hidpp(djrcv_dev->paired_dj_devices[device_index],
data, size);
out:
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
return false;
}
static int logi_dj_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *data,
int size)
{
dbg_hid("%s, size:%d\n", __func__, size);
switch (data[0]) {
case REPORT_ID_DJ_SHORT:
return logi_dj_dj_event(hdev, report, data, size);
case REPORT_ID_HIDPP_SHORT:
/* intentional fallthrough */
case REPORT_ID_HIDPP_LONG:
return logi_dj_hidpp_event(hdev, report, data, size);
}
return false;
}
static int logi_dj_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
......@@ -714,9 +979,6 @@ static int logi_dj_probe(struct hid_device *hdev,
struct dj_receiver_dev *djrcv_dev;
int retval;
if (is_dj_device((struct dj_device *)hdev->driver_data))
return -ENODEV;
dbg_hid("%s called for ifnum %d\n", __func__,
intf->cur_altsetting->desc.bInterfaceNumber);
......@@ -869,22 +1131,6 @@ static void logi_dj_remove(struct hid_device *hdev)
hid_set_drvdata(hdev, NULL);
}
static int logi_djdevice_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int ret;
struct dj_device *dj_dev = hdev->driver_data;
if (!is_dj_device(dj_dev))
return -ENODEV;
ret = hid_parse(hdev);
if (!ret)
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
return ret;
}
static const struct hid_device_id logi_dj_receivers[] = {
{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
......@@ -906,51 +1152,8 @@ static struct hid_driver logi_djreceiver_driver = {
#endif
};
module_hid_driver(logi_djreceiver_driver);
static const struct hid_device_id logi_dj_devices[] = {
{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)},
{}
};
static struct hid_driver logi_djdevice_driver = {
.name = "logitech-djdevice",
.id_table = logi_dj_devices,
.probe = logi_djdevice_probe,
};
static int __init logi_dj_init(void)
{
int retval;
dbg_hid("Logitech-DJ:%s\n", __func__);
retval = hid_register_driver(&logi_djreceiver_driver);
if (retval)
return retval;
retval = hid_register_driver(&logi_djdevice_driver);
if (retval)
hid_unregister_driver(&logi_djreceiver_driver);
return retval;
}
static void __exit logi_dj_exit(void)
{
dbg_hid("Logitech-DJ:%s\n", __func__);
hid_unregister_driver(&logi_djdevice_driver);
hid_unregister_driver(&logi_djreceiver_driver);
}
module_init(logi_dj_init);
module_exit(logi_dj_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Logitech");
MODULE_AUTHOR("Nestor Lopez Casado");
......
#ifndef __HID_LOGITECH_DJ_H
#define __HID_LOGITECH_DJ_H
/*
* HID driver for Logitech Unifying receivers
*
* Copyright (c) 2011 Logitech
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kfifo.h>
#define DJ_MAX_PAIRED_DEVICES 6
#define DJ_MAX_NUMBER_NOTIFICATIONS 8
#define DJ_RECEIVER_INDEX 0
#define DJ_DEVICE_INDEX_MIN 1
#define DJ_DEVICE_INDEX_MAX 6
#define DJREPORT_SHORT_LENGTH 15
#define DJREPORT_LONG_LENGTH 32
#define REPORT_ID_DJ_SHORT 0x20
#define REPORT_ID_DJ_LONG 0x21
#define REPORT_TYPE_RFREPORT_FIRST 0x01
#define REPORT_TYPE_RFREPORT_LAST 0x1F
/* Command Switch to DJ mode */
#define REPORT_TYPE_CMD_SWITCH 0x80
#define CMD_SWITCH_PARAM_DEVBITFIELD 0x00
#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS 0x01
#define TIMEOUT_NO_KEEPALIVE 0x00
/* Command to Get the list of Paired devices */
#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81
/* Device Paired Notification */
#define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41
#define SPFUNCTION_MORE_NOTIF_EXPECTED 0x01
#define SPFUNCTION_DEVICE_LIST_EMPTY 0x02
#define DEVICE_PAIRED_PARAM_SPFUNCTION 0x00
#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB 0x01
#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB 0x02
#define DEVICE_PAIRED_RF_REPORT_TYPE 0x03
/* Device Un-Paired Notification */
#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40
/* Connection Status Notification */
#define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42
#define CONNECTION_STATUS_PARAM_STATUS 0x00
#define STATUS_LINKLOSS 0x01
/* Error Notification */
#define REPORT_TYPE_NOTIF_ERROR 0x7F
#define NOTIF_ERROR_PARAM_ETYPE 0x00
#define ETYPE_KEEPALIVE_TIMEOUT 0x01
/* supported DJ HID && RF report types */
#define REPORT_TYPE_KEYBOARD 0x01
#define REPORT_TYPE_MOUSE 0x02
#define REPORT_TYPE_CONSUMER_CONTROL 0x03
#define REPORT_TYPE_SYSTEM_CONTROL 0x04
#define REPORT_TYPE_MEDIA_CENTER 0x08
#define REPORT_TYPE_LEDS 0x0E
/* RF Report types bitfield */
#define STD_KEYBOARD 0x00000002
#define STD_MOUSE 0x00000004
#define MULTIMEDIA 0x00000008
#define POWER_KEYS 0x00000010
#define MEDIA_CENTER 0x00000100
#define KBD_LEDS 0x00004000
struct dj_report {
u8 report_id;
u8 device_index;
u8 report_type;
u8 report_params[DJREPORT_SHORT_LENGTH - 3];
};
struct dj_receiver_dev {
struct hid_device *hdev;
struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
DJ_DEVICE_INDEX_MIN];
struct work_struct work;
struct kfifo notif_fifo;
spinlock_t lock;
bool querying_devices;
};
struct dj_device {
struct hid_device *hdev;
struct dj_receiver_dev *dj_receiver_dev;
u32 reports_supported;
u8 device_index;
};
/**
* is_dj_device - know if the given dj_device is not the receiver.
* @dj_dev: the dj device to test
*
* This macro tests if a struct dj_device pointer is a device created
* by the bus enumarator.
*/
#define is_dj_device(dj_dev) \
(&(dj_dev)->dj_receiver_dev->hdev->dev == (dj_dev)->hdev->dev.parent)
#endif
/*
* HIDPP protocol for Logitech Unifying receivers
*
* Copyright (c) 2011 Logitech (c)
* Copyright (c) 2012-2013 Google (c)
* Copyright (c) 2013-2014 Red Hat Inc.
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; version 2 of the License.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.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 <asm/unaligned.h>
#include "hid-ids.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
#define REPORT_ID_HIDPP_SHORT 0x10
#define REPORT_ID_HIDPP_LONG 0x11
#define HIDPP_REPORT_SHORT_LENGTH 7
#define HIDPP_REPORT_LONG_LENGTH 20
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
/* bits 1..20 are reserved for classes */
#define HIDPP_QUIRK_DELAYED_INIT BIT(21)
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
#define HIDPP_QUIRK_MULTI_INPUT BIT(23)
/*
* There are two hidpp protocols in use, the first version hidpp10 is known
* as register access protocol or RAP, the second version hidpp20 is known as
* feature access protocol or FAP
*
* Most older devices (including the Unifying usb receiver) use the RAP protocol
* where as most newer devices use the FAP protocol. Both protocols are
* compatible with the underlying transport, which could be usb, Unifiying, or
* bluetooth. The message lengths are defined by the hid vendor specific report
* descriptor for the HIDPP_SHORT report type (total message lenth 7 bytes) and
* the HIDPP_LONG report type (total message length 20 bytes)
*
* The RAP protocol uses both report types, whereas the FAP only uses HIDPP_LONG
* messages. The Unifying receiver itself responds to RAP messages (device index
* is 0xFF for the receiver), and all messages (short or long) with a device
* index between 1 and 6 are passed untouched to the corresponding paired
* Unifying device.
*
* The paired device can be RAP or FAP, it will receive the message untouched
* from the Unifiying receiver.
*/
struct fap {
u8 feature_index;
u8 funcindex_clientid;
u8 params[HIDPP_REPORT_LONG_LENGTH - 4U];
};
struct rap {
u8 sub_id;
u8 reg_address;
u8 params[HIDPP_REPORT_LONG_LENGTH - 4U];
};
struct hidpp_report {
u8 report_id;
u8 device_index;
union {
struct fap fap;
struct rap rap;
u8 rawbytes[sizeof(struct fap)];
};
} __packed;
struct hidpp_device {
struct hid_device *hid_dev;
struct mutex send_mutex;
void *send_receive_buf;
wait_queue_head_t wait;
bool answer_available;
u8 protocol_major;
u8 protocol_minor;
void *private_data;
struct work_struct work;
struct kfifo delayed_work_fifo;
atomic_t connected;
struct input_dev *delayed_input;
unsigned long quirks;
};
#define HIDPP_ERROR 0x8f
#define HIDPP_ERROR_SUCCESS 0x00
#define HIDPP_ERROR_INVALID_SUBID 0x01
#define HIDPP_ERROR_INVALID_ADRESS 0x02
#define HIDPP_ERROR_INVALID_VALUE 0x03
#define HIDPP_ERROR_CONNECT_FAIL 0x04
#define HIDPP_ERROR_TOO_MANY_DEVICES 0x05
#define HIDPP_ERROR_ALREADY_EXISTS 0x06
#define HIDPP_ERROR_BUSY 0x07
#define HIDPP_ERROR_UNKNOWN_DEVICE 0x08
#define HIDPP_ERROR_RESOURCE_ERROR 0x09
#define HIDPP_ERROR_REQUEST_UNAVAILABLE 0x0a
#define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b
#define HIDPP_ERROR_WRONG_PIN_CODE 0x0c
static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
static int __hidpp_send_report(struct hid_device *hdev,
struct hidpp_report *hidpp_report)
{
int fields_count, ret;
switch (hidpp_report->report_id) {
case REPORT_ID_HIDPP_SHORT:
fields_count = HIDPP_REPORT_SHORT_LENGTH;
break;
case REPORT_ID_HIDPP_LONG:
fields_count = HIDPP_REPORT_LONG_LENGTH;
break;
default:
return -ENODEV;
}
/*
* set the device_index as the receiver, it will be overwritten by
* hid_hw_request if needed
*/
hidpp_report->device_index = 0xff;
ret = hid_hw_raw_request(hdev, hidpp_report->report_id,
(u8 *)hidpp_report, fields_count, HID_OUTPUT_REPORT,
HID_REQ_SET_REPORT);
return ret == fields_count ? 0 : -1;
}
/**
* hidpp_send_message_sync() returns 0 in case of success, and something else
* in case of a failure.
* - If ' something else' is positive, that means that an error has been raised
* by the protocol itself.
* - If ' something else' is negative, that means that we had a classic error
* (-ENOMEM, -EPIPE, etc...)
*/
static int hidpp_send_message_sync(struct hidpp_device *hidpp,
struct hidpp_report *message,
struct hidpp_report *response)
{
int ret;
mutex_lock(&hidpp->send_mutex);
hidpp->send_receive_buf = response;
hidpp->answer_available = false;
/*
* So that we can later validate the answer when it arrives
* in hidpp_raw_event
*/
*response = *message;
ret = __hidpp_send_report(hidpp->hid_dev, message);
if (ret) {
dbg_hid("__hidpp_send_report returned err: %d\n", ret);
memset(response, 0, sizeof(struct hidpp_report));
goto exit;
}
if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
5*HZ)) {
dbg_hid("%s:timeout waiting for response\n", __func__);
memset(response, 0, sizeof(struct hidpp_report));
ret = -ETIMEDOUT;
}
if (response->report_id == REPORT_ID_HIDPP_SHORT &&
response->fap.feature_index == HIDPP_ERROR) {
ret = response->fap.params[1];
dbg_hid("__hidpp_send_report got hidpp error %02X\n", ret);
goto exit;
}
exit:
mutex_unlock(&hidpp->send_mutex);
return ret;
}
static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
u8 feat_index, u8 funcindex_clientid, u8 *params, int param_count,
struct hidpp_report *response)
{
struct hidpp_report *message;
int ret;
if (param_count > sizeof(message->fap.params))
return -EINVAL;
message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
if (!message)
return -ENOMEM;
message->report_id = REPORT_ID_HIDPP_LONG;
message->fap.feature_index = feat_index;
message->fap.funcindex_clientid = funcindex_clientid;
memcpy(&message->fap.params, params, param_count);
ret = hidpp_send_message_sync(hidpp, message, response);
kfree(message);
return ret;
}
static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
u8 report_id, u8 sub_id, u8 reg_address, u8 *params, int param_count,
struct hidpp_report *response)
{
struct hidpp_report *message;
int ret;
if ((report_id != REPORT_ID_HIDPP_SHORT) &&
(report_id != REPORT_ID_HIDPP_LONG))
return -EINVAL;
if (param_count > sizeof(message->rap.params))
return -EINVAL;
message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
if (!message)
return -ENOMEM;
message->report_id = report_id;
message->rap.sub_id = sub_id;
message->rap.reg_address = reg_address;
memcpy(&message->rap.params, params, param_count);
ret = hidpp_send_message_sync(hidpp_dev, message, response);
kfree(message);
return ret;
}
static void delayed_work_cb(struct work_struct *work)
{
struct hidpp_device *hidpp = container_of(work, struct hidpp_device,
work);
hidpp_connect_event(hidpp);
}
static inline bool hidpp_match_answer(struct hidpp_report *question,
struct hidpp_report *answer)
{
return (answer->fap.feature_index == question->fap.feature_index) &&
(answer->fap.funcindex_clientid == question->fap.funcindex_clientid);
}
static inline bool hidpp_match_error(struct hidpp_report *question,
struct hidpp_report *answer)
{
return (answer->fap.feature_index == HIDPP_ERROR) &&
(answer->fap.funcindex_clientid == question->fap.feature_index) &&
(answer->fap.params[0] == question->fap.funcindex_clientid);
}
static inline bool hidpp_report_is_connect_event(struct hidpp_report *report)
{
return (report->report_id == REPORT_ID_HIDPP_SHORT) &&
(report->rap.sub_id == 0x41);
}
/* -------------------------------------------------------------------------- */
/* HIDP++ 1.0 commands */
/* -------------------------------------------------------------------------- */
#define HIDPP_SET_REGISTER 0x80
#define HIDPP_GET_REGISTER 0x81
#define HIDPP_SET_LONG_REGISTER 0x82
#define HIDPP_GET_LONG_REGISTER 0x83
#define HIDPP_REG_PAIRING_INFORMATION 0xB5
#define DEVICE_NAME 0x40
static char *hidpp_get_unifying_name(struct hidpp_device *hidpp_dev)
{
struct hidpp_report response;
int ret;
/* hid-logitech-dj is in charge of setting the right device index */
u8 params[1] = { DEVICE_NAME };
char *name;
int len;
ret = hidpp_send_rap_command_sync(hidpp_dev,
REPORT_ID_HIDPP_SHORT,
HIDPP_GET_LONG_REGISTER,
HIDPP_REG_PAIRING_INFORMATION,
params, 1, &response);
if (ret)
return NULL;
len = response.rap.params[1];
if (2 + len > sizeof(response.rap.params))
return NULL;
name = kzalloc(len + 1, GFP_KERNEL);
if (!name)
return NULL;
memcpy(name, &response.rap.params[2], len);
return name;
}
/* -------------------------------------------------------------------------- */
/* 0x0000: Root */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_ROOT 0x0000
#define HIDPP_PAGE_ROOT_IDX 0x00
#define CMD_ROOT_GET_FEATURE 0x01
#define CMD_ROOT_GET_PROTOCOL_VERSION 0x11
static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16 feature,
u8 *feature_index, u8 *feature_type)
{
struct hidpp_report response;
int ret;
u8 params[2] = { feature >> 8, feature & 0x00FF };
ret = hidpp_send_fap_command_sync(hidpp,
HIDPP_PAGE_ROOT_IDX,
CMD_ROOT_GET_FEATURE,
params, 2, &response);
if (ret)
return ret;
*feature_index = response.fap.params[0];
*feature_type = response.fap.params[1];
return ret;
}
static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
{
struct hidpp_report response;
int ret;
ret = hidpp_send_fap_command_sync(hidpp,
HIDPP_PAGE_ROOT_IDX,
CMD_ROOT_GET_PROTOCOL_VERSION,
NULL, 0, &response);
if (ret == HIDPP_ERROR_INVALID_SUBID) {
hidpp->protocol_major = 1;
hidpp->protocol_minor = 0;
return 0;
}
/* the device might not be connected */
if (ret == HIDPP_ERROR_RESOURCE_ERROR)
return -EIO;
if (ret > 0) {
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
return -EPROTO;
}
if (ret)
return ret;
hidpp->protocol_major = response.fap.params[0];
hidpp->protocol_minor = response.fap.params[1];
return ret;
}
static bool hidpp_is_connected(struct hidpp_device *hidpp)
{
int ret;
ret = hidpp_root_get_protocol_version(hidpp);
if (!ret)
hid_dbg(hidpp->hid_dev, "HID++ %u.%u device connected.\n",
hidpp->protocol_major, hidpp->protocol_minor);
return ret == 0;
}
/* -------------------------------------------------------------------------- */
/* 0x0005: GetDeviceNameType */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_GET_DEVICE_NAME_TYPE 0x0005
#define CMD_GET_DEVICE_NAME_TYPE_GET_COUNT 0x01
#define CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME 0x11
#define CMD_GET_DEVICE_NAME_TYPE_GET_TYPE 0x21
static int hidpp_devicenametype_get_count(struct hidpp_device *hidpp,
u8 feature_index, u8 *nameLength)
{
struct hidpp_report response;
int ret;
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
CMD_GET_DEVICE_NAME_TYPE_GET_COUNT, NULL, 0, &response);
if (ret > 0) {
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
return -EPROTO;
}
if (ret)
return ret;
*nameLength = response.fap.params[0];
return ret;
}
static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp,
u8 feature_index, u8 char_index, char *device_name, int len_buf)
{
struct hidpp_report response;
int ret, i;
int count;
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME, &char_index, 1,
&response);
if (ret > 0) {
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
return -EPROTO;
}
if (ret)
return ret;
if (response.report_id == REPORT_ID_HIDPP_LONG)
count = HIDPP_REPORT_LONG_LENGTH - 4;
else
count = HIDPP_REPORT_SHORT_LENGTH - 4;
if (len_buf < count)
count = len_buf;
for (i = 0; i < count; i++)
device_name[i] = response.fap.params[i];
return count;
}
static char *hidpp_get_device_name(struct hidpp_device *hidpp)
{
u8 feature_type;
u8 feature_index;
u8 __name_length;
char *name;
unsigned index = 0;
int ret;
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_GET_DEVICE_NAME_TYPE,
&feature_index, &feature_type);
if (ret)
return NULL;
ret = hidpp_devicenametype_get_count(hidpp, feature_index,
&__name_length);
if (ret)
return NULL;
name = kzalloc(__name_length + 1, GFP_KERNEL);
if (!name)
return NULL;
while (index < __name_length) {
ret = hidpp_devicenametype_get_device_name(hidpp,
feature_index, index, name + index,
__name_length - index);
if (ret <= 0) {
kfree(name);
return NULL;
}
index += ret;
}
return name;
}
/* -------------------------------------------------------------------------- */
/* 0x6100: TouchPadRawXY */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_TOUCHPAD_RAW_XY 0x6100
#define CMD_TOUCHPAD_GET_RAW_INFO 0x01
#define CMD_TOUCHPAD_SET_RAW_REPORT_STATE 0x21
#define EVENT_TOUCHPAD_RAW_XY 0x00
#define TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT 0x01
#define TOUCHPAD_RAW_XY_ORIGIN_UPPER_LEFT 0x03
struct hidpp_touchpad_raw_info {
u16 x_size;
u16 y_size;
u8 z_range;
u8 area_range;
u8 timestamp_unit;
u8 maxcontacts;
u8 origin;
u16 res;
};
struct hidpp_touchpad_raw_xy_finger {
u8 contact_type;
u8 contact_status;
u16 x;
u16 y;
u8 z;
u8 area;
u8 finger_id;
};
struct hidpp_touchpad_raw_xy {
u16 timestamp;
struct hidpp_touchpad_raw_xy_finger fingers[2];
u8 spurious_flag;
u8 end_of_frame;
u8 finger_count;
u8 button;
};
static int hidpp_touchpad_get_raw_info(struct hidpp_device *hidpp,
u8 feature_index, struct hidpp_touchpad_raw_info *raw_info)
{
struct hidpp_report response;
int ret;
u8 *params = (u8 *)response.fap.params;
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
CMD_TOUCHPAD_GET_RAW_INFO, NULL, 0, &response);
if (ret > 0) {
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
return -EPROTO;
}
if (ret)
return ret;
raw_info->x_size = get_unaligned_be16(&params[0]);
raw_info->y_size = get_unaligned_be16(&params[2]);
raw_info->z_range = params[4];
raw_info->area_range = params[5];
raw_info->maxcontacts = params[7];
raw_info->origin = params[8];
/* res is given in unit per inch */
raw_info->res = get_unaligned_be16(&params[13]) * 2 / 51;
return ret;
}
static int hidpp_touchpad_set_raw_report_state(struct hidpp_device *hidpp_dev,
u8 feature_index, bool send_raw_reports,
bool sensor_enhanced_settings)
{
struct hidpp_report response;
/*
* Params:
* bit 0 - enable raw
* bit 1 - 16bit Z, no area
* bit 2 - enhanced sensitivity
* bit 3 - width, height (4 bits each) instead of area
* bit 4 - send raw + gestures (degrades smoothness)
* remaining bits - reserved
*/
u8 params = send_raw_reports | (sensor_enhanced_settings << 2);
return hidpp_send_fap_command_sync(hidpp_dev, feature_index,
CMD_TOUCHPAD_SET_RAW_REPORT_STATE, &params, 1, &response);
}
static void hidpp_touchpad_touch_event(u8 *data,
struct hidpp_touchpad_raw_xy_finger *finger)
{
u8 x_m = data[0] << 2;
u8 y_m = data[2] << 2;
finger->x = x_m << 6 | data[1];
finger->y = y_m << 6 | data[3];
finger->contact_type = data[0] >> 6;
finger->contact_status = data[2] >> 6;
finger->z = data[4];
finger->area = data[5];
finger->finger_id = data[6] >> 4;
}
static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
u8 *data, struct hidpp_touchpad_raw_xy *raw_xy)
{
memset(raw_xy, 0, sizeof(struct hidpp_touchpad_raw_xy));
raw_xy->end_of_frame = data[8] & 0x01;
raw_xy->spurious_flag = (data[8] >> 1) & 0x01;
raw_xy->finger_count = data[15] & 0x0f;
raw_xy->button = (data[8] >> 2) & 0x01;
if (raw_xy->finger_count) {
hidpp_touchpad_touch_event(&data[2], &raw_xy->fingers[0]);
hidpp_touchpad_touch_event(&data[9], &raw_xy->fingers[1]);
}
}
/* ************************************************************************** */
/* */
/* Device Support */
/* */
/* ************************************************************************** */
/* -------------------------------------------------------------------------- */
/* Touchpad HID++ devices */
/* -------------------------------------------------------------------------- */
#define WTP_MANUAL_RESOLUTION 39
struct wtp_data {
struct input_dev *input;
u16 x_size, y_size;
u8 finger_count;
u8 mt_feature_index;
u8 button_feature_index;
u8 maxcontacts;
bool flip_y;
unsigned int resolution;
};
static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) &&
(field->application == HID_GD_KEYBOARD))
return 0;
return -1;
}
static void wtp_populate_input(struct hidpp_device *hidpp,
struct input_dev *input_dev, bool origin_is_hid_core)
{
struct wtp_data *wd = hidpp->private_data;
if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) && origin_is_hid_core)
/* this is the generic hid-input call */
return;
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
__clear_bit(EV_REL, input_dev->evbit);
__clear_bit(EV_LED, input_dev->evbit);
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, wd->x_size, 0, 0);
input_abs_set_res(input_dev, ABS_MT_POSITION_X, wd->resolution);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, wd->y_size, 0, 0);
input_abs_set_res(input_dev, ABS_MT_POSITION_Y, wd->resolution);
/* Max pressure is not given by the devices, pick one */
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 50, 0, 0);
input_set_capability(input_dev, EV_KEY, BTN_LEFT);
if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS)
input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
else
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
input_mt_init_slots(input_dev, wd->maxcontacts, INPUT_MT_POINTER |
INPUT_MT_DROP_UNUSED);
wd->input = input_dev;
}
static void wtp_touch_event(struct wtp_data *wd,
struct hidpp_touchpad_raw_xy_finger *touch_report)
{
int slot;
if (!touch_report->finger_id || touch_report->contact_type)
/* no actual data */
return;
slot = input_mt_get_slot_by_key(wd->input, touch_report->finger_id);
input_mt_slot(wd->input, slot);
input_mt_report_slot_state(wd->input, MT_TOOL_FINGER,
touch_report->contact_status);
if (touch_report->contact_status) {
input_event(wd->input, EV_ABS, ABS_MT_POSITION_X,
touch_report->x);
input_event(wd->input, EV_ABS, ABS_MT_POSITION_Y,
wd->flip_y ? wd->y_size - touch_report->y :
touch_report->y);
input_event(wd->input, EV_ABS, ABS_MT_PRESSURE,
touch_report->area);
}
}
static void wtp_send_raw_xy_event(struct hidpp_device *hidpp,
struct hidpp_touchpad_raw_xy *raw)
{
struct wtp_data *wd = hidpp->private_data;
int i;
for (i = 0; i < 2; i++)
wtp_touch_event(wd, &(raw->fingers[i]));
if (raw->end_of_frame &&
!(hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS))
input_event(wd->input, EV_KEY, BTN_LEFT, raw->button);
if (raw->end_of_frame || raw->finger_count <= 2) {
input_mt_sync_frame(wd->input);
input_sync(wd->input);
}
}
static int wtp_mouse_raw_xy_event(struct hidpp_device *hidpp, u8 *data)
{
struct wtp_data *wd = hidpp->private_data;
u8 c1_area = ((data[7] & 0xf) * (data[7] & 0xf) +
(data[7] >> 4) * (data[7] >> 4)) / 2;
u8 c2_area = ((data[13] & 0xf) * (data[13] & 0xf) +
(data[13] >> 4) * (data[13] >> 4)) / 2;
struct hidpp_touchpad_raw_xy raw = {
.timestamp = data[1],
.fingers = {
{
.contact_type = 0,
.contact_status = !!data[7],
.x = get_unaligned_le16(&data[3]),
.y = get_unaligned_le16(&data[5]),
.z = c1_area,
.area = c1_area,
.finger_id = data[2],
}, {
.contact_type = 0,
.contact_status = !!data[13],
.x = get_unaligned_le16(&data[9]),
.y = get_unaligned_le16(&data[11]),
.z = c2_area,
.area = c2_area,
.finger_id = data[8],
}
},
.finger_count = wd->maxcontacts,
.spurious_flag = 0,
.end_of_frame = (data[0] >> 7) == 0,
.button = data[0] & 0x01,
};
wtp_send_raw_xy_event(hidpp, &raw);
return 1;
}
static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
struct wtp_data *wd = hidpp->private_data;
struct hidpp_report *report = (struct hidpp_report *)data;
struct hidpp_touchpad_raw_xy raw;
if (!wd || !wd->input)
return 1;
switch (data[0]) {
case 0x02:
if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS) {
input_event(wd->input, EV_KEY, BTN_LEFT,
!!(data[1] & 0x01));
input_event(wd->input, EV_KEY, BTN_RIGHT,
!!(data[1] & 0x02));
input_sync(wd->input);
} else {
if (size < 21)
return 1;
return wtp_mouse_raw_xy_event(hidpp, &data[7]);
}
case REPORT_ID_HIDPP_LONG:
if ((report->fap.feature_index != wd->mt_feature_index) ||
(report->fap.funcindex_clientid != EVENT_TOUCHPAD_RAW_XY))
return 1;
hidpp_touchpad_raw_xy_event(hidpp, data + 4, &raw);
wtp_send_raw_xy_event(hidpp, &raw);
return 0;
}
return 0;
}
static int wtp_get_config(struct hidpp_device *hidpp)
{
struct wtp_data *wd = hidpp->private_data;
struct hidpp_touchpad_raw_info raw_info = {0};
u8 feature_type;
int ret;
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_TOUCHPAD_RAW_XY,
&wd->mt_feature_index, &feature_type);
if (ret)
/* means that the device is not powered up */
return ret;
ret = hidpp_touchpad_get_raw_info(hidpp, wd->mt_feature_index,
&raw_info);
if (ret)
return ret;
wd->x_size = raw_info.x_size;
wd->y_size = raw_info.y_size;
wd->maxcontacts = raw_info.maxcontacts;
wd->flip_y = raw_info.origin == TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT;
wd->resolution = raw_info.res;
if (!wd->resolution)
wd->resolution = WTP_MANUAL_RESOLUTION;
return 0;
}
static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
struct wtp_data *wd;
wd = devm_kzalloc(&hdev->dev, sizeof(struct wtp_data),
GFP_KERNEL);
if (!wd)
return -ENOMEM;
hidpp->private_data = wd;
return 0;
};
static void wtp_connect(struct hid_device *hdev, bool connected)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
struct wtp_data *wd = hidpp->private_data;
int ret;
if (!connected)
return;
if (!wd->x_size) {
ret = wtp_get_config(hidpp);
if (ret) {
hid_err(hdev, "Can not get wtp config: %d\n", ret);
return;
}
}
hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index,
true, true);
}
/* -------------------------------------------------------------------------- */
/* Generic HID++ devices */
/* -------------------------------------------------------------------------- */
static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
return wtp_input_mapping(hdev, hi, field, usage, bit, max);
return 0;
}
static void hidpp_populate_input(struct hidpp_device *hidpp,
struct input_dev *input, bool origin_is_hid_core)
{
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
wtp_populate_input(hidpp, input, origin_is_hid_core);
}
static void hidpp_input_configured(struct hid_device *hdev,
struct hid_input *hidinput)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
struct input_dev *input = hidinput->input;
hidpp_populate_input(hidpp, input, true);
}
static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
int size)
{
struct hidpp_report *question = hidpp->send_receive_buf;
struct hidpp_report *answer = hidpp->send_receive_buf;
struct hidpp_report *report = (struct hidpp_report *)data;
/*
* If the mutex is locked then we have a pending answer from a
* previoulsly sent command
*/
if (unlikely(mutex_is_locked(&hidpp->send_mutex))) {
/*
* Check for a correct hidpp20 answer or the corresponding
* error
*/
if (hidpp_match_answer(question, report) ||
hidpp_match_error(question, report)) {
*answer = *report;
hidpp->answer_available = true;
wake_up(&hidpp->wait);
/*
* This was an answer to a command that this driver sent
* We return 1 to hid-core to avoid forwarding the
* command upstream as it has been treated by the driver
*/
return 1;
}
}
if (unlikely(hidpp_report_is_connect_event(report))) {
atomic_set(&hidpp->connected,
!(report->rap.params[0] & (1 << 6)));
if ((hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) &&
(schedule_work(&hidpp->work) == 0))
dbg_hid("%s: connect event already queued\n", __func__);
return 1;
}
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
return wtp_raw_event(hidpp->hid_dev, data, size);
return 0;
}
static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
switch (data[0]) {
case REPORT_ID_HIDPP_LONG:
if (size != HIDPP_REPORT_LONG_LENGTH) {
hid_err(hdev, "received hid++ report of bad size (%d)",
size);
return 1;
}
return hidpp_raw_hidpp_event(hidpp, data, size);
case REPORT_ID_HIDPP_SHORT:
if (size != HIDPP_REPORT_SHORT_LENGTH) {
hid_err(hdev, "received hid++ report of bad size (%d)",
size);
return 1;
}
return hidpp_raw_hidpp_event(hidpp, data, size);
}
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
return wtp_raw_event(hdev, data, size);
return 0;
}
static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
char *name;
if (use_unifying)
/*
* the device is connected through an Unifying receiver, and
* might not be already connected.
* Ask the receiver for its name.
*/
name = hidpp_get_unifying_name(hidpp);
else
name = hidpp_get_device_name(hidpp);
if (!name)
hid_err(hdev, "unable to retrieve the name of the device");
else
snprintf(hdev->name, sizeof(hdev->name), "%s", name);
kfree(name);
}
static int hidpp_input_open(struct input_dev *dev)
{
struct hid_device *hid = input_get_drvdata(dev);
return hid_hw_open(hid);
}
static void hidpp_input_close(struct input_dev *dev)
{
struct hid_device *hid = input_get_drvdata(dev);
hid_hw_close(hid);
}
static struct input_dev *hidpp_allocate_input(struct hid_device *hdev)
{
struct input_dev *input_dev = devm_input_allocate_device(&hdev->dev);
if (!input_dev)
return NULL;
input_set_drvdata(input_dev, hdev);
input_dev->open = hidpp_input_open;
input_dev->close = hidpp_input_close;
input_dev->name = hdev->name;
input_dev->phys = hdev->phys;
input_dev->uniq = hdev->uniq;
input_dev->id.bustype = hdev->bus;
input_dev->id.vendor = hdev->vendor;
input_dev->id.product = hdev->product;
input_dev->id.version = hdev->version;
input_dev->dev.parent = &hdev->dev;
return input_dev;
}
static void hidpp_connect_event(struct hidpp_device *hidpp)
{
struct hid_device *hdev = hidpp->hid_dev;
int ret = 0;
bool connected = atomic_read(&hidpp->connected);
struct input_dev *input;
char *name, *devm_name;
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
wtp_connect(hdev, connected);
if (!connected || hidpp->delayed_input)
return;
if (!hidpp->protocol_major) {
ret = !hidpp_is_connected(hidpp);
if (ret) {
hid_err(hdev, "Can not get the protocol version.\n");
return;
}
}
/* the device is already connected, we can ask for its name and
* protocol */
hid_info(hdev, "HID++ %u.%u device connected.\n",
hidpp->protocol_major, hidpp->protocol_minor);
input = hidpp_allocate_input(hdev);
if (!input) {
hid_err(hdev, "cannot allocate new input device: %d\n", ret);
return;
}
name = hidpp_get_device_name(hidpp);
if (!name) {
hid_err(hdev, "unable to retrieve the name of the device");
} else {
devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name);
if (devm_name)
input->name = devm_name;
kfree(name);
}
hidpp_populate_input(hidpp, input, false);
ret = input_register_device(input);
if (ret)
input_free_device(input);
hidpp->delayed_input = input;
}
static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct hidpp_device *hidpp;
int ret;
bool connected;
unsigned int connect_mask = HID_CONNECT_DEFAULT;
hidpp = devm_kzalloc(&hdev->dev, sizeof(struct hidpp_device),
GFP_KERNEL);
if (!hidpp)
return -ENOMEM;
hidpp->hid_dev = hdev;
hid_set_drvdata(hdev, hidpp);
hidpp->quirks = id->driver_data;
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
ret = wtp_allocate(hdev, id);
if (ret)
goto wtp_allocate_fail;
}
INIT_WORK(&hidpp->work, delayed_work_cb);
mutex_init(&hidpp->send_mutex);
init_waitqueue_head(&hidpp->wait);
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "%s:parse failed\n", __func__);
goto hid_parse_fail;
}
/* Allow incoming packets */
hid_device_io_start(hdev);
connected = hidpp_is_connected(hidpp);
if (id->group != HID_GROUP_LOGITECH_DJ_DEVICE) {
if (!connected) {
hid_err(hdev, "Device not connected");
hid_device_io_stop(hdev);
goto hid_parse_fail;
}
hid_info(hdev, "HID++ %u.%u device connected.\n",
hidpp->protocol_major, hidpp->protocol_minor);
}
hidpp_overwrite_name(hdev, id->group == HID_GROUP_LOGITECH_DJ_DEVICE);
atomic_set(&hidpp->connected, connected);
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
ret = wtp_get_config(hidpp);
if (ret)
goto hid_parse_fail;
}
/* Block incoming packets */
hid_device_io_stop(hdev);
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
connect_mask &= ~HID_CONNECT_HIDINPUT;
/* Re-enable hidinput for multi-input devices */
if (hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT)
connect_mask |= HID_CONNECT_HIDINPUT;
ret = hid_hw_start(hdev, connect_mask);
if (ret) {
hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
goto hid_hw_start_fail;
}
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) {
/* Allow incoming packets */
hid_device_io_start(hdev);
hidpp_connect_event(hidpp);
}
return ret;
hid_hw_start_fail:
hid_parse_fail:
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
wtp_allocate_fail:
hid_set_drvdata(hdev, NULL);
return ret;
}
static void hidpp_remove(struct hid_device *hdev)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
hid_hw_stop(hdev);
}
static const struct hid_device_id hidpp_devices[] = {
{ /* wireless touchpad */
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, 0x4011),
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
{ /* wireless touchpad T650 */
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, 0x4101),
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
{ /* wireless touchpad T651 */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_T651),
.driver_data = HIDPP_QUIRK_CLASS_WTP },
{ /* Keyboard TK820 */
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, 0x4102),
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_MULTI_INPUT |
HIDPP_QUIRK_CLASS_WTP },
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
{}
};
MODULE_DEVICE_TABLE(hid, hidpp_devices);
static struct hid_driver hidpp_driver = {
.name = "logitech-hidpp-device",
.id_table = hidpp_devices,
.probe = hidpp_probe,
.remove = hidpp_remove,
.raw_event = hidpp_raw_event,
.input_configured = hidpp_input_configured,
.input_mapping = hidpp_input_mapping,
};
module_hid_driver(hidpp_driver);
......@@ -274,6 +274,8 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
.driver_data = MS_DUPLICATE_USAGES },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3),
.driver_data = MS_HIDINPUT },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
.driver_data = MS_PRESENTER },
......
......@@ -67,6 +67,7 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_IGNORE_DUPLICATES (1 << 10)
#define MT_QUIRK_HOVERING (1 << 11)
#define MT_QUIRK_CONTACT_CNT_ACCURATE (1 << 12)
#define MT_QUIRK_FORCE_GET_FEATURE (1 << 13)
#define MT_INPUTMODE_TOUCHSCREEN 0x02
#define MT_INPUTMODE_TOUCHPAD 0x03
......@@ -150,6 +151,7 @@ static void mt_post_parse(struct mt_device *td);
#define MT_CLS_FLATFROG 0x0107
#define MT_CLS_GENERALTOUCH_TWOFINGERS 0x0108
#define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109
#define MT_CLS_VTL 0x0110
#define MT_DEFAULT_MAXCONTACT 10
#define MT_MAX_MAXCONTACT 250
......@@ -255,6 +257,11 @@ static struct mt_class mt_classes[] = {
.sn_move = 2048,
.maxcontacts = 40,
},
{ .name = MT_CLS_VTL,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_CONTACT_CNT_ACCURATE |
MT_QUIRK_FORCE_GET_FEATURE,
},
{ }
};
......@@ -809,6 +816,9 @@ static void mt_set_input_mode(struct hid_device *hdev)
struct mt_device *td = hid_get_drvdata(hdev);
struct hid_report *r;
struct hid_report_enum *re;
struct mt_class *cls = &td->mtclass;
char *buf;
int report_len;
if (td->inputmode < 0)
return;
......@@ -816,6 +826,18 @@ static void mt_set_input_mode(struct hid_device *hdev)
re = &(hdev->report_enum[HID_FEATURE_REPORT]);
r = re->report_id_hash[td->inputmode];
if (r) {
if (cls->quirks & MT_QUIRK_FORCE_GET_FEATURE) {
report_len = hid_report_len(r);
buf = hid_alloc_report_buf(r, GFP_KERNEL);
if (!buf) {
hid_err(hdev, "failed to allocate buffer for report\n");
return;
}
hid_hw_raw_request(hdev, r->id, buf, report_len,
HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
kfree(buf);
}
r->field[0]->value[td->inputmode_index] = td->inputmode_value;
hid_hw_request(hdev, r, HID_REQ_SET_REPORT);
}
......@@ -1281,6 +1303,11 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_UNITEC,
USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) },
/* VTL panels */
{ .driver_data = MT_CLS_VTL,
MT_USB_DEVICE(USB_VENDOR_ID_VTL,
USB_DEVICE_ID_VTL_MULTITOUCH_FF3F) },
/* Wistron panels */
{ .driver_data = MT_CLS_NSMU,
MT_USB_DEVICE(USB_VENDOR_ID_WISTRON,
......
/*
* Plantronics USB HID Driver
*
* Copyright (c) 2014 JD Cole <jd.cole@plantronics.com>
* Copyright (c) 2014 Terry Junge <terry.junge@plantronics.com>
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include "hid-ids.h"
#include <linux/hid.h>
#include <linux/module.h>
static int plantronics_input_mapping(struct hid_device *hdev,
struct hid_input *hi,
struct hid_field *field,
struct hid_usage *usage,
unsigned long **bit, int *max)
{
if (field->application == HID_CP_CONSUMERCONTROL
&& (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
usage->hid, field->application);
return 0;
}
hid_dbg(hdev, "usage: %08x (appl: %08x) - ignored\n",
usage->hid, field->application);
return -1;
}
static const struct hid_device_id plantronics_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) },
{ }
};
MODULE_DEVICE_TABLE(hid, plantronics_devices);
static struct hid_driver plantronics_driver = {
.name = "plantronics",
.id_table = plantronics_devices,
.input_mapping = plantronics_input_mapping,
};
module_hid_driver(plantronics_driver);
MODULE_AUTHOR("JD Cole <jd.cole@plantronics.com>");
MODULE_AUTHOR("Terry Junge <terry.junge@plantronics.com>");
MODULE_DESCRIPTION("Plantronics USB HID Driver");
MODULE_LICENSE("GPL");
......@@ -584,11 +584,15 @@ static int rmi_populate_f11(struct hid_device *hdev)
bool has_query10 = false;
bool has_query11;
bool has_query12;
bool has_query27;
bool has_query28;
bool has_query36 = false;
bool has_physical_props;
bool has_gestures;
bool has_rel;
bool has_data40 = false;
unsigned x_size, y_size;
u16 query12_offset;
u16 query_offset;
if (!data->f11.query_base_addr) {
hid_err(hdev, "No 2D sensor found, giving up.\n");
......@@ -604,6 +608,8 @@ static int rmi_populate_f11(struct hid_device *hdev)
has_query9 = !!(buf[0] & BIT(3));
has_query11 = !!(buf[0] & BIT(4));
has_query12 = !!(buf[0] & BIT(5));
has_query27 = !!(buf[0] & BIT(6));
has_query28 = !!(buf[0] & BIT(7));
/* query 1 to get the max number of fingers */
ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf);
......@@ -626,43 +632,43 @@ static int rmi_populate_f11(struct hid_device *hdev)
has_rel = !!(buf[0] & BIT(3));
has_gestures = !!(buf[0] & BIT(5));
/*
* At least 4 queries are guaranteed to be present in F11
* +1 for query 5 which is present since absolute events are
* reported and +1 for query 12.
*/
query_offset = 6;
if (has_rel)
++query_offset; /* query 6 is present */
if (has_gestures) {
/* query 8 to find out if query 10 exists */
ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf);
ret = rmi_read(hdev,
data->f11.query_base_addr + query_offset + 1, buf);
if (ret) {
hid_err(hdev, "can not read gesture information: %d.\n",
ret);
return ret;
}
has_query10 = !!(buf[0] & BIT(2));
}
/*
* At least 4 queries are guaranteed to be present in F11
* +1 for query 5 which is present since absolute events are
* reported and +1 for query 12.
*/
query12_offset = 6;
if (has_rel)
++query12_offset; /* query 6 is present */
if (has_gestures)
query12_offset += 2; /* query 7 and 8 are present */
query_offset += 2; /* query 7 and 8 are present */
}
if (has_query9)
++query12_offset;
++query_offset;
if (has_query10)
++query12_offset;
++query_offset;
if (has_query11)
++query12_offset;
++query_offset;
/* query 12 to know if the physical properties are reported */
if (has_query12) {
ret = rmi_read(hdev, data->f11.query_base_addr
+ query12_offset, buf);
+ query_offset, buf);
if (ret) {
hid_err(hdev, "can not get query 12: %d.\n", ret);
return ret;
......@@ -670,9 +676,10 @@ static int rmi_populate_f11(struct hid_device *hdev)
has_physical_props = !!(buf[0] & BIT(5));
if (has_physical_props) {
query_offset += 1;
ret = rmi_read_block(hdev,
data->f11.query_base_addr
+ query12_offset + 1, buf, 4);
+ query_offset, buf, 4);
if (ret) {
hid_err(hdev, "can not read query 15-18: %d.\n",
ret);
......@@ -687,9 +694,45 @@ static int rmi_populate_f11(struct hid_device *hdev)
hid_info(hdev, "%s: size in mm: %d x %d\n",
__func__, data->x_size_mm, data->y_size_mm);
/*
* query 15 - 18 contain the size of the sensor
* and query 19 - 26 contain bezel dimensions
*/
query_offset += 12;
}
}
if (has_query27)
++query_offset;
if (has_query28) {
ret = rmi_read(hdev, data->f11.query_base_addr
+ query_offset, buf);
if (ret) {
hid_err(hdev, "can not get query 28: %d.\n", ret);
return ret;
}
has_query36 = !!(buf[0] & BIT(6));
}
if (has_query36) {
query_offset += 2;
ret = rmi_read(hdev, data->f11.query_base_addr
+ query_offset, buf);
if (ret) {
hid_err(hdev, "can not get query 36: %d.\n", ret);
return ret;
}
has_data40 = !!(buf[0] & BIT(5));
}
if (has_data40)
data->f11.report_size += data->max_fingers * 2;
/*
* retrieve the ctrl registers
* the ctrl register has a size of 20 but a fw bug split it into 16 + 4,
......
......@@ -798,6 +798,12 @@ union sixaxis_output_report_01 {
__u8 buf[36];
};
#define DS4_REPORT_0x02_SIZE 37
#define DS4_REPORT_0x05_SIZE 32
#define DS4_REPORT_0x11_SIZE 78
#define DS4_REPORT_0x81_SIZE 7
#define SIXAXIS_REPORT_0xF2_SIZE 18
static spinlock_t sony_dev_list_lock;
static LIST_HEAD(sony_device_list);
static DEFINE_IDA(sony_device_id_allocator);
......@@ -811,6 +817,7 @@ struct sony_sc {
struct work_struct state_worker;
struct power_supply battery;
int device_id;
__u8 *output_report_dmabuf;
#ifdef CONFIG_SONY_FF
__u8 left;
......@@ -1142,9 +1149,20 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
static int sixaxis_set_operational_bt(struct hid_device *hdev)
{
unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
return hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
static const __u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
__u8 *buf;
int ret;
buf = kmemdup(report, sizeof(report), GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(report),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
kfree(buf);
return ret;
}
/*
......@@ -1153,10 +1171,19 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
*/
static int dualshock4_set_operational_bt(struct hid_device *hdev)
{
__u8 buf[37] = { 0 };
__u8 *buf;
int ret;
buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
return hid_hw_raw_request(hdev, 0x02, buf, sizeof(buf),
ret = hid_hw_raw_request(hdev, 0x02, buf, DS4_REPORT_0x02_SIZE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
kfree(buf);
return ret;
}
static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
......@@ -1471,9 +1498,7 @@ static int sony_leds_init(struct sony_sc *sc)
static void sixaxis_state_worker(struct work_struct *work)
{
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
int n;
union sixaxis_output_report_01 report = {
static const union sixaxis_output_report_01 default_report = {
.buf = {
0x01,
0x00, 0xff, 0x00, 0xff, 0x00,
......@@ -1485,20 +1510,27 @@ static void sixaxis_state_worker(struct work_struct *work)
0x00, 0x00, 0x00, 0x00, 0x00
}
};
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
struct sixaxis_output_report *report =
(struct sixaxis_output_report *)sc->output_report_dmabuf;
int n;
/* Initialize the report with default values */
memcpy(report, &default_report, sizeof(struct sixaxis_output_report));
#ifdef CONFIG_SONY_FF
report.data.rumble.right_motor_on = sc->right ? 1 : 0;
report.data.rumble.left_motor_force = sc->left;
report->rumble.right_motor_on = sc->right ? 1 : 0;
report->rumble.left_motor_force = sc->left;
#endif
report.data.leds_bitmap |= sc->led_state[0] << 1;
report.data.leds_bitmap |= sc->led_state[1] << 2;
report.data.leds_bitmap |= sc->led_state[2] << 3;
report.data.leds_bitmap |= sc->led_state[3] << 4;
report->leds_bitmap |= sc->led_state[0] << 1;
report->leds_bitmap |= sc->led_state[1] << 2;
report->leds_bitmap |= sc->led_state[2] << 3;
report->leds_bitmap |= sc->led_state[3] << 4;
/* Set flag for all leds off, required for 3rd party INTEC controller */
if ((report.data.leds_bitmap & 0x1E) == 0)
report.data.leds_bitmap |= 0x20;
if ((report->leds_bitmap & 0x1E) == 0)
report->leds_bitmap |= 0x20;
/*
* The LEDs in the report are indexed in reverse order to their
......@@ -1511,28 +1543,30 @@ static void sixaxis_state_worker(struct work_struct *work)
*/
for (n = 0; n < 4; n++) {
if (sc->led_delay_on[n] || sc->led_delay_off[n]) {
report.data.led[3 - n].duty_off = sc->led_delay_off[n];
report.data.led[3 - n].duty_on = sc->led_delay_on[n];
report->led[3 - n].duty_off = sc->led_delay_off[n];
report->led[3 - n].duty_on = sc->led_delay_on[n];
}
}
hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
hid_hw_raw_request(sc->hdev, report->report_id, (__u8 *)report,
sizeof(struct sixaxis_output_report),
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
static void dualshock4_state_worker(struct work_struct *work)
{
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
struct hid_device *hdev = sc->hdev;
__u8 *buf = sc->output_report_dmabuf;
int offset;
__u8 buf[78] = { 0 };
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
memset(buf, 0, DS4_REPORT_0x05_SIZE);
buf[0] = 0x05;
buf[1] = 0xFF;
offset = 4;
} else {
memset(buf, 0, DS4_REPORT_0x11_SIZE);
buf[0] = 0x11;
buf[1] = 0xB0;
buf[3] = 0x0F;
......@@ -1560,12 +1594,33 @@ static void dualshock4_state_worker(struct work_struct *work)
buf[offset++] = sc->led_delay_off[3];
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
hid_hw_output_report(hdev, buf, 32);
hid_hw_output_report(hdev, buf, DS4_REPORT_0x05_SIZE);
else
hid_hw_raw_request(hdev, 0x11, buf, 78,
hid_hw_raw_request(hdev, 0x11, buf, DS4_REPORT_0x11_SIZE,
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
static int sony_allocate_output_report(struct sony_sc *sc)
{
if (sc->quirks & SIXAXIS_CONTROLLER)
sc->output_report_dmabuf =
kmalloc(sizeof(union sixaxis_output_report_01),
GFP_KERNEL);
else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x11_SIZE,
GFP_KERNEL);
else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x05_SIZE,
GFP_KERNEL);
else
return 0;
if (!sc->output_report_dmabuf)
return -ENOMEM;
return 0;
}
#ifdef CONFIG_SONY_FF
static int sony_play_effect(struct input_dev *dev, void *data,
struct ff_effect *effect)
......@@ -1754,6 +1809,7 @@ static int sony_get_bt_devaddr(struct sony_sc *sc)
static int sony_check_add(struct sony_sc *sc)
{
__u8 *buf = NULL;
int n, ret;
if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
......@@ -1769,36 +1825,44 @@ static int sony_check_add(struct sony_sc *sc)
return 0;
}
} else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
__u8 buf[7];
buf = kmalloc(DS4_REPORT_0x81_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/*
* The MAC address of a DS4 controller connected via USB can be
* retrieved with feature report 0x81. The address begins at
* offset 1.
*/
ret = hid_hw_raw_request(sc->hdev, 0x81, buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
ret = hid_hw_raw_request(sc->hdev, 0x81, buf,
DS4_REPORT_0x81_SIZE, HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret != 7) {
if (ret != DS4_REPORT_0x81_SIZE) {
hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n");
return ret < 0 ? ret : -EINVAL;
ret = ret < 0 ? ret : -EINVAL;
goto out_free;
}
memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
} else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
__u8 buf[18];
buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/*
* The MAC address of a Sixaxis controller connected via USB can
* be retrieved with feature report 0xf2. The address begins at
* offset 4.
*/
ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
ret = hid_hw_raw_request(sc->hdev, 0xf2, buf,
SIXAXIS_REPORT_0xF2_SIZE, HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret != 18) {
if (ret != SIXAXIS_REPORT_0xF2_SIZE) {
hid_err(sc->hdev, "failed to retrieve feature report 0xf2 with the Sixaxis MAC address\n");
return ret < 0 ? ret : -EINVAL;
ret = ret < 0 ? ret : -EINVAL;
goto out_free;
}
/*
......@@ -1811,7 +1875,13 @@ static int sony_check_add(struct sony_sc *sc)
return 0;
}
return sony_check_add_dev_list(sc);
ret = sony_check_add_dev_list(sc);
out_free:
kfree(buf);
return ret;
}
static int sony_set_device_id(struct sony_sc *sc)
......@@ -1895,6 +1965,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
ret = sony_allocate_output_report(sc);
if (ret < 0) {
hid_err(hdev, "failed to allocate the output report buffer\n");
goto err_stop;
}
ret = sony_set_device_id(sc);
if (ret < 0) {
hid_err(hdev, "failed to allocate the device id\n");
......@@ -1984,6 +2060,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (sc->quirks & SONY_BATTERY_SUPPORT)
sony_battery_remove(sc);
sony_cancel_work_sync(sc);
kfree(sc->output_report_dmabuf);
sony_remove_dev_list(sc);
sony_release_device_id(sc);
hid_hw_stop(hdev);
......@@ -2004,6 +2081,8 @@ static void sony_remove(struct hid_device *hdev)
sony_cancel_work_sync(sc);
kfree(sc->output_report_dmabuf);
sony_remove_dev_list(sc);
sony_release_device_id(sc);
......@@ -2034,6 +2113,9 @@ static const struct hid_device_id sony_devices[] = {
/* Logitech Harmony Adapter for PS3 */
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3),
.driver_data = PS3REMOTE },
/* SMK-Link PS3 BD Remote Control */
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE),
.driver_data = PS3REMOTE },
/* Sony Dualshock 4 controllers for PS4 */
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER),
.driver_data = DUALSHOCK4_CONTROLLER_USB },
......
......@@ -137,6 +137,7 @@ struct i2c_hid {
* descriptor. */
unsigned int bufsize; /* i2c buffer size */
char *inbuf; /* Input buffer */
char *rawbuf; /* Raw Input buffer */
char *cmdbuf; /* Command buffer */
char *argsbuf; /* Command arguments buffer */
......@@ -437,7 +438,7 @@ static void i2c_hid_init_report(struct hid_report *report, u8 *buffer,
report->id, buffer, size))
return;
i2c_hid_dbg(ihid, "report (len=%d): %*ph\n", size, size, ihid->inbuf);
i2c_hid_dbg(ihid, "report (len=%d): %*ph\n", size, size, buffer);
ret_size = buffer[0] | (buffer[1] << 8);
......@@ -504,9 +505,11 @@ static void i2c_hid_find_max_report(struct hid_device *hid, unsigned int type,
static void i2c_hid_free_buffers(struct i2c_hid *ihid)
{
kfree(ihid->inbuf);
kfree(ihid->rawbuf);
kfree(ihid->argsbuf);
kfree(ihid->cmdbuf);
ihid->inbuf = NULL;
ihid->rawbuf = NULL;
ihid->cmdbuf = NULL;
ihid->argsbuf = NULL;
ihid->bufsize = 0;
......@@ -522,10 +525,11 @@ static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size)
report_size; /* report */
ihid->inbuf = kzalloc(report_size, GFP_KERNEL);
ihid->rawbuf = kzalloc(report_size, GFP_KERNEL);
ihid->argsbuf = kzalloc(args_len, GFP_KERNEL);
ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL);
if (!ihid->inbuf || !ihid->argsbuf || !ihid->cmdbuf) {
if (!ihid->inbuf || !ihid->rawbuf || !ihid->argsbuf || !ihid->cmdbuf) {
i2c_hid_free_buffers(ihid);
return -ENOMEM;
}
......@@ -552,12 +556,12 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
ret = i2c_hid_get_report(client,
report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
report_number, ihid->inbuf, ask_count);
report_number, ihid->rawbuf, ask_count);
if (ret < 0)
return ret;
ret_count = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
ret_count = ihid->rawbuf[0] | (ihid->rawbuf[1] << 8);
if (ret_count <= 2)
return 0;
......@@ -566,7 +570,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
/* The query buffer contains the size, dropping it in the reply */
count = min(count, ret_count - 2);
memcpy(buf, ihid->inbuf + 2, count);
memcpy(buf, ihid->rawbuf + 2, count);
return count;
}
......
......@@ -340,8 +340,7 @@ static int hid_submit_out(struct hid_device *hid)
report = usbhid->out[usbhid->outtail].report;
raw_report = usbhid->out[usbhid->outtail].raw_report;
usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) +
1 + (report->id > 0);
usbhid->urbout->transfer_buffer_length = hid_report_len(report);
usbhid->urbout->dev = hid_to_usb_dev(hid);
if (raw_report) {
memcpy(usbhid->outbuf, raw_report,
......
......@@ -79,6 +79,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
......
......@@ -140,7 +140,7 @@ extern const struct hid_device_id wacom_ids[];
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
void wacom_setup_device_quirks(struct wacom_features *features);
int wacom_setup_input_capabilities(struct input_dev *input_dev,
int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac);
int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac);
......
......@@ -13,6 +13,7 @@
#include "wacom_wac.h"
#include "wacom.h"
#include <linux/input/mt.h>
#define WAC_MSG_RETRIES 5
......@@ -185,9 +186,15 @@ static void wacom_usage_mapping(struct hid_device *hdev,
if (!pen && !finger)
return;
if (finger && !features->touch_max)
/* touch device at least supports one touch point */
features->touch_max = 1;
/*
* Bamboo models do not support HID_DG_CONTACTMAX.
* And, Bamboo Pen only descriptor contains touch.
*/
if (features->type != BAMBOO_PT) {
/* ISDv4 touch devices at least supports one touch point */
if (finger && !features->touch_max)
features->touch_max = 1;
}
switch (usage->hid) {
case HID_GD_X:
......@@ -223,6 +230,21 @@ static void wacom_usage_mapping(struct hid_device *hdev,
wacom_wac_usage_mapping(hdev, field, usage);
}
static void wacom_post_parse_hid(struct hid_device *hdev,
struct wacom_features *features)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
if (features->type == HID_GENERIC) {
/* Any last-minute generic device setup */
if (features->touch_max > 1) {
input_mt_init_slots(wacom_wac->input, wacom_wac->features.touch_max,
INPUT_MT_DIRECT);
}
}
}
static void wacom_parse_hid(struct hid_device *hdev,
struct wacom_features *features)
{
......@@ -257,6 +279,8 @@ static void wacom_parse_hid(struct hid_device *hdev,
wacom_usage_mapping(hdev, hreport->field[i],
hreport->field[i]->usage + j);
}
wacom_post_parse_hid(hdev, features);
}
static int wacom_hid_set_device_mode(struct hid_device *hdev)
......@@ -1122,7 +1146,7 @@ static void wacom_clean_inputs(struct wacom *wacom)
input_free_device(wacom->wacom_wac.input);
}
if (wacom->wacom_wac.pad_input) {
if (wacom->wacom_wac.input_registered)
if (wacom->wacom_wac.pad_registered)
input_unregister_device(wacom->wacom_wac.pad_input);
else
input_free_device(wacom->wacom_wac.pad_input);
......@@ -1144,13 +1168,13 @@ static int wacom_register_inputs(struct wacom *wacom)
if (!input_dev || !pad_input_dev)
return -EINVAL;
error = wacom_setup_input_capabilities(input_dev, wacom_wac);
if (error)
return error;
error = input_register_device(input_dev);
if (error)
return error;
error = wacom_setup_pentouch_input_capabilities(input_dev, wacom_wac);
if (!error) {
error = input_register_device(input_dev);
if (error)
return error;
wacom_wac->input_registered = true;
}
error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
if (error) {
......@@ -1162,22 +1186,23 @@ static int wacom_register_inputs(struct wacom *wacom)
error = input_register_device(pad_input_dev);
if (error)
goto fail_register_pad_input;
wacom_wac->pad_registered = true;
error = wacom_initialize_leds(wacom);
if (error)
goto fail_leds;
}
wacom_wac->input_registered = true;
return 0;
fail_leds:
input_unregister_device(pad_input_dev);
pad_input_dev = NULL;
wacom_wac->pad_registered = false;
fail_register_pad_input:
input_unregister_device(input_dev);
wacom_wac->input = NULL;
wacom_wac->input_registered = false;
return error;
}
......@@ -1314,12 +1339,6 @@ static void wacom_calculate_res(struct wacom_features *features)
features->unitExpo);
}
static int wacom_hid_report_len(struct hid_report *report)
{
/* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */
return ((report->size - 1) >> 3) + 1 + (report->id > 0);
}
static size_t wacom_compute_pktlen(struct hid_device *hdev)
{
struct hid_report_enum *report_enum;
......@@ -1329,7 +1348,7 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev)
report_enum = hdev->report_enum + HID_INPUT_REPORT;
list_for_each_entry(report, &report_enum->report_list, list) {
size_t report_size = wacom_hid_report_len(report);
size_t report_size = hid_report_len(report);
if (report_size > size)
size = report_size;
}
......
......@@ -25,6 +25,10 @@
#define WACOM_INTUOS_RES 100
#define WACOM_INTUOS3_RES 200
/* Newer Cintiq and DTU have an offset between tablet and screen areas */
#define WACOM_DTU_OFFSET 200
#define WACOM_CINTIQ_OFFSET 400
/*
* Scale factor relating reported contact size to logical contact area.
* 2^14/pi is a good approximation on Intuos5 and 3rd-gen Bamboo
......@@ -600,8 +604,8 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
}
input_report_abs(input, ABS_PRESSURE, t);
input_report_abs(input, ABS_TILT_X,
((data[7] << 1) & 0x7e) | (data[8] >> 7));
input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
(((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64);
input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64);
input_report_key(input, BTN_STYLUS, data[1] & 2);
input_report_key(input, BTN_STYLUS2, data[1] & 4);
input_report_key(input, BTN_TOUCH, t > 10);
......@@ -612,8 +616,8 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
input_report_abs(input, ABS_WHEEL,
(data[6] << 2) | ((data[7] >> 6) & 3));
input_report_abs(input, ABS_TILT_X,
((data[7] << 1) & 0x7e) | (data[8] >> 7));
input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
(((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64);
input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64);
}
}
......@@ -915,8 +919,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
input_report_key(input, BTN_EXTRA, data[6] & 0x10);
input_report_abs(input, ABS_TILT_X,
((data[7] << 1) & 0x7e) | (data[8] >> 7));
input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
(((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64);
input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64);
} else {
/* 2D mouse packet */
input_report_key(input, BTN_LEFT, data[8] & 0x04);
......@@ -1377,11 +1381,12 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct input_dev *input = wacom_wac->input;
struct wacom_features *features = &wacom_wac->features;
unsigned touch_max = wacom_wac->features.touch_max;
switch (usage->hid) {
case HID_GD_X:
features->last_slot_field = usage->hid;
if (touch_max == 1)
wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
else
......@@ -1389,6 +1394,7 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
ABS_MT_POSITION_X, 4);
break;
case HID_GD_Y:
features->last_slot_field = usage->hid;
if (touch_max == 1)
wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
else
......@@ -1396,19 +1402,48 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
ABS_MT_POSITION_Y, 4);
break;
case HID_DG_CONTACTID:
input_mt_init_slots(input, wacom_wac->features.touch_max,
INPUT_MT_DIRECT);
features->last_slot_field = usage->hid;
break;
case HID_DG_INRANGE:
features->last_slot_field = usage->hid;
break;
case HID_DG_INVERT:
features->last_slot_field = usage->hid;
break;
case HID_DG_TIPSWITCH:
features->last_slot_field = usage->hid;
wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
break;
}
}
static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
struct input_dev *input)
{
struct hid_data *hid_data = &wacom_wac->hid_data;
bool mt = wacom_wac->features.touch_max > 1;
bool prox = hid_data->tipswitch &&
!wacom_wac->shared->stylus_in_proximity;
if (mt) {
int slot;
slot = input_mt_get_slot_by_key(input, hid_data->id);
input_mt_slot(input, slot);
input_mt_report_slot_state(input, MT_TOOL_FINGER, prox);
}
else {
input_report_key(input, BTN_TOUCH, prox);
}
if (prox) {
input_report_abs(input, mt ? ABS_MT_POSITION_X : ABS_X,
hid_data->x);
input_report_abs(input, mt ? ABS_MT_POSITION_Y : ABS_Y,
hid_data->y);
}
}
static int wacom_wac_finger_event(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage, __s32 value)
{
......@@ -1431,36 +1466,35 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
}
if (usage->usage_index + 1 == field->report_count) {
if (usage->hid == wacom_wac->features.last_slot_field)
wacom_wac_finger_slot(wacom_wac, wacom_wac->input);
}
return 0;
}
static void wacom_wac_finger_mt_report(struct wacom_wac *wacom_wac,
struct input_dev *input, bool touch)
static int wacom_wac_finger_count_touches(struct hid_device *hdev)
{
int slot;
struct hid_data *hid_data = &wacom_wac->hid_data;
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct input_dev *input = wacom_wac->input;
unsigned touch_max = wacom_wac->features.touch_max;
int count = 0;
int i;
slot = input_mt_get_slot_by_key(input, hid_data->id);
if (touch_max == 1)
return wacom_wac->hid_data.tipswitch &&
!wacom_wac->shared->stylus_in_proximity;
input_mt_slot(input, slot);
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
if (touch) {
input_report_abs(input, ABS_MT_POSITION_X, hid_data->x);
input_report_abs(input, ABS_MT_POSITION_Y, hid_data->y);
for (i = 0; i < input->mt->num_slots; i++) {
struct input_mt_slot *ps = &input->mt->slots[i];
int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
if (id >= 0)
count++;
}
input_mt_sync_frame(input);
}
static void wacom_wac_finger_single_touch_report(struct wacom_wac *wacom_wac,
struct input_dev *input, bool touch)
{
struct hid_data *hid_data = &wacom_wac->hid_data;
if (touch) {
input_report_abs(input, ABS_X, hid_data->x);
input_report_abs(input, ABS_Y, hid_data->y);
}
input_report_key(input, BTN_TOUCH, touch);
return count;
}
static void wacom_wac_finger_report(struct hid_device *hdev,
......@@ -1469,24 +1503,23 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct input_dev *input = wacom_wac->input;
bool touch = wacom_wac->hid_data.tipswitch &&
!wacom_wac->shared->stylus_in_proximity;
unsigned touch_max = wacom_wac->features.touch_max;
if (touch_max > 1)
wacom_wac_finger_mt_report(wacom_wac, input, touch);
else
wacom_wac_finger_single_touch_report(wacom_wac, input, touch);
input_mt_sync_frame(input);
input_sync(input);
/* keep touch state for pen event */
wacom_wac->shared->touch_down = touch;
wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(hdev);
}
#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \
((f)->physical == HID_DG_STYLUS))
((f)->physical == HID_DG_STYLUS) || \
((f)->application == HID_DG_PEN))
#define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \
((f)->physical == HID_DG_FINGER))
((f)->physical == HID_DG_FINGER) || \
((f)->application == HID_DG_TOUCHSCREEN))
void wacom_wac_usage_mapping(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage)
......@@ -1681,7 +1714,9 @@ static int wacom_bpt_pen(struct wacom_wac *wacom)
return 0;
if (data[0] == WACOM_REPORT_USB) {
if (features->type == INTUOSHT && features->touch_max) {
if (features->type == INTUOSHT &&
wacom->shared->touch_input &&
features->touch_max) {
input_report_switch(wacom->shared->touch_input,
SW_MUTE_DEVICE, data[8] & 0x40);
input_sync(wacom->shared->touch_input);
......@@ -1774,7 +1809,8 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
int pid, battery, ps_connected;
if ((wacom->shared->type == INTUOSHT) &&
wacom->shared->touch_max) {
wacom->shared->touch_input &&
wacom->shared->touch_max) {
input_report_switch(wacom->shared->touch_input,
SW_MUTE_DEVICE, data[5] & 0x40);
input_sync(wacom->shared->touch_input);
......@@ -1838,6 +1874,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
break;
case DTUS:
case DTUSX:
sync = wacom_dtus_irq(wacom_wac);
break;
......@@ -1926,8 +1963,10 @@ static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
input_set_abs_params(input_dev, ABS_DISTANCE,
0, wacom_wac->features.distance_max, 0, 0);
input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_X, -64, 63, 0, 0);
input_abs_set_res(input_dev, ABS_TILT_X, 57);
input_set_abs_params(input_dev, ABS_TILT_Y, -64, 63, 0, 0);
input_abs_set_res(input_dev, ABS_TILT_Y, 57);
}
static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
......@@ -1947,6 +1986,7 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
__set_bit(BTN_TOOL_LENS, input_dev->keybit);
input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
input_abs_set_res(input_dev, ABS_RZ, 287);
input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
}
......@@ -2029,7 +2069,7 @@ static void wacom_abs_set_axis(struct input_dev *input_dev,
}
}
int wacom_setup_input_capabilities(struct input_dev *input_dev,
int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac)
{
struct wacom_features *features = &wacom_wac->features;
......@@ -2047,9 +2087,6 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
switch (features->type) {
case WACOM_MO:
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
/* fall through */
case WACOM_G4:
/* fall through */
......@@ -2092,6 +2129,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case WACOM_24HD:
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
input_abs_set_res(input_dev, ABS_Z, 287);
input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
/* fall through */
......@@ -2106,6 +2144,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case WACOM_BEE:
case CINTIQ:
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
input_abs_set_res(input_dev, ABS_Z, 287);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
......@@ -2114,6 +2153,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case WACOM_13HD:
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
input_abs_set_res(input_dev, ABS_Z, 287);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
wacom_setup_cintiq(wacom_wac);
break;
......@@ -2122,6 +2162,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case INTUOS3L:
case INTUOS3S:
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
input_abs_set_res(input_dev, ABS_Z, 287);
/* fall through */
case INTUOS:
......@@ -2144,6 +2185,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
0, 0);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
input_abs_set_res(input_dev, ABS_Z, 287);
wacom_setup_intuos(wacom_wac);
} else if (features->device_type == BTN_TOOL_FINGER) {
......@@ -2162,6 +2204,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case INTUOS4L:
case INTUOS4S:
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
input_abs_set_res(input_dev, ABS_Z, 287);
wacom_setup_intuos(wacom_wac);
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
......@@ -2196,6 +2239,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
/* fall through */
case DTUS:
case DTUSX:
case PL:
case DTU:
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
......@@ -2246,6 +2290,9 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
__clear_bit(ABS_X, input_dev->absbit);
__clear_bit(ABS_Y, input_dev->absbit);
__clear_bit(BTN_TOUCH, input_dev->keybit);
/* PAD is setup by wacom_setup_pad_input_capabilities later */
return 1;
}
} else if (features->device_type == BTN_TOOL_PEN) {
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
......@@ -2261,6 +2308,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case CINTIQ_HYBRID:
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
input_abs_set_res(input_dev, ABS_Z, 287);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
wacom_setup_cintiq(wacom_wac);
......@@ -2303,9 +2351,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
case WACOM_G4:
__set_bit(BTN_BACK, input_dev->keybit);
__set_bit(BTN_LEFT, input_dev->keybit);
__set_bit(BTN_FORWARD, input_dev->keybit);
__set_bit(BTN_RIGHT, input_dev->keybit);
input_set_capability(input_dev, EV_REL, REL_WHEEL);
break;
......@@ -2402,7 +2448,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
case INTUOSPS:
/* touch interface does not have the pad device */
if (features->device_type != BTN_TOOL_PEN)
return 1;
return -ENODEV;
for (i = 0; i < 7; i++)
__set_bit(BTN_0 + i, input_dev->keybit);
......@@ -2446,8 +2492,10 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
case INTUOSHT:
case BAMBOO_PT:
/* pad device is on the touch interface */
if (features->device_type != BTN_TOOL_FINGER)
return 1;
if ((features->device_type != BTN_TOOL_FINGER) ||
/* Bamboo Pen only tablet does not have pad */
((features->type == BAMBOO_PT) && !features->touch_max))
return -ENODEV;
__clear_bit(ABS_MISC, input_dev->absbit);
......@@ -2460,7 +2508,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
default:
/* no pad supported */
return 1;
return -ENODEV;
}
return 0;
}
......@@ -2664,11 +2712,13 @@ static const struct wacom_features wacom_features_0x317 =
INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
static const struct wacom_features wacom_features_0xF4 =
{ "Wacom Cintiq 24HD", 104280, 65400, 2047, 63,
WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
{ "Wacom Cintiq 24HD", 104080, 65200, 2047, 63,
WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0xF8 =
{ "Wacom Cintiq 24HD touch", 104280, 65400, 2047, 63, /* Pen */
WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
{ "Wacom Cintiq 24HD touch", 104080, 65200, 2047, 63, /* Pen */
WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 };
static const struct wacom_features wacom_features_0xF6 =
{ "Wacom Cintiq 24HD touch", .type = WACOM_24HDT, /* Touch */
......@@ -2684,8 +2734,9 @@ static const struct wacom_features wacom_features_0xC6 =
{ "Wacom Cintiq 12WX", 53020, 33440, 1023, 63,
WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
static const struct wacom_features wacom_features_0x304 =
{ "Wacom Cintiq 13HD", 59352, 33648, 1023, 63,
WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
{ "Wacom Cintiq 13HD", 59152, 33448, 1023, 63,
WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0xC7 =
{ "Wacom DTU1931", 37832, 30305, 511, 0,
PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
......@@ -2697,28 +2748,38 @@ static const struct wacom_features wacom_features_0xF0 =
{ "Wacom DTU1631", 34623, 19553, 511, 0,
DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xFB =
{ "Wacom DTU1031", 22096, 13960, 511, 0,
DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
{ "Wacom DTU1031", 21896, 13760, 511, 0,
DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
static const struct wacom_features wacom_features_0x32F =
{ "Wacom DTU1031X", 22472, 12728, 511, 0,
DTUSX, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
static const struct wacom_features wacom_features_0x57 =
{ "Wacom DTK2241", 95640, 54060, 2047, 63,
DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0x59 = /* Pen */
{ "Wacom DTH2242", 95640, 54060, 2047, 63,
DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D };
static const struct wacom_features wacom_features_0x5D = /* Touch */
{ "Wacom DTH2242", .type = WACOM_24HDT,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10,
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
static const struct wacom_features wacom_features_0xCC =
{ "Wacom Cintiq 21UX2", 87000, 65400, 2047, 63,
WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
{ "Wacom Cintiq 21UX2", 86800, 65200, 2047, 63,
WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0xFA =
{ "Wacom Cintiq 22HD", 95640, 54060, 2047, 63,
WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
{ "Wacom Cintiq 22HD", 95440, 53860, 2047, 63,
WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0x5B =
{ "Wacom Cintiq 22HDT", 95640, 54060, 2047, 63,
WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
{ "Wacom Cintiq 22HDT", 95440, 53860, 2047, 63,
WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e };
static const struct wacom_features wacom_features_0x5E =
{ "Wacom Cintiq 22HDT", .type = WACOM_24HDT,
......@@ -2863,21 +2924,27 @@ static const struct wacom_features wacom_features_0x6004 =
{ "ISD-V4", 12800, 8000, 255, 0,
TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x307 =
{ "Wacom ISDv5 307", 59352, 33648, 2047, 63,
CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
{ "Wacom ISDv5 307", 59152, 33448, 2047, 63,
CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 };
static const struct wacom_features wacom_features_0x309 =
{ "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10,
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
static const struct wacom_features wacom_features_0x30A =
{ "Wacom ISDv5 30A", 59352, 33648, 2047, 63,
CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
{ "Wacom ISDv5 30A", 59152, 33448, 2047, 63,
CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C };
static const struct wacom_features wacom_features_0x30C =
{ "Wacom ISDv5 30C", .type = WACOM_24HDT, /* Touch */
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30A, .touch_max = 10,
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
static const struct wacom_features wacom_features_0x323 =
{ "Wacom Intuos P M", 21600, 13500, 1023, 31,
INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
static const struct wacom_features wacom_features_HID_ANY_ID =
{ "Wacom HID", .type = HID_GENERIC };
......@@ -3022,6 +3089,8 @@ const struct hid_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x314) },
{ USB_DEVICE_WACOM(0x315) },
{ USB_DEVICE_WACOM(0x317) },
{ USB_DEVICE_WACOM(0x323) },
{ USB_DEVICE_WACOM(0x32F) },
{ USB_DEVICE_WACOM(0x4001) },
{ USB_DEVICE_WACOM(0x4004) },
{ USB_DEVICE_WACOM(0x5000) },
......
......@@ -80,6 +80,7 @@ enum {
PL,
DTU,
DTUS,
DTUSX,
INTUOS,
INTUOS3S,
INTUOS3,
......@@ -144,6 +145,7 @@ struct wacom_features {
int pktlen;
bool check_for_hid_type;
int hid_type;
int last_slot_field;
};
struct wacom_shared {
......@@ -183,6 +185,7 @@ struct wacom_wac {
struct input_dev *input;
struct input_dev *pad_input;
bool input_registered;
bool pad_registered;
int pid;
int battery_capacity;
int num_contacts_left;
......
......@@ -234,6 +234,33 @@ struct hid_item {
#define HID_DG_BARRELSWITCH 0x000d0044
#define HID_DG_ERASER 0x000d0045
#define HID_DG_TABLETPICK 0x000d0046
#define HID_CP_CONSUMERCONTROL 0x000c0001
#define HID_CP_NUMERICKEYPAD 0x000c0002
#define HID_CP_PROGRAMMABLEBUTTONS 0x000c0003
#define HID_CP_MICROPHONE 0x000c0004
#define HID_CP_HEADPHONE 0x000c0005
#define HID_CP_GRAPHICEQUALIZER 0x000c0006
#define HID_CP_FUNCTIONBUTTONS 0x000c0036
#define HID_CP_SELECTION 0x000c0080
#define HID_CP_MEDIASELECTION 0x000c0087
#define HID_CP_SELECTDISC 0x000c00ba
#define HID_CP_PLAYBACKSPEED 0x000c00f1
#define HID_CP_PROXIMITY 0x000c0109
#define HID_CP_SPEAKERSYSTEM 0x000c0160
#define HID_CP_CHANNELLEFT 0x000c0161
#define HID_CP_CHANNELRIGHT 0x000c0162
#define HID_CP_CHANNELCENTER 0x000c0163
#define HID_CP_CHANNELFRONT 0x000c0164
#define HID_CP_CHANNELCENTERFRONT 0x000c0165
#define HID_CP_CHANNELSIDE 0x000c0166
#define HID_CP_CHANNELSURROUND 0x000c0167
#define HID_CP_CHANNELLOWFREQUENCYENHANCEMENT 0x000c0168
#define HID_CP_CHANNELTOP 0x000c0169
#define HID_CP_CHANNELUNKNOWN 0x000c016a
#define HID_CP_APPLICATIONLAUNCHBUTTONS 0x000c0180
#define HID_CP_GENERICGUIAPPLICATIONCONTROLS 0x000c0200
#define HID_DG_CONFIDENCE 0x000d0047
#define HID_DG_WIDTH 0x000d0048
#define HID_DG_HEIGHT 0x000d0049
......@@ -313,6 +340,7 @@ struct hid_item {
*/
#define HID_GROUP_RMI 0x0100
#define HID_GROUP_WACOM 0x0101
#define HID_GROUP_LOGITECH_DJ_DEVICE 0x0102
/*
* This is the global environment of the parser. This information is
......@@ -1059,6 +1087,17 @@ static inline void hid_hw_wait(struct hid_device *hdev)
hdev->ll_driver->wait(hdev);
}
/**
* hid_report_len - calculate the report length
*
* @report: the report we want to know the length
*/
static inline int hid_report_len(struct hid_report *report)
{
/* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */
return ((report->size - 1) >> 3) + 1 + (report->id > 0);
}
int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
int interrupt);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment