Re: [PATCH] HID: wacom: Support switching from vendor-defined input format on G9 and G11

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 03/30/2016 02:24 AM, Benjamin Tissoires wrote:
> On Mar 29 2016 or thereabouts, Jason Gerecke wrote:
>> On 03/29/2016 12:05 AM, Benjamin Tissoires wrote:
>>> Hi Jason,
>>>
>>> On Mar 28 2016 or thereabouts, Jason Gerecke wrote:
>>>> A tablet PC booted into Windows may have its pen/touch hardware switched
>>>> into "Wacom mode" similar to what we do with explicitly-supported hardware.
>>>> Some devices appear to maintain this state across reboots, preventing their
>>>> use with the generic HID driver. This patch adds support for the vendor-
>>>> defined "input format" feature report of G9 and G11 chips and has the HID
>>>> codepath always attempt to reset the device back to an appropriate format.
>>>>
>>>> Fixes: https://sourceforge.net/p/linuxwacom/bugs/307/
>>>> Fixes: https://sourceforge.net/p/linuxwacom/bugs/310/
>>>> Fixes: https://github.com/linuxwacom/input-wacom/issues/15
>>>>
>>>> Signed-off-by: Jason Gerecke <jason.gerecke@xxxxxxxxx>
>>>> ---
>>>>  drivers/hid/wacom_sys.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++--
>>>>  drivers/hid/wacom_wac.h | 13 ++++++++++--
>>>>  2 files changed, 64 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
>>>> index 68a5609..d7ef7b0 100644
>>>> --- a/drivers/hid/wacom_sys.c
>>>> +++ b/drivers/hid/wacom_sys.c
>>>> @@ -152,6 +152,32 @@ static void wacom_feature_mapping(struct hid_device *hdev,
>>>>  		hid_data->inputmode = field->report->id;
>>>>  		hid_data->inputmode_index = usage->usage_index;
>>>>  		break;
>>>> +
>>>> +	/* The vendor-defined application collections leave most usages
>>>> +	 * as null (0x0000), making them much more difficult to match...
>>>> +	 */
>>>
>>> Not so sure this comments adds anything to the code. The application
>>> usage is well defined, so there is no need to worry I think.
>>>
>>> (Plus the coding style should be:
>>> /*
>>>  * multi lines
>>>  * comment.
>>>  */
>>> )
>>>
>>>> +	case HID_UP_DIGITIZER:
>>>> +		if (field->application == WACOM_G9_DIGITIZER ||
>>>> +		    field->application == WACOM_G11_DIGITIZER) {
>>>> +			if (field->report->id == 0x0B) {
>>>
>>> I'd rather have the test on the report id first, and have only one if
>>> (with id && (app || app). This way, you'll keep the extra tab below.
>>>
>>>> +				hid_data->inputformat = field->report->id;
>>>> +				hid_data->inputformat_index = 0;
>>>
>>> Shouldn't this be usage->usage_index instead? Unless it's defined
>>> several times, which would make the use of usage_index moot.
>>>
>>
>> The field is the entire contents of one of Wacom's infamous "n
>> vendor-defined bytes" reports, so although the usage itself isn't
>> defined multiple times it does have a report count associated with it
>> (with the same net effect).
> 
> Fair enough. But I wonder if we need to keep the inputformat_index field
> given that it will be always 0.
> 
>>
>>>> +				hid_data->inputformat_value = 0; /* 0: HID; 1: Vendor-defined */
>>>
>>> Looks like both usages are requesting this to be set at 0. I think it's
>>> fine to keep it, but I don't know if it's that important to keep then.
>>>
>>>> +			}
>>>> +		}
>>>> +		break;
>>>> +
>>>> +	case WACOM_G9_PAGE:
>>>> +	case WACOM_G11_PAGE:
>>>> +		if (field->application == WACOM_G9_TOUCHSCREEN ||
>>>> +		    field->application == WACOM_G11_TOUCHSCREEN) {
>>>> +			if (field->report->id == 0x03) {
>>>
>>> Same three comments above apply here.
>>>
>>>> +				hid_data->inputformat = field->report->id;
>>>> +				hid_data->inputformat_index = 0;
>>>> +				hid_data->inputformat_value = 0; /* 0: HID; 4: Vendor-defined */
>>>> +			}
>>>> +		}
>>>> +		break;
>>>>  	}
>>>>  }
>>>>  
>>>> @@ -322,6 +348,25 @@ static int wacom_hid_set_device_mode(struct hid_device *hdev)
>>>>  	return 0;
>>>>  }
>>>>  
>>>> +static int wacom_hid_set_device_format(struct hid_device *hdev)
>>>> +{
>>>> +	struct wacom *wacom = hid_get_drvdata(hdev);
>>>> +	struct hid_data *hid_data = &wacom->wacom_wac.hid_data;
>>>> +	struct hid_report *r;
>>>> +	struct hid_report_enum *re;
>>>> +
>>>> +	if (hid_data->inputformat < 0)
>>>> +		return 0;
>>>> +
>>>> +	re = &(hdev->report_enum[HID_FEATURE_REPORT]);
>>>> +	r = re->report_id_hash[hid_data->inputformat];
>>>> +	if (r) {
>>>> +		r->field[0]->value[hid_data->inputformat_index] = hid_data->inputformat_value;
>>>> +		hid_hw_request(hdev, r, HID_REQ_SET_REPORT);
>>>> +	}
>>>> +	return 0;
>>>> +}
>>>> +
>>>>  static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
>>>>  		int length, int mode)
>>>>  {
>>>> @@ -414,8 +459,14 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
>>>>  	if (hdev->bus == BUS_BLUETOOTH)
>>>>  		return wacom_bt_query_tablet_data(hdev, 1, features);
>>>>  
>>>> -	if (features->type == HID_GENERIC)
>>>> -		return wacom_hid_set_device_mode(hdev);
>>>> +	if (features->type == HID_GENERIC) {
>>>> +		int err;
>>>> +		err = wacom_hid_set_device_mode(hdev);
>>>> +		if (err)
>>>> +			return err;
>>>> +		err = wacom_hid_set_device_format(hdev);
>>>
>>> I don't really see the difference between "input mode" and "input
>>> format". Does the device needs both to be set? If not, couldn't we
>>> extend the input mode feature for these usages?
>>>
>>> Cheers,
>>> Benjamin
>>>
>>
>> Device / Input mode is a standard HID feature expected to be present on
>> Windows 7-compatible touch devices which is used to switch a device
>> between sending mouse, single, or multi-touch reports[1]. The
>> 'wacom_hid_set_device_mode' function right now takes care of switching
>> into multi-touch mode provided the hid_data->inputmode field has been set.
> 
> Well, "Device Mode" is also used by MS for Pen devices (values are 0 -
> Mouse, and 1 - Pen). So it's more a generic mode for the device than a
> multitouch mode.
> 
>>
>> Input format on the other hand is a vendor-defined feature that is used
>> to switch Wacom devices (both touchscreens and tablets) between sending
>> standard HID reports or vendor-defined reports. Devices are supposed to
>> start up sending the former, but at least two tablet PCs have had
>> hardware that seems to remember being switched to the vendor-defined format.
>>
>> If the format is explicitly switched to HID standard, we still should
>> probably set the mode as well since I don't think there's anything
>> implied about which mode the tablet should use after switching.
> 
> OK, then in that regard, it would make more sense to first switch the
> device back into the HID mode, and then switch it to multitouch.
> 
>>
>> Relatedly, it's probably a good idea to rename 'wacom_set_device_mode'
>> or 'wacom_hid_set_device_mode' since they each set different a different
>> kind of "mode". It _might_ also be reasonable to rework
>> 'wacom_set_device_mode' to be compatible with HID_GENERIC devices
>> (rather than introducing 'wacom_hid_set_device_format'), but keeping
>> compatibility the explicit devices might be tricky...
> 
> OK, how about the following:
> we keep wacom_set_device_mode() for switching the proprietary mode, but
> add a mode_value and mode_report in wacom_wac. This way, we can detect
> those proprietary mode changes in feature_mapping, and still keep the
> HID Device Mode bits in place.
> 
> I tried to come up with the following, which seems to be working on the
> Bamboo PAD, Intuos Pro S, Bamboo 16FG, and Cintiq 13HDT:
> 
> ---
> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
> index 68a5609..90fa4d4 100644
> --- a/drivers/hid/wacom_sys.c
> +++ b/drivers/hid/wacom_sys.c
> @@ -322,26 +322,41 @@ static int wacom_hid_set_device_mode(struct hid_device *hdev)
>  	return 0;
>  }
>  
> -static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
> -		int length, int mode)
> +static int wacom_set_device_mode(struct hid_device *hdev,
> +				 struct wacom_wac *wacom_wac)
>  {
> -	unsigned char *rep_data;
> +	u8 *rep_data;
> +	struct hid_report *r;
> +	struct hid_report_enum *re;
> +	int length;
>  	int error = -ENOMEM, limit = 0;
>  
> -	rep_data = kzalloc(length, GFP_KERNEL);
> +	if (wacom_wac->mode_report < 0)
> +		return 0;
> +
> +	re = &(hdev->report_enum[HID_FEATURE_REPORT]);
> +	r = re->report_id_hash[wacom_wac->mode_report];
> +	if (!r)
> +		return -EINVAL;
> +
> +	rep_data = hid_alloc_report_buf(r, GFP_KERNEL);
>  	if (!rep_data)
> -		return error;
> +		return -ENOMEM;
> +
> +	length = hid_report_len(r);
>  
>  	do {
> -		rep_data[0] = report_id;
> -		rep_data[1] = mode;
> +		rep_data[0] = wacom_wac->mode_report;
> +		rep_data[1] = wacom_wac->mode_value;
>  
>  		error = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data,
>  					 length, 1);
>  		if (error >= 0)
>  			error = wacom_get_report(hdev, HID_FEATURE_REPORT,
>  			                         rep_data, length, 1);
> -	} while (error >= 0 && rep_data[1] != mode && limit++ < WAC_MSG_RETRIES);
> +	} while (error >= 0 &&
> +		 rep_data[1] != wacom_wac->mode_report &&
> +		 limit++ < WAC_MSG_RETRIES);
>  
>  	kfree(rep_data);
>  
> @@ -411,32 +426,41 @@ static int wacom_bt_query_tablet_data(struct hid_device *hdev, u8 speed,
>  static int wacom_query_tablet_data(struct hid_device *hdev,
>  		struct wacom_features *features)
>  {
> +	struct wacom *wacom = hid_get_drvdata(hdev);
> +	struct wacom_wac *wacom_wac = &wacom->wacom_wac;
> +
>  	if (hdev->bus == BUS_BLUETOOTH)
>  		return wacom_bt_query_tablet_data(hdev, 1, features);
>  
> -	if (features->type == HID_GENERIC)
> -		return wacom_hid_set_device_mode(hdev);
> -
> -	if (features->device_type & WACOM_DEVICETYPE_TOUCH) {
> -		if (features->type > TABLETPC) {
> -			/* MT Tablet PC touch */
> -			return wacom_set_device_mode(hdev, 3, 4, 4);
> -		}
> -		else if (features->type == WACOM_24HDT) {
> -			return wacom_set_device_mode(hdev, 18, 3, 2);
> -		}
> -		else if (features->type == WACOM_27QHDT) {
> -			return wacom_set_device_mode(hdev, 131, 3, 2);
> -		}
> -		else if (features->type == BAMBOO_PAD) {
> -			return wacom_set_device_mode(hdev, 2, 2, 2);
> -		}
> -	} else if (features->device_type & WACOM_DEVICETYPE_PEN) {
> -		if (features->type <= BAMBOO_PT) {
> -			return wacom_set_device_mode(hdev, 2, 2, 2);
> +	if (features->type != HID_GENERIC) {
> +		if (features->device_type & WACOM_DEVICETYPE_TOUCH) {
> +			if (features->type > TABLETPC) {
> +				/* MT Tablet PC touch */
> +				wacom_wac->mode_report = 3;
> +				wacom_wac->mode_value = 4;
> +			} else if (features->type == WACOM_24HDT) {
> +				wacom_wac->mode_report = 18;
> +				wacom_wac->mode_value = 2;
> +			} else if (features->type == WACOM_27QHDT) {
> +				wacom_wac->mode_report = 131;
> +				wacom_wac->mode_value = 2;
> +			} else if (features->type == BAMBOO_PAD) {
> +				wacom_wac->mode_report = 2;
> +				wacom_wac->mode_value = 2;
> +			}
> +		} else if (features->device_type & WACOM_DEVICETYPE_PEN) {
> +			if (features->type <= BAMBOO_PT) {
> +				wacom_wac->mode_report = 2;
> +				wacom_wac->mode_value = 2;
> +			}
>  		}
>  	}
>  
> +	wacom_set_device_mode(hdev, wacom_wac);
> +
> +	if (features->type == HID_GENERIC)
> +		return wacom_hid_set_device_mode(hdev);
> +
>  	return 0;
>  }
>  
> @@ -1817,6 +1841,9 @@ static int wacom_probe(struct hid_device *hdev,
>  		goto fail_type;
>  	}
>  
> +	wacom_wac->hid_data.inputmode = -1;

I noticed this was missing the other day but then totally forgot to fix
it. Thanks for the reminder :)

> +	wacom_wac->mode_report = -1;
> +
>  	wacom->usbdev = dev;
>  	wacom->intf = intf;
>  	mutex_init(&wacom->lock);
> diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
> index 25baa7f..8f40e45 100644
> --- a/drivers/hid/wacom_wac.h
> +++ b/drivers/hid/wacom_wac.h
> @@ -238,6 +238,8 @@ struct wacom_wac {
>  	int ps_connected;
>  	u8 bt_features;
>  	u8 bt_high_speed;
> +	int mode_report;
> +	int mode_value;
>  	struct hid_data hid_data;
>  };
>   
> ---
>  
> This patch infers the length of the feature from the report descriptor,
> but I would expect this to be correct on all devices.
> 
> Any tests/remarks would be appreciated :)
> 
> Cheers,
> Benjamin
> 

I think I like the look of this :) I'll do a bit more checking on my end
and get back to you shortly.

