Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPTS on Surface Gen7 #4

Open
StollD opened this issue Jun 3, 2020 · 213 comments
Open

IPTS on Surface Gen7 #4

StollD opened this issue Jun 3, 2020 · 213 comments

Comments

@StollD
Copy link
Member

StollD commented Jun 3, 2020

On the surface gen7 devices, IPTS has undergone several changes:

The binary firmware that was used for previous models does not exist anymore. Due to the changes in the GuC firmware it seems like Intel / Microsoft are now forwarding the data to userspace(?) using HID reports, and parsing it using a daemon (or another driver?). The HID descriptor is also not loaded from a binary anymore and needs to be dumped in Windows to analyze it.

The SET_MODE command will not accept any parameter except for IPTS_SENSOR_MODE_MULTITOUCH. Other parameters will result in the status IPTS_ME_STATUS_INVALID_PARAMS being returned.

Even though we can only initialize the device in "multitouch" mode, once the initialization was finished, the only that that is being sent are HID reports: the ones for singletouch, as well as a new report (with report ID 07). Other than that the device behaves exactly expected for multitouch mode (doorbell is incremented instead of signaling new data via. MEI responses).

This issue will be for tracking the progress on full IPTS support for gen7 surface devices.

We suspect that the new IPTS needs HID feature reports to enable proper multitouch mode. The old driver from intel has some hints towards sending HID reports using a special type of feedback, but some quick tests didn't appear to be working. It also has no proper documentation so it is mostly just guessing how the different parts behave.

Another thing that has no been tested is simply not sending the SET_MODE event and directly sending SET_MEM_WINDOW. Maybe there was a change so that multitouch mode is the default, and sending anything will change it to singletouch mode? (i.e redefining SET_MODE as sth. like SET_SINGLETOUCH_MODE)

@StollD
Copy link
Member Author

StollD commented Jun 3, 2020

Example for the new 07 reports:

ipts 0000:00:16.4-3e8d0870-271a-4208-8eb5-9acb9402ae04: Buffer 2
07 b6 46 16 00 00 00 00 00 00 0b 00 00 00 00 ff 00 74 00 04 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ipts 0000:00:16.4-3e8d0870-271a-4208-8eb5-9acb9402ae04: Buffer 3
07 4c 5f 16 00 00 00 00 00 00 0b 00 00 00 00 ff 00 74 00 04 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ipts 0000:00:16.4-3e8d0870-271a-4208-8eb5-9acb9402ae04: Buffer 4
07 e2 77 16 00 00 00 00 00 00 0b 00 00 00 00 ff 00 74 00 04 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

@StollD
Copy link
Member Author

StollD commented Jun 3, 2020

Patch for not sending SET_MODE (untested):

From af13ea8a753cbe3f4e366a8a927c5f1892360843 Mon Sep 17 00:00:00 2001
From: Dorian Stoll <dorian.stoll@tmsp.io>
Date: Wed, 3 Jun 2020 15:41:15 +0200
Subject: [PATCH] no set-mode

---
 receiver.c | 25 ++-----------------------
 1 file changed, 2 insertions(+), 23 deletions(-)

