├── .clang-format ├── .clangd ├── .editorconfig ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── dkms.conf ├── dkms.sh ├── patches ├── 0001-mei-me-Add-Icelake-device-ID-for-iTouch.patch └── 0002-iommu-Use-IOMMU-passthrough-mode-for-IPTS.patch └── src ├── Kconfig ├── Makefile ├── context.h ├── control.c ├── control.h ├── eds1.c ├── eds1.h ├── eds2.c ├── eds2.h ├── hid.c ├── hid.h ├── main.c ├── mei.c ├── mei.h ├── receiver.c ├── receiver.h ├── resources.c ├── resources.h ├── spec-dma.h ├── spec-hid.h ├── spec-mei.h ├── thread.c └── thread.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | Language: Cpp 3 | 4 | # Indentation 5 | AccessModifierOffset: -8 6 | ConstructorInitializerIndentWidth: 8 7 | ContinuationIndentWidth: 8 8 | IndentWidth: 8 9 | TabWidth: 8 10 | UseTab: Always 11 | 12 | # Max Line Length 13 | ColumnLimit: 100 14 | 15 | # Put braces on a new line for functions 16 | BraceWrapping: 17 | AfterFunction: true 18 | BreakBeforeBraces: Custom 19 | 20 | # Dont add unneccessary breaks, stay on the same line 21 | AllowAllParametersOfDeclarationOnNextLine: false 22 | AlwaysBreakTemplateDeclarations: false 23 | BreakStringLiterals: false 24 | 25 | # Except for functions 26 | AllowShortFunctionsOnASingleLine: None 27 | 28 | # Misc 29 | AlignConsecutiveMacros: true 30 | Cpp11BracedListStyle: false 31 | KeepEmptyLinesAtTheStartOfBlocks: false 32 | ReflowComments: false 33 | SpacesInContainerLiterals: false 34 | -------------------------------------------------------------------------------- /.clangd: -------------------------------------------------------------------------------- 1 | # The kernel builds using gcc, and uses some flags that are 2 | # unknown to clang. This causes a lot of warnings when using 3 | # clangd for development, so remove the problematic flags. 4 | 5 | --- 6 | CompileFlags: 7 | Remove: 8 | - -mpreferred-stack-boundary=3 9 | - -mindirect-branch=thunk-extern 10 | - -mfunction-return=thunk-extern 11 | - -mindirect-branch-register 12 | - -mindirect-branch-cs-prefix 13 | - -fno-allow-store-data-races 14 | - -fconserve-stack 15 | - -mrecord-mcount 16 | - -ftrivial-auto-var-init=zero 17 | Compiler: clang 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 8 6 | end_of_line = lf 7 | indent_style = tab 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{c,h}] 12 | max_line_length = 100 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.o 3 | *.ko 4 | *.mod* 5 | *.cmd 6 | .tmp_versions/ 7 | modules.order 8 | Module.symvers 9 | Mkfile.old 10 | 11 | compile_commands.json 12 | 13 | !.gitignore 14 | !.gitattributes 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # 3 | # Makefile for the IPTS touchscreen driver 4 | # 5 | 6 | sources := dkms.conf 7 | sources += Makefile 8 | sources += src/context.h 9 | sources += src/control.c 10 | sources += src/control.h 11 | sources += src/hid.c 12 | sources += src/hid.h 13 | sources += src/Kconfig 14 | sources += src/Makefile 15 | sources += src/main.c 16 | sources += src/mei.c 17 | sources += src/mei.h 18 | sources += src/receiver.c 19 | sources += src/receiver.h 20 | sources += src/resources.c 21 | sources += src/resources.h 22 | sources += src/spec-dma.h 23 | sources += src/spec-hid.h 24 | sources += src/spec-mei.h 25 | sources += src/thread.c 26 | sources += src/thread.h 27 | 28 | KVERSION ?= $(shell uname -r) 29 | KDIR := /lib/modules/$(KVERSION)/build 30 | 31 | DEBUG ?= y 32 | 33 | all: 34 | $(MAKE) -C $(KDIR) M=$(PWD)/src CONFIG_HID_IPTS=m IPTS_DEBUG=$(DEBUG) modules 35 | 36 | clean: 37 | $(MAKE) -C $(KDIR) M=$(PWD)/src CONFIG_HID_IPTS=m IPTS_DEBUG=$(DEBUG) clean 38 | 39 | check: 40 | $(KDIR)/scripts/checkpatch.pl -f -q --no-tree --strict --ignore EMBEDDED_FILENAME,UNCOMMENTED_DEFINITION $(sources) 41 | 42 | dkms-install: $(sources) 43 | ./dkms.sh install 44 | 45 | dkms-uninstall: 46 | ./dkms.sh uninstall 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intel Precise Touch & Stylus 2 | 3 | This is the Linux driver for IPTS (Intel Precise Touch and Stylus). 4 | 5 | With IPTS the Intel Management Engine acts as an interface for a touch controller. 6 | This module provides a HID transport driver that forwards the data into the userspace. 7 | 8 | IPTS devices can return raw capacitive heatmap data instead of HID reports. This data is 9 | wrapped in a HID report and forwarded to userspace too, where it needs to be processed by 10 | a separate program and then passed back to the kernel using uinput. 11 | 12 | The userspace daemon for Microsoft Surface devices can be found here: 13 | https://github.com/linux-surface/iptsd 14 | 15 | ### Building (in-tree) 16 | * Apply the patches from `patches/` to your kernel 17 | * Copy the folder `src` to `drivers/hid/ipts` 18 | * Add `source "drivers/hid/ipts/Kconfig"` to `drivers/hid/Kconfig` 19 | * Add `obj-y += ipts/` to `drivers/hid/Makefile` 20 | * Enable the driver by setting `CONFIG_HID_IPTS=m` in the kernel config 21 | 22 | ### Building (out-of-tree) 23 | * Make sure you applied the patches from `patches/` to your kernel (if you are not using linux-surface builds) 24 | * Run `make` 25 | * Run `sudo insmod src/ipts.ko` 26 | 27 | ### Building (DKMS) 28 | * Make sure you applied the patches from `patches/` to your kernel (if you are not using linux-surface builds) 29 | * Run `sudo make dkms-install` 30 | * Reboot 31 | 32 | ### Original IPTS driver 33 | The original driver for IPTS was released by Intel in late 2016 (https://github.com/ipts-linux-org/ipts-linux-new). 34 | It uses GuC submission to process raw touch input data on the GPU, using firmware blobs extracted from Windows. 35 | 36 | With linux 5.3 the ability to use GuC submission was removed from the i915 graphics driver, rendering the old driver 37 | unusable. This lead to this new driver being written, without using GuC submission. It is loosely based on the original 38 | driver, but has undergone significant refactoring and cleanup. 39 | 40 | An updated version of the driver can be found here for reference purposes: 41 | https://github.com/linux-surface/kernel/tree/v4.19-surface-devel/drivers/misc/ipts 42 | -------------------------------------------------------------------------------- /dkms.conf: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME="ipts" 2 | PACKAGE_VERSION="1.0.0" 3 | CLEAN="make clean DEBUG=0" 4 | AUTOINSTALL="yes" 5 | MAKE="make all KVERSION=$kernelver DEBUG=0" 6 | 7 | BUILT_MODULE_NAME[0]="ipts" 8 | BUILT_MODULE_LOCATION[0]="src" 9 | DEST_MODULE_LOCATION[0]="/updates" 10 | -------------------------------------------------------------------------------- /dkms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | DIR="$(readlink -f "$(dirname "$0")")" 6 | MODULE="$(grep 'PACKAGE_NAME' "$DIR/dkms.conf" | cut -d'=' -f2 | grep -Eo '[^"]+')" 7 | 8 | uninstall() { 9 | for mod in $(dkms status | grep "$MODULE" | cut -d',' -f1); do 10 | dkms remove "$mod" --force --all 11 | done 12 | } 13 | 14 | install() { 15 | uninstall 16 | dkms add "$DIR" 17 | dkms autoinstall --force 18 | } 19 | 20 | case "$1" in 21 | install) 22 | install 23 | ;; 24 | uninstall) 25 | uninstall 26 | ;; 27 | esac 28 | -------------------------------------------------------------------------------- /patches/0001-mei-me-Add-Icelake-device-ID-for-iTouch.patch: -------------------------------------------------------------------------------- 1 | From 0a575b346481523b8ce3857f476d63581e84f6f2 Mon Sep 17 00:00:00 2001 2 | From: Dorian Stoll 3 | Date: Thu, 30 Jul 2020 13:21:53 +0200 4 | Subject: [PATCH 1/2] mei: me: Add Icelake device ID for iTouch 5 | 6 | Signed-off-by: Dorian Stoll 7 | Patchset: ipts 8 | --- 9 | drivers/misc/mei/hw-me-regs.h | 1 + 10 | drivers/misc/mei/pci-me.c | 1 + 11 | 2 files changed, 2 insertions(+) 12 | 13 | diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h 14 | index 961e5d53a27a..860f99b6ecd6 100644 15 | --- a/drivers/misc/mei/hw-me-regs.h 16 | +++ b/drivers/misc/mei/hw-me-regs.h 17 | @@ -92,6 +92,7 @@ 18 | #define MEI_DEV_ID_CDF 0x18D3 /* Cedar Fork */ 19 | 20 | #define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ 21 | +#define MEI_DEV_ID_ICP_LP_3 0x34E4 /* Ice Lake Point LP 3 (iTouch) */ 22 | #define MEI_DEV_ID_ICP_N 0x38E0 /* Ice Lake Point N */ 23 | 24 | #define MEI_DEV_ID_JSP_N 0x4DE0 /* Jasper Lake Point N */ 25 | diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c 26 | index 676d566f38dd..6b37dd1f8b2a 100644 27 | --- a/drivers/misc/mei/pci-me.c 28 | +++ b/drivers/misc/mei/pci-me.c 29 | @@ -97,6 +97,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { 30 | {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, 31 | 32 | {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, 33 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP_3, MEI_ME_PCH12_CFG)}, 34 | {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_N, MEI_ME_PCH12_CFG)}, 35 | 36 | {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)}, 37 | -- 38 | 2.43.2 39 | 40 | -------------------------------------------------------------------------------- /patches/0002-iommu-Use-IOMMU-passthrough-mode-for-IPTS.patch: -------------------------------------------------------------------------------- 1 | From 1eef2a51791ca35c39780be288529597de4b28ed Mon Sep 17 00:00:00 2001 2 | From: Liban Hannan 3 | Date: Tue, 12 Apr 2022 23:31:12 +0100 4 | Subject: [PATCH 2/2] iommu: Use IOMMU passthrough mode for IPTS 5 | 6 | Adds a quirk so that IOMMU uses passthrough mode for the IPTS device. 7 | Otherwise, when IOMMU is enabled, IPTS produces DMAR errors like: 8 | 9 | DMAR: [DMA Read NO_PASID] Request device [00:16.4] fault addr 10 | 0x104ea3000 [fault reason 0x06] PTE Read access is not set 11 | 12 | This is very similar to the bug described at: 13 | https://bugs.launchpad.net/bugs/1958004 14 | 15 | Fixed with the following patch which this patch basically copies: 16 | https://launchpadlibrarian.net/586396847/43255ca.diff 17 | 18 | Signed-off-by: Dorian Stoll 19 | Patchset: ipts 20 | --- 21 | drivers/iommu/intel/iommu.c | 29 +++++++++++++++++++++++++++++ 22 | 1 file changed, 29 insertions(+) 23 | 24 | diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c 25 | index 897159dba47d..1ad42a42fe04 100644 26 | --- a/drivers/iommu/intel/iommu.c 27 | +++ b/drivers/iommu/intel/iommu.c 28 | @@ -40,6 +40,11 @@ 29 | #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) 30 | #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e) 31 | 32 | +#define IS_IPTS(pdev) ( \ 33 | + ((pdev)->vendor == PCI_VENDOR_ID_INTEL && (pdev)->device == 0x9D3E) || \ 34 | + ((pdev)->vendor == PCI_VENDOR_ID_INTEL && (pdev)->device == 0x34E4) \ 35 | + ) 36 | + 37 | #define IOAPIC_RANGE_START (0xfee00000) 38 | #define IOAPIC_RANGE_END (0xfeefffff) 39 | #define IOVA_START_ADDR (0x1000) 40 | @@ -291,12 +296,14 @@ int intel_iommu_enabled = 0; 41 | EXPORT_SYMBOL_GPL(intel_iommu_enabled); 42 | 43 | static int dmar_map_gfx = 1; 44 | +static int dmar_map_ipts = 1; 45 | static int intel_iommu_superpage = 1; 46 | static int iommu_identity_mapping; 47 | static int iommu_skip_te_disable; 48 | 49 | #define IDENTMAP_GFX 2 50 | #define IDENTMAP_AZALIA 4 51 | +#define IDENTMAP_IPTS 16 52 | 53 | const struct iommu_ops intel_iommu_ops; 54 | static const struct iommu_dirty_ops intel_dirty_ops; 55 | @@ -2548,6 +2555,9 @@ static int device_def_domain_type(struct device *dev) 56 | 57 | if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev)) 58 | return IOMMU_DOMAIN_IDENTITY; 59 | + 60 | + if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev)) 61 | + return IOMMU_DOMAIN_IDENTITY; 62 | } 63 | 64 | return 0; 65 | @@ -2855,6 +2865,9 @@ static int __init init_dmars(void) 66 | if (!dmar_map_gfx) 67 | iommu_identity_mapping |= IDENTMAP_GFX; 68 | 69 | + if (!dmar_map_ipts) 70 | + iommu_identity_mapping |= IDENTMAP_IPTS; 71 | + 72 | check_tylersburg_isoch(); 73 | 74 | ret = si_domain_init(hw_pass_through); 75 | @@ -4977,6 +4990,18 @@ static void quirk_iommu_igfx(struct pci_dev *dev) 76 | dmar_map_gfx = 0; 77 | } 78 | 79 | +static void quirk_iommu_ipts(struct pci_dev *dev) 80 | +{ 81 | + if (!IS_IPTS(dev)) 82 | + return; 83 | + 84 | + if (risky_device(dev)) 85 | + return; 86 | + 87 | + pci_info(dev, "Disabling IOMMU for IPTS\n"); 88 | + dmar_map_ipts = 0; 89 | +} 90 | + 91 | /* G4x/GM45 integrated gfx dmar support is totally busted. */ 92 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx); 93 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx); 94 | @@ -5012,6 +5037,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx); 95 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx); 96 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx); 97 | 98 | +/* disable IPTS dmar support */ 99 | +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9D3E, quirk_iommu_ipts); 100 | +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x34E4, quirk_iommu_ipts); 101 | + 102 | static void quirk_iommu_rwbf(struct pci_dev *dev) 103 | { 104 | if (risky_device(dev)) 105 | -- 106 | 2.43.2 107 | 108 | -------------------------------------------------------------------------------- /src/Kconfig: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | 3 | config HID_IPTS 4 | tristate "Intel Precise Touch & Stylus" 5 | depends on INTEL_MEI 6 | depends on HID 7 | help 8 | Say Y here if your system has a touchscreen using Intels 9 | Precise Touch & Stylus (IPTS) technology. 10 | 11 | If unsure say N. 12 | 13 | To compile this driver as a module, choose M here: the 14 | module will be called ipts. 15 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # 3 | # Makefile for the IPTS touchscreen driver 4 | # 5 | 6 | obj-$(CONFIG_HID_IPTS) += ipts.o 7 | ipts-objs := control.o 8 | ipts-objs += eds1.o 9 | ipts-objs += eds2.o 10 | ipts-objs += hid.o 11 | ipts-objs += main.o 12 | ipts-objs += mei.o 13 | ipts-objs += receiver.o 14 | ipts-objs += resources.o 15 | ipts-objs += thread.o 16 | 17 | ccflags-$(IPTS_DEBUG) += -DDEBUG 18 | -------------------------------------------------------------------------------- /src/context.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (c) 2020-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #ifndef IPTS_CONTEXT_H 9 | #define IPTS_CONTEXT_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "mei.h" 20 | #include "resources.h" 21 | #include "spec-mei.h" 22 | #include "thread.h" 23 | 24 | /** 25 | * struct ipts_context - Central place to store information about the state of the driver. 26 | * 27 | * @dev: 28 | * The linux device object of the MEI client device. 29 | * 30 | * @mei: 31 | * The wrapper for the MEI bus logic. 32 | * 33 | * @resources: 34 | * The IPTS resource manager, responsible for DMA allocations. 35 | * 36 | * @receiver: 37 | * The receiver thread that polls the ME for new data or responds to events sent by it. 38 | * 39 | * @eds_intf_rev: 40 | * The maximum EDS interface revision that both, the ME and the touch sensor implement. 41 | * 42 | * @buffers: 43 | * How many data / feedback buffers the driver is using. 44 | * 45 | * @mode: 46 | * The current operating mode of the touch sensor. 47 | * 48 | * @info: 49 | * Information about the device we are driving. 50 | * 51 | * @feature_lock: 52 | * Prevents userspace from issuing multiple HID_GET_FEATURE requests at the same time. 53 | * 54 | * @feature_event: 55 | * The answer to a GET_FEATURE request is sent through a standard IPTS data buffer. 56 | * Using this event, the HID interface can wait until the receiver thread has read it. 57 | * 58 | * @hid_active: 59 | * Whether the HID interface should be accepting requests from userspace at the moment. 60 | * 61 | * @hid: 62 | * The linux HID device object. 63 | */ 64 | struct ipts_context { 65 | struct device *dev; 66 | 67 | struct ipts_mei mei; 68 | struct ipts_resources resources; 69 | struct ipts_thread receiver; 70 | 71 | u8 eds_intf_rev; 72 | u8 buffers; 73 | enum ipts_mode mode; 74 | struct ipts_rsp_get_device_info info; 75 | 76 | struct mutex feature_lock; 77 | struct completion feature_event; 78 | 79 | bool hid_active; 80 | struct hid_device *hid; 81 | }; 82 | 83 | #endif /* IPTS_CONTEXT_H */ 84 | -------------------------------------------------------------------------------- /src/control.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2020-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "context.h" 16 | #include "control.h" 17 | #include "hid.h" 18 | #include "mei.h" 19 | #include "receiver.h" 20 | #include "resources.h" 21 | #include "spec-dma.h" 22 | #include "spec-mei.h" 23 | 24 | static int ipts_control_notify_dev_ready(struct ipts_context *ipts) 25 | { 26 | int ret = 0; 27 | struct ipts_response rsp = { 0 }; 28 | 29 | ret = ipts_mei_send(&ipts->mei, IPTS_CMD_NOTIFY_DEV_READY, NULL, 0); 30 | if (ret) 31 | return ret; 32 | 33 | ret = ipts_mei_recv(&ipts->mei, IPTS_CMD_NOTIFY_DEV_READY, &rsp); 34 | if (ret) 35 | return ret; 36 | 37 | return rsp.status; 38 | } 39 | 40 | static int ipts_control_get_device_info(struct ipts_context *ipts) 41 | { 42 | int ret = 0; 43 | struct ipts_response rsp = { 0 }; 44 | 45 | ret = ipts_mei_send(&ipts->mei, IPTS_CMD_GET_DEVICE_INFO, NULL, 0); 46 | if (ret) 47 | return ret; 48 | 49 | ret = ipts_mei_recv(&ipts->mei, IPTS_CMD_GET_DEVICE_INFO, &rsp); 50 | if (ret) 51 | return ret; 52 | 53 | if (rsp.status == IPTS_STATUS_SUCCESS) { 54 | ipts->info = rsp.payload.get_device_info; 55 | ipts->eds_intf_rev = 56 | min(ipts->info.sensor_eds_intf_rev, ipts->info.me_eds_intf_rev); 57 | } 58 | 59 | return rsp.status; 60 | } 61 | 62 | static int ipts_control_set_mode(struct ipts_context *ipts) 63 | { 64 | int ret = 0; 65 | 66 | struct ipts_cmd_set_mode cmd = { 0 }; 67 | struct ipts_response rsp = { 0 }; 68 | 69 | cmd.mode = ipts->mode; 70 | 71 | ret = ipts_mei_send(&ipts->mei, IPTS_CMD_SET_MODE, &cmd, sizeof(cmd)); 72 | if (ret) 73 | return ret; 74 | 75 | ret = ipts_mei_recv(&ipts->mei, IPTS_CMD_SET_MODE, &rsp); 76 | if (ret) 77 | return ret; 78 | 79 | return rsp.status; 80 | } 81 | 82 | static int ipts_control_set_mem_window(struct ipts_context *ipts) 83 | { 84 | int i = 0; 85 | int ret = 0; 86 | 87 | struct ipts_cmd_set_mem_window cmd = { 0 }; 88 | struct ipts_response rsp = { 0 }; 89 | 90 | if (ipts->mode == IPTS_MODE_EVENT) 91 | ipts->buffers = 1; 92 | else 93 | ipts->buffers = IPTS_MAX_BUFFERS; 94 | 95 | for (i = 0; i < ipts->buffers; i++) { 96 | dma_addr_t data_addr = ipts->resources.data[i].dma_address; 97 | dma_addr_t feedback_addr = ipts->resources.feedback[i].dma_address; 98 | 99 | cmd.data_addr_lower[i] = lower_32_bits(data_addr); 100 | cmd.data_addr_upper[i] = upper_32_bits(data_addr); 101 | 102 | cmd.feedback_addr_lower[i] = lower_32_bits(feedback_addr); 103 | cmd.feedback_addr_upper[i] = upper_32_bits(feedback_addr); 104 | } 105 | 106 | if (ipts->mode == IPTS_MODE_POLL) { 107 | dma_addr_t wq_addr = ipts->resources.workqueue.dma_address; 108 | dma_addr_t db_addr = ipts->resources.doorbell.dma_address; 109 | 110 | cmd.tail_offset_addr_lower = lower_32_bits(wq_addr); 111 | cmd.tail_offset_addr_lower = upper_32_bits(wq_addr); 112 | 113 | cmd.doorbell_addr_lower = lower_32_bits(db_addr); 114 | cmd.doorbell_addr_upper = upper_32_bits(db_addr); 115 | 116 | cmd.wq_size = IPTS_DEFAULT_WQ_SIZE; 117 | cmd.wq_item_size = IPTS_DEFAULT_WQ_ITEM_SIZE; 118 | } 119 | 120 | cmd.hid2me_addr_lower = lower_32_bits(ipts->resources.hid2me.dma_address); 121 | cmd.hid2me_addr_upper = upper_32_bits(ipts->resources.hid2me.dma_address); 122 | 123 | cmd.hid2me_size = ipts->resources.hid2me.size; 124 | 125 | ret = ipts_mei_send(&ipts->mei, IPTS_CMD_SET_MEM_WINDOW, &cmd, sizeof(cmd)); 126 | if (ret) 127 | return ret; 128 | 129 | ret = ipts_mei_recv(&ipts->mei, IPTS_CMD_SET_MEM_WINDOW, &rsp); 130 | if (ret) 131 | return ret; 132 | 133 | return rsp.status; 134 | } 135 | 136 | static int ipts_control_get_descriptor(struct ipts_context *ipts) 137 | { 138 | int ret = 0; 139 | 140 | struct ipts_cmd_get_hid_desc cmd = { 0 }; 141 | struct ipts_response rsp = { 0 }; 142 | 143 | /* 144 | * This command is only supported on EDS v2 devices. 145 | * 146 | * EDS v1 devices without native HID support will use a fallback HID descriptor. 147 | */ 148 | if (ipts->eds_intf_rev == 1) 149 | return 0; 150 | 151 | memset(ipts->resources.descriptor.address, 0, ipts->resources.descriptor.size); 152 | 153 | cmd.addr_lower = lower_32_bits(ipts->resources.descriptor.dma_address); 154 | cmd.addr_upper = upper_32_bits(ipts->resources.descriptor.dma_address); 155 | cmd.magic = 8; 156 | 157 | ret = ipts_mei_send(&ipts->mei, IPTS_CMD_GET_HID_DESC, &cmd, sizeof(cmd)); 158 | if (ret) 159 | return ret; 160 | 161 | ret = ipts_mei_recv(&ipts->mei, IPTS_CMD_GET_HID_DESC, &rsp); 162 | if (ret) 163 | return ret; 164 | 165 | return rsp.status; 166 | } 167 | 168 | int ipts_control_request_flush(struct ipts_context *ipts) 169 | { 170 | struct ipts_cmd_quiesce_io cmd = { 0 }; 171 | 172 | return ipts_mei_send(&ipts->mei, IPTS_CMD_QUIESCE_IO, &cmd, sizeof(cmd)); 173 | } 174 | 175 | int ipts_control_wait_flush(struct ipts_context *ipts) 176 | { 177 | int ret = 0; 178 | struct ipts_response rsp = { 0 }; 179 | 180 | ret = ipts_mei_recv(&ipts->mei, IPTS_CMD_QUIESCE_IO, &rsp); 181 | if (ret) 182 | return ret; 183 | 184 | return rsp.status; 185 | } 186 | 187 | int ipts_control_request_data(struct ipts_context *ipts) 188 | { 189 | return ipts_mei_send(&ipts->mei, IPTS_CMD_READY_FOR_DATA, NULL, 0); 190 | } 191 | 192 | int ipts_control_wait_data(struct ipts_context *ipts, struct ipts_rsp_ready_for_data *response) 193 | { 194 | int ret = 0; 195 | struct ipts_response rsp = { 0 }; 196 | 197 | ret = ipts_mei_recv(&ipts->mei, IPTS_CMD_READY_FOR_DATA, &rsp); 198 | if (ret) 199 | return ret; 200 | 201 | /* 202 | * During shutdown, it is possible that the sensor has already been disabled. 203 | */ 204 | if (rsp.status == IPTS_STATUS_SENSOR_DISABLED) 205 | return 0; 206 | 207 | if (rsp.status == IPTS_STATUS_SUCCESS && response) 208 | *response = rsp.payload.ready_for_data; 209 | 210 | return rsp.status; 211 | } 212 | 213 | int ipts_control_refill_buffer(struct ipts_context *ipts, struct ipts_data_buffer *buffer) 214 | { 215 | int ret = 0; 216 | size_t index = buffer->total_index % ipts->buffers; 217 | 218 | struct ipts_feedback_buffer *feedback = 219 | (struct ipts_feedback_buffer *)ipts->resources.feedback[index].address; 220 | 221 | struct ipts_cmd_feedback cmd = { 0 }; 222 | struct ipts_response rsp = { 0 }; 223 | 224 | cmd.buffer_index = index; 225 | cmd.transaction = buffer->transaction; 226 | 227 | memset(feedback, 0, sizeof(*feedback)); 228 | 229 | /* 230 | * The ME expects vendor specific data in the feedback buffer. 231 | * 232 | * On devices implementing EDS interface revison 2, the following will cause the feedback 233 | * to silently fail and not refill the buffer. 234 | * 235 | * Sending a buffer full of zeros triggers an invalid parameters error, but the ME will 236 | * properly refill the buffer. 237 | */ 238 | if (ipts->eds_intf_rev == 1) { 239 | feedback->total_index = buffer->total_index; 240 | feedback->cmd_type = IPTS_FEEDBACK_CMD_TYPE_NONE; 241 | feedback->data_type = IPTS_FEEDBACK_DATA_TYPE_VENDOR; 242 | 243 | feedback->size = 0; 244 | feedback->protocol_ver = buffer->protocol_ver; 245 | } 246 | 247 | ret = ipts_mei_send(&ipts->mei, IPTS_CMD_FEEDBACK, &cmd, sizeof(cmd)); 248 | if (ret) 249 | return ret; 250 | 251 | ret = ipts_mei_recv(&ipts->mei, IPTS_CMD_FEEDBACK, &rsp); 252 | if (ret) 253 | return ret; 254 | 255 | if (ipts->eds_intf_rev > 1 && rsp.status == IPTS_STATUS_INVALID_PARAMS) 256 | return 0; 257 | 258 | return rsp.status; 259 | } 260 | 261 | int ipts_control_hid2me_feedback(struct ipts_context *ipts, enum ipts_feedback_cmd_type cmd_type, 262 | enum ipts_feedback_data_type data_type, void *data, size_t size) 263 | { 264 | int ret = 0; 265 | struct ipts_feedback_buffer *buffer = NULL; 266 | 267 | struct ipts_cmd_feedback cmd = { 0 }; 268 | struct ipts_response rsp = { 0 }; 269 | 270 | memset(ipts->resources.hid2me.address, 0, ipts->resources.hid2me.size); 271 | buffer = (struct ipts_feedback_buffer *)ipts->resources.hid2me.address; 272 | 273 | buffer->cmd_type = cmd_type; 274 | buffer->data_type = data_type; 275 | buffer->size = size; 276 | buffer->total_index = IPTS_HID_2_ME_BUFFER_INDEX; 277 | 278 | if (size + sizeof(*buffer) > ipts->resources.hid2me.size) 279 | return -EINVAL; 280 | 281 | if (data && size > 0) 282 | memcpy(buffer->data, data, size); 283 | 284 | cmd.buffer_index = IPTS_HID_2_ME_BUFFER_INDEX; 285 | cmd.transaction = 0; 286 | 287 | ret = ipts_mei_send(&ipts->mei, IPTS_CMD_FEEDBACK, &cmd, sizeof(cmd)); 288 | if (ret) 289 | return ret; 290 | 291 | ret = ipts_mei_recv(&ipts->mei, IPTS_CMD_FEEDBACK, &rsp); 292 | if (ret) 293 | return ret; 294 | 295 | return rsp.status; 296 | } 297 | 298 | int ipts_control_start(struct ipts_context *ipts) 299 | { 300 | int ret = 0; 301 | 302 | dev_info(ipts->dev, "Starting IPTS\n"); 303 | 304 | ret = ipts_control_notify_dev_ready(ipts); 305 | if (ret) { 306 | dev_err(ipts->dev, "Failed to probe the touch sensor: %d\n", ret); 307 | return ret; 308 | } 309 | 310 | ret = ipts_control_get_device_info(ipts); 311 | if (ret) { 312 | dev_err(ipts->dev, "Failed to get device info: %d\n", ret); 313 | return ret; 314 | } 315 | 316 | dev_dbg(ipts->dev, "IPTS Device Info:\n"); 317 | dev_dbg(ipts->dev, "vendor = %04X\n", ipts->info.vendor); 318 | dev_dbg(ipts->dev, "product = %04X\n", ipts->info.product); 319 | dev_dbg(ipts->dev, "hw_rev = %d\n", ipts->info.hw_rev); 320 | dev_dbg(ipts->dev, "fw_rev = %d\n", ipts->info.fw_rev); 321 | dev_dbg(ipts->dev, "data_size = %d\n", ipts->info.data_size); 322 | dev_dbg(ipts->dev, "feedback_size = %d\n", ipts->info.feedback_size); 323 | dev_dbg(ipts->dev, "sensor_mode = %d\n", ipts->info.sensor_mode); 324 | dev_dbg(ipts->dev, "max_touch_points = %d\n", ipts->info.max_touch_points); 325 | dev_dbg(ipts->dev, "spi_frequency = %d\n", ipts->info.spi_frequency); 326 | dev_dbg(ipts->dev, "spi_io_mode = %d\n", ipts->info.spi_io_mode); 327 | dev_dbg(ipts->dev, "sensor_minor_eds_rev = %d\n", ipts->info.sensor_minor_eds_rev); 328 | dev_dbg(ipts->dev, "sensor_major_eds_rev = %d\n", ipts->info.sensor_major_eds_rev); 329 | dev_dbg(ipts->dev, "me_minor_eds_rev = %d\n", ipts->info.me_minor_eds_rev); 330 | dev_dbg(ipts->dev, "me_major_eds_rev = %d\n", ipts->info.me_major_eds_rev); 331 | dev_dbg(ipts->dev, "sensor_eds_intf_rev = %d\n", ipts->info.sensor_eds_intf_rev); 332 | dev_dbg(ipts->dev, "me_eds_intf_rev = %d\n", ipts->info.me_eds_intf_rev); 333 | dev_dbg(ipts->dev, "vendor_compat_ver = %d\n", ipts->info.vendor_compat_ver); 334 | 335 | /* 336 | * On EDS v2 devices both modes return the same data, so we always initialize the ME 337 | * in poll mode. We could stay in event mode, but it has issues with handling large 338 | * amounts of data and starts to lag. 339 | * 340 | * EDS v1 devices have to be initialized in event mode to get fallback singletouch events 341 | * until userspace can explicitly turn on raw data once it is ready for processing. 342 | */ 343 | if (ipts->eds_intf_rev > 1) 344 | ipts->mode = IPTS_MODE_POLL; 345 | 346 | ret = ipts_resources_init(&ipts->resources, ipts->dev, ipts->info); 347 | if (ret) { 348 | dev_err(ipts->dev, "Failed to allocate buffers: %d", ret); 349 | return ret; 350 | } 351 | 352 | ret = ipts_control_get_descriptor(ipts); 353 | if (ret) { 354 | dev_err(ipts->dev, "Failed to fetch HID descriptor: %d\n", ret); 355 | return ret; 356 | } 357 | 358 | ret = ipts_control_set_mode(ipts); 359 | if (ret) { 360 | dev_err(ipts->dev, "Failed to set mode: %d\n", ret); 361 | return ret; 362 | } 363 | 364 | ret = ipts_control_set_mem_window(ipts); 365 | if (ret) { 366 | dev_err(ipts->dev, "Failed to set memory window: %d\n", ret); 367 | return ret; 368 | } 369 | 370 | ret = ipts_receiver_start(ipts); 371 | if (ret) { 372 | dev_err(ipts->dev, "Failed to start receiver: %d\n", ret); 373 | return ret; 374 | } 375 | 376 | ret = ipts_control_request_data(ipts); 377 | if (ret) { 378 | dev_err(ipts->dev, "Failed to request data: %d\n", ret); 379 | return ret; 380 | } 381 | 382 | ipts_hid_enable(ipts); 383 | 384 | ret = ipts_hid_init(ipts); 385 | if (ret) { 386 | dev_err(ipts->dev, "Failed to initialize HID device: %d\n", ret); 387 | return ret; 388 | } 389 | 390 | return 0; 391 | } 392 | 393 | static int _ipts_control_stop(struct ipts_context *ipts) 394 | { 395 | int ret = 0; 396 | 397 | ipts_hid_disable(ipts); 398 | dev_info(ipts->dev, "Stopping IPTS\n"); 399 | 400 | ret = ipts_receiver_stop(ipts); 401 | if (ret) { 402 | dev_err(ipts->dev, "Failed to stop receiver: %d\n", ret); 403 | return ret; 404 | } 405 | 406 | ipts_resources_free(&ipts->resources); 407 | return 0; 408 | } 409 | 410 | int ipts_control_stop(struct ipts_context *ipts) 411 | { 412 | int ret = 0; 413 | 414 | ret = _ipts_control_stop(ipts); 415 | if (ret) 416 | return ret; 417 | 418 | ret = ipts_hid_free(ipts); 419 | if (ret) { 420 | dev_err(ipts->dev, "Failed to free HID device: %d\n", ret); 421 | return ret; 422 | } 423 | 424 | return 0; 425 | } 426 | 427 | int ipts_control_restart(struct ipts_context *ipts) 428 | { 429 | int ret = 0; 430 | 431 | ret = _ipts_control_stop(ipts); 432 | if (ret) 433 | return ret; 434 | 435 | /* 436 | * Wait a second to give the sensor time to fully shut down. 437 | */ 438 | msleep(1000); 439 | 440 | ret = ipts_control_start(ipts); 441 | if (ret) 442 | return ret; 443 | 444 | return 0; 445 | } 446 | -------------------------------------------------------------------------------- /src/control.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (c) 2020-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #ifndef IPTS_CONTROL_H 9 | #define IPTS_CONTROL_H 10 | 11 | #include 12 | 13 | #include "context.h" 14 | #include "spec-dma.h" 15 | #include "spec-mei.h" 16 | 17 | int ipts_control_request_flush(struct ipts_context *ipts); 18 | int ipts_control_wait_flush(struct ipts_context *ipts); 19 | int ipts_control_request_data(struct ipts_context *ipts); 20 | int ipts_control_wait_data(struct ipts_context *ipts, struct ipts_rsp_ready_for_data *response); 21 | int ipts_control_refill_buffer(struct ipts_context *ipts, struct ipts_data_buffer *buffer); 22 | int ipts_control_hid2me_feedback(struct ipts_context *ipts, enum ipts_feedback_cmd_type cmd_type, 23 | enum ipts_feedback_data_type data_type, void *data, size_t size); 24 | 25 | int ipts_control_start(struct ipts_context *ipts); 26 | int ipts_control_stop(struct ipts_context *ipts); 27 | int ipts_control_restart(struct ipts_context *ipts); 28 | 29 | #endif /* IPTS_CONTROL_H */ 30 | -------------------------------------------------------------------------------- /src/eds1.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "eds1.h" 15 | #include "context.h" 16 | #include "control.h" 17 | #include "spec-hid.h" 18 | #include "spec-mei.h" 19 | 20 | int ipts_eds1_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size) 21 | { 22 | u8 *buffer = NULL; 23 | size_t size = sizeof(ipts_singletouch_descriptor) + sizeof(ipts_fallback_descriptor); 24 | 25 | buffer = kzalloc(size, GFP_KERNEL); 26 | if (!buffer) 27 | return -ENOMEM; 28 | 29 | memcpy(buffer, ipts_singletouch_descriptor, sizeof(ipts_singletouch_descriptor)); 30 | memcpy(&buffer[sizeof(ipts_singletouch_descriptor)], ipts_fallback_descriptor, 31 | sizeof(ipts_fallback_descriptor)); 32 | 33 | *desc_size = size; 34 | *desc_buffer = buffer; 35 | 36 | return 0; 37 | } 38 | 39 | static int ipts_eds1_switch_mode(struct ipts_context *ipts, enum ipts_mode mode) 40 | { 41 | int ret = 0; 42 | 43 | if (ipts->mode == mode) 44 | return 0; 45 | 46 | ipts->mode = mode; 47 | 48 | ret = ipts_control_restart(ipts); 49 | if (ret) 50 | dev_err(ipts->dev, "Failed to switch modes: %d\n", ret); 51 | 52 | return ret; 53 | } 54 | 55 | int ipts_eds1_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, 56 | enum hid_report_type report_type, enum hid_class_request request_type) 57 | { 58 | struct ipts_hid_report_set_mode *report = (struct ipts_hid_report_set_mode *)buffer; 59 | 60 | if (report_id != IPTS_HID_REPORT_SET_MODE) 61 | return -EIO; 62 | 63 | if (report_type != HID_FEATURE_REPORT) 64 | return -EIO; 65 | 66 | if (size != 2) 67 | return -EINVAL; 68 | 69 | /* 70 | * Implement mode switching report for older devices without native HID support. 71 | */ 72 | 73 | if (request_type == HID_REQ_SET_REPORT) 74 | return ipts_eds1_switch_mode(ipts, report->mode); 75 | 76 | if (request_type != HID_REQ_GET_REPORT) 77 | return -EIO; 78 | 79 | memset(report, 0, sizeof(*report)); 80 | 81 | report->report_id = IPTS_HID_REPORT_SET_MODE; 82 | report->mode = ipts->mode; 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /src/eds1.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #ifndef IPTS_EDS1_H 9 | #define IPTS_EDS1_H 10 | 11 | #include 12 | #include 13 | 14 | #include "context.h" 15 | 16 | int ipts_eds1_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size); 17 | int ipts_eds1_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, 18 | enum hid_report_type report_type, enum hid_class_request request_type); 19 | 20 | #endif /* IPTS_EDS1_H */ 21 | -------------------------------------------------------------------------------- /src/eds2.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "eds2.h" 16 | #include "context.h" 17 | #include "control.h" 18 | #include "resources.h" 19 | #include "spec-dma.h" 20 | #include "spec-hid.h" 21 | 22 | /** 23 | * GET_FEATURES_TIMEOUT - How long to wait for the reply to a GET_FEATURES request. 24 | * 25 | * Sometimes the answer can take 10 seconds or more to arrive, 26 | * so lets just wait for a long time to be sure. 27 | */ 28 | #define GET_FEATURES_TIMEOUT 30 * MSEC_PER_SEC 29 | 30 | int ipts_eds2_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size) 31 | { 32 | u8 *buffer = NULL; 33 | size_t size = 0; 34 | 35 | struct ipts_data_buffer *descbuffer = 36 | (struct ipts_data_buffer *)ipts->resources.descriptor.address; 37 | 38 | if (descbuffer->type != IPTS_DATA_TYPE_HID_DESCRIPTOR) 39 | return -ENODATA; 40 | 41 | size = sizeof(ipts_singletouch_descriptor) + descbuffer->size - 8; 42 | 43 | buffer = kzalloc(size, GFP_KERNEL); 44 | if (!buffer) 45 | return -ENOMEM; 46 | 47 | memcpy(buffer, ipts_singletouch_descriptor, sizeof(ipts_singletouch_descriptor)); 48 | memcpy(&buffer[sizeof(ipts_singletouch_descriptor)], &descbuffer->data[8], 49 | descbuffer->size - 8); 50 | 51 | *desc_size = size; 52 | *desc_buffer = buffer; 53 | 54 | return 0; 55 | } 56 | 57 | static int ipts_eds2_get_feature(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, 58 | enum ipts_feedback_data_type type) 59 | { 60 | int ret = 0; 61 | 62 | struct ipts_buffer feature = ipts->resources.feature; 63 | struct ipts_data_buffer *response = (struct ipts_data_buffer *)feature.address; 64 | 65 | mutex_lock(&ipts->feature_lock); 66 | 67 | memset(buffer, 0, size); 68 | buffer[0] = report_id; 69 | 70 | reinit_completion(&ipts->feature_event); 71 | 72 | ret = ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE, type, buffer, size); 73 | if (ret) { 74 | dev_err(ipts->dev, "Failed to send hid2me feedback: %d\n", ret); 75 | goto out; 76 | } 77 | 78 | ret = wait_for_completion_timeout(&ipts->feature_event, 79 | msecs_to_jiffies(GET_FEATURES_TIMEOUT)); 80 | 81 | if (ret == 0) { 82 | dev_warn(ipts->dev, "GET_FEATURES timed out!\n"); 83 | ret = -ETIMEDOUT; 84 | goto out; 85 | } 86 | 87 | if (response->size > size) { 88 | ret = -ETOOSMALL; 89 | goto out; 90 | } 91 | 92 | ret = response->size; 93 | memcpy(buffer, response->data, response->size); 94 | 95 | out: 96 | mutex_unlock(&ipts->feature_lock); 97 | return ret; 98 | } 99 | 100 | static int ipts_eds2_set_feature(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, 101 | enum ipts_feedback_data_type type) 102 | { 103 | int ret = 0; 104 | 105 | buffer[0] = report_id; 106 | 107 | ret = ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE, type, buffer, size); 108 | if (ret) 109 | dev_err(ipts->dev, "Failed to send hid2me feedback: %d\n", ret); 110 | 111 | return ret; 112 | } 113 | 114 | int ipts_eds2_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, 115 | enum hid_report_type report_type, enum hid_class_request request_type) 116 | { 117 | enum ipts_feedback_data_type feedback_type = IPTS_FEEDBACK_DATA_TYPE_VENDOR; 118 | 119 | if (report_type == HID_OUTPUT_REPORT && request_type == HID_REQ_SET_REPORT) 120 | feedback_type = IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT; 121 | else if (report_type == HID_FEATURE_REPORT && request_type == HID_REQ_GET_REPORT) 122 | feedback_type = IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES; 123 | else if (report_type == HID_FEATURE_REPORT && request_type == HID_REQ_SET_REPORT) 124 | feedback_type = IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES; 125 | else 126 | return -EIO; 127 | 128 | if (request_type == HID_REQ_GET_REPORT) 129 | return ipts_eds2_get_feature(ipts, buffer, size, report_id, feedback_type); 130 | else 131 | return ipts_eds2_set_feature(ipts, buffer, size, report_id, feedback_type); 132 | } 133 | -------------------------------------------------------------------------------- /src/eds2.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #ifndef IPTS_EDS2_H 9 | #define IPTS_EDS2_H 10 | 11 | #include 12 | #include 13 | 14 | #include "context.h" 15 | 16 | int ipts_eds2_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size); 17 | int ipts_eds2_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, 18 | enum hid_report_type report_type, enum hid_class_request request_type); 19 | 20 | #endif /* IPTS_EDS2_H */ 21 | -------------------------------------------------------------------------------- /src/hid.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2022-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "context.h" 17 | #include "eds1.h" 18 | #include "eds2.h" 19 | #include "hid.h" 20 | #include "resources.h" 21 | #include "spec-dma.h" 22 | #include "spec-hid.h" 23 | 24 | static int ipts_hid_start(struct hid_device *hid) 25 | { 26 | return 0; 27 | } 28 | 29 | static void ipts_hid_stop(struct hid_device *hid) 30 | { 31 | } 32 | 33 | static int ipts_hid_parse(struct hid_device *hid) 34 | { 35 | int ret = 0; 36 | 37 | u8 *buffer = NULL; 38 | size_t size = 0; 39 | 40 | struct ipts_context *ipts = hid->driver_data; 41 | 42 | if (!READ_ONCE(ipts->hid_active)) 43 | return -ENODEV; 44 | 45 | if (ipts->eds_intf_rev == 1) 46 | ret = ipts_eds1_get_descriptor(ipts, &buffer, &size); 47 | else 48 | ret = ipts_eds2_get_descriptor(ipts, &buffer, &size); 49 | 50 | if (ret) { 51 | dev_err(ipts->dev, "Failed to allocate HID descriptor: %d\n", ret); 52 | return ret; 53 | } 54 | 55 | ret = hid_parse_report(hid, buffer, size); 56 | kfree(buffer); 57 | 58 | if (ret) { 59 | dev_err(ipts->dev, "Failed to parse HID descriptor: %d\n", ret); 60 | return ret; 61 | } 62 | 63 | return 0; 64 | } 65 | 66 | static int ipts_hid_raw_request(struct hid_device *hid, unsigned char report_id, __u8 *buffer, 67 | size_t size, unsigned char report_type, int request_type) 68 | { 69 | struct ipts_context *ipts = hid->driver_data; 70 | 71 | if (!READ_ONCE(ipts->hid_active)) 72 | return -ENODEV; 73 | 74 | if (ipts->eds_intf_rev == 1) { 75 | return ipts_eds1_raw_request(ipts, buffer, size, report_id, report_type, 76 | request_type); 77 | } else { 78 | return ipts_eds2_raw_request(ipts, buffer, size, report_id, report_type, 79 | request_type); 80 | } 81 | } 82 | 83 | static struct hid_ll_driver ipts_hid_driver = { 84 | .start = ipts_hid_start, 85 | .stop = ipts_hid_stop, 86 | .open = ipts_hid_start, 87 | .close = ipts_hid_stop, 88 | .parse = ipts_hid_parse, 89 | .raw_request = ipts_hid_raw_request, 90 | }; 91 | 92 | /** 93 | * ipts_hid_handle_frame() - Forward an IPTS data frame to userspace. 94 | * 95 | * IPTS data frames are a custom format for wrapping raw multitouch data, for example capacitive 96 | * heatmaps. We cannot process this data in the kernel, so we wrap it in a HID report and forward 97 | * it to userspace, where a dedicated tool can do the required processing. 98 | * 99 | * @ipts: 100 | * The IPTS driver context. 101 | * 102 | * @buffer: 103 | * The data buffer containing the data frame that is being forwarded. 104 | * 105 | * Returns: 0 on success, negative errno code on error. 106 | */ 107 | static int ipts_hid_handle_frame(struct ipts_context *ipts, struct ipts_data_buffer *buffer) 108 | { 109 | struct ipts_hid_report_data *report = NULL; 110 | 111 | if (buffer->size + sizeof(*report) > IPTS_HID_REPORT_DATA_SIZE) 112 | return -ERANGE; 113 | 114 | report = (struct ipts_hid_report_data *)ipts->resources.report.address; 115 | memset(report, 0, ipts->resources.report.size); 116 | 117 | /* 118 | * Synthesize a HID report that matches how the Surface Pro 7 transmits multitouch data. 119 | */ 120 | 121 | report->report_id = IPTS_HID_REPORT_DATA; 122 | report->gesture_char_quality.type = IPTS_HID_FRAME_TYPE_RAW; 123 | report->gesture_char_quality.size = buffer->size + sizeof(report->gesture_char_quality); 124 | 125 | memcpy(report->gesture_char_quality.data, buffer->data, buffer->size); 126 | 127 | return hid_input_report(ipts->hid, HID_INPUT_REPORT, (u8 *)report, 128 | IPTS_HID_REPORT_DATA_SIZE, 1); 129 | } 130 | 131 | static int ipts_hid_handle_hid(struct ipts_context *ipts, struct ipts_data_buffer *buffer) 132 | { 133 | return hid_input_report(ipts->hid, HID_INPUT_REPORT, buffer->data, buffer->size, 1); 134 | } 135 | 136 | /** 137 | * ipts_hid_handle_get_features() - Process the answer to a GET_FEATURES request. 138 | * 139 | * When doing a GET_FEATURES request using HID2ME feedback, the answer will be sent in one of the 140 | * normal data buffers. When we get such data, we copy it, and then signal the waiting HID thread 141 | * that data has arrived. 142 | * 143 | * @ipts: 144 | * The IPTS driver context. 145 | * 146 | * @buffer: 147 | * The data buffer containing the result of the GET_FEATURES request. 148 | * 149 | * Returns: 0 on success, negative errno code on error. 150 | */ 151 | static int ipts_hid_handle_get_features(struct ipts_context *ipts, struct ipts_data_buffer *buffer) 152 | { 153 | size_t size = min(ipts->resources.feature.size, sizeof(*buffer) + buffer->size); 154 | 155 | memcpy(ipts->resources.feature.address, buffer, size); 156 | complete_all(&ipts->feature_event); 157 | 158 | return 0; 159 | } 160 | 161 | int ipts_hid_input_data(struct ipts_context *ipts, struct ipts_data_buffer *buffer) 162 | { 163 | if (!READ_ONCE(ipts->hid_active)) 164 | return -ENODEV; 165 | 166 | if (buffer->size == 0) 167 | return 0; 168 | 169 | switch (buffer->type) { 170 | case IPTS_DATA_TYPE_FRAME: 171 | return ipts_hid_handle_frame(ipts, buffer); 172 | case IPTS_DATA_TYPE_HID: 173 | return ipts_hid_handle_hid(ipts, buffer); 174 | case IPTS_DATA_TYPE_GET_FEATURES: 175 | return ipts_hid_handle_get_features(ipts, buffer); 176 | default: 177 | dev_info(ipts->dev, "Unhandled data type: %d\n", buffer->type); 178 | } 179 | 180 | return 0; 181 | } 182 | 183 | int ipts_hid_init(struct ipts_context *ipts) 184 | { 185 | int ret = 0; 186 | 187 | if (ipts->hid) 188 | return 0; 189 | 190 | ipts->hid = hid_allocate_device(); 191 | if (IS_ERR(ipts->hid)) { 192 | int err = PTR_ERR(ipts->hid); 193 | 194 | dev_err(ipts->dev, "Failed to allocate HID device: %d\n", err); 195 | return err; 196 | } 197 | 198 | ipts->hid->driver_data = ipts; 199 | ipts->hid->dev.parent = ipts->dev; 200 | ipts->hid->ll_driver = &ipts_hid_driver; 201 | 202 | ipts->hid->vendor = ipts->info.vendor; 203 | ipts->hid->product = ipts->info.product; 204 | ipts->hid->group = HID_GROUP_GENERIC; 205 | 206 | snprintf(ipts->hid->name, sizeof(ipts->hid->name), "IPTS %04X:%04X", ipts->info.vendor, 207 | ipts->info.product); 208 | 209 | ret = hid_add_device(ipts->hid); 210 | if (ret) { 211 | dev_err(ipts->dev, "Failed to add HID device: %d\n", ret); 212 | ipts_hid_free(ipts); 213 | return ret; 214 | } 215 | 216 | return 0; 217 | } 218 | 219 | int ipts_hid_free(struct ipts_context *ipts) 220 | { 221 | if (!ipts->hid) 222 | return 0; 223 | 224 | hid_destroy_device(ipts->hid); 225 | ipts->hid = NULL; 226 | 227 | return 0; 228 | } 229 | -------------------------------------------------------------------------------- /src/hid.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (c) 2022-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #ifndef IPTS_HID_H 9 | #define IPTS_HID_H 10 | 11 | #include 12 | 13 | #include "context.h" 14 | #include "spec-dma.h" 15 | 16 | static inline void ipts_hid_enable(struct ipts_context *ipts) 17 | { 18 | WRITE_ONCE(ipts->hid_active, true); 19 | } 20 | 21 | static inline void ipts_hid_disable(struct ipts_context *ipts) 22 | { 23 | WRITE_ONCE(ipts->hid_active, false); 24 | } 25 | 26 | int ipts_hid_input_data(struct ipts_context *ipts, struct ipts_data_buffer *buffer); 27 | 28 | int ipts_hid_init(struct ipts_context *ipts); 29 | int ipts_hid_free(struct ipts_context *ipts); 30 | 31 | #endif /* IPTS_HID_H */ 32 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2020-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "context.h" 21 | #include "control.h" 22 | #include "mei.h" 23 | #include "spec-mei.h" 24 | 25 | static int ipts_set_dma_mask(struct mei_cl_device *cldev) 26 | { 27 | if (!dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64))) 28 | return 0; 29 | 30 | return dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(32)); 31 | } 32 | 33 | static int ipts_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) 34 | { 35 | int ret = 0; 36 | struct ipts_context *ipts = NULL; 37 | 38 | ret = ipts_set_dma_mask(cldev); 39 | if (ret) { 40 | dev_err(&cldev->dev, "Failed to set DMA mask for IPTS: %d\n", ret); 41 | return ret; 42 | } 43 | 44 | ret = mei_cldev_enable(cldev); 45 | if (ret) { 46 | dev_err(&cldev->dev, "Failed to enable MEI device: %d\n", ret); 47 | return ret; 48 | } 49 | 50 | ipts = devm_kzalloc(&cldev->dev, sizeof(*ipts), GFP_KERNEL); 51 | if (!ipts) { 52 | mei_cldev_disable(cldev); 53 | return -ENOMEM; 54 | } 55 | 56 | ipts_mei_init(&ipts->mei, cldev); 57 | 58 | ipts->dev = &cldev->dev; 59 | ipts->mode = IPTS_MODE_EVENT; 60 | 61 | mutex_init(&ipts->feature_lock); 62 | init_completion(&ipts->feature_event); 63 | 64 | mei_cldev_set_drvdata(cldev, ipts); 65 | 66 | ret = ipts_control_start(ipts); 67 | if (ret) { 68 | dev_err(&cldev->dev, "Failed to start IPTS: %d\n", ret); 69 | return ret; 70 | } 71 | 72 | return 0; 73 | } 74 | 75 | static void ipts_remove(struct mei_cl_device *cldev) 76 | { 77 | int ret = 0; 78 | struct ipts_context *ipts = mei_cldev_get_drvdata(cldev); 79 | 80 | ret = ipts_control_stop(ipts); 81 | if (ret) 82 | dev_err(&cldev->dev, "Failed to stop IPTS: %d\n", ret); 83 | 84 | mei_cldev_disable(cldev); 85 | } 86 | 87 | static struct mei_cl_device_id ipts_device_id_table[] = { 88 | { .uuid = MEI_UUID_IPTS, .version = MEI_CL_VERSION_ANY }, 89 | {}, 90 | }; 91 | MODULE_DEVICE_TABLE(mei, ipts_device_id_table); 92 | 93 | static struct mei_cl_driver ipts_driver = { 94 | .id_table = ipts_device_id_table, 95 | .name = "ipts", 96 | .probe = ipts_probe, 97 | .remove = ipts_remove, 98 | }; 99 | module_mei_cl_driver(ipts_driver); 100 | 101 | MODULE_DESCRIPTION("IPTS touchscreen driver"); 102 | MODULE_AUTHOR("Dorian Stoll "); 103 | MODULE_LICENSE("GPL"); 104 | -------------------------------------------------------------------------------- /src/mei.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "context.h" 20 | #include "mei.h" 21 | #include "spec-mei.h" 22 | 23 | static void locked_list_add(struct list_head *new, struct list_head *head, 24 | struct rw_semaphore *lock) 25 | { 26 | down_write(lock); 27 | list_add(new, head); 28 | up_write(lock); 29 | } 30 | 31 | static void locked_list_del(struct list_head *entry, struct rw_semaphore *lock) 32 | { 33 | down_write(lock); 34 | list_del(entry); 35 | up_write(lock); 36 | } 37 | 38 | static void ipts_mei_incoming(struct mei_cl_device *cldev) 39 | { 40 | int i = 0; 41 | ssize_t ret = 0; 42 | 43 | struct ipts_mei_message *entry = NULL; 44 | struct ipts_context *ipts = mei_cldev_get_drvdata(cldev); 45 | 46 | entry = devm_kzalloc(ipts->dev, sizeof(*entry), GFP_KERNEL); 47 | if (!entry) 48 | return; 49 | 50 | INIT_LIST_HEAD(&entry->list); 51 | 52 | /* 53 | * System calls can interrupt the MEI bus API functions. 54 | * If this happens, try to repeat the call until it starts working. 55 | */ 56 | 57 | for (i = 0; i < 100; i++) { 58 | ret = mei_cldev_recv(cldev, (u8 *)&entry->response, sizeof(entry->response)); 59 | 60 | if (ret != -EINTR) 61 | break; 62 | 63 | msleep(100); 64 | } 65 | 66 | if (ret < 0) { 67 | dev_err(ipts->dev, "Failed to read MEI message: %ld\n", ret); 68 | return; 69 | } 70 | 71 | if (ret == 0) { 72 | dev_err(ipts->dev, "Received empty MEI message\n"); 73 | return; 74 | } 75 | 76 | dev_dbg(ipts->dev, "MEI thread received message with code 0x%X and status 0x%X\n", 77 | entry->response.cmd, entry->response.status); 78 | 79 | locked_list_add(&entry->list, &ipts->mei.messages, &ipts->mei.message_lock); 80 | wake_up_all(&ipts->mei.message_queue); 81 | } 82 | 83 | static int ipts_mei_search(struct ipts_mei *mei, enum ipts_command_code code, 84 | struct ipts_response *response) 85 | { 86 | struct ipts_mei_message *entry = NULL; 87 | 88 | down_read(&mei->message_lock); 89 | 90 | /* 91 | * Iterate over the list of received messages, and check if there is one 92 | * matching the requested command code. 93 | */ 94 | list_for_each_entry(entry, &mei->messages, list) { 95 | if (entry->response.cmd == IPTS_ME_2_HOST_MSG(code)) 96 | break; 97 | } 98 | 99 | up_read(&mei->message_lock); 100 | 101 | /* 102 | * If entry is not the list head, this means that the loop above has been stopped early, 103 | * and that we found a matching element. We drop the message from the list and return it. 104 | */ 105 | if (!list_entry_is_head(entry, &mei->messages, list)) { 106 | locked_list_del(&entry->list, &mei->message_lock); 107 | 108 | *response = entry->response; 109 | devm_kfree(&mei->cldev->dev, entry); 110 | 111 | dev_dbg(&mei->cldev->dev, "Driver read message with code 0x%X and status 0x%X\n", 112 | response->cmd, response->status); 113 | 114 | if (response->status == IPTS_STATUS_TIMEOUT) 115 | return -EAGAIN; 116 | 117 | /* 118 | * Ignore all errors that the spec allows us to ignore. 119 | */ 120 | 121 | if (response->status == IPTS_STATUS_COMPAT_CHECK_FAIL) 122 | response->status = IPTS_STATUS_SUCCESS; 123 | 124 | if (response->status == IPTS_STATUS_INVALID_DEVICE_CAPS) 125 | response->status = IPTS_STATUS_SUCCESS; 126 | 127 | if (response->status == IPTS_STATUS_SENSOR_FAIL_NONFATAL) 128 | response->status = IPTS_STATUS_SUCCESS; 129 | 130 | return 0; 131 | } 132 | 133 | return -EAGAIN; 134 | } 135 | 136 | int ipts_mei_recv_timeout(struct ipts_mei *mei, enum ipts_command_code code, 137 | struct ipts_response *response, u64 timeout) 138 | { 139 | int ret = 0; 140 | 141 | /* 142 | * A timeout of 0 means check and return immideately. 143 | */ 144 | if (timeout == 0) 145 | return ipts_mei_search(mei, code, response); 146 | 147 | /* 148 | * A timeout of less than 0 means to wait forever. 149 | */ 150 | if (timeout < 0) { 151 | wait_event(mei->message_queue, ipts_mei_search(mei, code, response) == 0); 152 | return 0; 153 | } 154 | 155 | ret = wait_event_timeout(mei->message_queue, ipts_mei_search(mei, code, response) == 0, 156 | msecs_to_jiffies(timeout)); 157 | 158 | if (ret > 0) 159 | return 0; 160 | 161 | return -EAGAIN; 162 | } 163 | 164 | int ipts_mei_send(struct ipts_mei *mei, enum ipts_command_code code, void *payload, size_t size) 165 | { 166 | int i = 0; 167 | int ret = 0; 168 | 169 | struct ipts_command cmd = { 0 }; 170 | 171 | cmd.cmd = code; 172 | 173 | if (payload && size > 0) 174 | memcpy(cmd.payload.raw, payload, size); 175 | 176 | dev_dbg(&mei->cldev->dev, "Driver sent message with code 0x%X and %ld bytes payload\n", 177 | code, size); 178 | 179 | /* 180 | * System calls can interrupt the MEI bus API functions. 181 | * If this happens, try to repeat the call until it starts working. 182 | */ 183 | 184 | for (i = 0; i < 100; i++) { 185 | ret = mei_cldev_send(mei->cldev, (u8 *)&cmd, sizeof(cmd.cmd) + size); 186 | 187 | if (ret != -EINTR) 188 | break; 189 | 190 | msleep(100); 191 | } 192 | 193 | if (ret < 0) { 194 | dev_err(&mei->cldev->dev, "Failed to send MEI message: %d\n", ret); 195 | return ret; 196 | } 197 | 198 | return 0; 199 | } 200 | 201 | void ipts_mei_init(struct ipts_mei *mei, struct mei_cl_device *cldev) 202 | { 203 | mei->cldev = cldev; 204 | 205 | INIT_LIST_HEAD(&mei->messages); 206 | init_waitqueue_head(&mei->message_queue); 207 | init_rwsem(&mei->message_lock); 208 | 209 | mei_cldev_register_rx_cb(cldev, ipts_mei_incoming); 210 | } 211 | -------------------------------------------------------------------------------- /src/mei.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (c) 2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #ifndef IPTS_MEI_H 9 | #define IPTS_MEI_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "spec-mei.h" 18 | 19 | /** 20 | * struct ipts_mei - Wrapper for interacting with the MEI bus. 21 | * 22 | * It is possible that the ME will sometimes send messages out of order. To universally support 23 | * this behaviour, the wrapper will store incoming messages in a list and then implement a 24 | * custom receive logic on top of that by explicitly searching for a certain command code. 25 | * 26 | * The MEI API also only allows the driver to either wait forever, or not at all. This wrapper 27 | * adds the ability to wait for a configurable amount of time and then return -EAGAIN. The caller 28 | * can then decide if it wants to try again or error out. 29 | * 30 | * @cldev: 31 | * The MEI client device. 32 | * 33 | * @messages: 34 | * The list of received messages. See &struct ipts_mei_message. 35 | * 36 | * @message_lock: 37 | * Prevent multiple threads from writing to the list of messages at the same time. 38 | * 39 | * @message_queue: 40 | * Wait queue where callers can wait for new messages with the correct command code. 41 | */ 42 | struct ipts_mei { 43 | struct mei_cl_device *cldev; 44 | 45 | struct list_head messages; 46 | struct rw_semaphore message_lock; 47 | 48 | wait_queue_head_t message_queue; 49 | }; 50 | 51 | struct ipts_mei_message { 52 | struct list_head list; 53 | struct ipts_response response; 54 | }; 55 | 56 | int ipts_mei_send(struct ipts_mei *mei, enum ipts_command_code code, void *payload, size_t size); 57 | int ipts_mei_recv_timeout(struct ipts_mei *mei, enum ipts_command_code code, 58 | struct ipts_response *response, u64 timeout); 59 | 60 | static inline int ipts_mei_recv(struct ipts_mei *mei, enum ipts_command_code code, 61 | struct ipts_response *response) 62 | { 63 | return ipts_mei_recv_timeout(mei, code, response, 1 * MSEC_PER_SEC); 64 | } 65 | 66 | void ipts_mei_init(struct ipts_mei *mei, struct mei_cl_device *cldev); 67 | 68 | #endif /* IPTS_MEI_H */ 69 | -------------------------------------------------------------------------------- /src/receiver.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2020-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "receiver.h" 16 | #include "context.h" 17 | #include "control.h" 18 | #include "hid.h" 19 | #include "resources.h" 20 | #include "spec-dma.h" 21 | #include "spec-mei.h" 22 | #include "thread.h" 23 | 24 | static int ipts_receiver_event(struct ipts_thread *thread) 25 | { 26 | int ret = 0; 27 | struct ipts_context *ipts = thread->data; 28 | 29 | dev_info(ipts->dev, "IPTS running in event mode\n"); 30 | 31 | while (!ipts_thread_should_stop(thread)) { 32 | struct ipts_rsp_ready_for_data rsp = { 0 }; 33 | struct ipts_data_buffer *buffer = NULL; 34 | 35 | ret = ipts_control_wait_data(ipts, &rsp); 36 | if (ret == -EAGAIN) 37 | continue; 38 | 39 | if (ret) { 40 | dev_err(ipts->dev, "Failed to wait for data: %d\n", ret); 41 | continue; 42 | } 43 | 44 | buffer = (struct ipts_data_buffer *)ipts->resources.data[rsp.buffer_index].address; 45 | 46 | ret = ipts_hid_input_data(ipts, buffer); 47 | if (ret) 48 | dev_err(ipts->dev, "Failed to process buffer: %d\n", ret); 49 | 50 | ret = ipts_control_refill_buffer(ipts, buffer); 51 | if (ret) 52 | dev_err(ipts->dev, "Failed to send feedback: %d\n", ret); 53 | 54 | ret = ipts_control_request_data(ipts); 55 | if (ret) 56 | dev_err(ipts->dev, "Failed to request data: %d\n", ret); 57 | } 58 | 59 | ret = ipts_control_request_flush(ipts); 60 | if (ret) { 61 | dev_err(ipts->dev, "Failed to request flush: %d\n", ret); 62 | return ret; 63 | } 64 | 65 | ret = ipts_control_wait_data(ipts, NULL); 66 | if (ret) { 67 | dev_err(ipts->dev, "Failed to wait for data: %d\n", ret); 68 | 69 | if (ret != -EAGAIN) 70 | return ret; 71 | else 72 | return 0; 73 | } 74 | 75 | ret = ipts_control_wait_flush(ipts); 76 | if (ret) { 77 | dev_err(ipts->dev, "Failed to wait for flush: %d\n", ret); 78 | 79 | if (ret != -EAGAIN) 80 | return ret; 81 | else 82 | return 0; 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | static int ipts_receiver_poll(struct ipts_thread *thread) 89 | { 90 | int ret = 0; 91 | 92 | struct ipts_context *ipts = thread->data; 93 | time64_t last = ktime_get_seconds(); 94 | 95 | u32 current_buffer = 0; 96 | u32 next_buffer = 0; 97 | 98 | dev_info(ipts->dev, "IPTS running in poll mode\n"); 99 | 100 | while (true) { 101 | if (ipts_thread_should_stop(thread)) { 102 | ret = ipts_control_request_flush(ipts); 103 | if (ret) { 104 | dev_err(ipts->dev, "Failed to request flush: %d\n", ret); 105 | return ret; 106 | } 107 | 108 | /* 109 | * We have to process all outstanding data for the flush to succeed. 110 | */ 111 | } 112 | 113 | /* 114 | * After filling up one of the data buffers, the ME will increment the doorbell. 115 | * The value of the doorbell stands for the *next* buffer that the ME will fill. 116 | * 117 | * We read the doorbell address only once to force the loop to sleep at some point. 118 | */ 119 | next_buffer = *(u32 *)ipts->resources.doorbell.address; 120 | 121 | while (current_buffer != next_buffer) { 122 | struct ipts_data_buffer *buffer = NULL; 123 | size_t index = current_buffer % ipts->buffers; 124 | 125 | buffer = (struct ipts_data_buffer *)ipts->resources.data[index].address; 126 | 127 | ret = ipts_hid_input_data(ipts, buffer); 128 | if (ret) 129 | dev_err(ipts->dev, "Failed to process buffer: %d\n", ret); 130 | 131 | ret = ipts_control_refill_buffer(ipts, buffer); 132 | if (ret) 133 | dev_err(ipts->dev, "Failed to send feedback: %d\n", ret); 134 | 135 | last = ktime_get_seconds(); 136 | current_buffer++; 137 | } 138 | 139 | if (ipts_thread_should_stop(thread)) 140 | break; 141 | 142 | /* 143 | * If the last change was less than 5 seconds ago, sleep for a shorter period so 144 | * that new data can be processed quickly. If there was no change for more than 145 | * 5 seconds, sleep longer to avoid wasting CPU cycles. 146 | */ 147 | if (last + 5 > ktime_get_seconds()) 148 | usleep_range(1 * USEC_PER_MSEC, 5 * USEC_PER_MSEC); 149 | else 150 | msleep(200); 151 | } 152 | 153 | ret = ipts_control_wait_data(ipts, NULL); 154 | if (ret) { 155 | dev_err(ipts->dev, "Failed to wait for data: %d\n", ret); 156 | 157 | if (ret != -EAGAIN) 158 | return ret; 159 | else 160 | return 0; 161 | } 162 | 163 | ret = ipts_control_wait_flush(ipts); 164 | if (ret) { 165 | dev_err(ipts->dev, "Failed to wait for flush: %d\n", ret); 166 | 167 | if (ret != -EAGAIN) 168 | return ret; 169 | else 170 | return 0; 171 | } 172 | 173 | return 0; 174 | } 175 | 176 | int ipts_receiver_start(struct ipts_context *ipts) 177 | { 178 | int ret = -EINVAL; 179 | 180 | if (ipts->mode == IPTS_MODE_EVENT) 181 | ret = ipts_thread_start(&ipts->receiver, ipts_receiver_event, ipts, "ipts_event"); 182 | else if (ipts->mode == IPTS_MODE_POLL) 183 | ret = ipts_thread_start(&ipts->receiver, ipts_receiver_poll, ipts, "ipts_poll"); 184 | 185 | if (ret) { 186 | dev_err(ipts->dev, "Failed to start receiver loop: %d\n", ret); 187 | return ret; 188 | } 189 | 190 | return 0; 191 | } 192 | 193 | int ipts_receiver_stop(struct ipts_context *ipts) 194 | { 195 | int ret = 0; 196 | 197 | ret = ipts_thread_stop(&ipts->receiver); 198 | if (ret) { 199 | dev_err(ipts->dev, "Failed to stop receiver loop: %d\n", ret); 200 | return ret; 201 | } 202 | 203 | return 0; 204 | } 205 | -------------------------------------------------------------------------------- /src/receiver.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (c) 2020-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #ifndef IPTS_RECEIVER_H 9 | #define IPTS_RECEIVER_H 10 | 11 | #include "context.h" 12 | 13 | int ipts_receiver_start(struct ipts_context *ipts); 14 | int ipts_receiver_stop(struct ipts_context *ipts); 15 | 16 | #endif /* IPTS_RECEIVER_H */ 17 | -------------------------------------------------------------------------------- /src/resources.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2020-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "resources.h" 13 | #include "spec-hid.h" 14 | #include "spec-mei.h" 15 | 16 | static int ipts_resources_alloc_dma(struct ipts_dma_buffer *buffer, struct device *dev, size_t size) 17 | { 18 | if (buffer->address) 19 | return 0; 20 | 21 | buffer->address = dma_alloc_coherent(dev, size, &buffer->dma_address, GFP_KERNEL); 22 | 23 | if (!buffer->address) 24 | return -ENOMEM; 25 | 26 | buffer->size = size; 27 | buffer->dma_device = dev; 28 | 29 | return 0; 30 | } 31 | 32 | static void ipts_resources_free_dma(struct ipts_dma_buffer *buffer) 33 | { 34 | if (!buffer->address) 35 | return; 36 | 37 | dma_free_coherent(buffer->dma_device, buffer->size, buffer->address, buffer->dma_address); 38 | 39 | buffer->address = NULL; 40 | buffer->size = 0; 41 | 42 | buffer->dma_address = 0; 43 | buffer->dma_device = NULL; 44 | } 45 | 46 | static int ipts_resources_alloc_buffer(struct ipts_buffer *buffer, size_t size) 47 | { 48 | if (buffer->address) 49 | return 0; 50 | 51 | buffer->size = size; 52 | buffer->address = kzalloc(size, GFP_KERNEL); 53 | 54 | if (!buffer->address) 55 | return -ENOMEM; 56 | 57 | return 0; 58 | } 59 | 60 | static void ipts_resources_free_buffer(struct ipts_buffer *buffer) 61 | { 62 | if (!buffer->address) 63 | return; 64 | 65 | kfree(buffer->address); 66 | buffer->address = NULL; 67 | buffer->size = 0; 68 | } 69 | 70 | int ipts_resources_init(struct ipts_resources *resources, struct device *dev, 71 | struct ipts_rsp_get_device_info info) 72 | { 73 | int i = 0; 74 | int ret = 0; 75 | 76 | size_t hid2me_size = min_t(size_t, info.feedback_size, IPTS_HID_2_ME_BUFFER_SIZE); 77 | 78 | for (i = 0; i < IPTS_MAX_BUFFERS; i++) { 79 | ret = ipts_resources_alloc_dma(&resources->data[i], dev, info.data_size); 80 | if (ret) 81 | goto err; 82 | } 83 | 84 | for (i = 0; i < IPTS_MAX_BUFFERS; i++) { 85 | ret = ipts_resources_alloc_dma(&resources->feedback[i], dev, info.feedback_size); 86 | if (ret) 87 | goto err; 88 | } 89 | 90 | ret = ipts_resources_alloc_dma(&resources->doorbell, dev, sizeof(u32)); 91 | if (ret) 92 | goto err; 93 | 94 | ret = ipts_resources_alloc_dma(&resources->workqueue, dev, sizeof(u32)); 95 | if (ret) 96 | goto err; 97 | 98 | ret = ipts_resources_alloc_dma(&resources->hid2me, dev, hid2me_size); 99 | if (ret) 100 | goto err; 101 | 102 | ret = ipts_resources_alloc_dma(&resources->descriptor, dev, info.data_size + 8); 103 | if (ret) 104 | goto err; 105 | 106 | ret = ipts_resources_alloc_buffer(&resources->report, IPTS_HID_REPORT_DATA_SIZE); 107 | if (ret) 108 | goto err; 109 | 110 | ret = ipts_resources_alloc_buffer(&resources->feature, info.data_size); 111 | if (ret) 112 | goto err; 113 | 114 | return 0; 115 | 116 | err: 117 | 118 | ipts_resources_free(resources); 119 | return ret; 120 | } 121 | 122 | void ipts_resources_free(struct ipts_resources *resources) 123 | { 124 | int i = 0; 125 | 126 | for (i = 0; i < IPTS_MAX_BUFFERS; i++) 127 | ipts_resources_free_dma(&resources->data[i]); 128 | 129 | for (i = 0; i < IPTS_MAX_BUFFERS; i++) 130 | ipts_resources_free_dma(&resources->feedback[i]); 131 | 132 | ipts_resources_free_dma(&resources->doorbell); 133 | ipts_resources_free_dma(&resources->workqueue); 134 | ipts_resources_free_dma(&resources->hid2me); 135 | ipts_resources_free_dma(&resources->descriptor); 136 | ipts_resources_free_buffer(&resources->report); 137 | ipts_resources_free_buffer(&resources->feature); 138 | } 139 | -------------------------------------------------------------------------------- /src/resources.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (c) 2020-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #ifndef IPTS_RESOURCES_H 9 | #define IPTS_RESOURCES_H 10 | 11 | #include 12 | #include 13 | 14 | #include "spec-mei.h" 15 | 16 | struct ipts_buffer { 17 | u8 *address; 18 | size_t size; 19 | }; 20 | 21 | struct ipts_dma_buffer { 22 | u8 *address; 23 | size_t size; 24 | 25 | dma_addr_t dma_address; 26 | struct device *dma_device; 27 | }; 28 | 29 | /** 30 | * struct ipts_resources - The IPTS resource manager. 31 | * 32 | * This object is responsible for allocating, storing and freeing all buffers that are needed to 33 | * communicate with the ME and process the data that it produces. 34 | * 35 | * Every buffer that owns the data it points to should go in here. Buffers that don't own the 36 | * data they point to should go into &struct ipts_context. 37 | * 38 | * @data: 39 | * The data buffers for ME to host DMA data transfer. The size of these buffers is determined 40 | * by &struct ipts_device_info->data_size. 41 | * 42 | * @feedback: 43 | * The feedback buffers for host to ME DMA data transfer. The size of these buffers is 44 | * determined by &struct ipts_device_info->feedback_size. 45 | * 46 | * @doorbell: 47 | * The doorbell buffer. The doorbell is an unsigned 32-bit integer that the ME will increment 48 | * when new data is available. 49 | * 50 | * @workqueue: 51 | * The buffer that holds the workqueue offset. The offset is a 32-bit integer that is only 52 | * required when using GuC submission with vendor provided OpenCL kernels. 53 | * 54 | * @hid2me: 55 | * The buffer for HID2ME feedback, a special feedback buffer intended for passing HID feature 56 | * and output reports to the ME. 57 | * 58 | * @descriptor: 59 | * The buffer for querying the native HID descriptor on EDS v2 devices. The size of the buffer 60 | * should &struct ipts_device_info->data_size + 8. 61 | * 62 | * @report: 63 | * A buffer that is used to synthesize HID reports on EDS v1 devices that don't natively support 64 | * HID. The size of this buffer should be %IPTS_HID_REPORT_DATA_SIZE. 65 | * 66 | * @feature: 67 | * A buffer that is used to cache the answer to a GET_FEATURES request, so that the data buffer 68 | * containing it can be safely refilled by the ME. The size of this buffer must be 69 | * &struct ipts_device_info->data_size 70 | */ 71 | struct ipts_resources { 72 | struct ipts_dma_buffer data[IPTS_MAX_BUFFERS]; 73 | struct ipts_dma_buffer feedback[IPTS_MAX_BUFFERS]; 74 | 75 | struct ipts_dma_buffer doorbell; 76 | struct ipts_dma_buffer workqueue; 77 | struct ipts_dma_buffer hid2me; 78 | struct ipts_dma_buffer descriptor; 79 | 80 | struct ipts_buffer report; 81 | struct ipts_buffer feature; 82 | }; 83 | 84 | int ipts_resources_init(struct ipts_resources *resources, struct device *dev, 85 | struct ipts_rsp_get_device_info info); 86 | void ipts_resources_free(struct ipts_resources *resources); 87 | 88 | #endif /* IPTS_RESOURCES_H */ 89 | -------------------------------------------------------------------------------- /src/spec-dma.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (c) 2016 Intel Corporation 4 | * Copyright (c) 2020-2023 Dorian Stoll 5 | * 6 | * Linux driver for Intel Precise Touch & Stylus 7 | */ 8 | 9 | #ifndef IPTS_SPEC_DMA_H 10 | #define IPTS_SPEC_DMA_H 11 | 12 | #include 13 | #include 14 | 15 | /** 16 | * enum ipts_data_type - The type of data that is contained in a data buffer. 17 | * 18 | * @IPTS_DATA_TYPE_FRAME: 19 | * The buffer contains IPTS data frames, a custom format for storing raw multitouch data 20 | * such as capacitive heatmap data or stylus antenna measurements. 21 | * 22 | * They are intended to be used together with vendor provided GPGPU kernels and GuC submission. 23 | * 24 | * @IPTS_DATA_TYPE_ERROR: 25 | * The buffer contains error data. 26 | * 27 | * @IPTS_DATA_TYPE_VENDOR: 28 | * The buffer contains vendor specific data. 29 | * 30 | * @IPTS_DATA_TYPE_HID: 31 | * The buffer contains a HID report. 32 | * 33 | * @IPTS_DATA_TYPE_GET_FEATURES: 34 | * The buffer contains the response to a HID feature request. 35 | * 36 | * @IPTS_DATA_TYPE_HID_DESCRIPTOR: 37 | * The buffer contains a HID descriptor. 38 | */ 39 | enum ipts_data_type { 40 | IPTS_DATA_TYPE_FRAME = 0x00, 41 | IPTS_DATA_TYPE_ERROR = 0x01, 42 | IPTS_DATA_TYPE_VENDOR = 0x02, 43 | IPTS_DATA_TYPE_HID = 0x03, 44 | IPTS_DATA_TYPE_GET_FEATURES = 0x04, 45 | IPTS_DATA_TYPE_HID_DESCRIPTOR = 0x05, 46 | }; 47 | 48 | /** 49 | * enum ipts_feedback_cmd_type - Commands that can be executed on the sensor through feedback. 50 | */ 51 | enum ipts_feedback_cmd_type { 52 | IPTS_FEEDBACK_CMD_TYPE_NONE = 0x00, 53 | IPTS_FEEDBACK_CMD_TYPE_SOFT_RESET = 0x01, 54 | IPTS_FEEDBACK_CMD_TYPE_GOTO_ARMED = 0x02, 55 | IPTS_FEEDBACK_CMD_TYPE_GOTO_SENSING = 0x03, 56 | IPTS_FEEDBACK_CMD_TYPE_GOTO_SLEEP = 0x04, 57 | IPTS_FEEDBACK_CMD_TYPE_GOTO_DOZE = 0x05, 58 | IPTS_FEEDBACK_CMD_TYPE_HARD_RESET = 0x06, 59 | }; 60 | 61 | /** 62 | * enum ipts_feedback_data_type - The type of data that is contained in a feedback buffer. 63 | * 64 | * @IPTS_FEEDBACK_DATA_TYPE_VENDOR: 65 | * The buffer contains vendor specific feedback data. 66 | * 67 | * Note: The expected format is unknown, but could be figured out by reverse engineering 68 | * the vendor GPGPU kernels from Microsoft. 69 | * 70 | * @IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES: 71 | * The buffer contains a HID SET_FEATURE report. This means one byte with the 72 | * report ID followed by the data to write, as defined by the HID descriptor. 73 | * 74 | * @IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES: 75 | * The buffer contains a HID GET_FEATURE report. This means one byte with the 76 | * report ID, followed by enough free space to write the requested data, as 77 | * defined by the HID descriptor. 78 | * 79 | * @IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT: 80 | * The buffer contains a HID output report. This means one byte with the 81 | * report ID followed by the data to write, as defined by the HID descriptor. 82 | * 83 | * @IPTS_FEEDBACK_DATA_TYPE_STORE_DATA: 84 | * The buffer contains calibration data for the sensor. 85 | * 86 | * Note: The expected format for this is unknown as there is no reference from Intel. 87 | * It is most likely vendor specific, just like %IPTS_FEEDBACK_DATA_TYPE_VENDOR. 88 | */ 89 | enum ipts_feedback_data_type { 90 | IPTS_FEEDBACK_DATA_TYPE_VENDOR = 0x00, 91 | IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES = 0x01, 92 | IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES = 0x02, 93 | IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT = 0x03, 94 | IPTS_FEEDBACK_DATA_TYPE_STORE_DATA = 0x04, 95 | }; 96 | 97 | /** 98 | * struct ipts_data_buffer - A message from the ME to the host through the DMA interface. 99 | * 100 | * @type: 101 | * The type of the data following this header. See &enum ipts_data_type. 102 | * 103 | * @size: 104 | * How many bytes of the data are following this header. 105 | * 106 | * @total_index: 107 | * The index of this message within all of the messages that were sent so far. 108 | * Building the modulo between this and the amount of buffers in use will yielt the 109 | * index of the buffer that this message was written to. 110 | * 111 | * @protocol_ver: 112 | * Must match protocol version of the EDS. 113 | * 114 | * @vendor_compat_ver: 115 | * Version number that is used to indicate which vendor kernel can process this data. 116 | * Only relevant when using GuC submission. 117 | * 118 | * @reserved1: 119 | * Padding to extend header to full 64 bytes and allow for growth. 120 | * 121 | * @transaction: 122 | * Transaction ID that will be passed back to the ME in &struct ipts_cmd_feedback->transaction. 123 | * Used to track round trip of a given transaction for performance measurements. 124 | * 125 | * @reserved2: 126 | * Padding to extend header to full 64 bytes and allow for growth. 127 | * 128 | * @data: 129 | * The contents of the buffer after this header. 130 | */ 131 | struct ipts_data_buffer { 132 | u32 type; 133 | u32 size; 134 | u32 total_index; 135 | u32 protocol_ver; 136 | u8 vendor_compat_ver; 137 | u8 reserved1[15]; 138 | u32 transaction; 139 | u8 reserved2[28]; 140 | u8 data[]; 141 | } __packed; 142 | 143 | static_assert(sizeof(struct ipts_data_buffer) == 64); 144 | 145 | /** 146 | * struct ipts_feedback_buffer - A message from the host to the ME through the DMA interface. 147 | * 148 | * @cmd_type: 149 | * The command that the ME should execute on the touch sensor. 150 | * See &enum ipts_feedback_cmd_type. 151 | * 152 | * @size: 153 | * How many bytes of the data are following this header. 154 | * 155 | * @total_index: 156 | * The index of this message within all of the messages that were sent so far. 157 | * See &struct ipts_data_buffer->total_index. 158 | * 159 | * @protocol_ver: 160 | * Must match the EDS protocol version of the sensor. 161 | * 162 | * @data_type: 163 | * The type of data following this header. See &enum ipts_feedback_data_type. 164 | * 165 | * @spi_offset: 166 | * The offset at which the ME should write the data to the touch sensor. Maximum is 0x1EFFF. 167 | * 168 | * @reserved: 169 | * Padding to extend header to full 64 bytes and allow for growth. 170 | * 171 | * @data: 172 | * The data that is sent to the ME and written to the touch sensor. 173 | */ 174 | struct ipts_feedback_buffer { 175 | u32 cmd_type; 176 | u32 size; 177 | u32 total_index; 178 | u32 protocol_ver; 179 | u32 data_type; 180 | u32 spi_offset; 181 | u8 reserved[40]; 182 | u8 data[]; 183 | } __packed; 184 | 185 | static_assert(sizeof(struct ipts_feedback_buffer) == 64); 186 | 187 | #endif /* IPTS_SPEC_DMA_H */ 188 | -------------------------------------------------------------------------------- /src/spec-hid.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (c) 2020-2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #ifndef IPTS_SPEC_HID_H 9 | #define IPTS_SPEC_HID_H 10 | 11 | #include 12 | #include 13 | 14 | /* 15 | * IPTS_HID_FRAME_TYPE_RAW - Made-up type for passing IPTS data frames in a HID report. 16 | */ 17 | #define IPTS_HID_FRAME_TYPE_RAW 0xEE 18 | 19 | /** 20 | * IPTS_HID_REPORT_SINGLETOUCH - The HID report ID of a fallback singletouch report. 21 | * 22 | * Singletouch reports are generated by the hardware with this ID, so this value is 23 | * dicated by the hardware / spec! 24 | */ 25 | #define IPTS_HID_REPORT_SINGLETOUCH 64 26 | 27 | /** 28 | * IPTS_HID_REPORT_DATA - The HID report ID of data report for EDS v1 devices. 29 | * 30 | * This report is synthesized in software, so this only needs to line up with 31 | * &ipts_fallback_descriptor. 32 | * 33 | * Userspace should not rely on this ID and instead find the report through the HID descriptor. 34 | */ 35 | #define IPTS_HID_REPORT_DATA 65 36 | 37 | /** 38 | * IPTS_HID_REPORT_SET_MODE - The HID report ID for switching modes on EDS v1 devices. 39 | * 40 | * This feature report is parsed / synthesized in software, so this only needs to line up with 41 | * &ipts_fallback_descriptor. 42 | * 43 | * Userspace should not rely on this ID and instead find the report through the HID descriptor. 44 | */ 45 | #define IPTS_HID_REPORT_SET_MODE 66 46 | 47 | /** 48 | * IPTS_HID_REPORT_DATA_SIZE - The amount of raw data in the data report for EDS v1 devices. 49 | * 50 | * Taken from the native HID implementation on Surface Pro 7 and later. 51 | * 52 | * The data report is synthesized in software, so this only needs to line up with 53 | * &ipts_fallback_descriptor. 54 | * 55 | * Userspace should not rely on this value and instead find the size through the HID descriptor. 56 | */ 57 | #define IPTS_HID_REPORT_DATA_SIZE 7485 58 | 59 | /** 60 | * ipts_singletouch_descriptor - The singletouch descriptor. 61 | * 62 | * This descriptor should be present on all IPTS devices to support the fallback 63 | * singletouch inputs when no userspace processing is available. 64 | * 65 | * This is not a part of the native HID descriptor on EDS v2 devices. 66 | */ 67 | static const u8 ipts_singletouch_descriptor[] = { 68 | 0x05, 0x0D, /* Usage Page (Digitizer), */ 69 | 0x09, 0x04, /* Usage (Touchscreen), */ 70 | 0xA1, 0x01, /* Collection (Application), */ 71 | 0x85, 0x40, /* Report ID (64), */ 72 | 0x09, 0x42, /* Usage (Tip Switch), */ 73 | 0x15, 0x00, /* Logical Minimum (0), */ 74 | 0x25, 0x01, /* Logical Maximum (1), */ 75 | 0x75, 0x01, /* Report Size (1), */ 76 | 0x95, 0x01, /* Report Count (1), */ 77 | 0x81, 0x02, /* Input (Variable), */ 78 | 0x95, 0x07, /* Report Count (7), */ 79 | 0x81, 0x03, /* Input (Constant, Variable), */ 80 | 0x05, 0x01, /* Usage Page (Desktop), */ 81 | 0x09, 0x30, /* Usage (X), */ 82 | 0x75, 0x10, /* Report Size (16), */ 83 | 0x95, 0x01, /* Report Count (1), */ 84 | 0xA4, /* Push, */ 85 | 0x55, 0x0E, /* Unit Exponent (14), */ 86 | 0x65, 0x11, /* Unit (Centimeter), */ 87 | 0x46, 0x76, 0x0B, /* Physical Maximum (2934), */ 88 | 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ 89 | 0x81, 0x02, /* Input (Variable), */ 90 | 0x09, 0x31, /* Usage (Y), */ 91 | 0x46, 0x74, 0x06, /* Physical Maximum (1652), */ 92 | 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ 93 | 0x81, 0x02, /* Input (Variable), */ 94 | 0xB4, /* Pop, */ 95 | 0xC0, /* End Collection */ 96 | }; 97 | 98 | /** 99 | * ipts_fallback_descriptor - The fallback descriptor for devices without native HID support. 100 | * 101 | * This exposes the most important functions of IPTS to userspace: A report that will contain 102 | * multitouch touchscreen data, as well as a feature report for changing between singletouch and 103 | * multitouch modes. 104 | * 105 | * The report definitions and usage values are based on the HID descriptor for Surface Pro 7. 106 | * Userspace should rely on the usage values and not on the report IDs to find the needed reports. 107 | */ 108 | static const u8 ipts_fallback_descriptor[] = { 109 | 0x05, 0x0D, /* Usage Page (Digitizer), */ 110 | 0x09, 0x0F, /* Usage (Capacitive Hm Digitizer), */ 111 | 0xA1, 0x01, /* Collection (Application), */ 112 | 0x85, 0x41, /* Report ID (65), */ 113 | 0x09, 0x56, /* Usage (Scan Time), */ 114 | 0x95, 0x01, /* Report Count (1), */ 115 | 0x75, 0x10, /* Report Size (16), */ 116 | 0x81, 0x02, /* Input (Variable), */ 117 | 0x09, 0x61, /* Usage (Gesture Char Quality), */ 118 | 0x75, 0x08, /* Report Size (8), */ 119 | 0x96, 0x3D, 0x1D, /* Report Count (7485), */ 120 | 0x81, 0x03, /* Input (Constant, Variable), */ 121 | 0x85, 0x42, /* Report ID (66), */ 122 | 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 123 | 0x09, 0xC8, /* Usage (C8h), */ 124 | 0x75, 0x08, /* Report Size (8), */ 125 | 0x95, 0x01, /* Report Count (1), */ 126 | 0xB1, 0x02, /* Feature (Variable), */ 127 | 0xC0, /* End Collection */ 128 | }; 129 | 130 | /** 131 | * struct ipts_hid_data_header - Header for touch data inside of &struct ipts_hid_report. 132 | * 133 | * For wrapping the IPTS data frames produced by devices that do not natively support HID we reuse 134 | * an existing implementation. This header is prefixed to raw touch data on Surface Pro 7 and 135 | * later devices (which natively support HID). 136 | * 137 | * Since the format of the data following this header is different as well, we are using a made-up 138 | * type ID to signal this difference to userspace. 139 | * 140 | * @size: 141 | * Size of the data in bytes, including this header. 142 | * 143 | * @reserved1: 144 | * Unknown value. 145 | * 146 | * @type: 147 | * The type of the data in this report. 148 | * For wrapping IPTS data frames, this should be %IPTS_HID_FRAME_TYPE_RAW. 149 | * 150 | * @reserved2: 151 | * Unknown value. 152 | * 153 | * @data: 154 | * The contents of the report after this header. 155 | * 156 | */ 157 | struct ipts_hid_data_header { 158 | u32 size; 159 | u8 reserved1; 160 | u8 type; 161 | u8 reserved2; 162 | u8 data[]; 163 | } __packed; 164 | 165 | static_assert(sizeof(struct ipts_hid_data_header) == 7); 166 | 167 | /** 168 | * struct ipts_hid_report_data - A HID report structure to wrap IPTS data frames. 169 | * 170 | * For wrapping the IPTS data frames produced by devices that do not natively support HID we reuse 171 | * an existing implementation. This report structure is how the Surface Pro 7 and later 172 | * devices (which natively support HID) pass raw touch data to userspace for further processing. 173 | * 174 | * @report_id: 175 | * The ID of the HID report. For EDS v1 devices, this should be %IPTS_HID_REPORT_DATA. 176 | * 177 | * @scan_time: 178 | * A timestamp that indicates when the data was generated. 179 | * 180 | * @gesture_char_quality: 181 | * The raw data plus a small header. 182 | */ 183 | struct ipts_hid_report_data { 184 | u8 report_id; 185 | u16 scan_time; 186 | struct ipts_hid_data_header gesture_char_quality; 187 | } __packed; 188 | 189 | static_assert(sizeof(struct ipts_hid_report_data) == 10); 190 | 191 | /** 192 | * struct ipts_hid_report_set_mode - A HID report structure to switch modes on EDS v1 devices. 193 | * 194 | * For wrapping the IPTS data frames produced by devices that do not natively support HID we reuse 195 | * an existing implementation. This report structure is how the Surface Pro 7 and later 196 | * devices (which natively support HID) switch between singletouch and multitouch / raw data mode. 197 | * 198 | * @report_id: 199 | * The ID of the HID report. For EDS v1 devices, this should be %IPTS_HID_REPORT_SET_MODE. 200 | * 201 | * @mode: 202 | * The desired sensor mode. See &enum ipts_mode. 203 | */ 204 | struct ipts_hid_report_set_mode { 205 | u8 report_id; 206 | u8 mode; 207 | } __packed; 208 | 209 | static_assert(sizeof(struct ipts_hid_report_set_mode) == 2); 210 | 211 | #endif /* IPTS_SPEC_HID_H */ 212 | -------------------------------------------------------------------------------- /src/spec-mei.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (c) 2016 Intel Corporation 4 | * Copyright (c) 2020-2023 Dorian Stoll 5 | * 6 | * Linux driver for Intel Precise Touch & Stylus 7 | */ 8 | 9 | #ifndef IPTS_SPEC_MEI_H 10 | #define IPTS_SPEC_MEI_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | /** 18 | * MEI_UUID_IPTS - The MEI client ID for IPTS functionality. 19 | */ 20 | #define MEI_UUID_IPTS \ 21 | UUID_LE(0x3e8d0870, 0x271a, 0x4208, 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04) 22 | 23 | /** 24 | * IPTS_MAX_BUFFERS - How many buffers can be used by the ME for transferring data. 25 | */ 26 | #define IPTS_MAX_BUFFERS 16 27 | 28 | /** 29 | * IPTS_DIRECTION_BIT - The bit that determines the direction in which a message is going. 30 | * 31 | * See &enum ipts_command_code as well as &struct ipts_response->cmd. 32 | */ 33 | #define IPTS_DIRECTION_BIT BIT(31) 34 | 35 | /** 36 | * IPTS_HOST_2_ME_MSG() - Explicitly mark the direction bit of a command code as host to ME. 37 | */ 38 | #define IPTS_HOST_2_ME_MSG(CODE) ((CODE) & ~IPTS_DIRECTION_BIT) 39 | 40 | /** 41 | * IPTS_ME_2_HOST_MSG() - Explicitly mark the direction bit of a command code as ME to host. 42 | */ 43 | #define IPTS_ME_2_HOST_MSG(CODE) ((CODE) | IPTS_DIRECTION_BIT) 44 | 45 | /** 46 | * IPTS_HID_2_ME_BUFFER_INDEX - The index of the HID2ME buffer. 47 | * 48 | * The HID2ME buffer is a special feedback buffer, intended for passing HID feature and output 49 | * reports from a HID interface to the ME. 50 | * 51 | * Setting &struct ipts_cmd_feedback->buffer_index to this value indicates that the ME should 52 | * get feedback data from the HID2ME buffer. 53 | * 54 | * HID2ME feedback is only implemented on devices where both, ME and touch sensor, implement EDS 55 | * interface revision 2. These devices natively support HID and have a queryable HID descriptor. 56 | */ 57 | #define IPTS_HID_2_ME_BUFFER_INDEX IPTS_MAX_BUFFERS 58 | 59 | /** 60 | * IPTS_HID_2_ME_BUFFER_SIZE - The maximum size of the HID2ME buffer. 61 | */ 62 | #define IPTS_HID_2_ME_BUFFER_SIZE 1024 63 | 64 | /** 65 | * IPTS_DEFAULT_WQ_SIZE - Default value of &struct ipts_cmd_set_mem_window->wq_size. 66 | * 67 | * This is only relevant when not using GuC submission. 68 | * When using GuC submission, this value comes from the vendor GPGPU kernel. 69 | */ 70 | #define IPTS_DEFAULT_WQ_SIZE 8192 71 | 72 | /** 73 | * IPTS_DEFAULT_WQ_ITEM_SIZE - Default value of &struct ipts_cmd_set_mem_window->wq_item_size. 74 | * 75 | * This is only relevant when not using GuC submission. 76 | * When using GuC submission, this value comes from the vendor GPGPU kernel. 77 | */ 78 | #define IPTS_DEFAULT_WQ_ITEM_SIZE 16 79 | 80 | /** 81 | * IPTS_DEFAULT_DOZE_TIMER_SECONDS - Default value of &struct ipts_policy->doze_timer. 82 | */ 83 | #define IPTS_DEFAULT_DOZE_TIMER_SECONDS 30 84 | 85 | /** 86 | * enum ipts_command_code - Command codes accepted by the ME. 87 | * 88 | * When communicating with the ME, the most significant bit of the command code indicates the 89 | * direction in which the message is going. 90 | * 91 | * MSB == 0 => Host to ME (H2M) 92 | * MSB == 1 => ME to Host (M2H) 93 | * 94 | * These commands can optionally require a payload in the command and return one in the response. 95 | * 96 | * @IPTS_CMD_GET_DEVICE_INFO: 97 | * Reads information about the device from the ME. 98 | * 99 | * H2M: No payload, M2H: &struct ipts_rsp_get_device_info 100 | * 101 | * @IPTS_CMD_SET_MODE: 102 | * Sets the operating mode of the ME. 103 | * 104 | * H2M: &struct ipts_cmd_set_mode, M2H: No payload 105 | * 106 | * @IPTS_CMD_SET_MEM_WINDOW: 107 | * Sets physical buffer addresses for passing data between host and ME. 108 | * 109 | * H2M: &struct ipts_cmd_set_mem_window, M2H: No payload 110 | * 111 | * @IPTS_CMD_QUIESCE_IO: 112 | * Stops the data flow from ME to host without clearing the buffer addresses. 113 | * 114 | * After sending this command, the host must return any outstanding buffers to the ME using 115 | * feedback before a response will be generated. 116 | * 117 | * H2M: &struct ipts_cmd_quiesce_io, M2H: No payload 118 | * 119 | * @IPTS_CMD_READY_FOR_DATA: 120 | * Informs the ME that the host is ready to receive data. 121 | * 122 | * In event mode, the ME will indicate that new data is available by sending a response to 123 | * this command. After processing the data, new data has to be requested by sending this 124 | * command again. 125 | * 126 | * In poll mode, this command will not generate a response as long as the data flow is active. 127 | * The ME will indicate that new data is available by incrementing the unsigned integer in the 128 | * doorbell buffer. This command will return, once the Quiesce IO flow has been completed and 129 | * the data flow from ME to host has been stopped. 130 | * 131 | * H2M: No payload, M2H: &struct ipts_rsp_ready_for_data 132 | * 133 | * @IPTS_CMD_FEEDBACK: 134 | * Submits a feedback buffer to the ME. 135 | * 136 | * Every data buffer has an accompanying feedback buffer that can contain either vendor 137 | * specific data or commands that will be executed on the touch sensor. 138 | * 139 | * Once filled, a data buffer will not be touched again by the ME until the host explicitly 140 | * returns it to the ME using feedback. 141 | * 142 | * H2M: &struct ipts_cmd_feedback, M2H: &struct ipts_rsp_feedback 143 | * 144 | * @IPTS_CMD_CLEAR_MEM_WINDOW: 145 | * Stops the data flow from ME to host and clears the buffer addresses. 146 | * 147 | * After sending this command, the host must return any outstanding buffers to the ME using 148 | * feedback before a response will be generated. 149 | * 150 | * H2M: No payload, M2H: No payload 151 | * 152 | * @IPTS_CMD_NOTIFY_DEV_READY: 153 | * Requests that the ME notifies the host when a touch sensor has been detected. 154 | * 155 | * H2M: No payload, M2H: No payload 156 | * 157 | * @IPTS_CMD_SET_POLICY: 158 | * Sets optional parameters on the ME. 159 | * 160 | * H2M: &struct ipts_cmd_set_policy, M2H: No payload 161 | * 162 | * @IPTS_CMD_GET_POLICY: 163 | * Reads optional parameters from the ME. 164 | * 165 | * H2M: No payload, M2H: &struct ipts_rsp_get_policy 166 | * 167 | * @IPTS_CMD_RESET_SENSOR: 168 | * Instructs the ME to reset the touch sensor. 169 | * 170 | * Note: This command will not work on some devices. The sensor will not come back from 171 | * resetting and every command will return with an error saying that the sensor has been 172 | * reset. A reboot is needed to fix it. 173 | * 174 | * H2M: &struct ipts_cmd_reset_sensor, M2H: No payload 175 | * 176 | * @IPTS_CMD_GET_HID_DESC: 177 | * Reads the HID descriptor from the ME. 178 | * 179 | * Note: This command was reverse engineered without a reference from Intel. 180 | * It is only available on devices that implement EDS v2. 181 | * 182 | * H2M: &struct ipts_cmd_get_hid_desc, M2H: Unknown 183 | */ 184 | enum ipts_command_code { 185 | IPTS_CMD_GET_DEVICE_INFO = 0x01, 186 | IPTS_CMD_SET_MODE = 0x02, 187 | IPTS_CMD_SET_MEM_WINDOW = 0x03, 188 | IPTS_CMD_QUIESCE_IO = 0x04, 189 | IPTS_CMD_READY_FOR_DATA = 0x05, 190 | IPTS_CMD_FEEDBACK = 0x06, 191 | IPTS_CMD_CLEAR_MEM_WINDOW = 0x07, 192 | IPTS_CMD_NOTIFY_DEV_READY = 0x08, 193 | IPTS_CMD_SET_POLICY = 0x09, 194 | IPTS_CMD_GET_POLICY = 0x0A, 195 | IPTS_CMD_RESET_SENSOR = 0x0B, 196 | IPTS_CMD_GET_HID_DESC = 0x0F, 197 | }; 198 | 199 | /** 200 | * enum ipts_status - Possible status codes returned by the ME. 201 | * 202 | * @IPTS_STATUS_SUCCESS: 203 | * Command was processed successfully. 204 | * 205 | * @IPTS_STATUS_INVALID_PARAMS: 206 | * Input parameters are out of range. 207 | * 208 | * @IPTS_STATUS_ACCESS_DENIED: 209 | * Unable to map host address ranges for DMA. 210 | * 211 | * @IPTS_STATUS_CMD_SIZE_ERROR: 212 | * Command sent did not match expected size. 213 | * 214 | * @IPTS_STATUS_NOT_READY: 215 | * Memory window not set or device is not armed for operation. 216 | * 217 | * @IPTS_STATUS_REQUEST_OUTSTANDING: 218 | * Previous request is still outstanding, ME firmware cannot handle another request for the 219 | * same command. 220 | * 221 | * @IPTS_STATUS_NO_SENSOR_FOUND: 222 | * No sensor could be found. Either no sensor is connected, the sensor has not yet initialized, 223 | * or the system is improperly configured. 224 | * 225 | * @IPTS_STATUS_OUT_OF_MEMORY: 226 | * Not enough memory/storage for requested operation. 227 | * 228 | * @IPTS_STATUS_INTERNAL_ERROR: 229 | * Unexpected error occurred. 230 | * 231 | * @IPTS_STATUS_SENSOR_DISABLED: 232 | * Touch sensor has been disabled or reset and must be reinitialized. 233 | * 234 | * @IPTS_STATUS_COMPAT_CHECK_FAIL: 235 | * Compatibility revision check between sensor and ME failed. The host can ignore this error 236 | * and attempt to continue. 237 | * 238 | * @IPTS_STATUS_SENSOR_EXPECTED_RESET: 239 | * Sensor signaled a Reset Interrupt. ME either directly requested this reset, or it was 240 | * expected as part of a defined flow in the EDS. 241 | * 242 | * @IPTS_STATUS_SENSOR_UNEXPECTED_RESET: 243 | * Sensor signaled a Reset Interrupt. ME did not expect this and has no info about why this 244 | * occurred. 245 | * 246 | * @IPTS_STATUS_RESET_FAILED: 247 | * Requested sensor reset failed to complete. Sensor generated an invalid or unexpected 248 | * interrupt. 249 | * 250 | * @IPTS_STATUS_TIMEOUT: 251 | * Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is 252 | * not connected or malfunctioning. 253 | * 254 | * @IPTS_STATUS_TEST_MODE_FAIL: 255 | * Test mode pattern did not match expected values. 256 | * 257 | * @IPTS_STATUS_SENSOR_FAIL_FATAL: 258 | * Indicates sensor reported fatal error during reset sequence. Further progress is not 259 | * possible. 260 | * 261 | * @IPTS_STATUS_SENSOR_FAIL_NONFATAL: 262 | * Indicates sensor reported non-fatal error during reset sequence. Host should log the error 263 | * and attempt to continue. 264 | * 265 | * @IPTS_STATUS_INVALID_DEVICE_CAPS: 266 | * Indicates sensor does not support minimum required frequency or I/O Mode. ME firmware will 267 | * choose the best possible option and the host should attempt to continue. 268 | * 269 | * @IPTS_STATUS_QUIESCE_IO_IN_PROGRESS: 270 | * Indicates that command cannot be complete until ongoing Quiesce I/O flow has completed. 271 | */ 272 | enum ipts_status { 273 | IPTS_STATUS_SUCCESS = 0x00, 274 | IPTS_STATUS_INVALID_PARAMS = 0x01, 275 | IPTS_STATUS_ACCESS_DENIED = 0x02, 276 | IPTS_STATUS_CMD_SIZE_ERROR = 0x03, 277 | IPTS_STATUS_NOT_READY = 0x04, 278 | IPTS_STATUS_REQUEST_OUTSTANDING = 0x05, 279 | IPTS_STATUS_NO_SENSOR_FOUND = 0x06, 280 | IPTS_STATUS_OUT_OF_MEMORY = 0x07, 281 | IPTS_STATUS_INTERNAL_ERROR = 0x08, 282 | IPTS_STATUS_SENSOR_DISABLED = 0x09, 283 | IPTS_STATUS_COMPAT_CHECK_FAIL = 0x0A, 284 | IPTS_STATUS_SENSOR_EXPECTED_RESET = 0x0B, 285 | IPTS_STATUS_SENSOR_UNEXPECTED_RESET = 0x0C, 286 | IPTS_STATUS_RESET_FAILED = 0x0D, 287 | IPTS_STATUS_TIMEOUT = 0x0E, 288 | IPTS_STATUS_TEST_MODE_FAIL = 0x0F, 289 | IPTS_STATUS_SENSOR_FAIL_FATAL = 0x10, 290 | IPTS_STATUS_SENSOR_FAIL_NONFATAL = 0x11, 291 | IPTS_STATUS_INVALID_DEVICE_CAPS = 0x12, 292 | IPTS_STATUS_QUIESCE_IO_IN_PROGRESS = 0x13, 293 | }; 294 | 295 | /** 296 | * enum ipts_mode - The operation mode of the ME. 297 | * 298 | * This mode defines, how the ME will notify the host about new data. 299 | * On EDS v1 devices, it will additionally change the type of data that is sent. 300 | * 301 | * @IPTS_MODE_EVENT: 302 | * In event mode, the ME indicates new data by producing a response to 303 | * IPTS_CMD_READY_FOR_DATA. New data has to be requested by re-sending this command. 304 | * 305 | * On EDS v1 devices, this mode will return fallback singletouch data. 306 | * 307 | * @IPTS_MODE_POLL: 308 | * In poll mode, the ME indicates new data by incrementing the unsigned integer in the 309 | * doorbell buffer. No event will be generated and it is not necessary to expliticly 310 | * request new data. 311 | * 312 | * On EDS v1 devices, this mode will return unprocessed multitouch data, for example 313 | * capacitive heatmaps or stylus antenna measurements. 314 | */ 315 | enum ipts_mode { 316 | IPTS_MODE_EVENT = 0x00, 317 | IPTS_MODE_POLL = 0x01, 318 | }; 319 | 320 | /** 321 | * enum ipts_quiesce_flags - Flags that change the behaviour of %IPTS_CMD_QUIESCE_IO. 322 | * 323 | * @IPTS_QUIESCE_FLAGS_GUC_RESET: 324 | * Indicates that the GuC got reset and the ME has to re-read the tail offset and doorbell. 325 | */ 326 | enum ipts_quiesce_flags { 327 | IPTS_QUIESCE_FLAGS_GUC_RESET = BIT(0), 328 | }; 329 | 330 | /** 331 | * enum ipts_reset_type - Possible ways of resetting the touch sensor. 332 | * 333 | * @IPTS_RESET_TYPE_HARD: 334 | * Perform hardware reset using GPIO pin. 335 | * 336 | * @IPTS_RESET_TYPE_SOFT: 337 | * Perform software reset using SPI command. 338 | */ 339 | enum ipts_reset_type { 340 | IPTS_RESET_TYPE_HARD = 0x00, 341 | IPTS_RESET_TYPE_SOFT = 0x01, 342 | }; 343 | 344 | /** 345 | * enum ipts_reset_reason - Reason why the touch sensor was reset. 346 | * 347 | * @IPTS_RESET_REASON_UNKNOWN: 348 | * Reason for sensor reset is not known. 349 | * 350 | * @IPTS_RESET_REASON_FEEDBACK_REQUEST: 351 | * Reset was requested as part of %IPTS_CMD_FEEDBACK. 352 | * 353 | * @IPTS_RESET_REASON_MEI_REQUEST: 354 | * Reset was requested via %IPTS_CMD_RESET_SENSOR. 355 | */ 356 | enum ipts_reset_reason { 357 | IPTS_RESET_REASON_UNKNOWN = 0x00, 358 | IPTS_RESET_REASON_FEEDBACK_REQUEST = 0x01, 359 | IPTS_RESET_REASON_MEI_REQUEST = 0x02, 360 | }; 361 | 362 | /** 363 | * enum ipts_spi_freq - The SPI bus frequency of the touch sensor. 364 | * 365 | * @IPTS_SPI_FREQ_RESERVED: 366 | * Reserved value. 367 | * 368 | * @IPTS_SPI_FREQ_17MHZ: 369 | * Sensor set for 17MHz operation. 370 | * 371 | * @IPTS_SPI_FREQ_30MHZ: 372 | * Sensor set for 30MHz operation. 373 | */ 374 | enum ipts_spi_freq { 375 | IPTS_SPI_FREQ_RESERVED = 0x00, 376 | IPTS_SPI_FREQ_17MHZ = 0x01, 377 | IPTS_SPI_FREQ_30MHZ = 0x02, 378 | }; 379 | 380 | /** 381 | * enum ipts_spi_freq_override - Override the default SPI bus frequency of the touch sensor. 382 | * 383 | * @IPTS_SPI_FREQ_OVERRIDE_NONE: 384 | * Do not apply any override. 385 | * 386 | * @IPTS_SPI_FREQ_OVERRIDE_10MHZ: 387 | * Force frequency to 10MHz (not currently supported). 388 | * 389 | * @IPTS_SPI_FREQ_OVERRIDE_17MHZ: 390 | * Force frequency to 17MHz. 391 | * 392 | * @IPTS_SPI_FREQ_OVERRIDE_30MHZ: 393 | * Force frequency to 30MHz. 394 | * 395 | * @IPTS_SPI_FREQ_OVERRIDE_50MHZ: 396 | * Force frequency to 50MHz (not currently supported). 397 | */ 398 | enum ipts_spi_freq_override { 399 | IPTS_SPI_FREQ_OVERRIDE_NONE = 0x00, 400 | IPTS_SPI_FREQ_OVERRIDE_10MHZ = 0x01, 401 | IPTS_SPI_FREQ_OVERRIDE_17MHZ = 0x02, 402 | IPTS_SPI_FREQ_OVERRIDE_30MHZ = 0x03, 403 | IPTS_SPI_FREQ_OVERRIDE_50MHZ = 0x04, 404 | }; 405 | 406 | /** 407 | * enum ipts_spi_io - The SPI IO mode of the touch sensor. 408 | * 409 | * @IPTS_SPI_IO_SINGLE: 410 | * Sensor set for Single I/O SPI. 411 | * 412 | * @IPTS_SPI_IO_DUAL: 413 | * Sensor set for Dual I/O SPI. 414 | * 415 | * @IPTS_SPI_IO_QUAD: 416 | * Sensor set for Quad I/O SPI. 417 | */ 418 | enum ipts_spi_io { 419 | IPTS_SPI_IO_SINGLE = 0x00, 420 | IPTS_SPI_IO_DUAL = 0x01, 421 | IPTS_SPI_IO_QUAD = 0x02, 422 | }; 423 | 424 | /** 425 | * enum ipts_spi_io_override - Override the default SPI IO mode of the touch sensor. 426 | * 427 | * @IPTS_SPI_IO_MODE_OVERRIDE_NONE: 428 | * Do not apply any override. 429 | * 430 | * @IPTS_SPI_IO_MODE_OVERRIDE_SINGLE: 431 | * Force Single I/O SPI. 432 | * 433 | * @IPTS_SPI_IO_MODE_OVERRIDE_DUAL: 434 | * Force Dual I/O SPI. 435 | * 436 | * @IPTS_SPI_IO_MODE_OVERRIDE_QUAD: 437 | * Force Quad I/O SPI. 438 | */ 439 | enum ipts_spi_io_override { 440 | IPTS_SPI_IO_MODE_OVERRIDE_NONE = 0x00, 441 | IPTS_SPI_IO_MODE_OVERRIDE_SINGLE = 0x01, 442 | IPTS_SPI_IO_MODE_OVERRIDE_DUAL = 0x02, 443 | IPTS_SPI_IO_MODE_OVERRIDE_QUAD = 0x03, 444 | }; 445 | 446 | /** 447 | * enum ipts_debug_override - Override the default debug policy of the touch sensor. 448 | */ 449 | enum ipts_debug_override { 450 | IPTS_DEBUG_OVERRIDE_DISABLE_STARTUP_TIMER = BIT(0), 451 | IPTS_DEBUG_OVERRIDE_DISABLE_SYNC_BYTE = BIT(1), 452 | IPTS_DEBUG_OVERRIDE_DISABLE_ERROR_RESET = BIT(2), 453 | }; 454 | 455 | /** 456 | * struct ipts_cmd_set_mode - Payload for %IPTS_CMD_SET_MODE. 457 | * 458 | * @mode: 459 | * The desired sensor mode. See &enum ipts_mode. 460 | * 461 | * @reserved: 462 | * For future expansion. 463 | */ 464 | struct ipts_cmd_set_mode { 465 | u32 mode; 466 | u8 reserved[12]; 467 | } __packed; 468 | 469 | static_assert(sizeof(struct ipts_cmd_set_mode) == 16); 470 | 471 | /** 472 | * struct ipts_cmd_set_mem_window - Payload for %IPTS_CMD_SET_MEM_WINDOW. 473 | * 474 | * @data_addr_lower: 475 | * Lower 32 bits of the physical address to the touch data buffer. 476 | * Size of each buffer should be &struct ipts_rsp_get_device_info->data_size. 477 | * 478 | * @data_addr_upper: 479 | * Upper 32 bits of the physical address to the touch data buffer. 480 | * Size of each buffer should be &struct ipts_rsp_get_device_info->data_size. 481 | * 482 | * @tail_offset_addr_lower: 483 | * Lower 32 bits of the physical address to the tail offset. 484 | * Size of the buffer is 32 bit, incremented by &struct ipts_cmd_set_mem_window->wq_item_size. 485 | * 486 | * Note: This buffer is required for using vendor GPGPU kernels through GuC submission. 487 | * Without using GuC submission, this still must be allocated and passed to the ME, 488 | * otherwise the command will return an error. 489 | * 490 | * @tail_offset_addr_upper: 491 | * Upper 32 bits of the physical address to the tail offset. 492 | * Size of the buffer is 32 bit, incremented by &struct ipts_cmd_set_mem_window->wq_item_size. 493 | * 494 | * Note: This buffer is required for using vendor GPGPU kernels through GuC submission. 495 | * Without using GuC submission, this still must be allocated and passed to the ME, 496 | * otherwise the command will return an error. 497 | * 498 | * @doorbell_addr_lower: 499 | * Lower 32 bits of the physical address to the doorbell register. 500 | * Size of the buffer is 32 bit, incremented as unsigned integer, rolls over to 1. 501 | * 502 | * @doorbell_addr_upper: 503 | * Upper 32 bits of the physical address to the doorbell register. 504 | * Size of the buffer is 32 bit, incremented as unsigned integer, rolls over to 1. 505 | * 506 | * @feedback_addr_lower: 507 | * Lower 32 bits of the physical address to the feedback buffer. 508 | * Size of each buffer should be &struct ipts_rsp_get_device_info->feedback_size. 509 | * 510 | * @feedback_addr_upper: 511 | * Upper 32 bits of the physical address to the feedback buffer. 512 | * Size of each buffer should be &struct ipts_rsp_get_device_info->feedback_size. 513 | * 514 | * @hid2me_addr_lower: 515 | * Lower 32 bits of the physical address to the dedicated HID to ME buffer. 516 | * Size of the buffer is &struct ipts_cmd_set_mem_window->hid2me_size. 517 | * 518 | * @hid2me_addr_upper: 519 | * Upper 32 bits of the physical address to the dedicated HID to ME buffer. 520 | * Size of the buffer is &struct ipts_cmd_set_mem_window->hid2me_size. 521 | * 522 | * @hid2me_size: 523 | * Size in bytes of the HID to ME buffer, cannot be bigger than %IPTS_HID_2_ME_BUFFER_SIZE. 524 | * 525 | * @reserved1: 526 | * For future expansion. 527 | * 528 | * @wq_item_size: 529 | * Size in bytes of the workqueue item pointed to by the tail offset. 530 | * 531 | * Note: This field is required for using vendor GPGPU kernels through GuC submission. 532 | * Without using GuC submission, this must be set to %IPTS_DEFAULT_WQ_ITEM_SIZE. 533 | * 534 | * @wq_size: 535 | * Size in bytes of the entire work queue. 536 | * 537 | * Note: This field is required for using vendor GPGPU kernels through GuC submission. 538 | * Without using GuC submission, this must be set to %IPTS_DEFAULT_WQ_SIZE. 539 | * 540 | * @reserved2: 541 | * For future expansion. 542 | */ 543 | struct ipts_cmd_set_mem_window { 544 | u32 data_addr_lower[IPTS_MAX_BUFFERS]; 545 | u32 data_addr_upper[IPTS_MAX_BUFFERS]; 546 | u32 tail_offset_addr_lower; 547 | u32 tail_offset_addr_upper; 548 | u32 doorbell_addr_lower; 549 | u32 doorbell_addr_upper; 550 | u32 feedback_addr_lower[IPTS_MAX_BUFFERS]; 551 | u32 feedback_addr_upper[IPTS_MAX_BUFFERS]; 552 | u32 hid2me_addr_lower; 553 | u32 hid2me_addr_upper; 554 | u32 hid2me_size; 555 | u8 reserved1; 556 | u8 wq_item_size; 557 | u16 wq_size; 558 | u8 reserved2[32]; 559 | } __packed; 560 | 561 | static_assert(sizeof(struct ipts_cmd_set_mem_window) == 320); 562 | 563 | /** 564 | * struct ipts_cmd_quiesce_io - Payload for %IPTS_CMD_QUIESCE_IO. 565 | * 566 | * @flags: 567 | * Optionally set flags that modify the commands behaviour. See &enum ipts_quiesce_flags. 568 | * 569 | * @reserved: 570 | * For future expansion. 571 | */ 572 | struct ipts_cmd_quiesce_io { 573 | u32 flags; 574 | u8 reserved[8]; 575 | } __packed; 576 | 577 | static_assert(sizeof(struct ipts_cmd_quiesce_io) == 12); 578 | 579 | /** 580 | * struct ipts_cmd_feedback - Payload for %IPTS_CMD_FEEDBACK. 581 | * 582 | * @buffer_index: 583 | * Index value from 0 to %IPTS_MAX_BUFFERS used to indicate which feedback buffer to use. 584 | * Using special value %IPTS_HID_2_ME_BUFFER_INDEX is an indication to the ME to get feedback 585 | * data from the HID to ME buffer instead of one of the standard feedback buffers. 586 | * 587 | * @reserved1: 588 | * For future expansion. 589 | * 590 | * @transaction: 591 | * Transaction ID that was passed to the host in &struct ipts_data_buffer->transaction. Used to 592 | * track round trip of a given transaction for performance measurements. 593 | * 594 | * @reserved2: 595 | * For future expansion. 596 | */ 597 | struct ipts_cmd_feedback { 598 | u8 buffer_index; 599 | u8 reserved1[3]; 600 | u32 transaction; 601 | u8 reserved2[8]; 602 | } __packed; 603 | 604 | static_assert(sizeof(struct ipts_cmd_feedback) == 16); 605 | 606 | /** 607 | * struct ipts_policy - Optional parameters for ME operation. 608 | * 609 | * @reserved1: 610 | * For future expansion. 611 | * 612 | * @doze_timer: 613 | * Value in seconds, after which the ME will put the sensor into doze power state if no 614 | * activity occurs. Set to 0 to disable doze mode (not recommended). Value will be set to 615 | * %IPTS_DEFAULT_DOZE_TIMER_SECONDS by default. 616 | * 617 | * @spi_freq_override: 618 | * Override SPI frequency requested by sensor. See &enum ipts_spi_freq_override. 619 | * 620 | * @spi_io_mode_override: 621 | * Override SPI IO mode requested by sensor. See &enum ipts_spi_io_override. 622 | * 623 | * @reserved2: 624 | * For future expansion. 625 | * 626 | * @debug_override: 627 | * Normally all bits will be zero. Set bits as needed for enabling special debug features. 628 | * See &enum ipts_debug_override. 629 | */ 630 | struct ipts_policy { 631 | u8 reserved1[4]; 632 | u16 doze_timer; 633 | u8 spi_freq_override : 3; 634 | u8 spi_io_mode_override : 3; 635 | u64 reserved2 : 42; 636 | u32 debug_override; 637 | } __packed; 638 | 639 | static_assert(sizeof(struct ipts_policy) == 16); 640 | 641 | /** 642 | * struct ipts_cmd_set_policy - Payload for %IPTS_CMD_SET_POLICY. 643 | * 644 | * @policy: 645 | * The desired policy to apply. 646 | */ 647 | struct ipts_cmd_set_policy { 648 | struct ipts_policy policy; 649 | } __packed; 650 | 651 | static_assert(sizeof(struct ipts_cmd_set_policy) == 16); 652 | 653 | /** 654 | * struct ipts_cmd_reset_sensor - Payload for %IPTS_CMD_RESET_SENSOR. 655 | * 656 | * @type: 657 | * How the ME should reset the sensor. See &enum ipts_reset_type. 658 | * 659 | * @reserved: 660 | * For future expansion. 661 | */ 662 | struct ipts_cmd_reset_sensor { 663 | u32 type; 664 | u8 reserved[4]; 665 | } __packed; 666 | 667 | static_assert(sizeof(struct ipts_cmd_reset_sensor) == 8); 668 | 669 | /** 670 | * struct ipts_cmd_get_hid_desc - Payload for %IPTS_CMD_GET_HID_DESC. 671 | * 672 | * Note: 673 | * This command is only implemented on EDS v2 devices. 674 | * 675 | * @addr_lower: 676 | * Lower 32 bits of the physical address to the HID descriptor buffer. 677 | * Size of the buffer should be &struct ipts_rsp_get_device_info->data_size + 8. 678 | * 679 | * @addr_upper: 680 | * Upper 32 bits of the physical address to the HID descriptor buffer. 681 | * Size of the buffer should be &struct ipts_rsp_get_device_info->data_size + 8. 682 | * 683 | * @magic: 684 | * A magic value with unknown significance. Must be set to 8. 685 | * 686 | * @reserved: 687 | * For future expansion. 688 | */ 689 | struct ipts_cmd_get_hid_desc { 690 | u32 addr_lower; 691 | u32 addr_upper; 692 | u32 magic; 693 | u8 reserved[12]; 694 | } __packed; 695 | 696 | static_assert(sizeof(struct ipts_cmd_get_hid_desc) == 24); 697 | 698 | /** 699 | * struct ipts_command - Structure that represents a message going from host to ME. 700 | * 701 | * @cmd: 702 | * The code of the command that will be executed. See &enum ipts_command_code. 703 | */ 704 | struct ipts_command { 705 | u32 cmd; 706 | union { 707 | struct ipts_cmd_set_mode set_mode; 708 | struct ipts_cmd_set_mem_window set_mem_window; 709 | struct ipts_cmd_quiesce_io quiesce_io; 710 | struct ipts_cmd_feedback feedback; 711 | struct ipts_cmd_set_policy set_policy; 712 | struct ipts_cmd_reset_sensor reset_sensor; 713 | struct ipts_cmd_get_hid_desc get_hid_desc; 714 | u8 raw[320]; 715 | } payload; 716 | } __packed; 717 | 718 | static_assert(sizeof(struct ipts_command) == 324); 719 | 720 | /** 721 | * struct ipts_rsp_get_device_info - Payload of the response to %IPTS_CMD_GET_DEVICE_INFO. 722 | * 723 | * @vendor: 724 | * The vendor ID of the touch sensor. 725 | * 726 | * @product: 727 | * The product ID of the touch sensor. 728 | * 729 | * @hw_rev: 730 | * Touch sensor hardware revision. 731 | * 732 | * @fw_rev: 733 | * Touch sensor firmware revision. 734 | * 735 | * @data_size: 736 | * The maximum amount of data returned by the touch sensor in bytes. This data will be 737 | * &struct ipts_data_buffer followed by raw data or a HID structure. 738 | * 739 | * @feedback_size: 740 | * Maximum size of one feedback structure in bytes. 741 | * 742 | * @sensor_mode: 743 | * Current operating mode of the sensor. See &enum ipts_mode. 744 | * 745 | * @max_touch_points: 746 | * Maximum number of simultaneous touch points that can be reported by sensor. 747 | * 748 | * @spi_frequency: 749 | * SPI frequency supported by sensor and ME firmware. See &enum ipts_spi_freq. 750 | * 751 | * @spi_io_mode: 752 | * SPI I/O mode supported by sensor and ME firmware. See &enum ipts_spi_io_mode. 753 | * 754 | * @reserved1: 755 | * For future expansion. 756 | * 757 | * @sensor_minor_eds_rev: 758 | * Minor version number of EDS spec supported by sensor. 759 | * 760 | * @sensor_major_eds_rev: 761 | * Major version number of EDS spec supported by sensor. 762 | * 763 | * @me_minor_eds_rev: 764 | * Minor version number of EDS spec supported by ME. 765 | * 766 | * @me_major_eds_rev: 767 | * Major version number of EDS spec supported by ME. 768 | * 769 | * @sensor_eds_intf_rev: 770 | * EDS interface revision supported by sensor. 771 | * 772 | * @me_eds_intf_rev: 773 | * EDS interface revision supported by ME. 774 | * 775 | * @vendor_compat_ver: 776 | * Vendor specific version number that indicates compatibility with GPGPU kernels. 777 | * Only relevant when using GuC submission. 778 | * 779 | * @reserved2: 780 | * For future expansion. 781 | */ 782 | struct ipts_rsp_get_device_info { 783 | u16 vendor; 784 | u16 product; 785 | u32 hw_rev; 786 | u32 fw_rev; 787 | u32 data_size; 788 | u32 feedback_size; 789 | u32 sensor_mode; 790 | u8 max_touch_points; 791 | u8 spi_frequency; 792 | u8 spi_io_mode; 793 | u8 reserved1; 794 | u8 sensor_minor_eds_rev; 795 | u8 sensor_major_eds_rev; 796 | u8 me_minor_eds_rev; 797 | u8 me_major_eds_rev; 798 | u8 sensor_eds_intf_rev; 799 | u8 me_eds_intf_rev; 800 | u8 vendor_compat_ver; 801 | u8 reserved2[9]; 802 | } __packed; 803 | 804 | static_assert(sizeof(struct ipts_rsp_get_device_info) == 44); 805 | 806 | /** 807 | * struct ipts_rsp_ready_for_data - Payload of the response to %IPTS_CMD_READY_FOR_DATA. 808 | * 809 | * @size: 810 | * How many bytes the ME wrote into the data buffer. 811 | * 812 | * @buffer_index: 813 | * The index of the buffer that the ME filled with data. 814 | * 815 | * @reset_reason: 816 | * If the status of the response is %IPTS_STATUS_SENSOR_EXPECTED_RESET, the ME will provide the 817 | * reason for the reset. See &enum ipts_reset_reason. 818 | * 819 | * @reserved: 820 | * For future expansion. 821 | */ 822 | struct ipts_rsp_ready_for_data { 823 | u32 size; 824 | u8 buffer_index; 825 | u8 reset_reason; 826 | u8 reserved[22]; 827 | } __packed; 828 | 829 | static_assert(sizeof(struct ipts_rsp_ready_for_data) == 28); 830 | 831 | /** 832 | * struct ipts_rsp_feedback - Payload of the response to %IPTS_CMD_FEEDBACK. 833 | * 834 | * @feedback_index: 835 | * Index value from 0 to %IPTS_MAX_BUFFERS used to indicate which feedback buffer was used. 836 | */ 837 | struct ipts_rsp_feedback { 838 | u8 feedback_index; 839 | u8 reserved[27]; 840 | } __packed; 841 | 842 | static_assert(sizeof(struct ipts_rsp_feedback) == 28); 843 | 844 | /** 845 | * struct ipts_rsp_get_policy - Payload of the response to %IPTS_GET_POLICY. 846 | * 847 | * @policy: 848 | * The current policy. 849 | */ 850 | struct ipts_rsp_get_policy { 851 | struct ipts_policy policy; 852 | } __packed; 853 | 854 | static_assert(sizeof(struct ipts_rsp_get_policy) == 16); 855 | 856 | /** 857 | * struct ipts_response - Structure that represents a message going from ME to host. 858 | * 859 | * @cmd: 860 | * The code of the command that was executed. See &enum ipts_command_code. 861 | * 862 | * @status: 863 | * The status of the sensor after executing the command. See &enum ipts_status. 864 | */ 865 | struct ipts_response { 866 | u32 cmd; 867 | u32 status; 868 | union { 869 | struct ipts_rsp_get_device_info get_device_info; 870 | struct ipts_rsp_ready_for_data ready_for_data; 871 | struct ipts_rsp_feedback feedback; 872 | struct ipts_rsp_get_policy get_policy; 873 | u8 raw[80]; 874 | } payload; 875 | } __packed; 876 | 877 | static_assert(sizeof(struct ipts_response) == 88); 878 | 879 | #endif /* IPTS_SPEC_MEI_H */ 880 | -------------------------------------------------------------------------------- /src/thread.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * Copyright (c) 2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "thread.h" 14 | 15 | /** 16 | * ipts_thread_runner() - The function that is passed to kthread as the thread function. 17 | * 18 | * The data pointer of kthread will be set to the &struct ipts_thread instance, so this function 19 | * will get the actual thread function from there and call it. 20 | * 21 | * Once the actual thread function has exited, this function will trigger the completion object 22 | * so that the thread can be cleaned up using kthread_stop(). 23 | */ 24 | static int ipts_thread_runner(void *data) 25 | { 26 | int ret = 0; 27 | struct ipts_thread *thread = data; 28 | 29 | ret = thread->threadfn(thread); 30 | complete_all(&thread->done); 31 | 32 | return ret; 33 | } 34 | 35 | bool ipts_thread_should_stop(struct ipts_thread *thread) 36 | { 37 | return READ_ONCE(thread->should_stop); 38 | } 39 | 40 | int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread), 41 | void *data, const char *name) 42 | { 43 | init_completion(&thread->done); 44 | 45 | thread->data = data; 46 | thread->should_stop = false; 47 | thread->threadfn = threadfn; 48 | 49 | thread->thread = kthread_run(ipts_thread_runner, thread, name); 50 | return PTR_ERR_OR_ZERO(thread->thread); 51 | } 52 | 53 | int ipts_thread_stop(struct ipts_thread *thread) 54 | { 55 | int ret = 0; 56 | 57 | if (!thread->thread) 58 | return 0; 59 | 60 | WRITE_ONCE(thread->should_stop, true); 61 | 62 | /* 63 | * Make sure that the write has gone through before waiting. 64 | */ 65 | wmb(); 66 | 67 | wait_for_completion(&thread->done); 68 | ret = kthread_stop(thread->thread); 69 | 70 | thread->thread = NULL; 71 | thread->data = NULL; 72 | thread->threadfn = NULL; 73 | 74 | return ret; 75 | } 76 | -------------------------------------------------------------------------------- /src/thread.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (c) 2023 Dorian Stoll 4 | * 5 | * Linux driver for Intel Precise Touch & Stylus 6 | */ 7 | 8 | #ifndef IPTS_THREAD_H 9 | #define IPTS_THREAD_H 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | /** 16 | * struct ipts_thread - Slim wrapper around kthread for MEI purposes. 17 | * 18 | * Calling kthread_stop puts the thread into a interrupt state, causes the MEI API functions 19 | * to always return -EINTR. This makes it impossible to issue MEI commands from a thread that 20 | * is shutting itself down. 21 | * 22 | * By using a custom boolean variable we can instruct the thread to shut down independently of 23 | * kthread_stop. Then we wait and only call kthread_stop once the thread has singnaled that 24 | * it has successfully shut itself down. 25 | * 26 | * @thread: 27 | * The thread that is being wrapped. 28 | * 29 | * @data: 30 | * Data pointer for passing data to the function running inside the thread. 31 | * 32 | * @threadfn: 33 | * The function that will be executed inside of the thread. 34 | * 35 | * @should_stop: 36 | * Whether the thread should start shutting itself down. 37 | * 38 | * @done: 39 | * Completion object that will be triggered by the actual thread runner function once 40 | * threadfn has exited. 41 | */ 42 | struct ipts_thread { 43 | struct task_struct *thread; 44 | 45 | void *data; 46 | int (*threadfn)(struct ipts_thread *thread); 47 | 48 | bool should_stop; 49 | struct completion done; 50 | }; 51 | 52 | bool ipts_thread_should_stop(struct ipts_thread *thread); 53 | int ipts_thread_stop(struct ipts_thread *thread); 54 | int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread), 55 | void *data, const char *name); 56 | 57 | #endif /* IPTS_THREAD_H */ 58 | --------------------------------------------------------------------------------