Commit 0b43615a authored by Jiri Kosina's avatar Jiri Kosina
Browse files

Merge branch 'for-6.8/wacom' into for-linus

- functional fix for handling Confidence in Wacom driver (Jason Gerecke)
- power management fix for Wacom userspace battery exporting (Tatsunosuke Tobita)

Conflicts:
	tools/testing/selftests/hid/tests/test_wacom_generic.py
parents 53eb9356 b0fb904d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ struct wacom {
	struct work_struct battery_work;
	struct work_struct remote_work;
	struct delayed_work init_work;
	struct delayed_work aes_battery_work;
	struct wacom_remote *remote;
	struct work_struct mode_change_work;
	struct timer_list idleprox_timer;
+8 −0
Original line number Diff line number Diff line
@@ -1813,6 +1813,13 @@ static void wacom_destroy_battery(struct wacom *wacom)
	}
}

static void wacom_aes_battery_handler(struct work_struct *work)
{
	struct wacom *wacom = container_of(work, struct wacom, aes_battery_work.work);

	wacom_destroy_battery(wacom);
}

static ssize_t wacom_show_speed(struct device *dev,
				struct device_attribute
				*attr, char *buf)
@@ -2794,6 +2801,7 @@ static int wacom_probe(struct hid_device *hdev,

	mutex_init(&wacom->lock);
	INIT_DELAYED_WORK(&wacom->init_work, wacom_init_work);
	INIT_DELAYED_WORK(&wacom->aes_battery_work, wacom_aes_battery_handler);
	INIT_WORK(&wacom->wireless_work, wacom_wireless_work);
	INIT_WORK(&wacom->battery_work, wacom_battery_work);
	INIT_WORK(&wacom->remote_work, wacom_remote_work);
+15 −29
Original line number Diff line number Diff line
@@ -2528,11 +2528,12 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
	struct input_dev *input = wacom_wac->pen_input;
	bool range = wacom_wac->hid_data.inrange_state;
	bool sense = wacom_wac->hid_data.sense_state;
	bool entering_range = !wacom_wac->tool[0] && range;

	if (wacom_wac->is_invalid_bt_frame)
		return;

	if (!wacom_wac->tool[0] && range) { /* first in range */
	if (entering_range) { /* first in range */
		/* Going into range select tool */
		if (wacom_wac->hid_data.invert_state)
			wacom_wac->tool[0] = BTN_TOOL_RUBBER;
@@ -2583,6 +2584,15 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
		input_sync(input);
	}

	/* Handle AES battery timeout behavior */
	if (wacom_wac->features.quirks & WACOM_QUIRK_AESPEN) {
		if (entering_range)
			cancel_delayed_work(&wacom->aes_battery_work);
		if (!sense)
			schedule_delayed_work(&wacom->aes_battery_work,
					      msecs_to_jiffies(WACOM_AES_BATTERY_TIMEOUT));
	}

	if (!sense) {
		wacom_wac->tool[0] = 0;
		wacom_wac->id[0] = 0;
@@ -2649,8 +2659,8 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
{
	struct hid_data *hid_data = &wacom_wac->hid_data;
	bool mt = wacom_wac->features.touch_max > 1;
	bool prox = hid_data->tipswitch &&
		    report_touch_events(wacom_wac);
	bool touch_down = hid_data->tipswitch && hid_data->confidence;
	bool prox = touch_down && report_touch_events(wacom_wac);

	if (touch_is_muted(wacom_wac)) {
		if (!wacom_wac->shared->touch_down)
@@ -2700,24 +2710,6 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
	}
}

static bool wacom_wac_slot_is_active(struct input_dev *dev, int key)
{
	struct input_mt *mt = dev->mt;
	struct input_mt_slot *s;

	if (!mt)
		return false;

	for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
		if (s->key == key &&
			input_mt_get_value(s, ABS_MT_TRACKING_ID) >= 0) {
			return true;
		}
	}

	return false;
}

static void wacom_wac_finger_event(struct hid_device *hdev,
		struct hid_field *field, struct hid_usage *usage, __s32 value)
{
@@ -2768,16 +2760,10 @@ static void wacom_wac_finger_event(struct hid_device *hdev,
	}

	if (usage->usage_index + 1 == field->report_count) {
		if (equivalent_usage == wacom_wac->hid_data.last_slot_field) {
			bool touch_removed = wacom_wac_slot_is_active(wacom_wac->touch_input,
				wacom_wac->hid_data.id) && !wacom_wac->hid_data.tipswitch;

			if (wacom_wac->hid_data.confidence || touch_removed) {
		if (equivalent_usage == wacom_wac->hid_data.last_slot_field)
			wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
	}
}
	}
}

static void wacom_wac_finger_pre_report(struct hid_device *hdev,
		struct hid_report *report)
+1 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#define WACOM_MAX_REMOTES	5
#define WACOM_STATUS_UNKNOWN	255
#define WACOM_REMOTE_BATTERY_TIMEOUT	21000000000ll
#define WACOM_AES_BATTERY_TIMEOUT       1800000

/* packet length for individual models */
#define WACOM_PKGLEN_BBFUN	 9
+276 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ from .descriptors_wacom import (
)

import attr
from collections import namedtuple
from enum import Enum
from hidtools.hut import HUT
from hidtools.hid import HidUnit
@@ -862,6 +863,8 @@ class TestPTHX60_Pen(TestOpaqueCTLTablet):


class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest):
    ContactIds = namedtuple("ContactIds", "contact_id, tracking_id, slot_num")

    def create_device(self):
        return test_multitouch.Digitizer(
            "DTH 2452",
@@ -869,6 +872,57 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest
            input_info=(0x3, 0x056A, 0x0383),
        )

    def make_contact(self, contact_id=0, t=0):
        """
        Make a single touch contact that can move over time.

        Creates a touch object that has a well-known position in space that
        does not overlap with other contacts. The value of `t` may be
        incremented over time to move the point along a linear path.
        """
        x = 50 + 10 * contact_id + t
        y = 100 + 100 * contact_id + t
        return test_multitouch.Touch(contact_id, x, y)

    def make_contacts(self, n, t=0):
        """
        Make multiple touch contacts that can move over time.

        Returns a list of `n` touch objects that are positioned at well-known
        locations. The value of `t` may be incremented over time to move the
        points along a linear path.
        """
        return [ self.make_contact(id, t) for id in range(0, n) ]

    def assert_contact(self, uhdev, evdev, contact_ids, t=0):
        """
        Assert properties of a contact generated by make_contact.
        """
        contact_id = contact_ids.contact_id
        tracking_id = contact_ids.tracking_id
        slot_num = contact_ids.slot_num

        x = 50 + 10 * contact_id + t
        y = 100 + 100 * contact_id + t

        # If the data isn't supposed to be stored in any slots, there is
        # nothing we can check for in the evdev stream.
        if slot_num is None:
            assert tracking_id == -1
            return

        assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == tracking_id
        if tracking_id != -1:
            assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_POSITION_X] == x
            assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_POSITION_Y] == y

    def assert_contacts(self, uhdev, evdev, data, t=0):
        """
        Assert properties of a list of contacts generated by make_contacts.
        """
        for contact_ids in data:
            self.assert_contact(uhdev, evdev, contact_ids, t)

    def test_contact_id_0(self):
        """
        Bring a finger in contact with the tablet, then hold it down and remove it.
@@ -920,3 +974,225 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest
        _slot = self.get_slot(uhdev, t0, 0)

        assert not events

    def test_confidence_multitouch(self):
        """
        Bring multiple fingers in contact with the tablet, some with the
        confidence bit set, and some without.

        Ensure that all confident touches are reported and that all non-
        confident touches are ignored.
        """
        uhdev = self.uhdev
        evdev = uhdev.get_evdev()

        touches = self.make_contacts(5)
        touches[0].confidence = False
        touches[2].confidence = False
        touches[4].confidence = False

        r = uhdev.event(touches)
        events = uhdev.next_sync_events()
        self.debug_reports(r, uhdev, events)

        assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events

        self.assert_contacts(uhdev, evdev,
            [ self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None),
              self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0),
              self.ContactIds(contact_id = 2, tracking_id = -1, slot_num = None),
              self.ContactIds(contact_id = 3, tracking_id = 1, slot_num = 1),
              self.ContactIds(contact_id = 4, tracking_id = -1, slot_num = None) ])

    def confidence_change_assert_playback(self, uhdev, evdev, timeline):
        """
        Assert proper behavior of contacts that move and change tipswitch /
        confidence status over time.

        Given a `timeline` list of touch states to iterate over, verify
        that the contacts move and are reported as up/down as expected
        by the state of the tipswitch and confidence bits.
        """
        t = 0

        for state in timeline:
            touches = self.make_contacts(len(state), t)

            for item in zip(touches, state):
                item[0].tipswitch = item[1][1]
                item[0].confidence = item[1][2]

            r = uhdev.event(touches)
            events = uhdev.next_sync_events()
            self.debug_reports(r, uhdev, events)

            ids = [ x[0] for x in state ]
            self.assert_contacts(uhdev, evdev, ids, t)

            t += 1

    def test_confidence_loss_a(self):
        """
        Transition a confident contact to a non-confident contact by
        first clearing the tipswitch.

        Ensure that the driver reports the transitioned contact as
        being removed and that other contacts continue to report
        normally. This mode of confidence loss is used by the
        DTH-2452.
        """
        uhdev = self.uhdev
        evdev = uhdev.get_evdev()

        self.confidence_change_assert_playback(uhdev, evdev, [
            # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
            # Both fingers confidently in contact
            [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=1: Contact 0 == !Down + confident; Contact 1 == Down + confident
            # First finger looses confidence and clears only the tipswitch flag
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, True),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident
            # First finger has lost confidence and has both flags cleared
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident
            # First finger has lost confidence and has both flags cleared
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)]
        ])

    def test_confidence_loss_b(self):
        """
        Transition a confident contact to a non-confident contact by
        cleraing both tipswitch and confidence bits simultaneously.

        Ensure that the driver reports the transitioned contact as
        being removed and that other contacts continue to report
        normally. This mode of confidence loss is used by some
        AES devices.
        """
        uhdev = self.uhdev
        evdev = uhdev.get_evdev()

        self.confidence_change_assert_playback(uhdev, evdev, [
            # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
            # Both fingers confidently in contact
            [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=1: Contact 0 == !Down + !confident; Contact 1 == Down + confident
            # First finger looses confidence and has both flags cleared simultaneously
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident
            # First finger has lost confidence and has both flags cleared
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident
            # First finger has lost confidence and has both flags cleared
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)]
        ])

    def test_confidence_loss_c(self):
        """
        Transition a confident contact to a non-confident contact by
        clearing only the confidence bit.

        Ensure that the driver reports the transitioned contact as
        being removed and that other contacts continue to report
        normally.
        """
        uhdev = self.uhdev
        evdev = uhdev.get_evdev()

        self.confidence_change_assert_playback(uhdev, evdev, [
            # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
            # Both fingers confidently in contact
            [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident
            # First finger looses confidence and clears only the confidence flag
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), True, False),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident
            # First finger has lost confidence and has both flags cleared
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident
            # First finger has lost confidence and has both flags cleared
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)]
        ])

    def test_confidence_gain_a(self):
        """
        Transition a contact that was always non-confident to confident.

        Ensure that the confident contact is reported normally.
        """
        uhdev = self.uhdev
        evdev = uhdev.get_evdev()

        self.confidence_change_assert_playback(uhdev, evdev, [
            # t=0: Contact 0 == Down + !confident; Contact 1 == Down + confident
            # Only second finger is confidently in contact
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None), True, False),
             (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)],

            # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident
            # First finger gains confidence
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None), True, False),
             (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)],

            # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident
            # First finger remains confident
            [(self.ContactIds(contact_id = 0, tracking_id = 1, slot_num = 1), True, True),
             (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)],

            # t=3: Contact 0 == Down + confident; Contact 1 == Down + confident
            # First finger remains confident
            [(self.ContactIds(contact_id = 0, tracking_id = 1, slot_num = 1), True, True),
             (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)]
        ])

    def test_confidence_gain_b(self):
        """
        Transition a contact from non-confident to confident.

        Ensure that the confident contact is reported normally.
        """
        uhdev = self.uhdev
        evdev = uhdev.get_evdev()

        self.confidence_change_assert_playback(uhdev, evdev, [
            # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
            # First and second finger confidently in contact
            [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident
            # Firtst finger looses confidence
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), True, False),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident
            # First finger gains confidence
            [(self.ContactIds(contact_id = 0, tracking_id = 2, slot_num = 0), True, True),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)],

            # t=3: Contact 0 == !Down + confident; Contact 1 == Down + confident
            # First finger goes up
            [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, True),
             (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)]
        ])