Jason
---
Now instead of four in the eights place /
you’ve got three, ‘Cause you added one /
(That is to say, eight) to the two, /
But you can’t take seven from three, /
So you look at the sixty-fours....

>>
>> Off to work on V2...
>>
>> [1]:
>> https://msdn.microsoft.com/en-us/library/windows/hardware/ff553739%28v=vs.85%29.aspx
>>
>> Jason
>> ---
>> Now instead of four in the eights place /
>> you’ve got three, ‘Cause you added one /
>> (That is to say, eight) to the two, /
>> But you can’t take seven from three, /
>> So you look at the sixty-fours....
>>
>>>> +		return err;
>>>> +	}
>>>>  
>>>>  	if (features->device_type & WACOM_DEVICETYPE_TOUCH) {
>>>>  		if (features->type > TABLETPC) {
>>>> diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
>>>> index 25baa7f..37d5dec 100644
>>>> --- a/drivers/hid/wacom_wac.h
>>>> +++ b/drivers/hid/wacom_wac.h
>>>> @@ -84,6 +84,12 @@
>>>>  #define WACOM_DEVICETYPE_WL_MONITOR     0x0008
>>>>  
>>>>  #define WACOM_VENDORDEFINED_PEN		0xff0d0001
>>>> +#define WACOM_G9_PAGE			0xff090000
>>>> +#define WACOM_G9_DIGITIZER		(WACOM_G9_PAGE | 0x02)
>>>> +#define WACOM_G9_TOUCHSCREEN		(WACOM_G9_PAGE | 0x11)
>>>> +#define WACOM_G11_PAGE			0xff110000
>>>> +#define WACOM_G11_DIGITIZER		(WACOM_G11_PAGE | 0x02)
>>>> +#define WACOM_G11_TOUCHSCREEN		(WACOM_G11_PAGE | 0x11)
>>>>  
>>>>  #define WACOM_PEN_FIELD(f)	(((f)->logical == HID_DG_STYLUS) || \
>>>>  				 ((f)->physical == HID_DG_STYLUS) || \
>>>> @@ -193,8 +199,11 @@ struct wacom_shared {
>>>>  };
>>>>  
>>>>  struct hid_data {
>>>> -	__s16 inputmode;	/* InputMode HID feature, -1 if non-existent */
>>>> -	__s16 inputmode_index;	/* InputMode HID feature index in the report */
>>>> +	__s16 inputmode;		/* InputMode HID feature, -1 if non-existent */
>>>> +	__s16 inputmode_index;		/* InputMode HID feature index in the report */
>>>> +	__s16 inputformat;		/* Vendor-defined "input format" feature, -1 if non-existent */
>>>> +	__s16 inputformat_index;	/* Vendor-defined "input format" feature index in the report */
>>>> +	__u8  inputformat_value;	/* Value expected by device to switch to HID mode */
>>>>  	bool inrange_state;
>>>>  	bool invert_state;
>>>>  	bool tipswitch;
>>>> -- 
>>>> 2.7.3
>>>>
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux