Re: [PATCH v10 2/4] Input: touch-overlay - Add touchscreen overlay handling

From: Javier Carrasco
Date: Wed Jul 10 2024 - 08:16:32 EST


On 09/07/2024 02:08, Dmitry Torokhov wrote:
> Hi Javier,
>
> On Wed, Jun 26, 2024 at 11:56:14AM +0200, Javier Carrasco wrote:
>> Some touch devices provide mechanical overlays with different objects
>> like buttons or clipped touchscreen surfaces.
>
> Thank you for your work. I think it is pretty much ready to be merged,
> just a few small comments:
>
>>
>> In order to support these objects, add a series of helper functions
>> to the input subsystem to transform them into overlay objects via
>> device tree nodes.
>>
>> These overlay objects consume the raw touch events and report the
>> expected input events depending on the object properties.
>
> So if we have overlays and also want to invert/swap axis then the
> overlays should be processed first and only then
> touchscreen_set_mt_pos() or touchscreen_report_pos() should be called?
>
> But then it will not work if we need help frm the input core to assign
> slots in cases when touch controller does not implement [reliable]
> contact tracing/identification.
>
> I think this all needs to be clarified.
>

I think this is the most critical point from your feedback.

So far, touch-overlay processes the coordinates of input_mt_pos elements
before passing them to touchscreen_set_mt_pos(). As you pointed out,
that means that it does not act on the slots i.e. it assumes that the
controller does the contact tracing and pos[i] is assigned to slot[i],
which is wrong.

Current status:
[Sensor]->[touch-overlay(filter + button
events)]->[touchscreen_set_mt_pos()]->[input_mt_assign_slots()]->[report
MT events]

If I am not mistaken, I would need a solution that processes the
coordinates associated to assigned slots via input_mt_assign_slots().
Then I would have to avoid generating MT events from slots whose
coordinates are outside of the overlay frame (ignored) or on overlay
buttons (generating button press/release events instead).

But once input_mt_assign_slots() is called, it is already too late for
that processing, isn't it? Unless assigned slots filtered by
touch-overlay are kept from generating MT events somehow, but still used
to keep contact tracing.

Any suggestion on this respect would be more than welcome.

>>
>> Note that the current implementation allows for multiple definitions
>> of touchscreen areas (regions that report touch events), but only the
>> first one will be used for the touchscreen device that the consumers
>> typically provide.
>> Should the need for multiple touchscreen areas arise, additional
>> touchscreen devices would be required at the consumer side.
>> There is no limitation in the number of touch areas defined as buttons.
>>
>> Reviewed-by: Jeff LaBundy <jeff@xxxxxxxxxxx>
>> Signed-off-by: Javier Carrasco <javier.carrasco@xxxxxxxxxxxxxx>
>
>> +int touch_overlay_map(struct list_head *list, struct input_dev *input)
>> +{
>> + struct fwnode_handle *overlay, *fw_segment;
>> + struct device *dev = input->dev.parent;
>> + struct touch_overlay_segment *segment;
>> + int error = 0;
>> +
>> + overlay = device_get_named_child_node(dev, "touch-overlay");
>
> We can annotate this as
>
> struct fwnode_handle *overlay __free(fwnode_handle) =
> device_get_named_child_node(dev, "touch-overlay");
>

That's right. A scoped version of the loop would have been nice too, but
there is still no such variant for this particular one. I am pushing
that somewhere else, though.

>> + if (!overlay)
>> + return 0;
>> +
>> + fwnode_for_each_available_child_node(overlay, fw_segment) {
>> + segment = devm_kzalloc(dev, sizeof(*segment), GFP_KERNEL);
>> + if (!segment) {
>> + fwnode_handle_put(fw_segment);
>> + error = -ENOMEM;
>
> return -ENOMEM;
>
>> + break;
>> + }
>> + error = touch_overlay_get_segment(fw_segment, segment, input);
>> + if (error) {
>> + fwnode_handle_put(fw_segment);
>
> return error;
>
>> + break;
>> + }
>> + list_add_tail(&segment->list, list);
>> + }
>> + fwnode_handle_put(overlay);
>
> Drop.
>
>> +
>> + return error;
>
> return 0;
>

Ack.

>> +}
>> +EXPORT_SYMBOL(touch_overlay_map);
>> +
>> +/**
>> + * touch_overlay_get_touchscreen_abs - get abs size from the touchscreen area.
>> + * @list: pointer to the list that holds the segments
>> + * @x: horizontal abs
>> + * @y: vertical abs
>> + */
>> +void touch_overlay_get_touchscreen_abs(struct list_head *list, u16 *x, u16 *y)
>> +{
>> + struct touch_overlay_segment *segment;
>> + struct list_head *ptr;
>> +
>> + list_for_each(ptr, list) {
>> + segment = list_entry(ptr, struct touch_overlay_segment, list);
>> + if (!segment->key) {
>> + *x = segment->x_size - 1;
>> + *y = segment->y_size - 1;
>> + break;
>> + }
>> + }
>> +}
>> +EXPORT_SYMBOL(touch_overlay_get_touchscreen_abs);
>> +
>> +static bool touch_overlay_segment_event(struct touch_overlay_segment *seg,
>> + u32 x, u32 y)
>> +{
>> + if (!seg)
>> + return false;
>
> This is a static function in the module, we are not passing NULL
> segments to it ever. Such tests should be done on API boundary.
>

Ack.

>> +
>> + if (x >= seg->x_origin && x < (seg->x_origin + seg->x_size) &&
>> + y >= seg->y_origin && y < (seg->y_origin + seg->y_size))
>> + return true;
>> +
>> + return false;
>> +}
>> +
>> +/**
>> + * touch_overlay_mapped_touchscreen - check if a touchscreen area is mapped
>> + * @list: pointer to the list that holds the segments
>> + *
>> + * Returns true if a touchscreen area is mapped or false otherwise.
>> + */
>> +bool touch_overlay_mapped_touchscreen(struct list_head *list)
>> +{
>> + struct touch_overlay_segment *segment;
>> + struct list_head *ptr;
>> +
>> + list_for_each(ptr, list) {
>> + segment = list_entry(ptr, struct touch_overlay_segment, list);
>> + if (!segment->key)
>> + return true;
>> + }
>> +
>> + return false;
>> +}
>> +EXPORT_SYMBOL(touch_overlay_mapped_touchscreen);
>> +
>> +static bool touch_overlay_event_on_ts(struct list_head *list, u32 *x, u32 *y)
>> +{
>> + struct touch_overlay_segment *segment;
>> + struct list_head *ptr;
>> + bool valid_touch = true;
>> +
>> + if (!x || !y)
>> + return false;
>> +
>> + list_for_each(ptr, list) {
>> + segment = list_entry(ptr, struct touch_overlay_segment, list);
>> + if (segment->key)
>> + continue;
>> +
>> + if (touch_overlay_segment_event(segment, *x, *y)) {
>> + *x -= segment->x_origin;
>> + *y -= segment->y_origin;
>> + return true;
>> + }
>> + /* ignore touch events outside the defined area */
>> + valid_touch = false;
>> + }
>> +
>> + return valid_touch;
>> +}
>> +
>> +static bool touch_overlay_button_event(struct input_dev *input,
>> + struct touch_overlay_segment *segment,
>> + const u32 *x, const u32 *y, u32 slot)
>> +{
>> + bool contact = x && y;
>> +
>> + if (segment->slot == slot && segment->pressed) {
>> + /* button release */
>> + if (!contact) {
>> + segment->pressed = false;
>> + input_report_key(input, segment->key, false);
>> + input_sync(input);
>
> Do we really need to emit sync here? Can we require/rely on the driver
> calling us to emit input_sync() once it's done processing current
> frame/packet?
>

I will test it without it, but that might change anyway depending on the
outcome of your first comment.

>> + return true;
>> + }
>> +
>> + /* sliding out of the button releases it */
>> + if (!touch_overlay_segment_event(segment, *x, *y)) {
>> + segment->pressed = false;
>> + input_report_key(input, segment->key, false);
>> + input_sync(input);
>> + /* keep available for a possible touch event */
>> + return false;
>> + }
>> + /* ignore sliding on the button while pressed */
>> + return true;
>> + } else if (contact && touch_overlay_segment_event(segment, *x, *y)) {
>> + segment->pressed = true;
>> + segment->slot = slot;
>> + input_report_key(input, segment->key, true);
>> + input_sync(input);
>> + return true;
>> + }
>> +
>> + return false;
>> +}
>> +
>> +/**
>> + * touch_overlay_process_event - process input events according to the overlay
>> + * mapping. This function acts as a filter to release the calling driver from
>> + * the events that are either related to overlay buttons or out of the overlay
>> + * touchscreen area, if defined.
>> + * @list: pointer to the list that holds the segments
>> + * @input: pointer to the input device associated to the event
>> + * @x: pointer to the x coordinate (NULL if not available - no contact)
>> + * @y: pointer to the y coordinate (NULL if not available - no contact)
>
> Would it be better to have a separate argument communicating slot state
> (contact/no contact)?
>

Passing NULL would be ok to save one extra argument, but I have no
strong feelings about it.

>> + * @slot: slot associated to the event
>
> What if we are not dealing with an MT device? Can we say that they
> should use slot 0 or maybe -1?
>

slot 0 would be ok. I will document it.

>> + *
>> + * Returns true if the event was processed (reported for valid key events
>> + * and dropped for events outside the overlay touchscreen area) or false
>> + * if the event must be processed by the caller. In that case this function
>> + * shifts the (x,y) coordinates to the overlay touchscreen axis if required.
>> + */
>> +bool touch_overlay_process_event(struct list_head *list,
>> + struct input_dev *input,
>> + u32 *x, u32 *y, u32 slot)
>> +{
>> + struct touch_overlay_segment *segment;
>> + struct list_head *ptr;
>> +
>> + /*
>> + * buttons must be prioritized over overlay touchscreens to account for
>> + * overlappings e.g. a button inside the touchscreen area.
>> + */
>> + list_for_each(ptr, list) {
>> + segment = list_entry(ptr, struct touch_overlay_segment, list);
>> + if (segment->key &&
>> + touch_overlay_button_event(input, segment, x, y, slot))
>> + return true;
>> + }
>> +
>> + /*
>> + * valid touch events on the overlay touchscreen are left for the client
>> + * to be processed/reported according to its (possibly) unique features.
>> + */
>> + return !touch_overlay_event_on_ts(list, x, y);
>> +}
>> +EXPORT_SYMBOL(touch_overlay_process_event);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("Helper functions for overlay objects on touch devices");
>> diff --git a/include/linux/input/touch-overlay.h b/include/linux/input/touch-overlay.h
>> new file mode 100644
>> index 000000000000..cef05c46000d
>> --- /dev/null
>> +++ b/include/linux/input/touch-overlay.h
>> @@ -0,0 +1,22 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (c) 2023 Javier Carrasco <javier.carrasco@xxxxxxxxxxxxxx>
>> + */
>> +
>> +#ifndef _TOUCH_OVERLAY
>> +#define _TOUCH_OVERLAY
>> +
>> +#include <linux/types.h>
>> +
>> +struct input_dev;
>> +
>> +int touch_overlay_map(struct list_head *list, struct input_dev *input);
>> +
>> +void touch_overlay_get_touchscreen_abs(struct list_head *list, u16 *x, u16 *y);
>> +
>> +bool touch_overlay_mapped_touchscreen(struct list_head *list);
>> +
>> +bool touch_overlay_process_event(struct list_head *list, struct input_dev *input,
>> + u32 *x, u32 *y, u32 slot);
>> +
>> +#endif
>>
>> --
>> 2.40.1
>>
>
> Thanks.
>

Thanks a lot for your feedback.

Best regards,
Javier Carrasco