diff --git a/receiver.c b/receiver.c
index ab28399..9884750 100644
--- a/receiver.c
+++ b/receiver.c
@@ -51,7 +51,8 @@ static void ipts_receiver_handle_get_device_info(struct ipts_context *ipts,
 static void ipts_receiver_handle_clear_mem_window(struct ipts_context *ipts,
 		struct ipts_response *msg, int *cmd_status, int *ret)
 {
-	struct ipts_set_mode_cmd sensor_mode_cmd;
+	int i;
+	struct ipts_set_mem_window_cmd cmd;
 
 	if (msg->status != IPTS_ME_STATUS_TIMEOUT &&
 			msg->status != IPTS_ME_STATUS_SUCCESS) {
@@ -68,25 +69,6 @@ static void ipts_receiver_handle_clear_mem_window(struct ipts_context *ipts,
 
 	ipts->status = IPTS_HOST_STATUS_RESOURCE_READY;
 
-	memset(&sensor_mode_cmd, 0, sizeof(struct ipts_set_mode_cmd));
-	sensor_mode_cmd.sensor_mode = ipts->mode;
-
-	*cmd_status = ipts_control_send(ipts, IPTS_CMD(SET_MODE),
-			&sensor_mode_cmd, sizeof(struct ipts_set_mode_cmd));
-}
-
-static void ipts_receiver_handle_set_mode(struct ipts_context *ipts,
-		struct ipts_response *msg, int *cmd_status)
-{
-	int i;
-	struct ipts_set_mem_window_cmd cmd;
-
-	if (msg->status != IPTS_ME_STATUS_SUCCESS) {
-		dev_err(ipts->dev, "0x%08x failed - status = %d\n",
-				msg->code, msg->status);
-		return;
-	}
-
 	memset(&cmd, 0, sizeof(struct ipts_set_mem_window_cmd));
 
 	for (i = 0; i < 16; i++) {
@@ -206,9 +188,6 @@ static int ipts_receiver_handle_response(struct ipts_context *ipts,
 		ipts_receiver_handle_clear_mem_window(ipts, msg,
 				&cmd_status, &ret);
 		break;
-	case IPTS_RSP(SET_MODE):
-		ipts_receiver_handle_set_mode(ipts, msg, &cmd_status);
-		break;
 	case IPTS_RSP(SET_MEM_WINDOW):
 		ipts_receiver_handle_set_mem_window(ipts, msg, &cmd_status);
 		break;
-- 
2.26.2

@StollD
Copy link
Member Author

StollD commented Jun 3, 2020

My earlier experiments with sending HID GET_FEATURE reports using host2me feedback:

https://gist.github.com/StollD/89f1c8d094c41e6ab5e3bbbe0dd0335b

Raw HID requests using host2me (aka. hid2me) feedback from the old Intel driver:

https://github.com/ipts-linux-org/ipts-linux-new/blob/master/drivers/misc/ipts/ipts-hid.c#L221

@xanf
Copy link

xanf commented Jul 13, 2020

Unfortunately it does not work without SET_MODE on Surface Book 3:

[  171.925322] ipts 0000:00:16.4-3e8d0870-271a-4208-8eb5-9acb9402ae04: Probing IPTS
[  171.925324] ipts 0000:00:16.4-3e8d0870-271a-4208-8eb5-9acb9402ae04: IPTS using DMA_BIT_MASK(64)
[  171.928265] ipts 0000:00:16.4-3e8d0870-271a-4208-8eb5-9acb9402ae04: Device 045E:09B1 found
[  171.930352] ipts 0000:00:16.4-3e8d0870-271a-4208-8eb5-9acb9402ae04: 0x80000003 failed: 1

@xanf
Copy link

xanf commented Jul 14, 2020

@StollD I've set up qemu windows 10 and forwarded IPTS device to windows 10

I can see data flowing when I touch screen, vfio_region_read / vfio_region_write calls. Could you probably give me a hint what should I look. raw HID protocol in this writes?

@StollD
Copy link
Member Author

StollD commented Jul 14, 2020

Nice!

We want to figure out the initialization sequence that the new IPTS uses, so we need to look for that. It will be encapsulated by an unknown amount of wrapper data, because IPTS doesn't talk to the PCI device that you stubbed directly, it talks to the virtual MEI bus. There is a IPTS specific UUID, so we might be able to look for that.

Can you upload an example of the calls that are logged? Never messed with this myself, would be useful to know how that looks like.

@xanf
Copy link

xanf commented Jul 14, 2020

Sure. This one is unprocessed, it should contain both initialization and several pen/finger touches

trace.gz

@StollD
Copy link
Member Author

StollD commented Jul 14, 2020

Alright, there are some interesting things.


https://gist.github.com/StollD/e2fa122521bef3241782ef827285d998#file-trace-001-L3049-L3053

This is the command that sets the mode of the IPTS hardware. The parameters are all zeros, which, on the old IPTS, meant singletouch mode. If the first zero would be a one, it would be multitouch mode.

If windows initializes the hardware in singletouch mode and gets multitouch events, and we initialize it in multitouch mode and get singletouch, it might be that Intel swapped the meaning of these two values. However, the device otherwise seems to behave exactly like the old singletouch mode: It doesn't signal a new report by increasing the number in the doorbell buffer but uses the READY_FOR_DATA event.

For example:

One issue with that theory though is that we already tested it: When the SL3 was just released, @archseer and I tried exactly that (swapping the meaning of single- and multitouch). The hardware returned an invalid-params error. Though it is possible that that was changed again in the SB3, or in a firmware revision. I also don't know what @archseer exactly changed back then, so it probably won't hurt to try it again.


https://gist.github.com/StollD/e2fa122521bef3241782ef827285d998#file-trace-001-L2874-L2878

IPTS sends a 0x9 command as part of the initialization sequence. According to the old driver from intel, 0x9 means SET_POLICIES. It's parameters doesn't seem to have anything to do with singletouch vs. multitouch, but there are some reserved fields, so maybe it was expanded to cover this.


https://gist.github.com/StollD/e2fa122521bef3241782ef827285d998#file-trace-001-L3001-L3007

There is a new 0xf command that is not documented in the old intel driver. Could be related, although I doubt it. It seems to carry a memory address as a parameter.


Feedback sending is...weird. It only seems to be sent for buffer 0xA and 0x2, and there are two instances of "special" feedback (with buffer ID 0x10).

That special feedback could be used to send HID feature reports to the hardware, but there is no way for us to know, because these are stored in a seperate buffer and don't go over the PCI bus. But we also don't know if that special feedback is new, because we have no trace from a gen4-6 device to compare against.

@xanf
Copy link

xanf commented Jul 14, 2020

@StollD I can get the same trace from gen6 (I believe) device (surface book 2) if it helps

Also if it will help in research I could record session with "pen only", "multitouch" and "touch"

@StollD
Copy link
Member Author

StollD commented Jul 14, 2020

@StollD I can get the same trace from gen6 (I believe) device (surface book 2) if it helps

That would be great actually! I was planning to research how to setup the passthrough myself, but if you already know how it works, it will probably be faster.

Also if it will help in research I could record session with "pen only", "multitouch" and "touch"

Not really. What I call multitouch covers all three of these. It is simply how we (and intel) call the mode that IPTS runs in. And windows doesn't allow changing it AFAIK.


Regarding my multitouch vs. singletouch theory: It should actually be enough if you loaded the master branch of this repo with the singletouch option set (which is also compiled into our builds already).

$ sudo modprobe -r ipts
$ sudo modprobe ipts singletouch debug

Then simply try to use the pen (or touch the screen), and watch dmesg.

You actually need to use the master version though, the UAPI version for iptsd won't work, because it doesn't support changing the mode.

@xanf
Copy link

xanf commented Jul 14, 2020

@StollD unfortunately no reaction on pen / multitouch with singletouch option (i see logs on single touches so it was loaded correctly)

@StollD
Copy link
Member Author

StollD commented Jul 14, 2020

Out of curiosity could you post the dmesg? Maybe I can find some clue in there.

If simply changing the parameter for SET_MODE doesn't work, I think the next step would be to look at that SET_POLICY command a bit more. I can write a patch so that the driver will send exactly the command you captured during initialization.

@xanf
Copy link

xanf commented Jul 14, 2020

Sure!
dmesg.log

I've also tried to record another very short session trace on qemu, touching device only with pen, that 0x10 are still there, exactly 2 :(
trace.log.gz

I will be able to get logs from Surface Book 2 somewhere this week

@xanf
Copy link

xanf commented Jul 14, 2020

Hmm. I'm actually very confused of size for trace.log.gz - it was a very short session of qemu (less than a minute). Why is it so big :/

@StollD
Copy link
Member Author

StollD commented Jul 14, 2020

I've also tried to record another very short session trace on qemu, touching device only with pen, that 0x10 are still there, exactly 2 :(

Well, its part of the device initialization. That doesn't care about what tool you want to use on the device. ;) The actual device data is passed using memory buffers, and not through the PCI interface, so you won't see a difference if you change the tool (stylus / finger).

Hmm. I'm actually very confused of size for trace.log.gz - it was a very short session of qemu (less than a minute). Why is it so big :/

The IPTS interface is just one function of the PCI device you are tracing. It could provide other interfaces that are used by windows that we don't really know about.

But I am confused about a thing too: Why is the linux driver logging singletouch events, if you added the singletouch option? Changing the mode should either produce an error (like when @archseer tried), or change the behaviour. IPTS shouldn't just ignore it.

@xanf
Copy link

xanf commented Jul 14, 2020

Double checked again, master branch built, installed via dkms, specified in modprobe.d/ipts.conf

options ipts singletouch=1 debug=1

I see logs, I see reaction to single touches, no pen, no multitouch

@StollD
Copy link
Member Author

StollD commented Jul 14, 2020

Ok. Maybe the parameter has actually lost all its meaning.

Btw, I guess I know why the feedback that was sent looks weird to me. On linux we often had issues, especially on older devices, where sending feedback too often would cause the device to crash. Maybe on windows they only send feedback for every eight input (every time you read from buffer 2 or 10) to mitigate these issues and prevent bad firmware from crashing the device.

That would actually be a pretty smart thing to do, so we might want to replicate that behaviour under linux, for all devices.

@StollD
Copy link
Member Author

StollD commented Jul 15, 2020

I wrote a patch that sends the same SET_POLICIES command that is being sent in your trace. It seems to work fine on SB2. I would be curious what would happen if you apply this to the master branch, and load the module with the debug parameter set?

diff --git a/control.c b/control.c
index 9179eca..1bc00a0 100644
--- a/control.c
+++ b/control.c
@@ -59,6 +59,7 @@ int ipts_control_send_feedback(struct ipts_context *ipts,
 
 int ipts_control_start(struct ipts_context *ipts)
 {
+	u8 data[16];
 	ipts->status = IPTS_HOST_STATUS_INIT;
 
 	if (ipts_params.singletouch)
@@ -66,7 +67,12 @@ int ipts_control_start(struct ipts_context *ipts)
 	else
 		ipts->mode = IPTS_SENSOR_MODE_MULTITOUCH;
 
-	return ipts_control_send(ipts, IPTS_CMD(NOTIFY_DEV_READY), NULL, 0);
+	memset(data, 0, 16);
+	data[0] = 0x12;
+	data[2] = 0x64;
+	data[4] = 0x1E;
+
+	return ipts_control_send(ipts, IPTS_CMD(SET_POLICIES), data, 16);
 }
 
 void ipts_control_stop(struct ipts_context *ipts)
diff --git a/protocol/events.h b/protocol/events.h
index f8b771f..b50d399 100644
--- a/protocol/events.h
+++ b/protocol/events.h
@@ -24,6 +24,7 @@ enum ipts_evt_code {
 	IPTS_EVT_FEEDBACK,
 	IPTS_EVT_CLEAR_MEM_WINDOW,
 	IPTS_EVT_NOTIFY_DEV_READY,
+	IPTS_EVT_SET_POLICIES,
 };
 
 #endif /* _IPTS_PROTOCOL_EVENTS_H_ */
diff --git a/receiver.c b/receiver.c
index ab28399..c0f60c2 100644
--- a/receiver.c
+++ b/receiver.c
@@ -10,6 +10,20 @@
 #include "protocol/responses.h"
 #include "resources.h"
 
+static void ipts_receiver_handle_set_policies(struct ipts_context *ipts,
+		struct ipts_response *msg, int *cmd_status)
+{
+	if (msg->status != IPTS_ME_STATUS_SENSOR_FAIL_NONFATAL &&
+			msg->status != IPTS_ME_STATUS_SUCCESS) {
+		dev_err(ipts->dev, "0x%08x failed - status = %d\n",
+				msg->code, msg->status);
+		return;
+	}
+
+	*cmd_status = ipts_control_send(ipts,
+			IPTS_CMD(NOTIFY_DEV_READY), NULL, 0);
+}
+
 static void ipts_receiver_handle_notify_dev_ready(struct ipts_context *ipts,
 		struct ipts_response *msg, int *cmd_status)
 {
@@ -196,6 +210,9 @@ static int ipts_receiver_handle_response(struct ipts_context *ipts,
 	int ret = 0;
 
 	switch (msg->code) {
+	case IPTS_RSP(SET_POLICIES):
+		ipts_receiver_handle_set_policies(ipts, msg, &cmd_status);
+		break;
 	case IPTS_RSP(NOTIFY_DEV_READY):
 		ipts_receiver_handle_notify_dev_ready(ipts, msg, &cmd_status);
 		break;

@xanf
Copy link

xanf commented Jul 15, 2020

No effect unfortunately. Still modprobes, still reports single touches only 😢 (with or without singletouch option)

@StollD
Copy link
Member Author

StollD commented Jul 15, 2020

Damm. I had really hoped that would work. The other new command (the 0xf one) seems to contain a memory address, so sending that ourselves might not be that simple.

It's either that, or changing the modes really happens through special feedback, which will be pretty hard for us to replicate.

@xanf
Copy link

xanf commented Jul 15, 2020 via email

@xanf
Copy link

xanf commented Jul 15, 2020

Additionally, I've put iaPreciseTouch.sys in my IDAPro64 and confused - I was expecting to see number of HID functons, so theoretically it will be possible to debug them with kernel debugger, but the only imported id function is HidNotifyPresence :/
I see IOCTL_HID_WRITE_REPORT invocation, so it definitely uses that one

@xanf
Copy link

xanf commented Jul 15, 2020

Sorry for "blogging". It seems that initial assumption about additional DLL for processing is correct. Surface is installing TouchPenProcessor.dll which is after quick look in disassembly imports non-typical for driver math functions (like log, pow, etc.) and it is named like "HEAT-compliant touchscreen" and has functions like CreateHeatProcessor

Unfortunately this DLL also includes just HidP_GetCaps and HidP_GetValueCaps calls

@xanf
Copy link

xanf commented Jul 17, 2020

Some new dumps from surface book 2

The first one is pretty funny. I've just copied qemu image with my surface book 3 driver to surface book 2. It initialized, but reported only single touches. Also I've noticed that same updates arrive when we're in (for example) boot sequence, so it seems like it's just a default mode for touch screen
trace-sb2-with-sb3-driver.gz

Second one is with sb2 driver
trace-clean.gz

@StollD
Copy link
Member Author

StollD commented Jul 17, 2020

Thank you!

Your trace-clean log confuses me a bit. Were you able to use the pen with that? Because the commands in the log are the ones that are used for singletouch.

@StollD
Copy link
Member Author

StollD commented Jul 26, 2020

I have played with it a bit myself, and I don't think it will be possible to enable multitouch mode on gen4-6 within the VM, at least not with a custom build of QEMU.

Multitouch mode on the old IPTS depends on binary firmwares that are run on the GPU. The firmware to use is selected by querying the name of the \_SB.TSML ACPI device, which does not exist on the VM. We would need to hack it in there by modifying qemu.

But even then it will probably not work, because we don't pass through the GPU, so the driver won't be able to process touch data there.

Both, the TSML ACPI device, and the touch processing on the GPU are not used on gen7 though. So it should be less of an issue there.

Just to be sure, @xanf when you tried it on gen7, was the hardware actually in multitouch mode? If it is in multitouch mode, either the pen works, or nothing works (because no touch processing driver is installed). If it detects single touches, and nothing else, it is in singletouch mode, which would match the results from our earlier experiments on linux.

@StollD
Copy link
Member Author

StollD commented Jul 26, 2020

Also, I would be curious about how your VM was set up. I just made a generic windows install, and tried to install the surface driver package. But that refused to run, because it didn't detect an actual SB2. I resorted to unpacking the installer and installing the drivers by hand, but there are so many, and we have no idea what is really needed. Also, at least on SB2, the installer seems to do some more setup in the registry, so we loose that too.

I am wondering if we might get better results if we create a disk image of an actual windows install, where all these drivers are installed properly, and use that for the VM. I've seen that microsoft has recovery images for the surfaces, maybe that can also be of use.

@xanf
Copy link

xanf commented Jul 26, 2020 via email

@qzed
Copy link
Member

qzed commented Jul 26, 2020

I think on the SB3, access to the GPU shouldn't be required as it looks like touch processing happens in the TouchPenProcessor.dll (the one with the log/sin/cos functions). On the SB2, the iGPU is required but I think (with some amount of work) it should be possible to pass it through via IOMMU, at least on the units with a dGPU. Probably requires you to run either no desktop at all or the desktop via the dGPU on the host (not sure if it's even possible without a dGPU, this seems to indicate it is).

Regarding ACPI, its possible to add/load additional ACPI tables in qemu (should be -acpitable parameter), so you don't have to modify qemu directly. However, you might not be able to directly copy the ACPI table and have to just write the relevant parts of it yourself instead (in case of dependencies etc.).

@martin-lueker
Copy link

martin-lueker commented Jan 16, 2022

As for the level multitouch functionality, I'm able to perform two-finger pan and zoom. However it seems that two finger tap is not working. A one finger-tap generates a tap event, but the two-finger tap generates a heatmap.

What do you mean by "tap event"? As output of IPTSd or is it some special event sent as a different report? AFAIK all touch data (even just single-finger taps) should be sent as heatmap.

Yes. I am seeing a new report type (code 08) associated with the tap events, as you can see in this sample. There are still plenty of heat maps but with these shorter reports mixed in.

Has there been any effort to extract a report descriptor? It would really interesting to see the device's take on the data it's spewing out!

Thanks for taking a look at my sandbox! I was thinking that one could do very well to apply something like Guo's algorithm to get a linear fit, and from there use an optimized vector library to efficiently manage all the regression operations with vectorized instructions. As I think you've discovered, the trickiest part seems to be just identifying the peaks, and choosing what data to include in your fit. However, I think I have something that works pretty well now for two fingers. I'll ping everyone here once I've posted a proper demo.

@StollD
Copy link
Member Author

StollD commented Jan 16, 2022

I see. I may have been a bit naive in my expectations. I'd been expecting that touch-finger tap to bring up something like a right click menu. (as that is how I have the touchpad configured.) Though it makes sense that the user-space apps treat screen events as wholly different that touch pad ones. So, do you have any personal recommendations for as to the most full featured desktop environment for touchscreen interaction?

I think a right click is usually done by a long one finger press. The best DE for using touch is probably GNOME, because their UX is somewhat gesture / touch oriented, and because they have a pretty solid Wayland implementation. If you dont like GNOME, I am running KDE pretty successfully at the moment, but its Wayland support is still a work in progress so you might have issues.

Has there been any effort to extract a report descriptor? It would really interesting to see the device's take on the data it's spewing out!

Yeah, there are commands in the IPTS interface that return the report descriptor. @archseer also dumped his descriptor from Windows (on an SL3). https://gist.github.com/archseer/80e33f90b02db83da0d88d95af1eb3a9 As far as I know, the values that come out of the processing are not different from what we are already receiving / sending on older devices.

@jWXZSCsqrpZRSVwauBjn
Copy link

Any updates on this?

@qzed
Copy link
Member

qzed commented Feb 18, 2022

Unfortunately not. We'll post an update here if we have something.

@yatli
Copy link

yatli commented Feb 18, 2022

I will have a try on Martin's work. :)

@StollD
Copy link
Member Author

StollD commented Feb 18, 2022

I've spent some time figuring out how the reports for the pen data are formatted. Together with the report names that @qzed already figured out, maybe someone has any ideas about the data in the reports

https://gist.github.com/StollD/ae901f46bf693a0c5355cc048cb21073

@yatli
Copy link

yatli commented Feb 20, 2022

Can confirm two-finger gestures are working on SB3 now. Great work!

@yatli
Copy link

yatli commented Feb 20, 2022

I've also dumped some data: https://gist.github.com/yatli/f248517c414cef8d460e08bab260c793

Data size goes [2000 -> 1500 -> 2000 -> 1500 -> 7488] then repeats this pattern.
And there are two types of [2000] data, interleaved:

"12 07 ... 0b 07 ...":

====== Buffer: 3 == Type: 125 == Size: 2000 =====
0000: 1c 00 00 12 07 00 00 00  00 00 0b 07 00 00 00 ff  00 00 0b 08 00 00 00 00  00 00 00 00 00 5f 00 10 
0020: 00 b7 af 00 00 01 06 02  ff ff ff ff ff ff ff ff  ff 57 00 40 00 aa b2 00  00 00 00 00 41 b7 af 00 
0040: 00 00 00 01 ff ff ff ff  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff 
0060: ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  ff ff ff ff ff 58 00 60  00 00 00 00 00 00 00 00 

"ba 07 ... b3 07"

====== Buffer: 3 == Type: 128 == Size: 2000 =====
0000: 1c 00 00 ba 07 00 00 00  00 00 b3 07 00 00 00 ff  00 00 0b 08 00 00 00 00  00 00 00 00 00 5f 00 10 
0020: 00 b7 af 00 00 09 0b 02  ff ff ff ff ff ff ff ff  ff 58 00 60 00 00 00 00  00 00 00 00 00 ff 00 00 
0040: 00 00 00 00 00 00 00 00  00 ff 00 00 00 00 00 00  00 00 00 00 00 ff 00 00  00 00 00 00 00 00 00 00 
0060: 00 ff 00 00 00 00 06 ff  ff ff ff 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 

... where 0x12 - 0x0b == 0xba - 0xb3 == 7, yet they both differ from @StollD 's record ... ba 06 ... b3 06

@yatli
Copy link

yatli commented Feb 20, 2022

Peek.2022-02-21.03-59.mp4

@ocpilot
Copy link

ocpilot commented Feb 21, 2022

This is great news. Thank you to all of you putting in such hard and great work

@KTibow
Copy link

KTibow commented Feb 21, 2022

Wait, what was figured out? Is it related to the Surface Gen7 models at all?

@yatli
Copy link

yatli commented Feb 21, 2022

TL;DR
SP7=SB3
Multitouch=OK
Pen=Being studied

@StollD
Copy link
Member Author

StollD commented Feb 21, 2022

@yatli Yeah, the messages can have a variable length, depending on the reports that they contain. The size of the outer HID report doesn't really matter. Since HID does not allow variable length reports, Microsoft had to define a bunch of reports for different sizes, so they could always send the one that fits best. Kinda like owning a 8GB, 16GB and 32GB USB drive and always using the smallest one possible to store your data.

(Also I think your dumping program has buffer and type swapped ;))

@yatli
Copy link

yatli commented Feb 21, 2022

ipts-pen.tar.gz

ImHex - pen log-3-2-7488 bin (-)_001

This is the ImHex version of @StollD 's hand-annotation. Tested a few reports of different sizes and it generalizes.

  • gen_reports.py: convert ipts-dump log into report binaries
  • pen.log: raw ipts-dump log
  • pen.log-buf-type-size.bin: binary reports
  • pen.hexpat: ImHex pattern file
  • pen.hexproj: ImHex project

@yatli
Copy link

yatli commented Feb 21, 2022

@StollD how did you figure out the names and the boundaries of the report slices? :O

@StollD
Copy link
Member Author

StollD commented Feb 21, 2022

@qzed figured out the names and report IDs, in the windows touch processing dll. (see here: #4 (comment))

As for the report slices, the same format is used in the data that we get on older gens, and we already figured that out. I first looked for a report ID, and then checked if I would get another report ID if I interpreted the next two bytes as a length value and skipped that many bytes. If it had been a different format, it would have been for difficult. After that I just had to figure out the length of the header, by following the same scheme.

@yatli
Copy link

yatli commented Feb 21, 2022

Okay so we now need to figure out the content format and what it means. Time to move to #14 :p

@Mathew-D
Copy link

Mathew-D commented May 2, 2022

Is anyone else having trouble with the thumb not reading as a touch with the libqzed version of iptsd and mult-touch

@quo
Copy link
Contributor

quo commented Jun 3, 2022

Possibly you've already seen this, but I ran across some interesting comments regarding Surface HID:

https://lore.kernel.org/all/87im072pfw.fsf@kernel.org/

Yeah, the touch controller is "peculiar". It sends to the host raw
frames which needs to be processed by a userspace application. We don't
get coordinates, pressure, etc. We get raw values from the touch
digitizer; these are passed to a daemon which runs your usual filters
(palm rejection, denoising, yada yada yada) and produces the actual
touch inputs. Those are, in turn, given to uinput.

In that case, maybe hidraw is not the best way to forward the events.

V4L has a capability to export raw touch events. You can have a look
at drivers/input/rmi4/rmi_f54.c, drivers/input/touchscreen/sur40.c or
drivers/input/touchscreen/atmel_mxt_ts.c for some examples.
The nice thing is you'll get parallel processing and DMA between the
kernel and userspace. Also, there must be userspace tools around that
are already capable of dealing with that kind of input. Though I also
understand the need to have your own sauce.

Right, I saw those but I'm not sure it applies to spi-hid. OTOH, maybe
spi-hid is just a dumb transport and the V4L2 interface would be
implemented in a hid-microsoft-surface.c driver, or something.

Right. I think you said it all there: spi-hid is the transport layer,
then all per-device logic/behaviour needs to be in the hid driver :)
So V4L2 should go into hid-microsoft.c (or another one if that makes
it too hard to melt the 2 together).

Thanks! The one thing I am not entirely sure is if V4L2 requires
polling. But with DMA, there is a chance we can just update the
buffers as the interrupts come in and let the V4L2 userspace client do
a regular polling (sorry, don't know enough of that area).

So it seems like the recommended way to send heatmap data to userspace is v4l (VFL_TYPE_TOUCH, /dev/v4l-touchX)? Not sure how I feel about that.

Also, I can't actually find any userspace components which use that interface. (Haven't looked very hard though.) Maybe it's just closed source Android stuff?

I don't think v4l would be a good fit for the pen data though.

@StollD
Copy link
Member Author

StollD commented Jun 3, 2022

I mean, I guess it makes sense. The heatmap we receive is not different from a 80x72 (idk if thats the right dimension) video. But for touch processing, you dont really want extensive parallelization (otherwise the inputs might jump around), and in our case, the data is already a HID report. The kernel maintainer probably thought that MS used HID to get raw data from the device to userspace, and not that the device already produces HID data.

@qzed
Copy link
Member

qzed commented Jun 3, 2022

@quo Oh nice! Haven't seen that before. It reads like the Duo also needs user-space processing, which is somewhat unexpected to me.

So it seems like the recommended way to send heatmap data to userspace is v4l (VFL_TYPE_TOUCH, /dev/v4l-touchX)? Not sure how I feel about that.

That'd complicate things. As mentioned it'd be possible to implement it in some HID device driver and let that do the basic heatmap frame parsing. Question is what to do with the whole other bunch of metadata being sent. It's not just the raw touch frame after all... also as you rightly mention, it doesn't particularly suit pen data well either. Also as @StollD writes: Some assumptions without actually knowing the protocol, I guess. If you purely look at "it's a heatmap", which is essentially some weird video format, v4l kinda makes sense. Just that in our case everything is already HID and it's not just a pure heatmap.

For the time being, I'd try to go the hidraw route, simply because it seems easiest. Ultimately, it'd probably be best if we then follow whatever MS decides to do. I suspect if they do need pen support, they'd probably at least handle that via hidraw (or maybe eBPF?).

It's quite nice that they actually try to upstream their SPI-HID driver, that's a big plus for the Pro X and AMD devices (although AMD devices still need ACPI support in that driver...).

Also, I can't actually find any userspace components which use that interface. (Haven't looked very hard though.) Maybe it's just closed source Android stuff?

I might try looking for that later... but yeah, I'm not sure it's open-source :/ Might need to dig for custom roms, they'd need to include that somehow...

@StollD
Copy link
Member Author

StollD commented Jun 3, 2022

It reads like the Duo also needs user-space processing, which is somewhat unexpected to me.

Now I kinda want to try and find the system image of the duo and find the service that they use for processing the data. It will be completely useless as is, because it will be an arm binary, but maybe it responds better to decompilation than the windows version xD

@quo
Copy link
Contributor

quo commented Jun 3, 2022

Yeah, I agree, hidraw is easier for now.

I was just looking at the HID feature reports. It looks like report 6 (digitizer usage 0x63) contains the metadata that iptsd currently needs the config files for. Format is as follows (values from my SP7+):

struct touch_metadata_feature_report {
	u8 report_id; // 6
	struct {
		u32 size; // 119
		u8 zero, type, unknown; // 00 00 00
		struct {
			u32 size; // 112
			u8 zero, type, unknown; // 00 02 01
			struct touch_metadata container_data;
		} container;
	} root_container;
}

struct touch_metadata {
	u32 rows; // 44
	u32 columns; // 64
	u32 screen_width; // 25978 (mm*100)
	u32 screen_height; // 17319 (mm*100)
	u8 unknown; // 1

	// transform matrix to convert row/col to physical screen coords
	struct {
		f32 xx; // -412.3492126464844 = -width/(columns-1)
		f32 yx; // 0.0
		f32 tx; // 25978.0 = width
		f32 xy; // 0.0
		f32 yy; // -402.7674560546875 = -height/(rows-1)
		f32 ty; // 17319.0 = height
	} transform;

	// unknown floats, possibly tilt transform?
	f32 a[4]; // 178.0, 182.0, 180.0,  1.0
	f32 b[4]; // 178.0, 182.0, 180.0,  1.0
	f32 c[4]; //  90.0, 171.0, 100.0, 20.0
	f32 d[4]; // 172.0, 177.0, 175.0,  2.0
}

@curioxide
Copy link

so now multitouch is enabled by default or need extra steps ?

@StollD
Copy link
Member Author

StollD commented Jul 20, 2022

Not really. You still need to build the kernel module (IPTS or ITHC, depending on your device) and the iptsd daemon from source. But we are getting there.

@danielzgtg
Copy link

I'm wondering whether we can put some AVX512 in the kernel. There is precedence for that.

If we do the processing in the kernel and Microsoft does it in userspace, then we might be able to beat them in speed! This would just be like the HTTP server wars with Linux's TUX and Microsoft Windows's IIS http.sys

@qzed
Copy link
Member

qzed commented Oct 10, 2022

Doable? Yes (you'll need to set up some additional registers and restore them before/after AVX and other vector or floating-point instructions).

Accepted by upstream: Very likely not. Too complex for kernel code, too little benefits, and too hard to maintain. See the whole graphics stack which essentially lives in user-space beyond "simple" buffer allocation and command submission.

@StollD
Copy link
Member Author

StollD commented Oct 12, 2022

Even if it is doable, the userspace approach allows us to support all MS devices with a single implementation. If we put the processing into the kernel we need to add or link it to three seperate drivers, and one is not even developed by us (spi-hid).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests