Merge branches 'for-4.13/multitouch', 'for-4.13/retrode', 'for-4.13/transport-open-close-consolidation', 'for-4.13/upstream' and 'for-4.13/wacom' into for-linus

This commit is contained in:
Jiri Kosina 2017-07-10 11:11:25 +02:00
865 changed files with 8217 additions and 5245 deletions

View file

@ -748,6 +748,14 @@ config HID_PRIMAX
Support for Primax devices that are not fully compliant with the
HID standard.
config HID_RETRODE
tristate "Retrode"
depends on USB_HID
---help---
Support for
* Retrode 2 cartridge and controller adapter
config HID_ROCCAT
tristate "Roccat device support"
depends on USB_HID

View file

@ -82,6 +82,7 @@ hid-picolcd-$(CONFIG_DEBUG_FS) += hid-picolcd_debugfs.o
obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o
obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
obj-$(CONFIG_HID_RETRODE) += hid-retrode.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \

View file

@ -1070,7 +1070,7 @@ static s32 snto32(__u32 value, unsigned n)
case 16: return ((__s16)value);
case 32: return ((__s32)value);
}
return value & (1 << (n - 1)) ? value | (-1 << n) : value;
return value & (1 << (n - 1)) ? value | (~0U << n) : value;
}
s32 hid_snto32(__u32 value, unsigned n)
@ -1774,6 +1774,94 @@ void hid_disconnect(struct hid_device *hdev)
}
EXPORT_SYMBOL_GPL(hid_disconnect);
/**
* hid_hw_start - start underlying HW
* @hdev: hid device
* @connect_mask: which outputs to connect, see HID_CONNECT_*
*
* Call this in probe function *after* hid_parse. This will setup HW
* buffers and start the device (if not defeirred to device open).
* hid_hw_stop must be called if this was successful.
*/
int hid_hw_start(struct hid_device *hdev, unsigned int connect_mask)
{
int error;
error = hdev->ll_driver->start(hdev);
if (error)
return error;
if (connect_mask) {
error = hid_connect(hdev, connect_mask);
if (error) {
hdev->ll_driver->stop(hdev);
return error;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(hid_hw_start);
/**
* hid_hw_stop - stop underlying HW
* @hdev: hid device
*
* This is usually called from remove function or from probe when something
* failed and hid_hw_start was called already.
*/
void hid_hw_stop(struct hid_device *hdev)
{
hid_disconnect(hdev);
hdev->ll_driver->stop(hdev);
}
EXPORT_SYMBOL_GPL(hid_hw_stop);
/**
* hid_hw_open - signal underlying HW to start delivering events
* @hdev: hid device
*
* Tell underlying HW to start delivering events from the device.
* This function should be called sometime after successful call
* to hid_hiw_start().
*/
int hid_hw_open(struct hid_device *hdev)
{
int ret;
ret = mutex_lock_killable(&hdev->ll_open_lock);
if (ret)
return ret;
if (!hdev->ll_open_count++) {
ret = hdev->ll_driver->open(hdev);
if (ret)
hdev->ll_open_count--;
}
mutex_unlock(&hdev->ll_open_lock);
return ret;
}
EXPORT_SYMBOL_GPL(hid_hw_open);
/**
* hid_hw_close - signal underlaying HW to stop delivering events
*
* @hdev: hid device
*
* This function indicates that we are not interested in the events
* from this device anymore. Delivery of events may or may not stop,
* depending on the number of users still outstanding.
*/
void hid_hw_close(struct hid_device *hdev)
{
mutex_lock(&hdev->ll_open_lock);
if (!--hdev->ll_open_count)
hdev->ll_driver->close(hdev);
mutex_unlock(&hdev->ll_open_lock);
}
EXPORT_SYMBOL_GPL(hid_hw_close);
/*
* A list of devices for which there is a specialized driver on HID bus.
*
@ -2155,6 +2243,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
#if IS_ENABLED(CONFIG_HID_PRODIKEYS)
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) },
#endif
#if IS_ENABLED(CONFIG_HID_RETRODE)
{ HID_USB_DEVICE(USB_VENDOR_ID_FUTURE_TECHNOLOGY, USB_DEVICE_ID_RETRODE2) },
#endif
#if IS_ENABLED(CONFIG_HID_RMI)
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_COVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14) },
@ -2911,6 +3002,7 @@ struct hid_device *hid_allocate_device(void)
spin_lock_init(&hdev->debug_list_lock);
sema_init(&hdev->driver_lock, 1);
sema_init(&hdev->driver_input_lock, 1);
mutex_init(&hdev->ll_open_lock);
return hdev;
}

View file

@ -75,6 +75,8 @@
#define USB_VENDOR_ID_ALPS_JP 0x044E
#define HID_DEVICE_ID_ALPS_U1_DUAL 0x120B
#define HID_DEVICE_ID_ALPS_U1_DUAL_PTP 0x121F
#define HID_DEVICE_ID_ALPS_U1_DUAL_3BTN_PTP 0x1220
#define USB_VENDOR_ID_AMI 0x046b
#define USB_DEVICE_ID_AMI_VIRT_KEYBOARD_AND_MOUSE 0xff10
@ -386,6 +388,9 @@
#define USB_VENDOR_ID_FUTABA 0x0547
#define USB_DEVICE_ID_LED_DISPLAY 0x7000
#define USB_VENDOR_ID_FUTURE_TECHNOLOGY 0x0403
#define USB_DEVICE_ID_RETRODE2 0x97c1
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
@ -428,6 +433,9 @@
#define USB_VENDOR_ID_GOODTOUCH 0x1aad
#define USB_DEVICE_ID_GOODTOUCH_000f 0x000f
#define USB_VENDOR_ID_GOOGLE 0x18d1
#define USB_DEVICE_ID_GOOGLE_TOUCH_ROSE 0x5028
#define USB_VENDOR_ID_GOTOP 0x08f2
#define USB_DEVICE_ID_SUPER_Q2 0x007f
#define USB_DEVICE_ID_GOGOPEN 0x00ce

View file

@ -44,6 +44,7 @@
#include <linux/slab.h>
#include <linux/input/mt.h>
#include <linux/string.h>
#include <linux/timer.h>
MODULE_AUTHOR("Stephane Chatty <[email protected]>");
@ -54,28 +55,33 @@ MODULE_LICENSE("GPL");
#include "hid-ids.h"
/* quirks to control the device */
#define MT_QUIRK_NOT_SEEN_MEANS_UP (1 << 0)
#define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1)
#define MT_QUIRK_CYPRESS (1 << 2)
#define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3)
#define MT_QUIRK_ALWAYS_VALID (1 << 4)
#define MT_QUIRK_VALID_IS_INRANGE (1 << 5)
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6)
#define MT_QUIRK_CONFIDENCE (1 << 7)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8)
#define MT_QUIRK_NO_AREA (1 << 9)
#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_QUIRK_FIX_CONST_CONTACT_ID (1 << 14)
#define MT_QUIRK_TOUCH_SIZE_SCALING (1 << 15)
#define MT_QUIRK_NOT_SEEN_MEANS_UP BIT(0)
#define MT_QUIRK_SLOT_IS_CONTACTID BIT(1)
#define MT_QUIRK_CYPRESS BIT(2)
#define MT_QUIRK_SLOT_IS_CONTACTNUMBER BIT(3)
#define MT_QUIRK_ALWAYS_VALID BIT(4)
#define MT_QUIRK_VALID_IS_INRANGE BIT(5)
#define MT_QUIRK_VALID_IS_CONFIDENCE BIT(6)
#define MT_QUIRK_CONFIDENCE BIT(7)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE BIT(8)
#define MT_QUIRK_NO_AREA BIT(9)
#define MT_QUIRK_IGNORE_DUPLICATES BIT(10)
#define MT_QUIRK_HOVERING BIT(11)
#define MT_QUIRK_CONTACT_CNT_ACCURATE BIT(12)
#define MT_QUIRK_FORCE_GET_FEATURE BIT(13)
#define MT_QUIRK_FIX_CONST_CONTACT_ID BIT(14)
#define MT_QUIRK_TOUCH_SIZE_SCALING BIT(15)
#define MT_QUIRK_STICKY_FINGERS BIT(16)
#define MT_INPUTMODE_TOUCHSCREEN 0x02
#define MT_INPUTMODE_TOUCHPAD 0x03
#define MT_BUTTONTYPE_CLICKPAD 0
#define MT_IO_FLAGS_RUNNING 0
#define MT_IO_FLAGS_ACTIVE_SLOTS 1
#define MT_IO_FLAGS_PENDING_SLOTS 2
struct mt_slot {
__s32 x, y, cx, cy, p, w, h;
__s32 contactid; /* the device ContactID assigned to this slot */
@ -104,8 +110,10 @@ struct mt_fields {
struct mt_device {
struct mt_slot curdata; /* placeholder of incoming data */
struct mt_class mtclass; /* our mt device class */
struct timer_list release_timer; /* to release sticky fingers */
struct mt_fields *fields; /* temporary placeholder for storing the
multitouch fields */
unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_*) */
int cc_index; /* contact count field index in the report */
int cc_value_index; /* contact count value index in the field */
unsigned last_slot_field; /* the last field of a slot */
@ -148,6 +156,7 @@ static void mt_post_parse(struct mt_device *td);
/* reserved 0x0011 */
#define MT_CLS_WIN_8 0x0012
#define MT_CLS_EXPORT_ALL_INPUTS 0x0013
#define MT_CLS_WIN_8_DUAL 0x0014
/* vendor specific classes */
#define MT_CLS_3M 0x0101
@ -161,6 +170,7 @@ static void mt_post_parse(struct mt_device *td);
#define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109
#define MT_CLS_LG 0x010a
#define MT_CLS_VTL 0x0110
#define MT_CLS_GOOGLE 0x0111
#define MT_DEFAULT_MAXCONTACT 10
#define MT_MAX_MAXCONTACT 250
@ -212,11 +222,18 @@ static struct mt_class mt_classes[] = {
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_IGNORE_DUPLICATES |
MT_QUIRK_HOVERING |
MT_QUIRK_CONTACT_CNT_ACCURATE },
MT_QUIRK_CONTACT_CNT_ACCURATE |
MT_QUIRK_STICKY_FINGERS },
{ .name = MT_CLS_EXPORT_ALL_INPUTS,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_CONTACT_CNT_ACCURATE,
.export_all_inputs = true },
{ .name = MT_CLS_WIN_8_DUAL,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_IGNORE_DUPLICATES |
MT_QUIRK_HOVERING |
MT_QUIRK_CONTACT_CNT_ACCURATE,
.export_all_inputs = true },
/*
* vendor specific classes
@ -278,6 +295,12 @@ static struct mt_class mt_classes[] = {
MT_QUIRK_CONTACT_CNT_ACCURATE |
MT_QUIRK_FORCE_GET_FEATURE,
},
{ .name = MT_CLS_GOOGLE,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_CONTACT_CNT_ACCURATE |
MT_QUIRK_SLOT_IS_CONTACTID |
MT_QUIRK_HOVERING
},
{ }
};
@ -512,7 +535,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
mt_store_field(usage, td, hi);
return 1;
case HID_DG_CONFIDENCE:
if (cls->name == MT_CLS_WIN_8 &&
if ((cls->name == MT_CLS_WIN_8 ||
cls->name == MT_CLS_WIN_8_DUAL) &&
field->application == HID_DG_TOUCHPAD)
cls->quirks |= MT_QUIRK_CONFIDENCE;
mt_store_field(usage, td, hi);
@ -579,7 +603,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
* MS PTP spec says that external buttons left and right have
* usages 2 and 3.
*/
if (cls->name == MT_CLS_WIN_8 &&
if ((cls->name == MT_CLS_WIN_8 ||
cls->name == MT_CLS_WIN_8_DUAL) &&
field->application == HID_DG_TOUCHPAD &&
(usage->hid & HID_USAGE) > 1)
code--;
@ -682,6 +707,8 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p);
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
set_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags);
}
}
@ -697,6 +724,11 @@ static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
input_mt_sync_frame(input);
input_sync(input);
td->num_received = 0;
if (test_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags))
set_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags);
else
clear_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags);
clear_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags);
}
static int mt_touch_event(struct hid_device *hid, struct hid_field *field,
@ -788,6 +820,10 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
unsigned count;
int r, n;
/* sticky fingers release in progress, abort */
if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags))
return;
/*
* Includes multi-packet support where subsequent
* packets are sent with zero contactcount.
@ -813,6 +849,34 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
if (td->num_received >= td->num_expected)
mt_sync_frame(td, report->field[0]->hidinput->input);
/*
* Windows 8 specs says 2 things:
* - once a contact has been reported, it has to be reported in each
* subsequent report
* - the report rate when fingers are present has to be at least
* the refresh rate of the screen, 60 or 120 Hz
*
* I interprete this that the specification forces a report rate of
* at least 60 Hz for a touchscreen to be certified.
* Which means that if we do not get a report whithin 16 ms, either
* something wrong happens, either the touchscreen forgets to send
* a release. Taking a reasonable margin allows to remove issues
* with USB communication or the load of the machine.
*
* Given that Win 8 devices are forced to send a release, this will
* only affect laggish machines and the ones that have a firmware
* defect.
*/
if (td->mtclass.quirks & MT_QUIRK_STICKY_FINGERS) {
if (test_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags))
mod_timer(&td->release_timer,
jiffies + msecs_to_jiffies(100));
else
del_timer(&td->release_timer);
}
clear_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
}
static int mt_touch_input_configured(struct hid_device *hdev,
@ -1124,6 +1188,47 @@ static void mt_fix_const_fields(struct hid_device *hdev, unsigned int usage)
}
}
static void mt_release_contacts(struct hid_device *hid)
{
struct hid_input *hidinput;
struct mt_device *td = hid_get_drvdata(hid);
list_for_each_entry(hidinput, &hid->inputs, list) {
struct input_dev *input_dev = hidinput->input;
struct input_mt *mt = input_dev->mt;
int i;
if (mt) {
for (i = 0; i < mt->num_slots; i++) {
input_mt_slot(input_dev, i);
input_mt_report_slot_state(input_dev,
MT_TOOL_FINGER,
false);
}
input_mt_sync_frame(input_dev);
input_sync(input_dev);
}
}
td->num_received = 0;
}
static void mt_expired_timeout(unsigned long arg)
{
struct hid_device *hdev = (void *)arg;
struct mt_device *td = hid_get_drvdata(hdev);
/*
* An input report came in just before we release the sticky fingers,
* it will take care of the sticky fingers.
*/
if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags))
return;
if (test_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags))
mt_release_contacts(hdev);
clear_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
}
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret, i;
@ -1193,6 +1298,8 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
*/
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
setup_timer(&td->release_timer, mt_expired_timeout, (long)hdev);
ret = hid_parse(hdev);
if (ret != 0)
return ret;
@ -1220,28 +1327,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
#ifdef CONFIG_PM
static void mt_release_contacts(struct hid_device *hid)
{
struct hid_input *hidinput;
list_for_each_entry(hidinput, &hid->inputs, list) {
struct input_dev *input_dev = hidinput->input;
struct input_mt *mt = input_dev->mt;
int i;
if (mt) {
for (i = 0; i < mt->num_slots; i++) {
input_mt_slot(input_dev, i);
input_mt_report_slot_state(input_dev,
MT_TOOL_FINGER,
false);
}
input_mt_sync_frame(input_dev);
input_sync(input_dev);
}
}
}
static int mt_reset_resume(struct hid_device *hdev)
{
mt_release_contacts(hdev);
@ -1266,6 +1351,8 @@ static void mt_remove(struct hid_device *hdev)
{
struct mt_device *td = hid_get_drvdata(hdev);
del_timer_sync(&td->release_timer);
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
hid_hw_stop(hdev);
hdev->quirks = td->initial_quirks;
@ -1290,6 +1377,16 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_3M,
USB_DEVICE_ID_3M3266) },
/* Alps devices */
{ .driver_data = MT_CLS_WIN_8_DUAL,
HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
USB_VENDOR_ID_ALPS_JP,
HID_DEVICE_ID_ALPS_U1_DUAL_PTP) },
{ .driver_data = MT_CLS_WIN_8_DUAL,
HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
USB_VENDOR_ID_ALPS_JP,
HID_DEVICE_ID_ALPS_U1_DUAL_3BTN_PTP) },
/* Anton devices */
{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
MT_USB_DEVICE(USB_VENDOR_ID_ANTON,
@ -1569,6 +1666,11 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_CSR2) },
/* Google MT devices */
{ .driver_data = MT_CLS_GOOGLE,
HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE,
USB_DEVICE_ID_GOOGLE_TOUCH_ROSE) },
/* Generic MT device */
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) },

100
drivers/hid/hid-retrode.c Normal file
View file

@ -0,0 +1,100 @@
/*
* HID driver for Retrode 2 controller adapter and plug-in extensions
*
* Copyright (c) 2017 Bastien Nocera <hadess@hadess.net>
*/
/*
* 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/input.h>
#include <linux/slab.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
#define CONTROLLER_NAME_BASE "Retrode"
static int retrode_input_configured(struct hid_device *hdev,
struct hid_input *hi)
{
struct hid_field *field = hi->report->field[0];
const char *suffix;
int number = 0;
char *name;
switch (field->report->id) {
case 0:
suffix = "SNES Mouse";
break;
case 1:
case 2:
suffix = "SNES / N64";
number = field->report->id;
break;
case 3:
case 4:
suffix = "Mega Drive";
number = field->report->id - 2;
break;
default:
hid_err(hdev, "Got unhandled report id %d\n", field->report->id);
suffix = "Unknown";
}
if (number)
name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
"%s %s #%d", CONTROLLER_NAME_BASE,
suffix, number);
else
name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
"%s %s", CONTROLLER_NAME_BASE, suffix);
if (!name)
return -ENOMEM;
hi->input->name = name;
return 0;
}
static int retrode_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int ret;
/* Has no effect on the mouse device */
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
ret = hid_parse(hdev);
if (ret)
return ret;
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret)
return ret;
return 0;
}
static const struct hid_device_id retrode_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_FUTURE_TECHNOLOGY, USB_DEVICE_ID_RETRODE2) },
{ }
};
MODULE_DEVICE_TABLE(hid, retrode_devices);
static struct hid_driver retrode_driver = {
.name = "hid-retrode",
.id_table = retrode_devices,
.input_configured = retrode_input_configured,
.probe = retrode_probe,
};
module_hid_driver(retrode_driver);
MODULE_LICENSE("GPL");

View file

@ -40,7 +40,7 @@
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/i2c/i2c-hid.h>
#include <linux/platform_data/i2c-hid.h>
#include "../hid-ids.h"
@ -743,18 +743,12 @@ static int i2c_hid_open(struct hid_device *hid)
struct i2c_hid *ihid = i2c_get_clientdata(client);
int ret = 0;
mutex_lock(&i2c_hid_open_mut);
if (!hid->open++) {
ret = pm_runtime_get_sync(&client->dev);
if (ret < 0) {
hid->open--;
goto done;
}
set_bit(I2C_HID_STARTED, &ihid->flags);
}
done:
mutex_unlock(&i2c_hid_open_mut);
return ret < 0 ? ret : 0;
ret = pm_runtime_get_sync(&client->dev);
if (ret < 0)
return ret;
set_bit(I2C_HID_STARTED, &ihid->flags);
return 0;
}
static void i2c_hid_close(struct hid_device *hid)
@ -762,18 +756,10 @@ static void i2c_hid_close(struct hid_device *hid)
struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client);
/* protecting hid->open to make sure we don't restart
* data acquistion due to a resumption we no longer
* care about
*/
mutex_lock(&i2c_hid_open_mut);
if (!--hid->open) {
clear_bit(I2C_HID_STARTED, &ihid->flags);
clear_bit(I2C_HID_STARTED, &ihid->flags);
/* Save some power */
pm_runtime_put(&client->dev);
}
mutex_unlock(&i2c_hid_open_mut);
/* Save some power */
pm_runtime_put(&client->dev);
}
static int i2c_hid_power(struct hid_device *hid, int lvl)

View file

@ -70,8 +70,6 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
/*
* Input submission and I/O error handler.
*/
static DEFINE_MUTEX(hid_open_mut);
static void hid_io_error(struct hid_device *hid);
static int hid_submit_out(struct hid_device *hid);
static int hid_submit_ctrl(struct hid_device *hid);
@ -85,10 +83,10 @@ static int hid_start_in(struct hid_device *hid)
struct usbhid_device *usbhid = hid->driver_data;
spin_lock_irqsave(&usbhid->lock, flags);
if ((hid->open > 0 || hid->quirks & HID_QUIRK_ALWAYS_POLL) &&
!test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
!test_bit(HID_SUSPENDED, &usbhid->iofl) &&
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
if (test_bit(HID_IN_POLLING, &usbhid->iofl) &&
!test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
!test_bit(HID_SUSPENDED, &usbhid->iofl) &&
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
if (rc != 0) {
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
@ -272,13 +270,13 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
static void hid_irq_in(struct urb *urb)
{
struct hid_device *hid = urb->context;
struct usbhid_device *usbhid = hid->driver_data;
struct usbhid_device *usbhid = hid->driver_data;
int status;
switch (urb->status) {
case 0: /* success */
usbhid->retry_delay = 0;
if ((hid->quirks & HID_QUIRK_ALWAYS_POLL) && !hid->open)
if (!test_bit(HID_OPENED, &usbhid->iofl))
break;
usbhid_mark_busy(usbhid);
if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
@ -677,73 +675,74 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
return result;
}
int usbhid_open(struct hid_device *hid)
static int usbhid_open(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
int res = 0;
int res;
mutex_lock(&hid_open_mut);
if (!hid->open++) {
res = usb_autopm_get_interface(usbhid->intf);
/* the device must be awake to reliably request remote wakeup */
if (res < 0) {
hid->open--;
res = -EIO;
goto done;
}
usbhid->intf->needs_remote_wakeup = 1;
set_bit(HID_RESUME_RUNNING, &usbhid->iofl);
res = hid_start_in(hid);
if (res) {
if (res != -ENOSPC) {
hid_io_error(hid);
res = 0;
} else {
/* no use opening if resources are insufficient */
hid->open--;
res = -EBUSY;
usbhid->intf->needs_remote_wakeup = 0;
}
}
usb_autopm_put_interface(usbhid->intf);
if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
return 0;
/*
* In case events are generated while nobody was listening,
* some are released when the device is re-opened.
* Wait 50 msec for the queue to empty before allowing events
* to go through hid.
*/
if (res == 0 && !(hid->quirks & HID_QUIRK_ALWAYS_POLL))
msleep(50);
clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
res = usb_autopm_get_interface(usbhid->intf);
/* the device must be awake to reliably request remote wakeup */
if (res < 0)
return -EIO;
usbhid->intf->needs_remote_wakeup = 1;
set_bit(HID_RESUME_RUNNING, &usbhid->iofl);
set_bit(HID_OPENED, &usbhid->iofl);
set_bit(HID_IN_POLLING, &usbhid->iofl);
res = hid_start_in(hid);
if (res) {
if (res != -ENOSPC) {
hid_io_error(hid);
res = 0;
} else {
/* no use opening if resources are insufficient */
res = -EBUSY;
clear_bit(HID_OPENED, &usbhid->iofl);
clear_bit(HID_IN_POLLING, &usbhid->iofl);
usbhid->intf->needs_remote_wakeup = 0;
}
}
done:
mutex_unlock(&hid_open_mut);
usb_autopm_put_interface(usbhid->intf);
/*
* In case events are generated while nobody was listening,
* some are released when the device is re-opened.
* Wait 50 msec for the queue to empty before allowing events
* to go through hid.
*/
if (res == 0)
msleep(50);
clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
return res;
}
void usbhid_close(struct hid_device *hid)
static void usbhid_close(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
mutex_lock(&hid_open_mut);
if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
return;
/* protecting hid->open to make sure we don't restart
* data acquistion due to a resumption we no longer
* care about
/*
* Make sure we don't restart data acquisition due to
* a resumption we no longer care about by avoiding racing
* with hid_start_in().
*/
spin_lock_irq(&usbhid->lock);
if (!--hid->open) {
spin_unlock_irq(&usbhid->lock);
hid_cancel_delayed_stuff(usbhid);
if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) {
usb_kill_urb(usbhid->urbin);
usbhid->intf->needs_remote_wakeup = 0;
}
} else {
spin_unlock_irq(&usbhid->lock);
}
mutex_unlock(&hid_open_mut);
clear_bit(HID_IN_POLLING, &usbhid->iofl);
clear_bit(HID_OPENED, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
hid_cancel_delayed_stuff(usbhid);
usb_kill_urb(usbhid->urbin);
usbhid->intf->needs_remote_wakeup = 0;
}
/*
@ -1135,6 +1134,7 @@ static int usbhid_start(struct hid_device *hid)
ret = usb_autopm_get_interface(usbhid->intf);
if (ret)
goto fail;
set_bit(HID_IN_POLLING, &usbhid->iofl);
usbhid->intf->needs_remote_wakeup = 1;
ret = hid_start_in(hid);
if (ret) {
@ -1176,8 +1176,10 @@ static void usbhid_stop(struct hid_device *hid)
if (WARN_ON(!usbhid))
return;
if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
clear_bit(HID_IN_POLLING, &usbhid->iofl);
usbhid->intf->needs_remote_wakeup = 0;
}
clear_bit(HID_STARTED, &usbhid->iofl);
spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */
@ -1203,16 +1205,19 @@ static void usbhid_stop(struct hid_device *hid)
static int usbhid_power(struct hid_device *hid, int lvl)
{
struct usbhid_device *usbhid = hid->driver_data;
int r = 0;
switch (lvl) {
case PM_HINT_FULLON:
r = usbhid_get_power(hid);
r = usb_autopm_get_interface(usbhid->intf);
break;
case PM_HINT_NORMAL:
usbhid_put_power(hid);
usb_autopm_put_interface(usbhid->intf);
break;
}
return r;
}
@ -1492,21 +1497,6 @@ static int hid_post_reset(struct usb_interface *intf)
return 0;
}
int usbhid_get_power(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
return usb_autopm_get_interface(usbhid->intf);
}
void usbhid_put_power(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
usb_autopm_put_interface(usbhid->intf);
}
#ifdef CONFIG_PM
static int hid_resume_common(struct hid_device *hid, bool driver_suspended)
{

View file

@ -237,8 +237,8 @@ static int hiddev_release(struct inode * inode, struct file * file)
mutex_lock(&list->hiddev->existancelock);
if (!--list->hiddev->open) {
if (list->hiddev->exist) {
usbhid_close(list->hiddev->hid);
usbhid_put_power(list->hiddev->hid);
hid_hw_close(list->hiddev->hid);
hid_hw_power(list->hiddev->hid, PM_HINT_NORMAL);
} else {
mutex_unlock(&list->hiddev->existancelock);
kfree(list->hiddev);
@ -282,11 +282,9 @@ static int hiddev_open(struct inode *inode, struct file *file)
*/
if (list->hiddev->exist) {
if (!list->hiddev->open++) {
res = usbhid_open(hiddev->hid);
if (res < 0) {
res = -EIO;
res = hid_hw_open(hiddev->hid);
if (res < 0)
goto bail;
}
}
} else {
res = -ENODEV;
@ -301,15 +299,17 @@ static int hiddev_open(struct inode *inode, struct file *file)
if (!list->hiddev->open++)
if (list->hiddev->exist) {
struct hid_device *hid = hiddev->hid;
res = usbhid_get_power(hid);
if (res < 0) {
res = -EIO;
res = hid_hw_power(hid, PM_HINT_FULLON);
if (res < 0)
goto bail_unlock;
}
usbhid_open(hid);
res = hid_hw_open(hid);
if (res < 0)
goto bail_normal_power;
}
mutex_unlock(&hiddev->existancelock);
return 0;
bail_normal_power:
hid_hw_power(hid, PM_HINT_NORMAL);
bail_unlock:
mutex_unlock(&hiddev->existancelock);
bail:
@ -935,7 +935,7 @@ void hiddev_disconnect(struct hid_device *hid)
if (hiddev->open) {
mutex_unlock(&hiddev->existancelock);
usbhid_close(hiddev->hid);
hid_hw_close(hiddev->hid);
wake_up_interruptible(&hiddev->wait);
} else {
mutex_unlock(&hiddev->existancelock);

View file

@ -34,11 +34,7 @@
#include <linux/input.h>
/* API provided by hid-core.c for USB HID drivers */
void usbhid_close(struct hid_device *hid);
int usbhid_open(struct hid_device *hid);
void usbhid_init_reports(struct hid_device *hid);
int usbhid_get_power(struct hid_device *hid);
void usbhid_put_power(struct hid_device *hid);
struct usb_interface *usbhid_find_interface(int minor);
/* iofl flags */
@ -53,6 +49,17 @@ struct usb_interface *usbhid_find_interface(int minor);
#define HID_KEYS_PRESSED 10
#define HID_NO_BANDWIDTH 11
#define HID_RESUME_RUNNING 12
/*
* The device is opened, meaning there is a client that is interested
* in data coming from the device.
*/
#define HID_OPENED 13
/*
* We are polling input endpoint by [re]submitting IN URB, because
* either HID device is opened or ALWAYS POLL quirk is set for the
* device.
*/
#define HID_IN_POLLING 14
/*
* USB-specific HID struct, to be pointed to

View file

@ -138,6 +138,7 @@ struct wacom_battery {
struct power_supply_desc bat_desc;
struct power_supply *battery;
char bat_name[WACOM_NAME_MAX];
int bat_status;
int battery_capacity;
int bat_charging;
int bat_connected;

View file

@ -1547,7 +1547,9 @@ static int wacom_battery_get_property(struct power_supply *psy,
val->intval = battery->battery_capacity;
break;
case POWER_SUPPLY_PROP_STATUS:
if (battery->bat_charging)
if (battery->bat_status != WACOM_POWER_SUPPLY_STATUS_AUTO)
val->intval = battery->bat_status;
else if (battery->bat_charging)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else if (battery->battery_capacity == 100 &&
battery->ps_connected)

View file

@ -57,15 +57,18 @@ static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 };
static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 };
static void __wacom_notify_battery(struct wacom_battery *battery,
int bat_capacity, bool bat_charging,
bool bat_connected, bool ps_connected)
int bat_status, int bat_capacity,
bool bat_charging, bool bat_connected,
bool ps_connected)
{
bool changed = battery->battery_capacity != bat_capacity ||
bool changed = battery->bat_status != bat_status ||
battery->battery_capacity != bat_capacity ||
battery->bat_charging != bat_charging ||
battery->bat_connected != bat_connected ||
battery->ps_connected != ps_connected;
if (changed) {
battery->bat_status = bat_status;
battery->battery_capacity = bat_capacity;
battery->bat_charging = bat_charging;
battery->bat_connected = bat_connected;
@ -77,13 +80,13 @@ static void __wacom_notify_battery(struct wacom_battery *battery,
}
static void wacom_notify_battery(struct wacom_wac *wacom_wac,
int bat_capacity, bool bat_charging, bool bat_connected,
bool ps_connected)
int bat_status, int bat_capacity, bool bat_charging,
bool bat_connected, bool ps_connected)
{
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
__wacom_notify_battery(&wacom->battery, bat_capacity, bat_charging,
bat_connected, ps_connected);
__wacom_notify_battery(&wacom->battery, bat_status, bat_capacity,
bat_charging, bat_connected, ps_connected);
}
static int wacom_penpartner_irq(struct wacom_wac *wacom)
@ -448,8 +451,9 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
rw = (data[7] >> 2 & 0x07);
battery_capacity = batcap_gr[rw];
ps_connected = rw == 7;
wacom_notify_battery(wacom, battery_capacity, ps_connected,
1, ps_connected);
wacom_notify_battery(wacom, WACOM_POWER_SUPPLY_STATUS_AUTO,
battery_capacity, ps_connected, 1,
ps_connected);
}
exit:
return retval;
@ -1071,7 +1075,8 @@ static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len)
wacom->led.groups[i].select = touch_ring_mode;
}
__wacom_notify_battery(&remote->remotes[index].battery, bat_percent,
__wacom_notify_battery(&remote->remotes[index].battery,
WACOM_POWER_SUPPLY_STATUS_AUTO, bat_percent,
bat_charging, 1, bat_charging);
out:
@ -1157,7 +1162,8 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
bat_charging = (power_raw & 0x08) ? 1 : 0;
ps_connected = (power_raw & 0x10) ? 1 : 0;
battery_capacity = batcap_i4[power_raw & 0x07];
wacom_notify_battery(wacom, battery_capacity, bat_charging,
wacom_notify_battery(wacom, WACOM_POWER_SUPPLY_STATUS_AUTO,
battery_capacity, bat_charging,
battery_capacity || bat_charging,
ps_connected);
break;
@ -1334,7 +1340,8 @@ static void wacom_intuos_pro2_bt_battery(struct wacom_wac *wacom)
bool chg = data[284] & 0x80;
int battery_status = data[284] & 0x7F;
wacom_notify_battery(wacom, battery_status, chg, 1, chg);
wacom_notify_battery(wacom, WACOM_POWER_SUPPLY_STATUS_AUTO,
battery_status, chg, 1, chg);
}
static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len)
@ -1696,6 +1703,82 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
}
}
static void wacom_wac_battery_usage_mapping(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features;
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
switch (equivalent_usage) {
case HID_DG_BATTERYSTRENGTH:
case WACOM_HID_WD_BATTERY_LEVEL:
case WACOM_HID_WD_BATTERY_CHARGING:
features->quirks |= WACOM_QUIRK_BATTERY;
break;
}
}
static void wacom_wac_battery_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
switch (equivalent_usage) {
case HID_DG_BATTERYSTRENGTH:
if (value == 0) {
wacom_wac->hid_data.bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
}
else {
value = value * 100 / (field->logical_maximum - field->logical_minimum);
wacom_wac->hid_data.battery_capacity = value;
wacom_wac->hid_data.bat_connected = 1;
wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO;
}
break;
case WACOM_HID_WD_BATTERY_LEVEL:
value = value * 100 / (field->logical_maximum - field->logical_minimum);
wacom_wac->hid_data.battery_capacity = value;
wacom_wac->hid_data.bat_connected = 1;
wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO;
break;
case WACOM_HID_WD_BATTERY_CHARGING:
wacom_wac->hid_data.bat_charging = value;
wacom_wac->hid_data.ps_connected = value;
wacom_wac->hid_data.bat_connected = 1;
wacom_wac->hid_data.bat_status = WACOM_POWER_SUPPLY_STATUS_AUTO;
break;
}
}
static void wacom_wac_battery_pre_report(struct hid_device *hdev,
struct hid_report *report)
{
return;
}
static void wacom_wac_battery_report(struct hid_device *hdev,
struct hid_report *report)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features;
if (features->quirks & WACOM_QUIRK_BATTERY) {
int status = wacom_wac->hid_data.bat_status;
int capacity = wacom_wac->hid_data.battery_capacity;
bool charging = wacom_wac->hid_data.bat_charging;
bool connected = wacom_wac->hid_data.bat_connected;
bool powered = wacom_wac->hid_data.ps_connected;
wacom_notify_battery(wacom_wac, status, capacity, charging,
connected, powered);
}
}
static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage)
{
@ -1706,10 +1789,6 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
switch (equivalent_usage) {
case WACOM_HID_WD_BATTERY_LEVEL:
case WACOM_HID_WD_BATTERY_CHARGING:
features->quirks |= WACOM_QUIRK_BATTERY;
break;
case WACOM_HID_WD_ACCELEROMETER_X:
__set_bit(INPUT_PROP_ACCELEROMETER, input->propbit);
wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 0);
@ -1803,27 +1882,6 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
}
}
static void wacom_wac_pad_battery_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
switch (equivalent_usage) {
case WACOM_HID_WD_BATTERY_LEVEL:
wacom_wac->hid_data.battery_capacity = value;
wacom_wac->hid_data.bat_connected = 1;
break;
case WACOM_HID_WD_BATTERY_CHARGING:
wacom_wac->hid_data.bat_charging = value;
wacom_wac->hid_data.ps_connected = value;
wacom_wac->hid_data.bat_connected = 1;
break;
}
}
static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
@ -1897,24 +1955,6 @@ static void wacom_wac_pad_pre_report(struct hid_device *hdev,
wacom_wac->hid_data.inrange_state = 0;
}
static void wacom_wac_pad_battery_report(struct hid_device *hdev,
struct hid_report *report)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features;
if (features->quirks & WACOM_QUIRK_BATTERY) {
int capacity = wacom_wac->hid_data.battery_capacity;
bool charging = wacom_wac->hid_data.bat_charging;
bool connected = wacom_wac->hid_data.bat_connected;
bool powered = wacom_wac->hid_data.ps_connected;
wacom_notify_battery(wacom_wac, capacity, charging,
connected, powered);
}
}
static void wacom_wac_pad_report(struct hid_device *hdev,
struct hid_report *report)
{
@ -1960,9 +2000,6 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
case HID_DG_INRANGE:
wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
break;
case HID_DG_BATTERYSTRENGTH:
features->quirks |= WACOM_QUIRK_BATTERY;
break;
case HID_DG_INVERT:
wacom_map_usage(input, usage, field, EV_KEY,
BTN_TOOL_RUBBER, 0);
@ -2035,10 +2072,6 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
if (!(features->quirks & WACOM_QUIRK_SENSE))
wacom_wac->hid_data.sense_state = value;
return;
case HID_DG_BATTERYSTRENGTH:
wacom_wac->hid_data.battery_capacity = value;
wacom_wac->hid_data.bat_connected = 1;
break;
case HID_DG_INVERT:
wacom_wac->hid_data.invert_state = value;
return;
@ -2077,28 +2110,28 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
return;
case WACOM_HID_WD_OFFSETLEFT:
if (features->offset_left && value != features->offset_left)
hid_warn(hdev, "%s: overriding exising left offset "
hid_warn(hdev, "%s: overriding existing left offset "
"%d -> %d\n", __func__, value,
features->offset_left);
features->offset_left = value;
return;
case WACOM_HID_WD_OFFSETRIGHT:
if (features->offset_right && value != features->offset_right)
hid_warn(hdev, "%s: overriding exising right offset "
hid_warn(hdev, "%s: overriding existing right offset "
"%d -> %d\n", __func__, value,
features->offset_right);
features->offset_right = value;
return;
case WACOM_HID_WD_OFFSETTOP:
if (features->offset_top && value != features->offset_top)
hid_warn(hdev, "%s: overriding exising top offset "
hid_warn(hdev, "%s: overriding existing top offset "
"%d -> %d\n", __func__, value,
features->offset_top);
features->offset_top = value;
return;
case WACOM_HID_WD_OFFSETBOTTOM:
if (features->offset_bottom && value != features->offset_bottom)
hid_warn(hdev, "%s: overriding exising bottom offset "
hid_warn(hdev, "%s: overriding existing bottom offset "
"%d -> %d\n", __func__, value,
features->offset_bottom);
features->offset_bottom = value;
@ -2395,7 +2428,10 @@ void wacom_wac_usage_mapping(struct hid_device *hdev,
if (WACOM_DIRECT_DEVICE(field))
features->device_type |= WACOM_DEVICETYPE_DIRECT;
if (WACOM_PAD_FIELD(field))
/* usage tests must precede field tests */
if (WACOM_BATTERY_USAGE(usage))
wacom_wac_battery_usage_mapping(hdev, field, usage);
else if (WACOM_PAD_FIELD(field))
wacom_wac_pad_usage_mapping(hdev, field, usage);
else if (WACOM_PEN_FIELD(field))
wacom_wac_pen_usage_mapping(hdev, field, usage);
@ -2414,11 +2450,12 @@ void wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
if (value > field->logical_maximum || value < field->logical_minimum)
return;
if (WACOM_PAD_FIELD(field)) {
wacom_wac_pad_battery_event(hdev, field, usage, value);
if (wacom->wacom_wac.pad_input)
wacom_wac_pad_event(hdev, field, usage, value);
} else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input)
/* usage tests must precede field tests */
if (WACOM_BATTERY_USAGE(usage))
wacom_wac_battery_event(hdev, field, usage, value);
else if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input)
wacom_wac_pad_event(hdev, field, usage, value);
else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input)
wacom_wac_pen_event(hdev, field, usage, value);
else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input)
wacom_wac_finger_event(hdev, field, usage, value);
@ -2452,6 +2489,8 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
if (wacom_wac->features.type != HID_GENERIC)
return;
wacom_wac_battery_pre_report(hdev, report);
if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input)
wacom_wac_pad_pre_report(hdev, report);
else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input)
@ -2471,11 +2510,11 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
if (report->type != HID_INPUT_REPORT)
return;
if (WACOM_PAD_FIELD(field)) {
wacom_wac_pad_battery_report(hdev, report);
if (wacom->wacom_wac.pad_input)
wacom_wac_pad_report(hdev, report);
} else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input)
wacom_wac_battery_report(hdev, report);
if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input)
wacom_wac_pad_report(hdev, report);
else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input)
wacom_wac_pen_report(hdev, report);
else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input)
wacom_wac_finger_report(hdev, report);
@ -2813,13 +2852,14 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
wacom_schedule_work(wacom, WACOM_WORKER_WIRELESS);
}
wacom_notify_battery(wacom, battery, charging, 1, 0);
wacom_notify_battery(wacom, WACOM_POWER_SUPPLY_STATUS_AUTO,
battery, charging, 1, 0);
} else if (wacom->pid != 0) {
/* disconnected while previously connected */
wacom->pid = 0;
wacom_schedule_work(wacom, WACOM_WORKER_WIRELESS);
wacom_notify_battery(wacom, 0, 0, 0, 0);
wacom_notify_battery(wacom, POWER_SUPPLY_STATUS_UNKNOWN, 0, 0, 0, 0);
}
return 0;
@ -2847,8 +2887,8 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len)
int battery = (data[8] & 0x3f) * 100 / 31;
bool charging = !!(data[8] & 0x80);
wacom_notify_battery(wacom_wac, battery, charging,
battery || charging, 1);
wacom_notify_battery(wacom_wac, WACOM_POWER_SUPPLY_STATUS_AUTO,
battery, charging, battery || charging, 1);
if (!wacom->battery.battery &&
!(features->quirks & WACOM_QUIRK_BATTERY)) {
@ -2860,7 +2900,7 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len)
wacom->battery.battery) {
features->quirks &= ~WACOM_QUIRK_BATTERY;
wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY);
wacom_notify_battery(wacom_wac, 0, 0, 0, 0);
wacom_notify_battery(wacom_wac, POWER_SUPPLY_STATUS_UNKNOWN, 0, 0, 0, 0);
}
return 0;
}

View file

@ -96,6 +96,8 @@
#define WACOM_DEVICETYPE_WL_MONITOR 0x0008
#define WACOM_DEVICETYPE_DIRECT 0x0010
#define WACOM_POWER_SUPPLY_STATUS_AUTO -1
#define WACOM_HID_UP_WACOMDIGITIZER 0xff0d0000
#define WACOM_HID_SP_PAD 0x00040000
#define WACOM_HID_SP_BUTTON 0x00090000
@ -151,6 +153,10 @@
#define WACOM_HID_WT_X (WACOM_HID_UP_WACOMTOUCH | 0x130)
#define WACOM_HID_WT_Y (WACOM_HID_UP_WACOMTOUCH | 0x131)
#define WACOM_BATTERY_USAGE(f) (((f)->hid == HID_DG_BATTERYSTRENGTH) || \
((f)->hid == WACOM_HID_WD_BATTERY_CHARGING) || \
((f)->hid == WACOM_HID_WD_BATTERY_LEVEL))
#define WACOM_PAD_FIELD(f) (((f)->physical == HID_DG_TABLETFUNCTIONKEY) || \
((f)->physical == WACOM_HID_WD_DIGITIZERFNKEYS) || \
((f)->physical == WACOM_HID_WD_DIGITIZERINFO))
@ -297,6 +303,7 @@ struct hid_data {
int last_slot_field;
int num_expected;
int num_received;
int bat_status;
int battery_capacity;
int bat_charging;
int bat_connected;