usbhid.mousepoll=
[USBHID] The interval which mice are to be polled at.
+ usbhid.jspoll=
+ [USBHID] The interval which joysticks are to be polled at.
+
usb-storage.delay_use=
[UMS] The delay in seconds before a new device is
scanned for Logical Units (default 1).
- interrupt-parent: the phandle for the interrupt controller
- interrupts: interrupt line
+Additional optional properties:
+
+Some devices may support additional optional properties to help with, e.g.,
+power sequencing. The following properties can be supported by one or more
+device-specific compatible properties, which should be used in addition to the
+"hid-over-i2c" string.
+
+- compatible:
+ * "wacom,w9013" (Wacom W9013 digitizer). Supports:
+ - vdd-supply
+ - post-power-on-delay-ms
+
+- vdd-supply: phandle of the regulator that provides the supply voltage.
+- post-power-on-delay-ms: time required by the device after enabling its regulators
+ before it is ready for communication. Must be used with 'vdd-supply'.
+
Example:
i2c-hid-dev@2c {
Support for Cherry Cymotion keyboard.
config HID_CHICONY
- tristate "Chicony Tactical pad"
+ tristate "Chicony devices"
depends on HID
default !EXPERT
---help---
- Support for Chicony Tactical pad.
+ Support for Chicony Tactical pad and special keys on Chicony keyboards.
config HID_CORSAIR
tristate "Corsair devices"
Supported devices:
- Vengeance K90
+ - Scimitar PRO RGB
config HID_PRODIKEYS
tristate "Prodikeys PC-MIDI Keyboard support"
config HID_CP2112
tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
- depends on USB_HID && I2C && GPIOLIB && GPIOLIB_IRQCHIP
+ depends on USB_HID && I2C && GPIOLIB
+ select GPIOLIB_IRQCHIP
---help---
Support for Silicon Labs CP2112 HID USB to SMBus Master Bridge.
This is a HID device driver which registers as an i2c adapter
config HID_LOGITECH_HIDPP
tristate "Logitech HID++ devices support"
depends on HID_LOGITECH
+ select POWER_SUPPLY
---help---
Support for Logitech devices relyingon the HID++ Logitech specification
To compile this driver as a module, choose M here: the
module will be called hid-multitouch.
+config HID_NTI
+ tristate "NTI keyboard adapters"
+ ---help---
+ Support for the "extra" Sun keyboard keys on keyboards attached
+ through Network Technologies USB-SUN keyboard adapters.
+
config HID_NTRIG
tristate "N-Trig touch screen"
depends on USB_HID
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
+obj-$(CONFIG_HID_NTI) += hid-nti.o
obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
obj-$(CONFIG_HID_ORTEK) += hid-ortek.o
obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_JESS_ZEN_AIO_KBD) },
{ }
};
MODULE_DEVICE_TABLE(hid, ch_devices);
hid->group = HID_GROUP_WACOM;
break;
case USB_VENDOR_ID_SYNAPTICS:
- if (hid->group == HID_GROUP_GENERIC ||
- hid->group == HID_GROUP_MULTITOUCH_WIN_8)
+ if (hid->group == HID_GROUP_GENERIC)
if ((parser->scan_flags & HID_SCAN_FLAG_VENDOR_SPECIFIC)
&& (parser->scan_flags & HID_SCAN_FLAG_GD_POINTER))
/*
len += sprintf(buf + len, "input");
if (hdev->claimed & HID_CLAIMED_HIDDEV)
len += sprintf(buf + len, "%shiddev%d", len ? "," : "",
- hdev->minor);
+ ((struct hiddev *)hdev->hiddev)->minor);
if (hdev->claimed & HID_CLAIMED_HIDRAW)
len += sprintf(buf + len, "%shidraw%d", len ? "," : "",
((struct hidraw *)hdev->hidraw)->minor);
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_JESS_ZEN_AIO_KBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER) },
{ 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_NTI, USB_DEVICE_ID_USB_SUN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_THT_2P_ARCADE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
*
* Supported devices:
* - Vengeance K90 Keyboard
+ * - Scimitar PRO RGB Gaming Mouse
*
* Copyright (c) 2015 Clement Vuchener
+ * Copyright (c) 2017 Oscar Campos
*/
/*
return 0;
}
+/*
+ * The report descriptor of Corsair Scimitar RGB Pro gaming mouse is
+ * non parseable as they define two consecutive Logical Minimum for
+ * the Usage Page (Consumer) in rdescs bytes 75 and 77 being 77 0x16
+ * that should be obviousy 0x26 for Logical Magimum of 16 bits. This
+ * prevents poper parsing of the report descriptor due Logical
+ * Minimum being larger than Logical Maximum.
+ *
+ * This driver fixes the report descriptor for:
+ * - USB ID b1c:1b3e, sold as Scimitar RGB Pro Gaming mouse
+ */
+
+static __u8 *corsair_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
+ /*
+ * Corsair Scimitar RGB Pro report descriptor is broken and
+ * defines two different Logical Minimum for the Consumer
+ * Application. The byte 77 should be a 0x26 defining a 16
+ * bits integer for the Logical Maximum but it is a 0x16
+ * instead (Logical Minimum)
+ */
+ switch (hdev->product) {
+ case USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB:
+ if (*rsize >= 172 && rdesc[75] == 0x15 && rdesc[77] == 0x16
+ && rdesc[78] == 0xff && rdesc[79] == 0x0f) {
+ hid_info(hdev, "Fixing up report descriptor\n");
+ rdesc[77] = 0x26;
+ }
+ break;
+ }
+
+ }
+ return rdesc;
+}
+
static const struct hid_device_id corsair_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90),
.driver_data = CORSAIR_USE_K90_MACRO |
CORSAIR_USE_K90_BACKLIGHT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR,
+ USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB) },
{}
};
.event = corsair_event,
.remove = corsair_remove,
.input_mapping = corsair_input_mapping,
+ .report_fixup = corsair_mouse_report_fixup,
};
module_hid_driver(corsair_driver);
MODULE_LICENSE("GPL");
+/* Original K90 driver author */
MODULE_AUTHOR("Clement Vuchener");
+/* Scimitar PRO RGB driver author */
+MODULE_AUTHOR("Oscar Campos");
MODULE_DESCRIPTION("HID driver for Corsair devices");
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/hid.h>
+#include <linux/hidraw.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/nls.h>
dev->adap.algo_data = dev;
dev->adap.dev.parent = &hdev->dev;
snprintf(dev->adap.name, sizeof(dev->adap.name),
- "CP2112 SMBus Bridge on hiddev%d", hdev->minor);
+ "CP2112 SMBus Bridge on hidraw%d",
+ ((struct hidraw *)hdev->hidraw)->minor);
dev->hwversion = buf[2];
init_waitqueue_head(&dev->wait);
{0, 0x03, "LightPen"},
{0, 0x04, "TouchScreen"},
{0, 0x05, "TouchPad"},
+ {0, 0x0e, "DeviceConfiguration"},
{0, 0x20, "Stylus"},
{0, 0x21, "Puck"},
{0, 0x22, "Finger"},
+ {0, 0x23, "DeviceSettings"},
{0, 0x30, "TipPressure"},
{0, 0x31, "BarrelPressure"},
{0, 0x32, "InRange"},
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
#define USB_DEVICE_ID_ATEN_CS682 0x2213
#define USB_DEVICE_ID_ATEN_CS692 0x8021
+#define USB_DEVICE_ID_ATEN_CS1758 0x2220
#define USB_VENDOR_ID_ATMEL 0x03eb
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
#define USB_DEVICE_ID_CORSAIR_K70RGB 0x1b13
#define USB_DEVICE_ID_CORSAIR_STRAFE 0x1b15
#define USB_DEVICE_ID_CORSAIR_K65RGB 0x1b17
+#define USB_DEVICE_ID_CORSAIR_K70RGB_RAPIDFIRE 0x1b38
+#define USB_DEVICE_ID_CORSAIR_K65RGB_RAPIDFIRE 0x1b39
+#define USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB 0x1b3e
#define USB_VENDOR_ID_CREATIVELABS 0x041e
#define USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51 0x322c
#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615
#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070
+#define USB_VENDOR_ID_INNOMEDIA 0x1292
+#define USB_DEVICE_ID_INNEX_GENESIS_ATARI 0x4745
+
#define USB_VENDOR_ID_ITE 0x048d
#define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386
#define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350
#define USB_VENDOR_ID_JESS 0x0c45
#define USB_DEVICE_ID_JESS_YUREX 0x1010
+#define USB_DEVICE_ID_JESS_ZEN_AIO_KBD 0x5112
#define USB_VENDOR_ID_JESS2 0x0f30
#define USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD 0x0111
#define USB_DEVICE_ID_NOVATEK_PCT 0x0600
#define USB_DEVICE_ID_NOVATEK_MOUSE 0x1602
+#define USB_VENDOR_ID_NTI 0x0757
+#define USB_DEVICE_ID_USB_SUN 0x0a00
+
#define USB_VENDOR_ID_NTRIG 0x1b96
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003
#define USB_DEVICE_ID_UGEE_TABLET_45 0x0045
#define USB_DEVICE_ID_YIYNOVA_TABLET 0x004d
+#define USB_VENDOR_ID_UGEE 0x28bd
+#define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071
+
#define USB_VENDOR_ID_UNITEC 0x227d
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19 0x0a19
#define USB_VENDOR_ID_XIN_MO 0x16c0
#define USB_DEVICE_ID_XIN_MO_DUAL_ARCADE 0x05e1
+#define USB_DEVICE_ID_THT_2P_ARCADE 0x75e1
#define USB_VENDOR_ID_XIROKU 0x1477
#define USB_DEVICE_ID_XIROKU_SPX 0x1006
/*
* Ignore out-of-range values as per HID specification,
- * section 5.10 and 6.2.25.
+ * section 5.10 and 6.2.25, when NULL state bit is present.
+ * When it's not, clamp the value to match Microsoft's input
+ * driver as mentioned in "Required HID usages for digitizers":
+ * https://msdn.microsoft.com/en-us/library/windows/hardware/dn672278(v=vs.85).asp
*
* The logical_minimum < logical_maximum check is done so that we
* don't unintentionally discard values sent by devices which
* don't specify logical min and max.
*/
if ((field->flags & HID_MAIN_ITEM_VARIABLE) &&
- (field->logical_minimum < field->logical_maximum) &&
- (value < field->logical_minimum ||
- value > field->logical_maximum)) {
- dbg_hid("Ignoring out-of-range value %x\n", value);
- return;
+ (field->logical_minimum < field->logical_maximum)) {
+ if (field->flags & HID_MAIN_ITEM_NULL_STATE &&
+ (value < field->logical_minimum ||
+ value > field->logical_maximum)) {
+ dbg_hid("Ignoring out-of-range value %x\n", value);
+ return;
+ }
+ value = clamp(value,
+ field->logical_minimum,
+ field->logical_maximum);
}
/*
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};
+/*
+ * Register 0xB5 is "pairing information". It is solely intended for the
+ * receiver, so do not overwrite the device index.
+ */
+static u8 unifying_pairing_query[] = {0x10, 0xff, 0x83, 0xb5};
+static u8 unifying_pairing_answer[] = {0x11, 0xff, 0x83, 0xb5};
static int logi_dj_ll_raw_request(struct hid_device *hid,
unsigned char reportnum, __u8 *buf,
/* 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;
+ if (count == 7 && !memcmp(buf, unifying_pairing_query,
+ sizeof(unifying_pairing_query)))
+ buf[4] = (buf[4] & 0xf0) | (djdev->device_index - 1);
else
buf[1] = djdev->device_index;
return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf,
/* 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))
+ !memcmp(data, unifying_pairing_answer,
+ sizeof(unifying_pairing_answer)))
device_index = (data[4] & 0x0F) + 1;
else
return false;
#define HIDPP_QUIRK_CLASS_M560 BIT(1)
#define HIDPP_QUIRK_CLASS_K400 BIT(2)
#define HIDPP_QUIRK_CLASS_G920 BIT(3)
+#define HIDPP_QUIRK_CLASS_K750 BIT(4)
/* bits 2..20 are reserved for classes */
-#define HIDPP_QUIRK_CONNECT_EVENTS BIT(21)
+/* #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) disabled */
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
+#define HIDPP_QUIRK_UNIFYING BIT(25)
-#define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \
- HIDPP_QUIRK_CONNECT_EVENTS)
+#define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT
+
+#define HIDPP_CAPABILITY_HIDPP10_BATTERY BIT(0)
+#define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1)
+#define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2)
+#define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS BIT(3)
/*
* There are two hidpp protocols in use, the first version hidpp10 is known
};
} __packed;
+struct hidpp_battery {
+ u8 feature_index;
+ u8 solar_feature_index;
+ struct power_supply_desc desc;
+ struct power_supply *ps;
+ char name[64];
+ int status;
+ int capacity;
+ int level;
+ bool online;
+};
+
struct hidpp_device {
struct hid_device *hid_dev;
struct mutex send_mutex;
struct input_dev *delayed_input;
unsigned long quirks;
-};
+ unsigned long capabilities;
+ struct hidpp_battery battery;
+};
/* HID++ 1.0 error codes */
#define HIDPP_ERROR 0x8f
#define HIDPP_SET_LONG_REGISTER 0x82
#define HIDPP_GET_LONG_REGISTER 0x83
+#define HIDPP_REG_GENERAL 0x00
+
+static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
+{
+ struct hidpp_report response;
+ int ret;
+ u8 params[3] = { 0 };
+
+ ret = hidpp_send_rap_command_sync(hidpp_dev,
+ REPORT_ID_HIDPP_SHORT,
+ HIDPP_GET_REGISTER,
+ HIDPP_REG_GENERAL,
+ NULL, 0, &response);
+ if (ret)
+ return ret;
+
+ memcpy(params, response.rap.params, 3);
+
+ /* Set the battery bit */
+ params[0] |= BIT(4);
+
+ return hidpp_send_rap_command_sync(hidpp_dev,
+ REPORT_ID_HIDPP_SHORT,
+ HIDPP_SET_REGISTER,
+ HIDPP_REG_GENERAL,
+ params, 3, &response);
+}
+
+#define HIDPP_REG_BATTERY_STATUS 0x07
+
+static int hidpp10_battery_status_map_level(u8 param)
+{
+ int level;
+
+ switch (param) {
+ case 1 ... 2:
+ level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+ break;
+ case 3 ... 4:
+ level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+ break;
+ case 5 ... 6:
+ level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+ break;
+ case 7:
+ level = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+ break;
+ default:
+ level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+ }
+
+ return level;
+}
+
+static int hidpp10_battery_status_map_status(u8 param)
+{
+ int status;
+
+ switch (param) {
+ case 0x00:
+ /* discharging (in use) */
+ status = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ case 0x21: /* (standard) charging */
+ case 0x24: /* fast charging */
+ case 0x25: /* slow charging */
+ status = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case 0x26: /* topping charge */
+ case 0x22: /* charge complete */
+ status = POWER_SUPPLY_STATUS_FULL;
+ break;
+ case 0x20: /* unknown */
+ status = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+ /*
+ * 0x01...0x1F = reserved (not charging)
+ * 0x23 = charging error
+ * 0x27..0xff = reserved
+ */
+ default:
+ status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ }
+
+ return status;
+}
+
+static int hidpp10_query_battery_status(struct hidpp_device *hidpp)
+{
+ struct hidpp_report response;
+ int ret, status;
+
+ ret = hidpp_send_rap_command_sync(hidpp,
+ REPORT_ID_HIDPP_SHORT,
+ HIDPP_GET_REGISTER,
+ HIDPP_REG_BATTERY_STATUS,
+ NULL, 0, &response);
+ if (ret)
+ return ret;
+
+ hidpp->battery.level =
+ hidpp10_battery_status_map_level(response.rap.params[0]);
+ status = hidpp10_battery_status_map_status(response.rap.params[1]);
+ hidpp->battery.status = status;
+ /* the capacity is only available when discharging or full */
+ hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
+ status == POWER_SUPPLY_STATUS_FULL;
+
+ return 0;
+}
+
+#define HIDPP_REG_BATTERY_MILEAGE 0x0D
+
+static int hidpp10_battery_mileage_map_status(u8 param)
+{
+ int status;
+
+ switch (param >> 6) {
+ case 0x00:
+ /* discharging (in use) */
+ status = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ case 0x01: /* charging */
+ status = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case 0x02: /* charge complete */
+ status = POWER_SUPPLY_STATUS_FULL;
+ break;
+ /*
+ * 0x03 = charging error
+ */
+ default:
+ status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ }
+
+ return status;
+}
+
+static int hidpp10_query_battery_mileage(struct hidpp_device *hidpp)
+{
+ struct hidpp_report response;
+ int ret, status;
+
+ ret = hidpp_send_rap_command_sync(hidpp,
+ REPORT_ID_HIDPP_SHORT,
+ HIDPP_GET_REGISTER,
+ HIDPP_REG_BATTERY_MILEAGE,
+ NULL, 0, &response);
+ if (ret)
+ return ret;
+
+ hidpp->battery.capacity = response.rap.params[0];
+ status = hidpp10_battery_mileage_map_status(response.rap.params[2]);
+ hidpp->battery.status = status;
+ /* the capacity is only available when discharging or full */
+ hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
+ status == POWER_SUPPLY_STATUS_FULL;
+
+ return 0;
+}
+
+static int hidpp10_battery_event(struct hidpp_device *hidpp, u8 *data, int size)
+{
+ struct hidpp_report *report = (struct hidpp_report *)data;
+ int status, capacity, level;
+ bool changed;
+
+ if (report->report_id != REPORT_ID_HIDPP_SHORT)
+ return 0;
+
+ switch (report->rap.sub_id) {
+ case HIDPP_REG_BATTERY_STATUS:
+ capacity = hidpp->battery.capacity;
+ level = hidpp10_battery_status_map_level(report->rawbytes[1]);
+ status = hidpp10_battery_status_map_status(report->rawbytes[2]);
+ break;
+ case HIDPP_REG_BATTERY_MILEAGE:
+ capacity = report->rap.params[0];
+ level = hidpp->battery.level;
+ status = hidpp10_battery_mileage_map_status(report->rawbytes[3]);
+ break;
+ default:
+ return 0;
+ }
+
+ changed = capacity != hidpp->battery.capacity ||
+ level != hidpp->battery.level ||
+ status != hidpp->battery.status;
+
+ /* the capacity is only available when discharging or full */
+ hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
+ status == POWER_SUPPLY_STATUS_FULL;
+
+ if (changed) {
+ hidpp->battery.level = level;
+ hidpp->battery.status = status;
+ if (hidpp->battery.ps)
+ power_supply_changed(hidpp->battery.ps);
+ }
+
+ return 0;
+}
+
#define HIDPP_REG_PAIRING_INFORMATION 0xB5
-#define DEVICE_NAME 0x40
+#define HIDPP_EXTENDED_PAIRING 0x30
+#define HIDPP_DEVICE_NAME 0x40
-static char *hidpp_get_unifying_name(struct hidpp_device *hidpp_dev)
+static char *hidpp_unifying_get_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 };
+ u8 params[1] = { HIDPP_DEVICE_NAME };
char *name;
int len;
return name;
}
+static int hidpp_unifying_get_serial(struct hidpp_device *hidpp, u32 *serial)
+{
+ struct hidpp_report response;
+ int ret;
+ u8 params[1] = { HIDPP_EXTENDED_PAIRING };
+
+ ret = hidpp_send_rap_command_sync(hidpp,
+ REPORT_ID_HIDPP_SHORT,
+ HIDPP_GET_LONG_REGISTER,
+ HIDPP_REG_PAIRING_INFORMATION,
+ params, 1, &response);
+ if (ret)
+ return ret;
+
+ /*
+ * We don't care about LE or BE, we will output it as a string
+ * with %4phD, so we need to keep the order.
+ */
+ *serial = *((u32 *)&response.rap.params[1]);
+ return 0;
+}
+
+static int hidpp_unifying_init(struct hidpp_device *hidpp)
+{
+ struct hid_device *hdev = hidpp->hid_dev;
+ const char *name;
+ u32 serial;
+ int ret;
+
+ ret = hidpp_unifying_get_serial(hidpp, &serial);
+ if (ret)
+ return ret;
+
+ snprintf(hdev->uniq, sizeof(hdev->uniq), "%04x-%4phD",
+ hdev->product, &serial);
+ dbg_hid("HID++ Unifying: Got serial: %s\n", hdev->uniq);
+
+ name = hidpp_unifying_get_name(hidpp);
+ if (!name)
+ return -EIO;
+
+ snprintf(hdev->name, sizeof(hdev->name), "%s", name);
+ dbg_hid("HID++ Unifying: Got name: %s\n", name);
+
+ kfree(name);
+ return 0;
+}
+
/* -------------------------------------------------------------------------- */
/* 0x0000: Root */
/* -------------------------------------------------------------------------- */
if (ret)
return ret;
+ if (response.fap.params[0] == 0)
+ return -ENOENT;
+
*feature_index = response.fap.params[0];
*feature_type = response.fap.params[1];
return name;
}
+/* -------------------------------------------------------------------------- */
+/* 0x1000: Battery level status */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_BATTERY_LEVEL_STATUS 0x1000
+
+#define CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS 0x00
+#define CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_CAPABILITY 0x10
+
+#define EVENT_BATTERY_LEVEL_STATUS_BROADCAST 0x00
+
+#define FLAG_BATTERY_LEVEL_DISABLE_OSD BIT(0)
+#define FLAG_BATTERY_LEVEL_MILEAGE BIT(1)
+#define FLAG_BATTERY_LEVEL_RECHARGEABLE BIT(2)
+
+static int hidpp_map_battery_level(int capacity)
+{
+ if (capacity < 11)
+ return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+ else if (capacity < 31)
+ return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+ else if (capacity < 81)
+ return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+ return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+}
+
+static int hidpp20_batterylevel_map_status_capacity(u8 data[3], int *capacity,
+ int *next_capacity,
+ int *level)
+{
+ int status;
+
+ *capacity = data[0];
+ *next_capacity = data[1];
+ *level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+
+ /* When discharging, we can rely on the device reported capacity.
+ * For all other states the device reports 0 (unknown).
+ */
+ switch (data[2]) {
+ case 0: /* discharging (in use) */
+ status = POWER_SUPPLY_STATUS_DISCHARGING;
+ *level = hidpp_map_battery_level(*capacity);
+ break;
+ case 1: /* recharging */
+ status = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case 2: /* charge in final stage */
+ status = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case 3: /* charge complete */
+ status = POWER_SUPPLY_STATUS_FULL;
+ *level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+ *capacity = 100;
+ break;
+ case 4: /* recharging below optimal speed */
+ status = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ /* 5 = invalid battery type
+ 6 = thermal error
+ 7 = other charging error */
+ default:
+ status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ }
+
+ return status;
+}
+
+static int hidpp20_batterylevel_get_battery_capacity(struct hidpp_device *hidpp,
+ u8 feature_index,
+ int *status,
+ int *capacity,
+ int *next_capacity,
+ int *level)
+{
+ struct hidpp_report response;
+ int ret;
+ u8 *params = (u8 *)response.fap.params;
+
+ ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS,
+ 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;
+
+ *status = hidpp20_batterylevel_map_status_capacity(params, capacity,
+ next_capacity,
+ level);
+
+ return 0;
+}
+
+static int hidpp20_batterylevel_get_battery_info(struct hidpp_device *hidpp,
+ u8 feature_index)
+{
+ struct hidpp_report response;
+ int ret;
+ u8 *params = (u8 *)response.fap.params;
+ unsigned int level_count, flags;
+
+ ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_CAPABILITY,
+ 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;
+
+ level_count = params[0];
+ flags = params[1];
+
+ if (level_count < 10 || !(flags & FLAG_BATTERY_LEVEL_MILEAGE))
+ hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS;
+ else
+ hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE;
+
+ return 0;
+}
+
+static int hidpp20_query_battery_info(struct hidpp_device *hidpp)
+{
+ u8 feature_type;
+ int ret;
+ int status, capacity, next_capacity, level;
+
+ if (hidpp->battery.feature_index == 0xff) {
+ ret = hidpp_root_get_feature(hidpp,
+ HIDPP_PAGE_BATTERY_LEVEL_STATUS,
+ &hidpp->battery.feature_index,
+ &feature_type);
+ if (ret)
+ return ret;
+ }
+
+ ret = hidpp20_batterylevel_get_battery_capacity(hidpp,
+ hidpp->battery.feature_index,
+ &status, &capacity,
+ &next_capacity, &level);
+ if (ret)
+ return ret;
+
+ ret = hidpp20_batterylevel_get_battery_info(hidpp,
+ hidpp->battery.feature_index);
+ if (ret)
+ return ret;
+
+ hidpp->battery.status = status;
+ hidpp->battery.capacity = capacity;
+ hidpp->battery.level = level;
+ /* the capacity is only available when discharging or full */
+ hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
+ status == POWER_SUPPLY_STATUS_FULL;
+
+ return 0;
+}
+
+static int hidpp20_battery_event(struct hidpp_device *hidpp,
+ u8 *data, int size)
+{
+ struct hidpp_report *report = (struct hidpp_report *)data;
+ int status, capacity, next_capacity, level;
+ bool changed;
+
+ if (report->fap.feature_index != hidpp->battery.feature_index ||
+ report->fap.funcindex_clientid != EVENT_BATTERY_LEVEL_STATUS_BROADCAST)
+ return 0;
+
+ status = hidpp20_batterylevel_map_status_capacity(report->fap.params,
+ &capacity,
+ &next_capacity,
+ &level);
+
+ /* the capacity is only available when discharging or full */
+ hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
+ status == POWER_SUPPLY_STATUS_FULL;
+
+ changed = capacity != hidpp->battery.capacity ||
+ level != hidpp->battery.level ||
+ status != hidpp->battery.status;
+
+ if (changed) {
+ hidpp->battery.level = level;
+ hidpp->battery.capacity = capacity;
+ hidpp->battery.status = status;
+ if (hidpp->battery.ps)
+ power_supply_changed(hidpp->battery.ps);
+ }
+
+ return 0;
+}
+
+static enum power_supply_property hidpp_battery_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_SCOPE,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_SERIAL_NUMBER,
+ 0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY, */
+ 0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY_LEVEL, */
+};
+
+static int hidpp_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct hidpp_device *hidpp = power_supply_get_drvdata(psy);
+ int ret = 0;
+
+ switch(psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = hidpp->battery.status;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = hidpp->battery.capacity;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+ val->intval = hidpp->battery.level;
+ break;
+ case POWER_SUPPLY_PROP_SCOPE:
+ val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = hidpp->battery.online;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ if (!strncmp(hidpp->name, "Logitech ", 9))
+ val->strval = hidpp->name + 9;
+ else
+ val->strval = hidpp->name;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = "Logitech";
+ break;
+ case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+ val->strval = hidpp->hid_dev->uniq;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+/* 0x4301: Solar Keyboard */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_SOLAR_KEYBOARD 0x4301
+
+#define CMD_SOLAR_SET_LIGHT_MEASURE 0x00
+
+#define EVENT_SOLAR_BATTERY_BROADCAST 0x00
+#define EVENT_SOLAR_BATTERY_LIGHT_MEASURE 0x10
+#define EVENT_SOLAR_CHECK_LIGHT_BUTTON 0x20
+
+static int hidpp_solar_request_battery_event(struct hidpp_device *hidpp)
+{
+ struct hidpp_report response;
+ u8 params[2] = { 1, 1 };
+ u8 feature_type;
+ int ret;
+
+ if (hidpp->battery.feature_index == 0xff) {
+ ret = hidpp_root_get_feature(hidpp,
+ HIDPP_PAGE_SOLAR_KEYBOARD,
+ &hidpp->battery.solar_feature_index,
+ &feature_type);
+ if (ret)
+ return ret;
+ }
+
+ ret = hidpp_send_fap_command_sync(hidpp,
+ hidpp->battery.solar_feature_index,
+ CMD_SOLAR_SET_LIGHT_MEASURE,
+ params, 2, &response);
+ if (ret > 0) {
+ hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+ __func__, ret);
+ return -EPROTO;
+ }
+ if (ret)
+ return ret;
+
+ hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE;
+
+ return 0;
+}
+
+static int hidpp_solar_battery_event(struct hidpp_device *hidpp,
+ u8 *data, int size)
+{
+ struct hidpp_report *report = (struct hidpp_report *)data;
+ int capacity, lux, status;
+ u8 function;
+
+ function = report->fap.funcindex_clientid;
+
+
+ if (report->fap.feature_index != hidpp->battery.solar_feature_index ||
+ !(function == EVENT_SOLAR_BATTERY_BROADCAST ||
+ function == EVENT_SOLAR_BATTERY_LIGHT_MEASURE ||
+ function == EVENT_SOLAR_CHECK_LIGHT_BUTTON))
+ return 0;
+
+ capacity = report->fap.params[0];
+
+ switch (function) {
+ case EVENT_SOLAR_BATTERY_LIGHT_MEASURE:
+ lux = (report->fap.params[1] << 8) | report->fap.params[2];
+ if (lux > 200)
+ status = POWER_SUPPLY_STATUS_CHARGING;
+ else
+ status = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ case EVENT_SOLAR_CHECK_LIGHT_BUTTON:
+ default:
+ if (capacity < hidpp->battery.capacity)
+ status = POWER_SUPPLY_STATUS_DISCHARGING;
+ else
+ status = POWER_SUPPLY_STATUS_CHARGING;
+
+ }
+
+ if (capacity == 100)
+ status = POWER_SUPPLY_STATUS_FULL;
+
+ hidpp->battery.online = true;
+ if (capacity != hidpp->battery.capacity ||
+ status != hidpp->battery.status) {
+ hidpp->battery.capacity = capacity;
+ hidpp->battery.status = status;
+ if (hidpp->battery.ps)
+ power_supply_changed(hidpp->battery.ps);
+ }
+
+ return 0;
+}
+
/* -------------------------------------------------------------------------- */
/* 0x6010: Touchpad FW items */
/* -------------------------------------------------------------------------- */
struct wtp_data *wd = hidpp->private_data;
int ret;
- if (!connected)
- return 0;
-
if (!wd->x_size) {
ret = wtp_get_config(hidpp);
if (ret) {
hidpp_dev = hid_get_drvdata(hdev);
- if (!connected)
- return -ENODEV;
-
return hidpp_send_rap_command_sync(
hidpp_dev,
REPORT_ID_HIDPP_SHORT,
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
- if (!connected)
- return 0;
-
if (!disable_tap_to_click)
return 0;
struct hidpp_report *question = hidpp->send_receive_buf;
struct hidpp_report *answer = hidpp->send_receive_buf;
struct hidpp_report *report = (struct hidpp_report *)data;
+ int ret;
/*
* If the mutex is locked then we have a pending answer from a
if (unlikely(hidpp_report_is_connect_event(report))) {
atomic_set(&hidpp->connected,
!(report->rap.params[0] & (1 << 6)));
- if ((hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) &&
- (schedule_work(&hidpp->work) == 0))
+ if (schedule_work(&hidpp->work) == 0)
dbg_hid("%s: connect event already queued\n", __func__);
return 1;
}
+ if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
+ ret = hidpp20_battery_event(hidpp, data, size);
+ if (ret != 0)
+ return ret;
+ ret = hidpp_solar_battery_event(hidpp, data, size);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
+ ret = hidpp10_battery_event(hidpp, data, size);
+ if (ret != 0)
+ return ret;
+ }
+
return 0;
}
return 0;
}
-static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
+static int hidpp_initialize_battery(struct hidpp_device *hidpp)
+{
+ static atomic_t battery_no = ATOMIC_INIT(0);
+ struct power_supply_config cfg = { .drv_data = hidpp };
+ struct power_supply_desc *desc = &hidpp->battery.desc;
+ enum power_supply_property *battery_props;
+ struct hidpp_battery *battery;
+ unsigned int num_battery_props;
+ unsigned long n;
+ int ret;
+
+ if (hidpp->battery.ps)
+ return 0;
+
+ hidpp->battery.feature_index = 0xff;
+ hidpp->battery.solar_feature_index = 0xff;
+
+ if (hidpp->protocol_major >= 2) {
+ if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750)
+ ret = hidpp_solar_request_battery_event(hidpp);
+ else
+ ret = hidpp20_query_battery_info(hidpp);
+
+ if (ret)
+ return ret;
+ hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_BATTERY;
+ } else {
+ ret = hidpp10_query_battery_status(hidpp);
+ if (ret) {
+ ret = hidpp10_query_battery_mileage(hidpp);
+ if (ret)
+ return -ENOENT;
+ hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE;
+ } else {
+ hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS;
+ }
+ hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP10_BATTERY;
+ }
+
+ battery_props = devm_kmemdup(&hidpp->hid_dev->dev,
+ hidpp_battery_props,
+ sizeof(hidpp_battery_props),
+ GFP_KERNEL);
+ num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 2;
+
+ if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE)
+ battery_props[num_battery_props++] =
+ POWER_SUPPLY_PROP_CAPACITY;
+
+ if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS)
+ battery_props[num_battery_props++] =
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL;
+
+ battery = &hidpp->battery;
+
+ n = atomic_inc_return(&battery_no) - 1;
+ desc->properties = battery_props;
+ desc->num_properties = num_battery_props;
+ desc->get_property = hidpp_battery_get_property;
+ sprintf(battery->name, "hidpp_battery_%ld", n);
+ desc->name = battery->name;
+ desc->type = POWER_SUPPLY_TYPE_BATTERY;
+ desc->use_for_apm = 0;
+
+ battery->ps = devm_power_supply_register(&hidpp->hid_dev->dev,
+ &battery->desc,
+ &cfg);
+ if (IS_ERR(battery->ps))
+ return PTR_ERR(battery->ps);
+
+ power_supply_powers(battery->ps, &hidpp->hid_dev->dev);
+
+ return ret;
+}
+
+static void hidpp_overwrite_name(struct hid_device *hdev)
{
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 (hidpp->protocol_major < 2)
+ return;
+
+ name = hidpp_get_device_name(hidpp);
if (!name) {
hid_err(hdev, "unable to retrieve the name of the device");
struct input_dev *input;
char *name, *devm_name;
+ if (!connected) {
+ if (hidpp->battery.ps) {
+ hidpp->battery.online = false;
+ hidpp->battery.status = POWER_SUPPLY_STATUS_UNKNOWN;
+ hidpp->battery.level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+ power_supply_changed(hidpp->battery.ps);
+ }
+ return;
+ }
+
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
ret = wtp_connect(hdev, connected);
if (ret)
return;
}
- if (!connected || hidpp->delayed_input)
- return;
-
/* the device is already connected, we can ask for its name and
* protocol */
if (!hidpp->protocol_major) {
hidpp->protocol_major, hidpp->protocol_minor);
}
- if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT))
- /* if HID created the input nodes for us, we can stop now */
- return;
-
- if (!hidpp->name || hidpp->name == hdev->name) {
+ if (hidpp->name == hdev->name && hidpp->protocol_major >= 2) {
name = hidpp_get_device_name(hidpp);
if (!name) {
hid_err(hdev,
hidpp->name = devm_name;
}
+ hidpp_initialize_battery(hidpp);
+
+ /* forward current battery state */
+ if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
+ hidpp10_enable_battery_reporting(hidpp);
+ if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE)
+ hidpp10_query_battery_mileage(hidpp);
+ else
+ hidpp10_query_battery_status(hidpp);
+ } else if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
+ hidpp20_query_battery_info(hidpp);
+ }
+ if (hidpp->battery.ps)
+ power_supply_changed(hidpp->battery.ps);
+
+ if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
+ /* if the input nodes are already created, we can stop now */
+ return;
+
input = hidpp_allocate_input(hdev);
if (!input) {
hid_err(hdev, "cannot allocate new input device: %d\n", ret);
hidpp->delayed_input = input;
}
+static DEVICE_ATTR(builtin_power_supply, 0000, NULL, NULL);
+
+static struct attribute *sysfs_attrs[] = {
+ &dev_attr_builtin_power_supply.attr,
+ NULL
+};
+
+static struct attribute_group ps_attribute_group = {
+ .attrs = sysfs_attrs
+};
+
static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct hidpp_device *hidpp;
hidpp->quirks = id->driver_data;
+ if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
+ hidpp->quirks |= HIDPP_QUIRK_UNIFYING;
+
if (disable_raw_mode) {
hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP;
- hidpp->quirks &= ~HIDPP_QUIRK_CONNECT_EVENTS;
hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT;
}
mutex_init(&hidpp->send_mutex);
init_waitqueue_head(&hidpp->wait);
+ /* indicates we are handling the battery properties in the kernel */
+ ret = sysfs_create_group(&hdev->dev.kobj, &ps_attribute_group);
+ if (ret)
+ hid_warn(hdev, "Cannot allocate sysfs group for %s\n",
+ hdev->name);
+
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "%s:parse failed\n", __func__);
/* Allow incoming packets */
hid_device_io_start(hdev);
+ if (hidpp->quirks & HIDPP_QUIRK_UNIFYING)
+ hidpp_unifying_init(hidpp);
+
connected = hidpp_is_connected(hidpp);
- if (id->group != HID_GROUP_LOGITECH_DJ_DEVICE) {
+ atomic_set(&hidpp->connected, connected);
+ if (!(hidpp->quirks & HIDPP_QUIRK_UNIFYING)) {
if (!connected) {
ret = -ENODEV;
hid_err(hdev, "Device not connected");
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);
+ hidpp_overwrite_name(hdev);
+ }
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
ret = wtp_get_config(hidpp);
}
}
- if (hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) {
- /* Allow incoming packets */
- hid_device_io_start(hdev);
+ /* Allow incoming packets */
+ hid_device_io_start(hdev);
- hidpp_connect_event(hidpp);
- }
+ hidpp_connect_event(hidpp);
return ret;
}
hid_hw_start_fail:
hid_parse_fail:
+ sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
allocate_fail:
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);
+
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
hidpp_ff_deinit(hdev);
hid_hw_close(hdev);
{ /* Keyboard logitech K400 */
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, 0x4024),
- .driver_data = HIDPP_QUIRK_CONNECT_EVENTS | HIDPP_QUIRK_CLASS_K400 },
+ .driver_data = HIDPP_QUIRK_CLASS_K400 },
+ { /* Solar Keyboard Logitech K750 */
+ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+ USB_VENDOR_ID_LOGITECH, 0x4002),
+ .driver_data = HIDPP_QUIRK_CLASS_K750 },
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
#define MT_QUIRK_CONTACT_CNT_ACCURATE (1 << 12)
#define MT_QUIRK_FORCE_GET_FEATURE (1 << 13)
#define MT_QUIRK_FIX_CONST_CONTACT_ID (1 << 14)
+#define MT_QUIRK_TOUCH_SIZE_SCALING (1 << 15)
#define MT_INPUTMODE_TOUCHSCREEN 0x02
#define MT_INPUTMODE_TOUCHPAD 0x03
*/
{ .name = MT_CLS_3M,
.quirks = MT_QUIRK_VALID_IS_CONFIDENCE |
- MT_QUIRK_SLOT_IS_CONTACTID,
+ MT_QUIRK_SLOT_IS_CONTACTID |
+ MT_QUIRK_TOUCH_SIZE_SCALING,
.sn_move = 2048,
.sn_width = 128,
.sn_height = 128,
if (active) {
/* this finger is in proximity of the sensor */
int wide = (s->w > s->h);
- /* divided by two to match visual scale of touch */
- int major = max(s->w, s->h) >> 1;
- int minor = min(s->w, s->h) >> 1;
+ int major = max(s->w, s->h);
+ int minor = min(s->w, s->h);
+
+ /*
+ * divided by two to match visual scale of touch
+ * for devices with this quirk
+ */
+ if (td->mtclass.quirks & MT_QUIRK_TOUCH_SIZE_SCALING) {
+ major = major >> 1;
+ minor = minor >> 1;
+ }
input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y);
--- /dev/null
+/*
+ * USB HID quirks support for Network Technologies, Inc. "USB-SUN" USB
+ * adapter for pre-USB Sun keyboards
+ *
+ * Copyright (c) 2011 Google, Inc.
+ *
+ * Based on HID apple driver by
+ * Copyright (c) 1999 Andreas Gal
+ * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ * Copyright (c) 2006-2007 Jiri Kosina
+ * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.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 <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+MODULE_AUTHOR("Jonathan Klabunde Tomer <jktomer@google.com>");
+MODULE_DESCRIPTION("HID driver for Network Technologies USB-SUN keyboard adapter");
+
+/*
+ * NTI Sun keyboard adapter has wrong logical maximum in report descriptor
+ */
+static __u8 *nti_usbsun_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ if (*rsize >= 60 && rdesc[53] == 0x65 && rdesc[59] == 0x65) {
+ hid_info(hdev, "fixing up NTI USB-SUN keyboard adapter report descriptor\n");
+ rdesc[53] = rdesc[59] = 0xe7;
+ }
+ return rdesc;
+}
+
+static const struct hid_device_id nti_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_NTI, USB_DEVICE_ID_USB_SUN) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, nti_devices);
+
+static struct hid_driver nti_driver = {
+ .name = "nti",
+ .id_table = nti_devices,
+ .report_fixup = nti_usbsun_report_fixup
+};
+
+module_hid_driver(nti_driver);
+
+MODULE_LICENSE("GPL");
sony_leds_remove(sc);
if (sc->quirks & SONY_BATTERY_SUPPORT)
sony_battery_remove(sc);
+ if (sc->touchpad)
+ sony_unregister_touchpad(sc);
sony_cancel_work_sync(sc);
kfree(sc->output_report_dmabuf);
sony_remove_dev_list(sc);
}
break;
case USB_DEVICE_ID_UGTIZER_TABLET_GP0610:
+ case USB_DEVICE_ID_UGEE_TABLET_EX07S:
/* If this is the pen interface */
if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
rc = uclogic_tablet_enable(hdev);
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) },
{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);
static const struct hid_device_id xinmo_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_THT_2P_ARCADE) },
{ }
};
report->device->report_enum[report->type].numbered + 2;
}
-static void i2c_hid_init_report(struct hid_report *report, u8 *buffer,
- size_t bufsize)
-{
- struct hid_device *hid = report->device;
- struct i2c_client *client = hid->driver_data;
- struct i2c_hid *ihid = i2c_get_clientdata(client);
- unsigned int size, ret_size;
-
- size = i2c_hid_get_report_length(report);
- if (i2c_hid_get_report(client,
- report->type == HID_FEATURE_REPORT ? 0x03 : 0x01,
- report->id, buffer, size))
- return;
-
- i2c_hid_dbg(ihid, "report (len=%d): %*ph\n", size, size, buffer);
-
- ret_size = buffer[0] | (buffer[1] << 8);
-
- if (ret_size != size) {
- dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n",
- __func__, size, ret_size);
- return;
- }
-
- /* hid->driver_lock is held as we are in probe function,
- * we just need to setup the input fields, so using
- * hid_report_raw_event is safe. */
- hid_report_raw_event(hid, report->type, buffer + 2, size - 2, 1);
-}
-
-/*
- * Initialize all reports
- */
-static void i2c_hid_init_reports(struct hid_device *hid)
-{
- struct hid_report *report;
- struct i2c_client *client = hid->driver_data;
- struct i2c_hid *ihid = i2c_get_clientdata(client);
- u8 *inbuf = kzalloc(ihid->bufsize, GFP_KERNEL);
-
- if (!inbuf) {
- dev_err(&client->dev, "can not retrieve initial reports\n");
- return;
- }
-
- /*
- * The device must be powered on while we fetch initial reports
- * from it.
- */
- pm_runtime_get_sync(&client->dev);
-
- list_for_each_entry(report,
- &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
- i2c_hid_init_report(report, inbuf, ihid->bufsize);
-
- pm_runtime_put(&client->dev);
-
- kfree(inbuf);
-}
-
/*
* Traverse the supplied list of reports and find the longest
*/
return ret;
}
- if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
- i2c_hid_init_reports(hid);
-
return 0;
}
}
pdata->hid_descriptor_address = val;
+ ret = of_property_read_u32(dev->of_node, "post-power-on-delay-ms",
+ &val);
+ if (!ret)
+ pdata->post_power_delay_ms = val;
+
return 0;
}
ihid->pdata = *platform_data;
}
+ ihid->pdata.supply = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(ihid->pdata.supply)) {
+ ret = PTR_ERR(ihid->pdata.supply);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&client->dev, "Failed to get regulator: %d\n",
+ ret);
+ goto err;
+ }
+
+ ret = regulator_enable(ihid->pdata.supply);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to enable regulator: %d\n",
+ ret);
+ goto err;
+ }
+ if (ihid->pdata.post_power_delay_ms)
+ msleep(ihid->pdata.post_power_delay_ms);
+
i2c_set_clientdata(client, ihid);
ihid->client = client;
* real computation later. */
ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE);
if (ret < 0)
- goto err;
+ goto err_regulator;
pm_runtime_get_noresume(&client->dev);
pm_runtime_set_active(&client->dev);
pm_runtime_put_noidle(&client->dev);
pm_runtime_disable(&client->dev);
+err_regulator:
+ regulator_disable(ihid->pdata.supply);
+
err:
i2c_hid_free_buffers(ihid);
kfree(ihid);
if (ihid->bufsize)
i2c_hid_free_buffers(ihid);
+ regulator_disable(ihid->pdata.supply);
+
kfree(ihid);
return 0;
else
hid_warn(hid, "Failed to enable irq wake: %d\n",
wake_status);
+ } else {
+ ret = regulator_disable(ihid->pdata.supply);
+ if (ret < 0)
+ hid_warn(hid, "Failed to disable supply: %d\n", ret);
}
return 0;
struct hid_device *hid = ihid->hid;
int wake_status;
- if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) {
+ if (!device_may_wakeup(&client->dev)) {
+ ret = regulator_enable(ihid->pdata.supply);
+ if (ret < 0)
+ hid_warn(hid, "Failed to enable supply: %d\n", ret);
+ if (ihid->pdata.post_power_delay_ms)
+ msleep(ihid->pdata.post_power_delay_ms);
+ } else if (ihid->irq_wake_enabled) {
wake_status = disable_irq_wake(client->irq);
if (!wake_status)
ihid->irq_wake_enabled = false;
module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
+static unsigned int hid_jspoll_interval;
+module_param_named(jspoll, hid_jspoll_interval, uint, 0644);
+MODULE_PARM_DESC(jspoll, "Polling interval of joysticks");
+
static unsigned int ignoreled;
module_param_named(ignoreled, ignoreled, uint, 0644);
MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds");
struct hid_report_enum *report_enum;
int err, ret;
- if (!(hid->quirks & HID_QUIRK_NO_INIT_INPUT_REPORTS)) {
- report_enum = &hid->report_enum[HID_INPUT_REPORT];
- list_for_each_entry(report, &report_enum->report_list, list)
- usbhid_submit_report(hid, report, USB_DIR_IN);
- }
+ report_enum = &hid->report_enum[HID_INPUT_REPORT];
+ list_for_each_entry(report, &report_enum->report_list, list)
+ usbhid_submit_report(hid, report, USB_DIR_IN);
report_enum = &hid->report_enum[HID_FEATURE_REPORT];
list_for_each_entry(report, &report_enum->report_list, list)
return -EINVAL;
}
- if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
- dbg_hid("couldn't allocate rdesc memory\n");
+ rdesc = kmalloc(rsize, GFP_KERNEL);
+ if (!rdesc)
return -ENOMEM;
- }
hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
dev->speed == USB_SPEED_HIGH) {
interval = fls(endpoint->bInterval*8);
- printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
- hid->name, endpoint->bInterval, interval);
+ pr_info("%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
+ hid->name, endpoint->bInterval, interval);
}
- /* Change the polling interval of mice. */
- if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
- interval = hid_mousepoll_interval;
+ /* Change the polling interval of mice and joysticks. */
+ switch (hid->collection->usage) {
+ case HID_GD_MOUSE:
+ if (hid_mousepoll_interval > 0)
+ interval = hid_mousepoll_interval;
+ break;
+ case HID_GD_JOYSTICK:
+ if (hid_jspoll_interval > 0)
+ interval = hid_jspoll_interval;
+ break;
+ }
ret = -ENOMEM;
if (usb_endpoint_dir_in(endpoint)) {
usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
usbhid->urbctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
- usbhid_init_reports(hid);
-
set_bit(HID_STARTED, &usbhid->iofl);
if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
* the size of the HID report descriptor has not changed.
*/
rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL);
- if (!rdesc) {
- dbg_hid("couldn't allocate rdesc memory (post_reset)\n");
+ if (!rdesc)
return -ENOMEM;
- }
+
status = hid_get_class_descriptor(dev,
interface->desc.bInterfaceNumber,
HID_DT_REPORT, rdesc, hid->dev_rsize);
retval = usb_register(&hid_driver);
if (retval)
goto usb_register_fail;
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
+ pr_info(KBUILD_MODNAME ": " DRIVER_DESC "\n");
return 0;
usb_register_fail:
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS682, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS692, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS1758, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FIGHTERSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_STRAFE, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
+ { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB_RAPIDFIRE, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
+ { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB_RAPIDFIRE, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
+ { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS },
- { USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_INPUT_REPORTS },
+ { USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_INNOMEDIA, USB_DEVICE_ID_INNEX_GENESIS_ATARI, HID_QUIRK_MULTI_INPUT },
{ 0, 0 }
};
}
q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
- if (!q_new) {
- dbg_hid("Could not allocate quirks_list_struct\n");
+ if (!q_new)
return -ENOMEM;
- }
q_new->hid_bl_item.idVendor = idVendor;
q_new->hid_bl_item.idProduct = idProduct;
&idVendor, &idProduct, &quirks);
if (m != 3 ||
- usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
- printk(KERN_WARNING
- "Could not parse HID quirk module param %s\n",
- quirks_param[n]);
+ usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
+ pr_warn("Could not parse HID quirk module param %s\n",
+ quirks_param[n]);
}
}
#endif
#define HIDDEV_BUFFER_SIZE 2048
-struct hiddev {
- int exist;
- int open;
- struct mutex existancelock;
- wait_queue_head_t wait;
- struct hid_device *hid;
- struct list_head list;
- spinlock_t list_lock;
-};
-
struct hiddev_list {
struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
int head;
case HIDIOCINITREPORT:
usbhid_init_reports(hid);
+ hiddev->initialized = true;
r = 0;
break;
case HIDIOCGUSAGES:
case HIDIOCSUSAGES:
case HIDIOCGCOLLECTIONINDEX:
+ if (!hiddev->initialized) {
+ usbhid_init_reports(hid);
+ hiddev->initialized = true;
+ }
r = hiddev_ioctl_usage(hiddev, cmd, user_arg);
break;
kfree(hiddev);
return -1;
}
+
+ /*
+ * If HID_QUIRK_NO_INIT_REPORTS is set, make sure we don't initialize
+ * the reports.
+ */
+ hiddev->initialized = hid->quirks & HID_QUIRK_NO_INIT_REPORTS;
+
+ hiddev->minor = usbhid->intf->minor;
+
return 0;
}
WACOM_WORKER_WIRELESS,
WACOM_WORKER_BATTERY,
WACOM_WORKER_REMOTE,
+ WACOM_WORKER_MODE_CHANGE,
};
struct wacom;
struct work_struct remote_work;
struct delayed_work init_work;
struct wacom_remote *remote;
+ struct work_struct mode_change_work;
bool generic_has_leds;
struct wacom_leds {
struct wacom_group_leds *groups;
case WACOM_WORKER_REMOTE:
schedule_work(&wacom->remote_work);
break;
+ case WACOM_WORKER_MODE_CHANGE:
+ schedule_work(&wacom->mode_change_work);
+ break;
}
}
if (features->type == HID_GENERIC) {
/* Any last-minute generic device setup */
+ if (wacom_wac->has_mode_change) {
+ if (wacom_wac->is_direct_mode)
+ features->device_type |= WACOM_DEVICETYPE_DIRECT;
+ else
+ features->device_type &= ~WACOM_DEVICETYPE_DIRECT;
+ }
+
if (features->touch_max > 1) {
if (features->device_type & WACOM_DEVICETYPE_DIRECT)
input_mt_init_slots(wacom_wac->touch_input,
wacom_wac->shared->touch_input = wacom_wac->touch_input;
}
- if (wacom_wac->has_mute_touch_switch)
+ if (wacom_wac->has_mute_touch_switch) {
wacom_wac->shared->has_mute_touch_switch = true;
+ wacom_wac->shared->is_touch_on = true;
+ }
if (wacom_wac->shared->has_mute_touch_switch &&
wacom_wac->shared->touch_input) {
wacom_update_name(wacom, wireless ? " (WL)" : "");
+ /* pen only Bamboo neither support touch nor pad */
+ if ((features->type == BAMBOO_PEN) &&
+ ((features->device_type & WACOM_DEVICETYPE_TOUCH) ||
+ (features->device_type & WACOM_DEVICETYPE_PAD))) {
+ error = -ENODEV;
+ goto fail;
+ }
+
error = wacom_add_shared_data(hdev);
if (error)
goto fail;
/* touch only Bamboo doesn't support pen */
if ((features->type == BAMBOO_TOUCH) &&
(features->device_type & WACOM_DEVICETYPE_PEN)) {
- error = -ENODEV;
- goto fail_quirks;
- }
-
- /* pen only Bamboo neither support touch nor pad */
- if ((features->type == BAMBOO_PEN) &&
- ((features->device_type & WACOM_DEVICETYPE_TOUCH) ||
- (features->device_type & WACOM_DEVICETYPE_PAD))) {
+ cancel_delayed_work_sync(&wacom->init_work);
+ _wacom_query_tablet_data(wacom);
error = -ENODEV;
goto fail_quirks;
}
}
}
+static void wacom_mode_change_work(struct work_struct *work)
+{
+ struct wacom *wacom = container_of(work, struct wacom, mode_change_work);
+ struct wacom_shared *shared = wacom->wacom_wac.shared;
+ struct wacom *wacom1 = NULL;
+ struct wacom *wacom2 = NULL;
+ bool is_direct = wacom->wacom_wac.is_direct_mode;
+ int error = 0;
+
+ if (shared->pen) {
+ wacom1 = hid_get_drvdata(shared->pen);
+ wacom_release_resources(wacom1);
+ hid_hw_stop(wacom1->hdev);
+ wacom1->wacom_wac.has_mode_change = true;
+ wacom1->wacom_wac.is_direct_mode = is_direct;
+ }
+
+ if (shared->touch) {
+ wacom2 = hid_get_drvdata(shared->touch);
+ wacom_release_resources(wacom2);
+ hid_hw_stop(wacom2->hdev);
+ wacom2->wacom_wac.has_mode_change = true;
+ wacom2->wacom_wac.is_direct_mode = is_direct;
+ }
+
+ if (wacom1) {
+ error = wacom_parse_and_register(wacom1, false);
+ if (error)
+ return;
+ }
+
+ if (wacom2) {
+ error = wacom_parse_and_register(wacom2, false);
+ if (error)
+ return;
+ }
+
+ return;
+}
+
static int wacom_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
INIT_WORK(&wacom->wireless_work, wacom_wireless_work);
INIT_WORK(&wacom->battery_work, wacom_battery_work);
INIT_WORK(&wacom->remote_work, wacom_remote_work);
+ INIT_WORK(&wacom->mode_change_work, wacom_mode_change_work);
/* ask for the report descriptor to be loaded by HID */
error = hid_parse(hdev);
cancel_work_sync(&wacom->wireless_work);
cancel_work_sync(&wacom->battery_work);
cancel_work_sync(&wacom->remote_work);
+ cancel_work_sync(&wacom->mode_change_work);
if (hdev->bus == BUS_BLUETOOTH)
device_remove_file(&hdev->dev, &dev_attr_speed);
/* make sure we don't trigger the LEDs */
wacom_led_groups_release(wacom);
- wacom_release_resources(wacom);
+
+ if (wacom->wacom_wac.features.type != REMOTE)
+ wacom_release_resources(wacom);
hid_set_drvdata(hdev, NULL);
}
return 0;
}
-static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len)
-{
- unsigned char *data = wacom_wac->data;
- struct input_dev *input;
- struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
- struct wacom_remote *remote = wacom->remote;
- int bat_charging, bat_percent, touch_ring_mode;
- __u32 serial;
- int i, index = -1;
- unsigned long flags;
-
- if (data[0] != WACOM_REPORT_REMOTE) {
- hid_dbg(wacom->hdev, "%s: received unknown report #%d",
- __func__, data[0]);
- return 0;
- }
-
- serial = data[3] + (data[4] << 8) + (data[5] << 16);
- wacom_wac->id[0] = PAD_DEVICE_ID;
-
- spin_lock_irqsave(&remote->remote_lock, flags);
-
- for (i = 0; i < WACOM_MAX_REMOTES; i++) {
- if (remote->remotes[i].serial == serial) {
- index = i;
- break;
- }
- }
-
- if (index < 0 || !remote->remotes[index].registered)
- goto out;
-
- input = remote->remotes[index].input;
-
- input_report_key(input, BTN_0, (data[9] & 0x01));
- input_report_key(input, BTN_1, (data[9] & 0x02));
- input_report_key(input, BTN_2, (data[9] & 0x04));
- input_report_key(input, BTN_3, (data[9] & 0x08));
- input_report_key(input, BTN_4, (data[9] & 0x10));
- input_report_key(input, BTN_5, (data[9] & 0x20));
- input_report_key(input, BTN_6, (data[9] & 0x40));
- input_report_key(input, BTN_7, (data[9] & 0x80));
-
- input_report_key(input, BTN_8, (data[10] & 0x01));
- input_report_key(input, BTN_9, (data[10] & 0x02));
- input_report_key(input, BTN_A, (data[10] & 0x04));
- input_report_key(input, BTN_B, (data[10] & 0x08));
- input_report_key(input, BTN_C, (data[10] & 0x10));
- input_report_key(input, BTN_X, (data[10] & 0x20));
- input_report_key(input, BTN_Y, (data[10] & 0x40));
- input_report_key(input, BTN_Z, (data[10] & 0x80));
-
- input_report_key(input, BTN_BASE, (data[11] & 0x01));
- input_report_key(input, BTN_BASE2, (data[11] & 0x02));
-
- if (data[12] & 0x80)
- input_report_abs(input, ABS_WHEEL, (data[12] & 0x7f));
- else
- input_report_abs(input, ABS_WHEEL, 0);
-
- bat_percent = data[7] & 0x7f;
- bat_charging = !!(data[7] & 0x80);
-
- if (data[9] | data[10] | (data[11] & 0x03) | data[12])
- input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
- else
- input_report_abs(input, ABS_MISC, 0);
-
- input_event(input, EV_MSC, MSC_SERIAL, serial);
-
- input_sync(input);
-
- /*Which mode select (LED light) is currently on?*/
- touch_ring_mode = (data[11] & 0xC0) >> 6;
-
- for (i = 0; i < WACOM_MAX_REMOTES; i++) {
- if (remote->remotes[i].serial == serial)
- wacom->led.groups[i].select = touch_ring_mode;
- }
-
- __wacom_notify_battery(&remote->remotes[index].battery, bat_percent,
- bat_charging, 1, bat_charging);
-
-out:
- spin_unlock_irqrestore(&remote->remote_lock, flags);
- return 0;
-}
-
-static void wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len)
-{
- struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
- unsigned char *data = wacom_wac->data;
- struct wacom_remote *remote = wacom->remote;
- struct wacom_remote_data remote_data;
- unsigned long flags;
- int i, ret;
-
- if (data[0] != WACOM_REPORT_DEVICE_LIST)
- return;
-
- memset(&remote_data, 0, sizeof(struct wacom_remote_data));
-
- for (i = 0; i < WACOM_MAX_REMOTES; i++) {
- int j = i * 6;
- int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4];
- bool connected = data[j+2];
-
- remote_data.remote[i].serial = serial;
- remote_data.remote[i].connected = connected;
- }
-
- spin_lock_irqsave(&remote->remote_lock, flags);
-
- ret = kfifo_in(&remote->remote_fifo, &remote_data, sizeof(remote_data));
- if (ret != sizeof(remote_data)) {
- spin_unlock_irqrestore(&remote->remote_lock, flags);
- hid_err(wacom->hdev, "Can't queue Remote status event.\n");
- return;
- }
-
- spin_unlock_irqrestore(&remote->remote_lock, flags);
-
- wacom_schedule_work(wacom_wac, WACOM_WORKER_REMOTE);
-}
-
static inline bool report_touch_events(struct wacom_wac *wacom)
{
return (touch_arbitration ? !wacom->shared->stylus_in_proximity : 1);
return 0;
}
+static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len)
+{
+ unsigned char *data = wacom_wac->data;
+ struct input_dev *input;
+ struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+ struct wacom_remote *remote = wacom->remote;
+ int bat_charging, bat_percent, touch_ring_mode;
+ __u32 serial;
+ int i, index = -1;
+ unsigned long flags;
+
+ if (data[0] != WACOM_REPORT_REMOTE) {
+ hid_dbg(wacom->hdev, "%s: received unknown report #%d",
+ __func__, data[0]);
+ return 0;
+ }
+
+ serial = data[3] + (data[4] << 8) + (data[5] << 16);
+ wacom_wac->id[0] = PAD_DEVICE_ID;
+
+ spin_lock_irqsave(&remote->remote_lock, flags);
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ if (remote->remotes[i].serial == serial) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index < 0 || !remote->remotes[index].registered)
+ goto out;
+
+ input = remote->remotes[index].input;
+
+ input_report_key(input, BTN_0, (data[9] & 0x01));
+ input_report_key(input, BTN_1, (data[9] & 0x02));
+ input_report_key(input, BTN_2, (data[9] & 0x04));
+ input_report_key(input, BTN_3, (data[9] & 0x08));
+ input_report_key(input, BTN_4, (data[9] & 0x10));
+ input_report_key(input, BTN_5, (data[9] & 0x20));
+ input_report_key(input, BTN_6, (data[9] & 0x40));
+ input_report_key(input, BTN_7, (data[9] & 0x80));
+
+ input_report_key(input, BTN_8, (data[10] & 0x01));
+ input_report_key(input, BTN_9, (data[10] & 0x02));
+ input_report_key(input, BTN_A, (data[10] & 0x04));
+ input_report_key(input, BTN_B, (data[10] & 0x08));
+ input_report_key(input, BTN_C, (data[10] & 0x10));
+ input_report_key(input, BTN_X, (data[10] & 0x20));
+ input_report_key(input, BTN_Y, (data[10] & 0x40));
+ input_report_key(input, BTN_Z, (data[10] & 0x80));
+
+ input_report_key(input, BTN_BASE, (data[11] & 0x01));
+ input_report_key(input, BTN_BASE2, (data[11] & 0x02));
+
+ if (data[12] & 0x80)
+ input_report_abs(input, ABS_WHEEL, (data[12] & 0x7f));
+ else
+ input_report_abs(input, ABS_WHEEL, 0);
+
+ bat_percent = data[7] & 0x7f;
+ bat_charging = !!(data[7] & 0x80);
+
+ if (data[9] | data[10] | (data[11] & 0x03) | data[12])
+ input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+ else
+ input_report_abs(input, ABS_MISC, 0);
+
+ input_event(input, EV_MSC, MSC_SERIAL, serial);
+
+ input_sync(input);
+
+ /*Which mode select (LED light) is currently on?*/
+ touch_ring_mode = (data[11] & 0xC0) >> 6;
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ if (remote->remotes[i].serial == serial)
+ wacom->led.groups[i].select = touch_ring_mode;
+ }
+
+ __wacom_notify_battery(&remote->remotes[index].battery, bat_percent,
+ bat_charging, 1, bat_charging);
+
+out:
+ spin_unlock_irqrestore(&remote->remote_lock, flags);
+ return 0;
+}
+
+static void wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len)
+{
+ struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+ unsigned char *data = wacom_wac->data;
+ struct wacom_remote *remote = wacom->remote;
+ struct wacom_remote_data remote_data;
+ unsigned long flags;
+ int i, ret;
+
+ if (data[0] != WACOM_REPORT_DEVICE_LIST)
+ return;
+
+ memset(&remote_data, 0, sizeof(struct wacom_remote_data));
+
+ for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+ int j = i * 6;
+ int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4];
+ bool connected = data[j+2];
+
+ remote_data.remote[i].serial = serial;
+ remote_data.remote[i].connected = connected;
+ }
+
+ spin_lock_irqsave(&remote->remote_lock, flags);
+
+ ret = kfifo_in(&remote->remote_fifo, &remote_data, sizeof(remote_data));
+ if (ret != sizeof(remote_data)) {
+ spin_unlock_irqrestore(&remote->remote_lock, flags);
+ hid_err(wacom->hdev, "Can't queue Remote status event.\n");
+ return;
+ }
+
+ spin_unlock_irqrestore(&remote->remote_lock, flags);
+
+ wacom_schedule_work(wacom_wac, WACOM_WORKER_REMOTE);
+}
+
static int int_dist(int x1, int y1, int x2, int y2)
{
int x = x2 - x1;
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
case WACOM_HID_WD_TOUCHONOFF:
+ case WACOM_HID_WD_MUTE_DEVICE:
/*
* This usage, which is used to mute touch events, comes
* from the pad packet, but is reported on the touch
wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
features->device_type |= WACOM_DEVICETYPE_PAD;
break;
+ case WACOM_HID_WD_BUTTONCONFIG:
+ wacom_map_usage(input, usage, field, EV_KEY, KEY_BUTTONCONFIG, 0);
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+ break;
+ case WACOM_HID_WD_ONSCREEN_KEYBOARD:
+ wacom_map_usage(input, usage, field, EV_KEY, KEY_ONSCREEN_KEYBOARD, 0);
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+ break;
+ case WACOM_HID_WD_CONTROLPANEL:
+ wacom_map_usage(input, usage, field, EV_KEY, KEY_CONTROLPANEL, 0);
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+ break;
+ case WACOM_HID_WD_MODE_CHANGE:
+ /* do not overwrite previous data */
+ if (!wacom_wac->has_mode_change) {
+ wacom_wac->has_mode_change = true;
+ wacom_wac->is_direct_mode = true;
+ }
+ features->device_type |= WACOM_DEVICETYPE_PAD;
+ break;
}
switch (equivalent_usage & 0xfffffff0) {
struct wacom_features *features = &wacom_wac->features;
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
int i;
+ bool is_touch_on = value;
/*
* Avoid reporting this event and setting inrange_state if this usage
* hasn't been mapped.
*/
- if (!usage->type)
+ if (!usage->type && equivalent_usage != WACOM_HID_WD_MODE_CHANGE)
return;
if (wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) {
input_event(input, usage->type, usage->code, 0);
break;
+ case WACOM_HID_WD_MUTE_DEVICE:
+ if (wacom_wac->shared->touch_input && value) {
+ wacom_wac->shared->is_touch_on = !wacom_wac->shared->is_touch_on;
+ is_touch_on = wacom_wac->shared->is_touch_on;
+ }
+
+ /* fall through*/
case WACOM_HID_WD_TOUCHONOFF:
if (wacom_wac->shared->touch_input) {
input_report_switch(wacom_wac->shared->touch_input,
- SW_MUTE_DEVICE, !value);
+ SW_MUTE_DEVICE, !is_touch_on);
input_sync(wacom_wac->shared->touch_input);
}
break;
+ case WACOM_HID_WD_MODE_CHANGE:
+ if (wacom_wac->is_direct_mode != value) {
+ wacom_wac->is_direct_mode = value;
+ wacom_schedule_work(&wacom->wacom_wac, WACOM_WORKER_MODE_CHANGE);
+ }
+ break;
+
case WACOM_HID_WD_BUTTONCENTER:
for (i = 0; i < wacom->led.count; i++)
wacom_update_led(wacom, features->numbered_buttons,
/* fall through*/
default:
input_event(input, usage->type, usage->code, value);
+ if (value)
+ wacom_wac->hid_data.pad_input_event_flag = true;
break;
}
}
bool active = wacom_wac->hid_data.inrange_state != 0;
/* report prox for expresskey events */
- if (wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) {
+ if ((wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) &&
+ wacom_wac->hid_data.pad_input_event_flag) {
input_event(input, EV_ABS, ABS_MISC, active ? PAD_DEVICE_ID : 0);
input_sync(input);
+ if (!active)
+ wacom_wac->hid_data.pad_input_event_flag = false;
}
}
input_set_capability(input, EV_KEY, BTN_TOOL_BRUSH);
input_set_capability(input, EV_KEY, BTN_TOOL_PENCIL);
input_set_capability(input, EV_KEY, BTN_TOOL_AIRBRUSH);
- input_set_capability(input, EV_KEY, BTN_TOOL_MOUSE);
- input_set_capability(input, EV_KEY, BTN_TOOL_LENS);
+ if (!(features->device_type & WACOM_DEVICETYPE_DIRECT)) {
+ input_set_capability(input, EV_KEY, BTN_TOOL_MOUSE);
+ input_set_capability(input, EV_KEY, BTN_TOOL_LENS);
+ }
break;
case WACOM_HID_WD_FINGERWHEEL:
wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
return;
case HID_DG_TOOLSERIALNUMBER:
wacom_wac->serial[0] = (wacom_wac->serial[0] & ~0xFFFFFFFFULL);
- wacom_wac->serial[0] |= value;
+ wacom_wac->serial[0] |= (__u32)value;
return;
case WACOM_HID_WD_SENSE:
wacom_wac->hid_data.sense_state = value;
wacom_wac->hid_data.cc_index = field->index;
wacom_wac->hid_data.cc_value_index = usage->usage_index;
break;
+ case HID_DG_CONTACTID:
+ if ((field->logical_maximum - field->logical_minimum) < touch_max) {
+ /*
+ * The HID descriptor for G11 sensors leaves logical
+ * maximum set to '1' despite it being a multitouch
+ * device. Override to a sensible number.
+ */
+ field->logical_maximum = 255;
+ }
+ break;
}
}
bool prox = hid_data->tipswitch &&
report_touch_events(wacom_wac);
+ if (wacom_wac->shared->has_mute_touch_switch &&
+ !wacom_wac->shared->is_touch_on) {
+ if (!wacom_wac->shared->touch_down)
+ return;
+ prox = 0;
+ }
+
wacom_wac->hid_data.num_received++;
if (wacom_wac->hid_data.num_received > wacom_wac->hid_data.num_expected)
return;
BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x301 =
{ "Wacom Bamboo One M", 21648, 13530, 1023, 31,
- BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x302 =
{ "Wacom Intuos PT S", 15200, 9500, 1023, 31,
INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16,
WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
static const struct wacom_features wacom_features_0x360 =
{ "Wacom Intuos Pro M", 44800, 29600, 8191, 63,
- INTUOSP2_BT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 9, .touch_max = 10 };
+ INTUOSP2_BT, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 10 };
static const struct wacom_features wacom_features_0x361 =
{ "Wacom Intuos Pro L", 62200, 43200, 8191, 63,
- INTUOSP2_BT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 9, .touch_max = 10 };
+ INTUOSP2_BT, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 10 };
static const struct wacom_features wacom_features_HID_ANY_ID =
{ "Wacom HID", .type = HID_GENERIC, .oVid = HID_ANY_ID, .oPid = HID_ANY_ID };
#define WACOM_HID_WD_BATTERY_LEVEL (WACOM_HID_UP_WACOMDIGITIZER | 0x043b)
#define WACOM_HID_WD_EXPRESSKEY00 (WACOM_HID_UP_WACOMDIGITIZER | 0x0910)
#define WACOM_HID_WD_EXPRESSKEYCAP00 (WACOM_HID_UP_WACOMDIGITIZER | 0x0950)
+#define WACOM_HID_WD_MODE_CHANGE (WACOM_HID_UP_WACOMDIGITIZER | 0x0980)
+#define WACOM_HID_WD_MUTE_DEVICE (WACOM_HID_UP_WACOMDIGITIZER | 0x0981)
+#define WACOM_HID_WD_CONTROLPANEL (WACOM_HID_UP_WACOMDIGITIZER | 0x0982)
+#define WACOM_HID_WD_ONSCREEN_KEYBOARD (WACOM_HID_UP_WACOMDIGITIZER | 0x0983)
+#define WACOM_HID_WD_BUTTONCONFIG (WACOM_HID_UP_WACOMDIGITIZER | 0x0986)
#define WACOM_HID_WD_BUTTONHOME (WACOM_HID_UP_WACOMDIGITIZER | 0x0990)
#define WACOM_HID_WD_BUTTONUP (WACOM_HID_UP_WACOMDIGITIZER | 0x0991)
#define WACOM_HID_WD_BUTTONDOWN (WACOM_HID_UP_WACOMDIGITIZER | 0x0992)
struct hid_device *pen;
struct hid_device *touch;
bool has_mute_touch_switch;
+ bool is_touch_on;
};
struct hid_data {
int bat_charging;
int bat_connected;
int ps_connected;
+ bool pad_input_event_flag;
};
struct wacom_remote_data {
int mode_value;
struct hid_data hid_data;
bool has_mute_touch_switch;
+ bool has_mode_change;
+ bool is_direct_mode;
+
};
#endif
#define HID_CP_APPLICATIONLAUNCHBUTTONS 0x000c0180
#define HID_CP_GENERICGUIAPPLICATIONCONTROLS 0x000c0200
+#define HID_DG_DEVICECONFIG 0x000d000e
+#define HID_DG_DEVICESETTINGS 0x000d0023
#define HID_DG_CONFIDENCE 0x000d0047
#define HID_DG_WIDTH 0x000d0048
#define HID_DG_HEIGHT 0x000d0049
#define HID_QUIRK_MULTI_INPUT 0x00000040
#define HID_QUIRK_HIDINPUT_FORCE 0x00000080
#define HID_QUIRK_NO_EMPTY_INPUT 0x00000100
-#define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200
+/* 0x00000200 reserved for backward compatibility, was NO_INIT_INPUT_REPORTS */
#define HID_QUIRK_ALWAYS_POLL 0x00000400
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID 0x00020000
struct list_head inputs; /* The list of inputs */
void *hiddev; /* The hiddev structure */
void *hidraw;
- int minor; /* Hiddev minor number */
int open; /* is the device open by anyone? */
char name[128]; /* Device name */
* In-kernel definitions.
*/
+struct hiddev {
+ int minor;
+ int exist;
+ int open;
+ struct mutex existancelock;
+ wait_queue_head_t wait;
+ struct hid_device *hid;
+ struct list_head list;
+ spinlock_t list_lock;
+ bool initialized;
+};
+
struct hid_device;
struct hid_usage;
struct hid_field;
#include <linux/types.h>
+struct regulator;
+
/**
* struct i2chid_platform_data - used by hid over i2c implementation.
* @hid_descriptor_address: i2c register where the HID descriptor is stored.
+ * @supply: regulator for powering on the device.
+ * @post_power_delay_ms: delay after powering on before device is usable.
*
* Note that it is the responsibility of the platform driver (or the acpi 5.0
* driver, or the flattened device tree) to setup the irq related to the gpio in
*/
struct i2c_hid_platform_data {
u16 hid_descriptor_address;
+ struct regulator *supply;
+ int post_power_delay_ms;
};
#endif /* __LINUX_I2C_HID_H */
* e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.)
*/
#define KEY_DATA 0x277
+#define KEY_ONSCREEN_KEYBOARD 0x278
#define BTN_TRIGGER_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0