|
@@ -1,4 +1,4 @@
|
|
-From 2c2d01dbe82ccd154da707a53f5dab45b37300aa Mon Sep 17 00:00:00 2001
|
|
|
|
|
|
+From c478dcf8c11deacc16b2d9a80c26af3c22ccdd3e Mon Sep 17 00:00:00 2001
|
|
From: Dorian Stoll <dorian.stoll@tmsp.io>
|
|
From: Dorian Stoll <dorian.stoll@tmsp.io>
|
|
Date: Thu, 30 Jul 2020 13:21:53 +0200
|
|
Date: Thu, 30 Jul 2020 13:21:53 +0200
|
|
Subject: [PATCH] misc: mei: Add missing IPTS device IDs
|
|
Subject: [PATCH] misc: mei: Add missing IPTS device IDs
|
|
@@ -36,76 +36,197 @@ index 5bf0d50d55a0..c13864512229 100644
|
|
--
|
|
--
|
|
2.39.1
|
|
2.39.1
|
|
|
|
|
|
-From 0d59af85033cbff1496b6a4de1a5dce755c0b30f Mon Sep 17 00:00:00 2001
|
|
|
|
|
|
+From 25bccbb65a0f464a6da0913f3485270e2c840d76 Mon Sep 17 00:00:00 2001
|
|
|
|
+From: Liban Hannan <liban.p@gmail.com>
|
|
|
|
+Date: Tue, 12 Apr 2022 23:31:12 +0100
|
|
|
|
+Subject: [PATCH] iommu: ipts: use IOMMU passthrough mode for IPTS
|
|
|
|
+
|
|
|
|
+Adds a quirk so that IOMMU uses passthrough mode for the IPTS device.
|
|
|
|
+Otherwise, when IOMMU is enabled, IPTS produces DMAR errors like:
|
|
|
|
+
|
|
|
|
+DMAR: [DMA Read NO_PASID] Request device [00:16.4] fault addr
|
|
|
|
+0x104ea3000 [fault reason 0x06] PTE Read access is not set
|
|
|
|
+
|
|
|
|
+This is very similar to the bug described at:
|
|
|
|
+https://bugs.launchpad.net/bugs/1958004
|
|
|
|
+
|
|
|
|
+Fixed with the following patch which this patch basically copies:
|
|
|
|
+https://launchpadlibrarian.net/586396847/43255ca.diff
|
|
|
|
+Patchset: ipts
|
|
|
|
+---
|
|
|
|
+ drivers/iommu/intel/iommu.c | 24 ++++++++++++++++++++++++
|
|
|
|
+ 1 file changed, 24 insertions(+)
|
|
|
|
+
|
|
|
|
+diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
|
|
|
|
+index 644ca49e8cf8..408c321b929a 100644
|
|
|
|
+--- a/drivers/iommu/intel/iommu.c
|
|
|
|
++++ b/drivers/iommu/intel/iommu.c
|
|
|
|
+@@ -37,6 +37,8 @@
|
|
|
|
+ #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
|
|
|
|
+ #define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB)
|
|
|
|
+ #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
|
|
|
|
++#define IS_IPTS(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \
|
|
|
|
++ ((pdev)->device == 0x9d3e))
|
|
|
|
+ #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e)
|
|
|
|
+
|
|
|
|
+ #define IOAPIC_RANGE_START (0xfee00000)
|
|
|
|
+@@ -286,12 +288,14 @@ int intel_iommu_enabled = 0;
|
|
|
|
+ EXPORT_SYMBOL_GPL(intel_iommu_enabled);
|
|
|
|
+
|
|
|
|
+ static int dmar_map_gfx = 1;
|
|
|
|
++static int dmar_map_ipts = 1;
|
|
|
|
+ static int intel_iommu_superpage = 1;
|
|
|
|
+ static int iommu_identity_mapping;
|
|
|
|
+ static int iommu_skip_te_disable;
|
|
|
|
+
|
|
|
|
+ #define IDENTMAP_GFX 2
|
|
|
|
+ #define IDENTMAP_AZALIA 4
|
|
|
|
++#define IDENTMAP_IPTS 16
|
|
|
|
+
|
|
|
|
+ const struct iommu_ops intel_iommu_ops;
|
|
|
|
+
|
|
|
|
+@@ -2630,6 +2634,9 @@ static int device_def_domain_type(struct device *dev)
|
|
|
|
+
|
|
|
|
+ if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
|
|
|
|
+ return IOMMU_DOMAIN_IDENTITY;
|
|
|
|
++
|
|
|
|
++ if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev))
|
|
|
|
++ return IOMMU_DOMAIN_IDENTITY;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+@@ -3019,6 +3026,9 @@ static int __init init_dmars(void)
|
|
|
|
+ if (!dmar_map_gfx)
|
|
|
|
+ iommu_identity_mapping |= IDENTMAP_GFX;
|
|
|
|
+
|
|
|
|
++ if (!dmar_map_ipts)
|
|
|
|
++ iommu_identity_mapping |= IDENTMAP_IPTS;
|
|
|
|
++
|
|
|
|
+ check_tylersburg_isoch();
|
|
|
|
+
|
|
|
|
+ ret = si_domain_init(hw_pass_through);
|
|
|
|
+@@ -4774,6 +4784,17 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
|
|
|
|
+ dmar_map_gfx = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
++static void quirk_iommu_ipts(struct pci_dev *dev)
|
|
|
|
++{
|
|
|
|
++ if (!IS_IPTS(dev))
|
|
|
|
++ return;
|
|
|
|
++
|
|
|
|
++ if (risky_device(dev))
|
|
|
|
++ return;
|
|
|
|
++
|
|
|
|
++ pci_info(dev, "Passthrough IOMMU for IPTS\n");
|
|
|
|
++ dmar_map_ipts = 0;
|
|
|
|
++}
|
|
|
|
+ /* G4x/GM45 integrated gfx dmar support is totally busted. */
|
|
|
|
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx);
|
|
|
|
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx);
|
|
|
|
+@@ -4809,6 +4830,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
|
|
|
|
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx);
|
|
|
|
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx);
|
|
|
|
+
|
|
|
|
++/* disable IPTS dmar support */
|
|
|
|
++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9D3E, quirk_iommu_ipts);
|
|
|
|
++
|
|
|
|
+ static void quirk_iommu_rwbf(struct pci_dev *dev)
|
|
|
|
+ {
|
|
|
|
+ if (risky_device(dev))
|
|
|
|
+--
|
|
|
|
+2.39.1
|
|
|
|
+
|
|
|
|
+From f7a9a34b94d87014af3d459a7abe67189c02348c Mon Sep 17 00:00:00 2001
|
|
From: Dorian Stoll <dorian.stoll@tmsp.io>
|
|
From: Dorian Stoll <dorian.stoll@tmsp.io>
|
|
-Date: Thu, 6 Aug 2020 11:20:41 +0200
|
|
|
|
-Subject: [PATCH] misc: Add support for Intel Precise Touch & Stylus
|
|
|
|
|
|
+Date: Sun, 11 Dec 2022 12:00:59 +0100
|
|
|
|
+Subject: [PATCH] hid: Add support for Intel Precise Touch and Stylus
|
|
|
|
|
|
-Based on linux-surface/intel-precise-touch@3f362c
|
|
|
|
|
|
+Based on linux-surface/intel-precise-touch@8abe268
|
|
|
|
|
|
Signed-off-by: Dorian Stoll <dorian.stoll@tmsp.io>
|
|
Signed-off-by: Dorian Stoll <dorian.stoll@tmsp.io>
|
|
Patchset: ipts
|
|
Patchset: ipts
|
|
---
|
|
---
|
|
- drivers/misc/Kconfig | 1 +
|
|
|
|
- drivers/misc/Makefile | 1 +
|
|
|
|
- drivers/misc/ipts/Kconfig | 17 ++
|
|
|
|
- drivers/misc/ipts/Makefile | 12 ++
|
|
|
|
- drivers/misc/ipts/context.h | 47 +++++
|
|
|
|
- drivers/misc/ipts/control.c | 113 +++++++++++
|
|
|
|
- drivers/misc/ipts/control.h | 24 +++
|
|
|
|
- drivers/misc/ipts/mei.c | 125 ++++++++++++
|
|
|
|
- drivers/misc/ipts/protocol.h | 347 ++++++++++++++++++++++++++++++++++
|
|
|
|
- drivers/misc/ipts/receiver.c | 224 ++++++++++++++++++++++
|
|
|
|
- drivers/misc/ipts/receiver.h | 16 ++
|
|
|
|
- drivers/misc/ipts/resources.c | 128 +++++++++++++
|
|
|
|
- drivers/misc/ipts/resources.h | 17 ++
|
|
|
|
- drivers/misc/ipts/uapi.c | 208 ++++++++++++++++++++
|
|
|
|
- drivers/misc/ipts/uapi.h | 47 +++++
|
|
|
|
- 15 files changed, 1327 insertions(+)
|
|
|
|
- create mode 100644 drivers/misc/ipts/Kconfig
|
|
|
|
- create mode 100644 drivers/misc/ipts/Makefile
|
|
|
|
- create mode 100644 drivers/misc/ipts/context.h
|
|
|
|
- create mode 100644 drivers/misc/ipts/control.c
|
|
|
|
- create mode 100644 drivers/misc/ipts/control.h
|
|
|
|
- create mode 100644 drivers/misc/ipts/mei.c
|
|
|
|
- create mode 100644 drivers/misc/ipts/protocol.h
|
|
|
|
- create mode 100644 drivers/misc/ipts/receiver.c
|
|
|
|
- create mode 100644 drivers/misc/ipts/receiver.h
|
|
|
|
- create mode 100644 drivers/misc/ipts/resources.c
|
|
|
|
- create mode 100644 drivers/misc/ipts/resources.h
|
|
|
|
- create mode 100644 drivers/misc/ipts/uapi.c
|
|
|
|
- create mode 100644 drivers/misc/ipts/uapi.h
|
|
|
|
|
|
+ drivers/hid/Kconfig | 2 +
|
|
|
|
+ drivers/hid/Makefile | 2 +
|
|
|
|
+ drivers/hid/ipts/Kconfig | 14 +
|
|
|
|
+ drivers/hid/ipts/Makefile | 14 +
|
|
|
|
+ drivers/hid/ipts/cmd.c | 62 +++++
|
|
|
|
+ drivers/hid/ipts/cmd.h | 61 ++++
|
|
|
|
+ drivers/hid/ipts/context.h | 51 ++++
|
|
|
|
+ drivers/hid/ipts/control.c | 495 +++++++++++++++++++++++++++++++++
|
|
|
|
+ drivers/hid/ipts/control.h | 127 +++++++++
|
|
|
|
+ drivers/hid/ipts/desc.h | 81 ++++++
|
|
|
|
+ drivers/hid/ipts/hid.c | 348 +++++++++++++++++++++++
|
|
|
|
+ drivers/hid/ipts/hid.h | 22 ++
|
|
|
|
+ drivers/hid/ipts/main.c | 127 +++++++++
|
|
|
|
+ drivers/hid/ipts/mei.c | 189 +++++++++++++
|
|
|
|
+ drivers/hid/ipts/mei.h | 67 +++++
|
|
|
|
+ drivers/hid/ipts/receiver.c | 249 +++++++++++++++++
|
|
|
|
+ drivers/hid/ipts/receiver.h | 17 ++
|
|
|
|
+ drivers/hid/ipts/resources.c | 108 +++++++
|
|
|
|
+ drivers/hid/ipts/resources.h | 39 +++
|
|
|
|
+ drivers/hid/ipts/spec-data.h | 100 +++++++
|
|
|
|
+ drivers/hid/ipts/spec-device.h | 285 +++++++++++++++++++
|
|
|
|
+ drivers/hid/ipts/spec-hid.h | 35 +++
|
|
|
|
+ drivers/hid/ipts/thread.c | 85 ++++++
|
|
|
|
+ drivers/hid/ipts/thread.h | 60 ++++
|
|
|
|
+ 24 files changed, 2640 insertions(+)
|
|
|
|
+ create mode 100644 drivers/hid/ipts/Kconfig
|
|
|
|
+ create mode 100644 drivers/hid/ipts/Makefile
|
|
|
|
+ create mode 100644 drivers/hid/ipts/cmd.c
|
|
|
|
+ create mode 100644 drivers/hid/ipts/cmd.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/context.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/control.c
|
|
|
|
+ create mode 100644 drivers/hid/ipts/control.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/desc.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/hid.c
|
|
|
|
+ create mode 100644 drivers/hid/ipts/hid.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/main.c
|
|
|
|
+ create mode 100644 drivers/hid/ipts/mei.c
|
|
|
|
+ create mode 100644 drivers/hid/ipts/mei.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/receiver.c
|
|
|
|
+ create mode 100644 drivers/hid/ipts/receiver.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/resources.c
|
|
|
|
+ create mode 100644 drivers/hid/ipts/resources.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/spec-data.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/spec-device.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/spec-hid.h
|
|
|
|
+ create mode 100644 drivers/hid/ipts/thread.c
|
|
|
|
+ create mode 100644 drivers/hid/ipts/thread.h
|
|
|
|
|
|
-diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
|
|
|
|
-index 358ad56f6524..1fd6f755f98b 100644
|
|
|
|
---- a/drivers/misc/Kconfig
|
|
|
|
-+++ b/drivers/misc/Kconfig
|
|
|
|
-@@ -514,4 +514,5 @@ source "drivers/misc/habanalabs/Kconfig"
|
|
|
|
- source "drivers/misc/uacce/Kconfig"
|
|
|
|
- source "drivers/misc/pvpanic/Kconfig"
|
|
|
|
- source "drivers/misc/mchp_pci1xxxx/Kconfig"
|
|
|
|
-+source "drivers/misc/ipts/Kconfig"
|
|
|
|
|
|
+diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
|
|
|
|
+index 185a077d59cd..1523ccdf73b5 100644
|
|
|
|
+--- a/drivers/hid/Kconfig
|
|
|
|
++++ b/drivers/hid/Kconfig
|
|
|
|
+@@ -1290,4 +1290,6 @@ source "drivers/hid/amd-sfh-hid/Kconfig"
|
|
|
|
+
|
|
|
|
+ source "drivers/hid/surface-hid/Kconfig"
|
|
|
|
+
|
|
|
|
++source "drivers/hid/ipts/Kconfig"
|
|
|
|
++
|
|
endmenu
|
|
endmenu
|
|
-diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
|
|
|
|
-index ac9b3e757ba1..5a82ffe77911 100644
|
|
|
|
---- a/drivers/misc/Makefile
|
|
|
|
-+++ b/drivers/misc/Makefile
|
|
|
|
-@@ -62,3 +62,4 @@ obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
|
|
|
|
- obj-$(CONFIG_OPEN_DICE) += open-dice.o
|
|
|
|
- obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
|
|
|
|
- obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o
|
|
|
|
-+obj-$(CONFIG_MISC_IPTS) += ipts/
|
|
|
|
-diff --git a/drivers/misc/ipts/Kconfig b/drivers/misc/ipts/Kconfig
|
|
|
|
|
|
+diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
|
|
|
|
+index e8014c1a2f8b..e48300bcea9b 100644
|
|
|
|
+--- a/drivers/hid/Makefile
|
|
|
|
++++ b/drivers/hid/Makefile
|
|
|
|
+@@ -164,3 +164,5 @@ obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/
|
|
|
|
+ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/
|
|
|
|
+
|
|
|
|
+ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/
|
|
|
|
++
|
|
|
|
++obj-$(CONFIG_HID_IPTS) += ipts/
|
|
|
|
+diff --git a/drivers/hid/ipts/Kconfig b/drivers/hid/ipts/Kconfig
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..83e2a930c396
|
|
|
|
|
|
+index 000000000000..297401bd388d
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/Kconfig
|
|
|
|
-@@ -0,0 +1,17 @@
|
|
|
|
|
|
++++ b/drivers/hid/ipts/Kconfig
|
|
|
|
+@@ -0,0 +1,14 @@
|
|
+# SPDX-License-Identifier: GPL-2.0-or-later
|
|
+# SPDX-License-Identifier: GPL-2.0-or-later
|
|
+
|
|
+
|
|
-+config MISC_IPTS
|
|
|
|
|
|
++config HID_IPTS
|
|
+ tristate "Intel Precise Touch & Stylus"
|
|
+ tristate "Intel Precise Touch & Stylus"
|
|
+ depends on INTEL_MEI
|
|
+ depends on INTEL_MEI
|
|
|
|
++ depends on HID
|
|
+ help
|
|
+ help
|
|
+ Say Y here if your system has a touchscreen using Intels
|
|
+ Say Y here if your system has a touchscreen using Intels
|
|
+ Precise Touch & Stylus (IPTS) technology.
|
|
+ Precise Touch & Stylus (IPTS) technology.
|
|
@@ -114,1490 +235,2754 @@ index 000000000000..83e2a930c396
|
|
+
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called ipts.
|
|
+ module will be called ipts.
|
|
-+
|
|
|
|
-+ Building this driver alone will not give you a working touchscreen.
|
|
|
|
-+ It only exposed a userspace API that can be used by a daemon to
|
|
|
|
-+ receive and process data from the touchscreen hardware.
|
|
|
|
-diff --git a/drivers/misc/ipts/Makefile b/drivers/misc/ipts/Makefile
|
|
|
|
|
|
+diff --git a/drivers/hid/ipts/Makefile b/drivers/hid/ipts/Makefile
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..8f58b9adbc94
|
|
|
|
|
|
+index 000000000000..0fe655bccdc0
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/Makefile
|
|
|
|
-@@ -0,0 +1,12 @@
|
|
|
|
|
|
++++ b/drivers/hid/ipts/Makefile
|
|
|
|
+@@ -0,0 +1,14 @@
|
|
+# SPDX-License-Identifier: GPL-2.0-or-later
|
|
+# SPDX-License-Identifier: GPL-2.0-or-later
|
|
+#
|
|
+#
|
|
+# Makefile for the IPTS touchscreen driver
|
|
+# Makefile for the IPTS touchscreen driver
|
|
+#
|
|
+#
|
|
+
|
|
+
|
|
-+obj-$(CONFIG_MISC_IPTS) += ipts.o
|
|
|
|
-+ipts-objs := control.o
|
|
|
|
|
|
++obj-$(CONFIG_HID_IPTS) += ipts.o
|
|
|
|
++ipts-objs := cmd.o
|
|
|
|
++ipts-objs += control.o
|
|
|
|
++ipts-objs += hid.o
|
|
|
|
++ipts-objs += main.o
|
|
+ipts-objs += mei.o
|
|
+ipts-objs += mei.o
|
|
+ipts-objs += receiver.o
|
|
+ipts-objs += receiver.o
|
|
+ipts-objs += resources.o
|
|
+ipts-objs += resources.o
|
|
-+ipts-objs += uapi.o
|
|
|
|
-+
|
|
|
|
-diff --git a/drivers/misc/ipts/context.h b/drivers/misc/ipts/context.h
|
|
|
|
-new file mode 100644
|
|
|
|
-index 000000000000..f4b06a2d3f72
|
|
|
|
---- /dev/null
|
|
|
|
-+++ b/drivers/misc/ipts/context.h
|
|
|
|
-@@ -0,0 +1,47 @@
|
|
|
|
-+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
-+/*
|
|
|
|
-+ * Copyright (c) 2016 Intel Corporation
|
|
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
-+ *
|
|
|
|
-+ * Linux driver for Intel Precise Touch & Stylus
|
|
|
|
-+ */
|
|
|
|
-+
|
|
|
|
-+#ifndef _IPTS_CONTEXT_H_
|
|
|
|
-+#define _IPTS_CONTEXT_H_
|
|
|
|
-+
|
|
|
|
-+#include <linux/cdev.h>
|
|
|
|
-+#include <linux/device.h>
|
|
|
|
-+#include <linux/mei_cl_bus.h>
|
|
|
|
-+#include <linux/types.h>
|
|
|
|
-+
|
|
|
|
-+#include "protocol.h"
|
|
|
|
-+
|
|
|
|
-+enum ipts_host_status {
|
|
|
|
-+ IPTS_HOST_STATUS_STARTING,
|
|
|
|
-+ IPTS_HOST_STATUS_STARTED,
|
|
|
|
-+ IPTS_HOST_STATUS_STOPPING,
|
|
|
|
-+ IPTS_HOST_STATUS_STOPPED,
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+struct ipts_buffer_info {
|
|
|
|
-+ u8 *address;
|
|
|
|
-+ dma_addr_t dma_address;
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+struct ipts_context {
|
|
|
|
-+ struct mei_cl_device *cldev;
|
|
|
|
-+ struct device *dev;
|
|
|
|
-+
|
|
|
|
-+ bool restart;
|
|
|
|
-+ enum ipts_host_status status;
|
|
|
|
-+ struct ipts_get_device_info_rsp device_info;
|
|
|
|
-+
|
|
|
|
-+ struct ipts_buffer_info data[IPTS_BUFFERS];
|
|
|
|
-+ struct ipts_buffer_info doorbell;
|
|
|
|
-+
|
|
|
|
-+ struct ipts_buffer_info feedback[IPTS_BUFFERS];
|
|
|
|
-+ struct ipts_buffer_info workqueue;
|
|
|
|
-+ struct ipts_buffer_info host2me;
|
|
|
|
-+};
|
|
|
|
-+
|
|
|
|
-+#endif /* _IPTS_CONTEXT_H_ */
|
|
|
|
-diff --git a/drivers/misc/ipts/control.c b/drivers/misc/ipts/control.c
|
|
|
|
|
|
++ipts-objs += thread.o
|
|
|
|
+diff --git a/drivers/hid/ipts/cmd.c b/drivers/hid/ipts/cmd.c
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..a1d1f97a13d7
|
|
|
|
|
|
+index 000000000000..7fd69271ccd5
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/control.c
|
|
|
|
-@@ -0,0 +1,113 @@
|
|
|
|
|
|
++++ b/drivers/hid/ipts/cmd.c
|
|
|
|
+@@ -0,0 +1,62 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+/*
|
|
+/*
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
+ *
|
|
+ *
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
+
|
|
+
|
|
-+#include <linux/mei_cl_bus.h>
|
|
|
|
|
|
++#include <linux/errno.h>
|
|
|
|
++#include <linux/types.h>
|
|
+
|
|
+
|
|
|
|
++#include "cmd.h"
|
|
+#include "context.h"
|
|
+#include "context.h"
|
|
-+#include "protocol.h"
|
|
|
|
-+#include "resources.h"
|
|
|
|
-+#include "uapi.h"
|
|
|
|
|
|
++#include "mei.h"
|
|
|
|
++#include "spec-device.h"
|
|
+
|
|
+
|
|
-+int ipts_control_send(struct ipts_context *ipts, u32 code, void *payload,
|
|
|
|
-+ size_t size)
|
|
|
|
|
|
++int ipts_cmd_recv_timeout(struct ipts_context *ipts, enum ipts_command_code code,
|
|
|
|
++ struct ipts_response *rsp, u64 timeout)
|
|
+{
|
|
+{
|
|
-+ int ret;
|
|
|
|
-+ struct ipts_command cmd;
|
|
|
|
-+
|
|
|
|
-+ memset(&cmd, 0, sizeof(struct ipts_command));
|
|
|
|
-+ cmd.code = code;
|
|
|
|
|
|
++ int ret = 0;
|
|
+
|
|
+
|
|
-+ if (payload && size > 0)
|
|
|
|
-+ memcpy(&cmd.payload, payload, size);
|
|
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+ ret = mei_cldev_send(ipts->cldev, (u8 *)&cmd, sizeof(cmd.code) + size);
|
|
|
|
-+ if (ret >= 0)
|
|
|
|
-+ return 0;
|
|
|
|
|
|
++ if (!rsp)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
+ /*
|
|
+ /*
|
|
-+ * During shutdown the device might get pulled away from below our feet.
|
|
|
|
-+ * Dont log an error in this case, because it will confuse people.
|
|
|
|
|
|
++ * In a response, the command code will have the most significant bit flipped to 1.
|
|
|
|
++ * If code is passed to ipts_mei_recv as is, no messages will be received.
|
|
+ */
|
|
+ */
|
|
-+ if (ret != -ENODEV || ipts->status != IPTS_HOST_STATUS_STOPPING)
|
|
|
|
-+ dev_err(ipts->dev, "Error while sending: 0x%X:%d\n", code, ret);
|
|
|
|
-+
|
|
|
|
-+ return ret;
|
|
|
|
-+}
|
|
|
|
|
|
++ ret = ipts_mei_recv(&ipts->mei, code | IPTS_RSP_BIT, rsp, timeout);
|
|
|
|
++ if (ret < 0)
|
|
|
|
++ return ret;
|
|
+
|
|
+
|
|
-+int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer)
|
|
|
|
-+{
|
|
|
|
-+ struct ipts_feedback_cmd cmd;
|
|
|
|
|
|
++ dev_dbg(ipts->dev, "Received 0x%02X with status 0x%02X\n", code, rsp->status);
|
|
+
|
|
+
|
|
-+ memset(&cmd, 0, sizeof(struct ipts_feedback_cmd));
|
|
|
|
-+ cmd.buffer = buffer;
|
|
|
|
|
|
++ /*
|
|
|
|
++ * Some devices will always return this error.
|
|
|
|
++ * It is allowed to ignore it and to try continuing.
|
|
|
|
++ */
|
|
|
|
++ if (rsp->status == IPTS_STATUS_COMPAT_CHECK_FAIL)
|
|
|
|
++ rsp->status = IPTS_STATUS_SUCCESS;
|
|
+
|
|
+
|
|
-+ return ipts_control_send(ipts, IPTS_CMD_FEEDBACK, &cmd,
|
|
|
|
-+ sizeof(struct ipts_feedback_cmd));
|
|
|
|
|
|
++ return 0;
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+int ipts_control_set_feature(struct ipts_context *ipts, u8 report, u8 value)
|
|
|
|
|
|
++int ipts_cmd_send(struct ipts_context *ipts, enum ipts_command_code code, void *data, size_t size)
|
|
+{
|
|
+{
|
|
-+ struct ipts_feedback_buffer *feedback;
|
|
|
|
-+
|
|
|
|
-+ memset(ipts->host2me.address, 0, ipts->device_info.feedback_size);
|
|
|
|
-+ feedback = (struct ipts_feedback_buffer *)ipts->host2me.address;
|
|
|
|
|
|
++ struct ipts_command cmd = { 0 };
|
|
+
|
|
+
|
|
-+ feedback->cmd_type = IPTS_FEEDBACK_CMD_TYPE_NONE;
|
|
|
|
-+ feedback->data_type = IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES;
|
|
|
|
-+ feedback->buffer = IPTS_HOST2ME_BUFFER;
|
|
|
|
-+ feedback->size = 2;
|
|
|
|
-+ feedback->payload[0] = report;
|
|
|
|
-+ feedback->payload[1] = value;
|
|
|
|
-+
|
|
|
|
-+ return ipts_control_send_feedback(ipts, IPTS_HOST2ME_BUFFER);
|
|
|
|
-+}
|
|
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+int ipts_control_start(struct ipts_context *ipts)
|
|
|
|
-+{
|
|
|
|
-+ if (ipts->status != IPTS_HOST_STATUS_STOPPED)
|
|
|
|
-+ return -EBUSY;
|
|
|
|
|
|
++ cmd.cmd = code;
|
|
+
|
|
+
|
|
-+ dev_info(ipts->dev, "Starting IPTS\n");
|
|
|
|
-+ ipts->status = IPTS_HOST_STATUS_STARTING;
|
|
|
|
-+ ipts->restart = false;
|
|
|
|
|
|
++ if (data && size > 0)
|
|
|
|
++ memcpy(cmd.payload, data, size);
|
|
+
|
|
+
|
|
-+ ipts_uapi_link(ipts);
|
|
|
|
-+ return ipts_control_send(ipts, IPTS_CMD_GET_DEVICE_INFO, NULL, 0);
|
|
|
|
|
|
++ dev_dbg(ipts->dev, "Sending 0x%02X with %ld bytes payload\n", code, size);
|
|
|
|
++ return ipts_mei_send(&ipts->mei, &cmd, sizeof(cmd.cmd) + size);
|
|
+}
|
|
+}
|
|
|
|
+diff --git a/drivers/hid/ipts/cmd.h b/drivers/hid/ipts/cmd.h
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..924758ffee67
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/cmd.h
|
|
|
|
+@@ -0,0 +1,61 @@
|
|
|
|
++/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
|
|
++ */
|
|
+
|
|
+
|
|
-+int ipts_control_stop(struct ipts_context *ipts)
|
|
|
|
-+{
|
|
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ if (ipts->status == IPTS_HOST_STATUS_STOPPING)
|
|
|
|
-+ return -EBUSY;
|
|
|
|
|
|
++#ifndef IPTS_CMD_H
|
|
|
|
++#define IPTS_CMD_H
|
|
+
|
|
+
|
|
-+ if (ipts->status == IPTS_HOST_STATUS_STOPPED)
|
|
|
|
-+ return -EBUSY;
|
|
|
|
|
|
++#include <linux/types.h>
|
|
+
|
|
+
|
|
-+ dev_info(ipts->dev, "Stopping IPTS\n");
|
|
|
|
-+ ipts->status = IPTS_HOST_STATUS_STOPPING;
|
|
|
|
|
|
++#include "context.h"
|
|
|
|
++#include "spec-device.h"
|
|
+
|
|
+
|
|
-+ ipts_uapi_unlink();
|
|
|
|
-+ ipts_resources_free(ipts);
|
|
|
|
|
|
++/*
|
|
|
|
++ * The default timeout for receiving responses
|
|
|
|
++ */
|
|
|
|
++#define IPTS_CMD_DEFAULT_TIMEOUT 1000
|
|
+
|
|
+
|
|
-+ ret = ipts_control_send_feedback(ipts, 0);
|
|
|
|
-+ if (ret == -ENODEV)
|
|
|
|
-+ ipts->status = IPTS_HOST_STATUS_STOPPED;
|
|
|
|
|
|
++/*
|
|
|
|
++ * ipts_cmd_recv_timeout() - Receives a response to a command.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ * @code: The type of the command / response.
|
|
|
|
++ * @rsp: The address that the received response will be copied to.
|
|
|
|
++ * @timeout: How many milliseconds the function will wait at most.
|
|
|
|
++ *
|
|
|
|
++ * A negative timeout means to wait forever.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received.
|
|
|
|
++ */
|
|
|
|
++int ipts_cmd_recv_timeout(struct ipts_context *ipts, enum ipts_command_code code,
|
|
|
|
++ struct ipts_response *rsp, u64 timeout);
|
|
+
|
|
+
|
|
-+ return ret;
|
|
|
|
|
|
++/*
|
|
|
|
++ * ipts_cmd_recv() - Receives a response to a command.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ * @code: The type of the command / response.
|
|
|
|
++ * @rsp: The address that the received response will be copied to.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received.
|
|
|
|
++ */
|
|
|
|
++static inline int ipts_cmd_recv(struct ipts_context *ipts, enum ipts_command_code code,
|
|
|
|
++ struct ipts_response *rsp)
|
|
|
|
++{
|
|
|
|
++ return ipts_cmd_recv_timeout(ipts, code, rsp, IPTS_CMD_DEFAULT_TIMEOUT);
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+int ipts_control_restart(struct ipts_context *ipts)
|
|
|
|
-+{
|
|
|
|
-+ if (ipts->restart)
|
|
|
|
-+ return -EBUSY;
|
|
|
|
|
|
++/*
|
|
|
|
++ * ipts_cmd_send() - Executes a command on the device.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ * @code: The type of the command to execute.
|
|
|
|
++ * @data: The payload containing parameters for the command.
|
|
|
|
++ * @size: The size of the payload.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
|
|
++ */
|
|
|
|
++int ipts_cmd_send(struct ipts_context *ipts, enum ipts_command_code code, void *data, size_t size);
|
|
+
|
|
+
|
|
-+ ipts->restart = true;
|
|
|
|
-+ return ipts_control_stop(ipts);
|
|
|
|
-+}
|
|
|
|
-diff --git a/drivers/misc/ipts/control.h b/drivers/misc/ipts/control.h
|
|
|
|
|
|
++#endif /* IPTS_CMD_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/context.h b/drivers/hid/ipts/context.h
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..2c44e9e0e99f
|
|
|
|
|
|
+index 000000000000..3450a95e66ee
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/control.h
|
|
|
|
-@@ -0,0 +1,24 @@
|
|
|
|
|
|
++++ b/drivers/hid/ipts/context.h
|
|
|
|
+@@ -0,0 +1,51 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/*
|
|
+/*
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
+ *
|
|
+ *
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
+
|
|
+
|
|
-+#ifndef _IPTS_CONTROL_H_
|
|
|
|
-+#define _IPTS_CONTROL_H_
|
|
|
|
|
|
++#ifndef IPTS_CONTEXT_H
|
|
|
|
++#define IPTS_CONTEXT_H
|
|
+
|
|
+
|
|
|
|
++#include <linux/completion.h>
|
|
|
|
++#include <linux/device.h>
|
|
|
|
++#include <linux/hid.h>
|
|
|
|
++#include <linux/mei_cl_bus.h>
|
|
|
|
++#include <linux/mutex.h>
|
|
|
|
++#include <linux/sched.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+
|
|
-+#include "context.h"
|
|
|
|
|
|
++#include "mei.h"
|
|
|
|
++#include "resources.h"
|
|
|
|
++#include "spec-device.h"
|
|
|
|
++#include "thread.h"
|
|
+
|
|
+
|
|
-+int ipts_control_send(struct ipts_context *ipts, u32 cmd, void *payload,
|
|
|
|
-+ size_t size);
|
|
|
|
-+int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer);
|
|
|
|
-+int ipts_control_set_feature(struct ipts_context *ipts, u8 report, u8 value);
|
|
|
|
-+int ipts_control_start(struct ipts_context *ipts);
|
|
|
|
-+int ipts_control_restart(struct ipts_context *ipts);
|
|
|
|
-+int ipts_control_stop(struct ipts_context *ipts);
|
|
|
|
|
|
++struct ipts_context {
|
|
|
|
++ struct device *dev;
|
|
|
|
++ struct ipts_mei mei;
|
|
|
|
++
|
|
|
|
++ enum ipts_mode mode;
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * Prevents concurrent GET_FEATURE reports.
|
|
|
|
++ */
|
|
|
|
++ struct mutex feature_lock;
|
|
|
|
++ struct completion feature_event;
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * These are not inside of struct ipts_resources
|
|
|
|
++ * because they don't own the memory they point to.
|
|
|
|
++ */
|
|
|
|
++ struct ipts_buffer feature_report;
|
|
|
|
++ struct ipts_buffer descriptor;
|
|
|
|
++
|
|
|
|
++ struct hid_device *hid;
|
|
|
|
++ struct ipts_device_info info;
|
|
|
|
++ struct ipts_resources resources;
|
|
|
|
++
|
|
|
|
++ struct ipts_thread receiver_loop;
|
|
|
|
++};
|
|
+
|
|
+
|
|
-+#endif /* _IPTS_CONTROL_H_ */
|
|
|
|
-diff --git a/drivers/misc/ipts/mei.c b/drivers/misc/ipts/mei.c
|
|
|
|
|
|
++#endif /* IPTS_CONTEXT_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/control.c b/drivers/hid/ipts/control.c
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..59ecf13e00d2
|
|
|
|
|
|
+index 000000000000..2f61500b5119
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/mei.c
|
|
|
|
-@@ -0,0 +1,125 @@
|
|
|
|
|
|
++++ b/drivers/hid/ipts/control.c
|
|
|
|
+@@ -0,0 +1,495 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+/*
|
|
+/*
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
+ *
|
|
+ *
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
+
|
|
+
|
|
+#include <linux/delay.h>
|
|
+#include <linux/delay.h>
|
|
-+#include <linux/dma-mapping.h>
|
|
|
|
-+#include <linux/mei_cl_bus.h>
|
|
|
|
-+#include <linux/mod_devicetable.h>
|
|
|
|
-+#include <linux/module.h>
|
|
|
|
-+#include <linux/slab.h>
|
|
|
|
|
|
++#include <linux/dev_printk.h>
|
|
|
|
++#include <linux/errno.h>
|
|
|
|
++#include <linux/kernel.h>
|
|
|
|
++#include <linux/kthread.h>
|
|
|
|
++#include <linux/types.h>
|
|
+
|
|
+
|
|
|
|
++#include "cmd.h"
|
|
+#include "context.h"
|
|
+#include "context.h"
|
|
+#include "control.h"
|
|
+#include "control.h"
|
|
-+#include "protocol.h"
|
|
|
|
|
|
++#include "desc.h"
|
|
|
|
++#include "hid.h"
|
|
+#include "receiver.h"
|
|
+#include "receiver.h"
|
|
-+#include "uapi.h"
|
|
|
|
|
|
++#include "resources.h"
|
|
|
|
++#include "spec-data.h"
|
|
|
|
++#include "spec-device.h"
|
|
+
|
|
+
|
|
-+static int ipts_mei_set_dma_mask(struct mei_cl_device *cldev)
|
|
|
|
|
|
++static int ipts_control_get_device_info(struct ipts_context *ipts, struct ipts_device_info *info)
|
|
+{
|
|
+{
|
|
-+ int ret;
|
|
|
|
-+
|
|
|
|
-+ ret = dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64));
|
|
|
|
-+ if (!ret)
|
|
|
|
-+ return 0;
|
|
|
|
-+
|
|
|
|
-+ return dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(32));
|
|
|
|
-+}
|
|
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_response rsp = { 0 };
|
|
+
|
|
+
|
|
-+static int ipts_mei_probe(struct mei_cl_device *cldev,
|
|
|
|
-+ const struct mei_cl_device_id *id)
|
|
|
|
-+{
|
|
|
|
-+ int ret;
|
|
|
|
-+ struct ipts_context *ipts;
|
|
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+ if (ipts_mei_set_dma_mask(cldev)) {
|
|
|
|
-+ dev_err(&cldev->dev, "Failed to set DMA mask for IPTS\n");
|
|
|
|
|
|
++ if (!info)
|
|
+ return -EFAULT;
|
|
+ return -EFAULT;
|
|
-+ }
|
|
|
|
+
|
|
+
|
|
-+ ret = mei_cldev_enable(cldev);
|
|
|
|
|
|
++ ret = ipts_cmd_send(ipts, IPTS_CMD_GET_DEVICE_INFO, NULL, 0);
|
|
+ if (ret) {
|
|
+ if (ret) {
|
|
-+ dev_err(&cldev->dev, "Failed to enable MEI device: %d\n", ret);
|
|
|
|
|
|
++ dev_err(ipts->dev, "GET_DEVICE_INFO: send failed: %d\n", ret);
|
|
+ return ret;
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
-+ ipts = kzalloc(sizeof(*ipts), GFP_KERNEL);
|
|
|
|
-+ if (!ipts) {
|
|
|
|
-+ mei_cldev_disable(cldev);
|
|
|
|
-+ return -ENOMEM;
|
|
|
|
|
|
++ ret = ipts_cmd_recv(ipts, IPTS_CMD_GET_DEVICE_INFO, &rsp);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "GET_DEVICE_INFO: recv failed: %d\n", ret);
|
|
|
|
++ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
-+ ipts->cldev = cldev;
|
|
|
|
-+ ipts->dev = &cldev->dev;
|
|
|
|
-+ ipts->status = IPTS_HOST_STATUS_STOPPED;
|
|
|
|
-+
|
|
|
|
-+ mei_cldev_set_drvdata(cldev, ipts);
|
|
|
|
-+ mei_cldev_register_rx_cb(cldev, ipts_receiver_callback);
|
|
|
|
|
|
++ if (rsp.status != IPTS_STATUS_SUCCESS) {
|
|
|
|
++ dev_err(ipts->dev, "GET_DEVICE_INFO: cmd failed: %d\n", rsp.status);
|
|
|
|
++ return -EBADR;
|
|
|
|
++ }
|
|
+
|
|
+
|
|
-+ return ipts_control_start(ipts);
|
|
|
|
|
|
++ memcpy(info, rsp.payload, sizeof(*info));
|
|
|
|
++ return 0;
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+static void ipts_mei_remove(struct mei_cl_device *cldev)
|
|
|
|
|
|
++static int ipts_control_set_mode(struct ipts_context *ipts, enum ipts_mode mode)
|
|
+{
|
|
+{
|
|
-+ int i;
|
|
|
|
-+ struct ipts_context *ipts = mei_cldev_get_drvdata(cldev);
|
|
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_set_mode cmd = { 0 };
|
|
|
|
++ struct ipts_response rsp = { 0 };
|
|
+
|
|
+
|
|
-+ ipts_control_stop(ipts);
|
|
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+ for (i = 0; i < 20; i++) {
|
|
|
|
-+ if (ipts->status == IPTS_HOST_STATUS_STOPPED)
|
|
|
|
-+ break;
|
|
|
|
|
|
++ cmd.mode = mode;
|
|
+
|
|
+
|
|
-+ msleep(25);
|
|
|
|
|
|
++ ret = ipts_cmd_send(ipts, IPTS_CMD_SET_MODE, &cmd, sizeof(cmd));
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "SET_MODE: send failed: %d\n", ret);
|
|
|
|
++ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
-+ mei_cldev_disable(cldev);
|
|
|
|
-+ kfree(ipts);
|
|
|
|
-+}
|
|
|
|
|
|
++ ret = ipts_cmd_recv(ipts, IPTS_CMD_SET_MODE, &rsp);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "SET_MODE: recv failed: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
+
|
|
+
|
|
-+static struct mei_cl_device_id ipts_mei_device_id_table[] = {
|
|
|
|
-+ { "", IPTS_MEI_UUID, MEI_CL_VERSION_ANY },
|
|
|
|
-+ {},
|
|
|
|
-+};
|
|
|
|
-+MODULE_DEVICE_TABLE(mei, ipts_mei_device_id_table);
|
|
|
|
|
|
++ if (rsp.status != IPTS_STATUS_SUCCESS) {
|
|
|
|
++ dev_err(ipts->dev, "SET_MODE: cmd failed: %d\n", rsp.status);
|
|
|
|
++ return -EBADR;
|
|
|
|
++ }
|
|
+
|
|
+
|
|
-+static struct mei_cl_driver ipts_mei_driver = {
|
|
|
|
-+ .id_table = ipts_mei_device_id_table,
|
|
|
|
-+ .name = "ipts",
|
|
|
|
-+ .probe = ipts_mei_probe,
|
|
|
|
-+ .remove = ipts_mei_remove,
|
|
|
|
-+};
|
|
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
+
|
|
+
|
|
-+static int __init ipts_mei_init(void)
|
|
|
|
|
|
++static int ipts_control_set_mem_window(struct ipts_context *ipts, struct ipts_resources *res)
|
|
+{
|
|
+{
|
|
-+ int ret;
|
|
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_mem_window cmd = { 0 };
|
|
|
|
++ struct ipts_response rsp = { 0 };
|
|
+
|
|
+
|
|
-+ ret = ipts_uapi_init();
|
|
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!res)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ for (int i = 0; i < IPTS_BUFFERS; i++) {
|
|
|
|
++ cmd.data_addr_lower[i] = lower_32_bits(res->data[i].dma_address);
|
|
|
|
++ cmd.data_addr_upper[i] = upper_32_bits(res->data[i].dma_address);
|
|
|
|
++ cmd.feedback_addr_lower[i] = lower_32_bits(res->feedback[i].dma_address);
|
|
|
|
++ cmd.feedback_addr_upper[i] = upper_32_bits(res->feedback[i].dma_address);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ cmd.workqueue_addr_lower = lower_32_bits(res->workqueue.dma_address);
|
|
|
|
++ cmd.workqueue_addr_upper = upper_32_bits(res->workqueue.dma_address);
|
|
|
|
++
|
|
|
|
++ cmd.doorbell_addr_lower = lower_32_bits(res->doorbell.dma_address);
|
|
|
|
++ cmd.doorbell_addr_upper = upper_32_bits(res->doorbell.dma_address);
|
|
|
|
++
|
|
|
|
++ cmd.hid2me_addr_lower = lower_32_bits(res->hid2me.dma_address);
|
|
|
|
++ cmd.hid2me_addr_upper = upper_32_bits(res->hid2me.dma_address);
|
|
|
|
++
|
|
|
|
++ cmd.workqueue_size = IPTS_WORKQUEUE_SIZE;
|
|
|
|
++ cmd.workqueue_item_size = IPTS_WORKQUEUE_ITEM_SIZE;
|
|
|
|
++
|
|
|
|
++ ret = ipts_cmd_send(ipts, IPTS_CMD_SET_MEM_WINDOW, &cmd, sizeof(cmd));
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "SET_MEM_WINDOW: send failed: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_cmd_recv(ipts, IPTS_CMD_SET_MEM_WINDOW, &rsp);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "SET_MEM_WINDOW: recv failed: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (rsp.status != IPTS_STATUS_SUCCESS) {
|
|
|
|
++ dev_err(ipts->dev, "SET_MEM_WINDOW: cmd failed: %d\n", rsp.status);
|
|
|
|
++ return -EBADR;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_control_get_descriptor(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_data_header *header = NULL;
|
|
|
|
++ struct ipts_get_descriptor cmd = { 0 };
|
|
|
|
++ struct ipts_response rsp = { 0 };
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!ipts->resources.descriptor.address)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ memset(ipts->resources.descriptor.address, 0, ipts->resources.descriptor.size);
|
|
|
|
++
|
|
|
|
++ cmd.addr_lower = lower_32_bits(ipts->resources.descriptor.dma_address);
|
|
|
|
++ cmd.addr_upper = upper_32_bits(ipts->resources.descriptor.dma_address);
|
|
|
|
++ cmd.magic = 8;
|
|
|
|
++
|
|
|
|
++ ret = ipts_cmd_send(ipts, IPTS_CMD_GET_DESCRIPTOR, &cmd, sizeof(cmd));
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "GET_DESCRIPTOR: send failed: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_cmd_recv(ipts, IPTS_CMD_GET_DESCRIPTOR, &rsp);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "GET_DESCRIPTOR: recv failed: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (rsp.status != IPTS_STATUS_SUCCESS) {
|
|
|
|
++ dev_err(ipts->dev, "GET_DESCRIPTOR: cmd failed: %d\n", rsp.status);
|
|
|
|
++ return -EBADR;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ header = (struct ipts_data_header *)ipts->resources.descriptor.address;
|
|
|
|
++
|
|
|
|
++ if (header->type == IPTS_DATA_TYPE_DESCRIPTOR) {
|
|
|
|
++ ipts->descriptor.address = &header->data[8];
|
|
|
|
++ ipts->descriptor.size = header->size - 8;
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return -ENODATA;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_control_request_flush(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_quiesce_io cmd = { 0 };
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ ret = ipts_cmd_send(ipts, IPTS_CMD_QUIESCE_IO, &cmd, sizeof(cmd));
|
|
+ if (ret)
|
|
+ if (ret)
|
|
|
|
++ dev_err(ipts->dev, "QUIESCE_IO: send failed: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ return ret;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_control_wait_flush(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_response rsp = { 0 };
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ ret = ipts_cmd_recv(ipts, IPTS_CMD_QUIESCE_IO, &rsp);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "QUIESCE_IO: recv failed: %d\n", ret);
|
|
+ return ret;
|
|
+ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (rsp.status == IPTS_STATUS_TIMEOUT)
|
|
|
|
++ return -EAGAIN;
|
|
|
|
++
|
|
|
|
++ if (rsp.status != IPTS_STATUS_SUCCESS) {
|
|
|
|
++ dev_err(ipts->dev, "QUIESCE_IO: cmd failed: %d\n", rsp.status);
|
|
|
|
++ return -EBADR;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_control_request_data(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ ret = ipts_cmd_send(ipts, IPTS_CMD_READY_FOR_DATA, NULL, 0);
|
|
|
|
++ if (ret)
|
|
|
|
++ dev_err(ipts->dev, "READY_FOR_DATA: send failed: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ return ret;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_control_wait_data(struct ipts_context *ipts, bool shutdown)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_response rsp = { 0 };
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!shutdown)
|
|
|
|
++ ret = ipts_cmd_recv_timeout(ipts, IPTS_CMD_READY_FOR_DATA, &rsp, 0);
|
|
|
|
++ else
|
|
|
|
++ ret = ipts_cmd_recv(ipts, IPTS_CMD_READY_FOR_DATA, &rsp);
|
|
+
|
|
+
|
|
-+ ret = mei_cldev_driver_register(&ipts_mei_driver);
|
|
|
|
+ if (ret) {
|
|
+ if (ret) {
|
|
-+ ipts_uapi_free();
|
|
|
|
|
|
++ if (ret != -EAGAIN)
|
|
|
|
++ dev_err(ipts->dev, "READY_FOR_DATA: recv failed: %d\n", ret);
|
|
|
|
++
|
|
+ return ret;
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
|
|
++ /*
|
|
|
|
++ * During shutdown, it is possible that the sensor has already been disabled.
|
|
|
|
++ */
|
|
|
|
++ if (rsp.status == IPTS_STATUS_SENSOR_DISABLED)
|
|
|
|
++ return 0;
|
|
|
|
++
|
|
|
|
++ if (rsp.status == IPTS_STATUS_TIMEOUT)
|
|
|
|
++ return -EAGAIN;
|
|
|
|
++
|
|
|
|
++ if (rsp.status != IPTS_STATUS_SUCCESS) {
|
|
|
|
++ dev_err(ipts->dev, "READY_FOR_DATA: cmd failed: %d\n", rsp.status);
|
|
|
|
++ return -EBADR;
|
|
|
|
++ }
|
|
|
|
++
|
|
+ return 0;
|
|
+ return 0;
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+static void __exit ipts_mei_exit(void)
|
|
|
|
|
|
++int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer)
|
|
+{
|
|
+{
|
|
-+ mei_cldev_driver_unregister(&ipts_mei_driver);
|
|
|
|
-+ ipts_uapi_free();
|
|
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_feedback cmd = { 0 };
|
|
|
|
++ struct ipts_response rsp = { 0 };
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ cmd.buffer = buffer;
|
|
|
|
++
|
|
|
|
++ ret = ipts_cmd_send(ipts, IPTS_CMD_FEEDBACK, &cmd, sizeof(cmd));
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "FEEDBACK: send failed: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_cmd_recv(ipts, IPTS_CMD_FEEDBACK, &rsp);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "FEEDBACK: recv failed: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * We don't know what feedback data looks like so we are sending zeros.
|
|
|
|
++ * See also ipts_control_refill_buffer.
|
|
|
|
++ */
|
|
|
|
++ if (rsp.status == IPTS_STATUS_INVALID_PARAMS)
|
|
|
|
++ return 0;
|
|
|
|
++
|
|
|
|
++ if (rsp.status != IPTS_STATUS_SUCCESS) {
|
|
|
|
++ dev_err(ipts->dev, "FEEDBACK: cmd failed: %d\n", rsp.status);
|
|
|
|
++ return -EBADR;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+MODULE_DESCRIPTION("IPTS touchscreen driver");
|
|
|
|
-+MODULE_AUTHOR("Dorian Stoll <dorian.stoll@tmsp.io>");
|
|
|
|
-+MODULE_LICENSE("GPL");
|
|
|
|
|
|
++int ipts_control_hid2me_feedback(struct ipts_context *ipts, enum ipts_feedback_cmd_type cmd,
|
|
|
|
++ enum ipts_feedback_data_type type, void *data, size_t size)
|
|
|
|
++{
|
|
|
|
++ struct ipts_feedback_header *header = NULL;
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!ipts->resources.hid2me.address)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ memset(ipts->resources.hid2me.address, 0, ipts->resources.hid2me.size);
|
|
|
|
++ header = (struct ipts_feedback_header *)ipts->resources.hid2me.address;
|
|
|
|
++
|
|
|
|
++ header->cmd_type = cmd;
|
|
|
|
++ header->data_type = type;
|
|
|
|
++ header->size = size;
|
|
|
|
++ header->buffer = IPTS_HID2ME_BUFFER;
|
|
|
|
++
|
|
|
|
++ if (size + sizeof(*header) > ipts->resources.hid2me.size)
|
|
|
|
++ return -EINVAL;
|
|
|
|
++
|
|
|
|
++ if (data && size > 0)
|
|
|
|
++ memcpy(header->payload, data, size);
|
|
|
|
++
|
|
|
|
++ return ipts_control_send_feedback(ipts, IPTS_HID2ME_BUFFER);
|
|
|
|
++}
|
|
+
|
|
+
|
|
-+module_init(ipts_mei_init);
|
|
|
|
-+module_exit(ipts_mei_exit);
|
|
|
|
-diff --git a/drivers/misc/ipts/protocol.h b/drivers/misc/ipts/protocol.h
|
|
|
|
|
|
++static inline int ipts_control_reset_sensor(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ return ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_SOFT_RESET,
|
|
|
|
++ IPTS_FEEDBACK_DATA_TYPE_VENDOR, NULL, 0);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_control_start(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_device_info info = { 0 };
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ dev_info(ipts->dev, "Starting IPTS\n");
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_get_device_info(ipts, &info);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to get device info: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ipts->info = info;
|
|
|
|
++
|
|
|
|
++ ret = ipts_resources_init(&ipts->resources, ipts->dev, info.data_size, info.feedback_size);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to allocate buffers: %d", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ dev_info(ipts->dev, "IPTS EDS Version: %d\n", info.intf_eds);
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * Handle newer devices
|
|
|
|
++ */
|
|
|
|
++ if (info.intf_eds > 1) {
|
|
|
|
++ /*
|
|
|
|
++ * Fetching the descriptor will only work on newer devices.
|
|
|
|
++ * For older devices, a fallback descriptor will be used.
|
|
|
|
++ */
|
|
|
|
++ ret = ipts_control_get_descriptor(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to fetch HID descriptor: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * Newer devices can be directly initialized in doorbell mode.
|
|
|
|
++ */
|
|
|
|
++ ipts->mode = IPTS_MODE_DOORBELL;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_set_mode(ipts, ipts->mode);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to set mode: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_set_mem_window(ipts, &ipts->resources);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to set memory window: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_receiver_start(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to start receiver: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_request_data(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to request data: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_hid_init(ipts, info);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to initialize HID device: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int _ipts_control_stop(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ dev_info(ipts->dev, "Stopping IPTS\n");
|
|
|
|
++
|
|
|
|
++ ret = ipts_receiver_stop(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to stop receiver: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_reset_sensor(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to reset sensor: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_resources_free(&ipts->resources);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to free resources: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_control_stop(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++
|
|
|
|
++ ret = _ipts_control_stop(ipts);
|
|
|
|
++ if (ret)
|
|
|
|
++ return ret;
|
|
|
|
++
|
|
|
|
++ ret = ipts_hid_free(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to free HID device: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_control_restart(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++
|
|
|
|
++ ret = _ipts_control_stop(ipts);
|
|
|
|
++ if (ret)
|
|
|
|
++ return ret;
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * Give the sensor some time to come back from resetting
|
|
|
|
++ */
|
|
|
|
++ msleep(1000);
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_start(ipts);
|
|
|
|
++ if (ret)
|
|
|
|
++ return ret;
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
+diff --git a/drivers/hid/ipts/control.h b/drivers/hid/ipts/control.h
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..c3458904a94d
|
|
|
|
|
|
+index 000000000000..744bb92d682a
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/protocol.h
|
|
|
|
-@@ -0,0 +1,347 @@
|
|
|
|
|
|
++++ b/drivers/hid/ipts/control.h
|
|
|
|
+@@ -0,0 +1,127 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/*
|
|
+/*
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
+ *
|
|
+ *
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
+
|
|
+
|
|
-+#ifndef _IPTS_PROTOCOL_H_
|
|
|
|
-+#define _IPTS_PROTOCOL_H_
|
|
|
|
|
|
++#ifndef IPTS_CONTROL_H
|
|
|
|
++#define IPTS_CONTROL_H
|
|
+
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+
|
|
-+/*
|
|
|
|
-+ * The MEI client ID for IPTS functionality.
|
|
|
|
-+ */
|
|
|
|
-+#define IPTS_MEI_UUID \
|
|
|
|
-+ UUID_LE(0x3e8d0870, 0x271a, 0x4208, 0x8e, 0xb5, 0x9a, 0xcb, 0x94, \
|
|
|
|
-+ 0x02, 0xae, 0x04)
|
|
|
|
|
|
++#include "context.h"
|
|
|
|
++#include "spec-data.h"
|
|
|
|
++#include "spec-device.h"
|
|
+
|
|
+
|
|
+/*
|
|
+/*
|
|
-+ * Queries the device for vendor specific information.
|
|
|
|
|
|
++ * ipts_control_request_flush() - Stop the data flow.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ *
|
|
|
|
++ * Runs the command to stop the data flow on the device.
|
|
|
|
++ * All outstanding data needs to be acknowledged using feedback before the command will return.
|
|
+ *
|
|
+ *
|
|
-+ * The command must not contain any payload.
|
|
|
|
-+ * The response will contain struct ipts_get_device_info_rsp as payload.
|
|
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
+ */
|
|
+ */
|
|
-+#define IPTS_CMD_GET_DEVICE_INFO 0x00000001
|
|
|
|
-+#define IPTS_RSP_GET_DEVICE_INFO 0x80000001
|
|
|
|
|
|
++int ipts_control_request_flush(struct ipts_context *ipts);
|
|
+
|
|
+
|
|
+/*
|
|
+/*
|
|
-+ * Sets the mode that IPTS will operate in.
|
|
|
|
|
|
++ * ipts_control_wait_flush() - Wait until data flow has been stopped.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
+ *
|
|
+ *
|
|
-+ * The command must contain struct ipts_set_mode_cmd as payload.
|
|
|
|
-+ * The response will not contain any payload.
|
|
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
+ */
|
|
+ */
|
|
-+#define IPTS_CMD_SET_MODE 0x00000002
|
|
|
|
-+#define IPTS_RSP_SET_MODE 0x80000002
|
|
|
|
|
|
++int ipts_control_wait_flush(struct ipts_context *ipts);
|
|
+
|
|
+
|
|
+/*
|
|
+/*
|
|
-+ * Configures the memory buffers that the ME will use
|
|
|
|
-+ * for passing data to the host.
|
|
|
|
|
|
++ * ipts_control_wait_flush() - Notify the device that the driver can receive new data.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
+ *
|
|
+ *
|
|
-+ * The command must contain struct ipts_set_mem_window_cmd as payload.
|
|
|
|
-+ * The response will not contain any payload.
|
|
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
+ */
|
|
+ */
|
|
-+#define IPTS_CMD_SET_MEM_WINDOW 0x00000003
|
|
|
|
-+#define IPTS_RSP_SET_MEM_WINDOW 0x80000003
|
|
|
|
|
|
++int ipts_control_request_data(struct ipts_context *ipts);
|
|
+
|
|
+
|
|
+/*
|
|
+/*
|
|
-+ * Signals that the host is ready to receive data to the ME.
|
|
|
|
|
|
++ * ipts_control_wait_data() - Wait until new data is available.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ * @block: Whether to block execution until data is available.
|
|
+ *
|
|
+ *
|
|
-+ * The command must not contain any payload.
|
|
|
|
-+ * The response will not contain any payload.
|
|
|
|
|
|
++ * In doorbell mode, this function will never return while the data flow is active. Instead,
|
|
|
|
++ * the doorbell will be incremented when new data is available.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error, -EAGAIN if no data is available.
|
|
+ */
|
|
+ */
|
|
-+#define IPTS_CMD_READY_FOR_DATA 0x00000005
|
|
|
|
-+#define IPTS_RSP_READY_FOR_DATA 0x80000005
|
|
|
|
|
|
++int ipts_control_wait_data(struct ipts_context *ipts, bool block);
|
|
+
|
|
+
|
|
+/*
|
|
+/*
|
|
-+ * Signals that a buffer can be refilled to the ME.
|
|
|
|
|
|
++ * ipts_control_send_feedback() - Submits a feedback buffer to the device.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ * @buffer: The ID of the buffer containing feedback data.
|
|
+ *
|
|
+ *
|
|
-+ * The command must contain struct ipts_feedback_cmd as payload.
|
|
|
|
-+ * The response will not contain any payload.
|
|
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
+ */
|
|
+ */
|
|
-+#define IPTS_CMD_FEEDBACK 0x00000006
|
|
|
|
-+#define IPTS_RSP_FEEDBACK 0x80000006
|
|
|
|
|
|
++int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer);
|
|
+
|
|
+
|
|
+/*
|
|
+/*
|
|
-+ * Resets the data flow from the ME to the hosts and
|
|
|
|
-+ * clears the buffers that were set with SET_MEM_WINDOW.
|
|
|
|
|
|
++ * ipts_control_hid2me_feedback() - Sends HID2ME feedback, a special type of feedback.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ * @cmd: The command that will be run on the device.
|
|
|
|
++ * @type: The type of the payload that is sent to the device.
|
|
|
|
++ * @data: The payload of the feedback command.
|
|
|
|
++ * @size: The size of the payload.
|
|
+ *
|
|
+ *
|
|
-+ * The command must not contain any payload.
|
|
|
|
-+ * The response will not contain any payload.
|
|
|
|
|
|
++ * HID2ME feedback is a special type of feedback, because it allows interfacing with
|
|
|
|
++ * the HID API of the device at any moment, without requiring a buffer that has to
|
|
|
|
++ * be acknowledged.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
+ */
|
|
+ */
|
|
-+#define IPTS_CMD_CLEAR_MEM_WINDOW 0x00000007
|
|
|
|
-+#define IPTS_RSP_CLEAR_MEM_WINDOW 0x80000007
|
|
|
|
|
|
++int ipts_control_hid2me_feedback(struct ipts_context *ipts, enum ipts_feedback_cmd_type cmd,
|
|
|
|
++ enum ipts_feedback_data_type type, void *data, size_t size);
|
|
+
|
|
+
|
|
+/*
|
|
+/*
|
|
-+ * Instructs the ME to reset the touch sensor.
|
|
|
|
|
|
++ * ipts_control_refill_buffer() - Acknowledges that data in a buffer has been processed.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ * @buffer: The buffer that has been processed and can be refilled.
|
|
+ *
|
|
+ *
|
|
-+ * The command must contain struct ipts_reset_sensor_cmd as payload.
|
|
|
|
-+ * The response will not contain any payload.
|
|
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
+ */
|
|
+ */
|
|
-+#define IPTS_CMD_RESET_SENSOR 0x0000000B
|
|
|
|
-+#define IPTS_RSP_RESET_SENSOR 0x8000000B
|
|
|
|
|
|
++static inline int ipts_control_refill_buffer(struct ipts_context *ipts, u32 buffer)
|
|
|
|
++{
|
|
|
|
++ /*
|
|
|
|
++ * IPTS expects structured data in the feedback buffer matching the buffer that will be
|
|
|
|
++ * refilled. We don't know what that data looks like, so we just keep the buffer empty.
|
|
|
|
++ * This results in an INVALID_PARAMS error, but the buffer gets refilled without an issue.
|
|
|
|
++ * Sending a minimal structure with the buffer ID fixes the error, but breaks refilling
|
|
|
|
++ * the buffers on some devices.
|
|
|
|
++ */
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * enum ipts_status - Possible status codes returned by IPTS commands.
|
|
|
|
-+ * @IPTS_STATUS_SUCCESS: Operation completed successfully.
|
|
|
|
-+ * @IPTS_STATUS_INVALID_PARAMS: Command contained a payload with invalid parameters.
|
|
|
|
-+ * @IPTS_STATUS_ACCESS_DENIED: ME could not validate buffer addresses supplied by host.
|
|
|
|
-+ * @IPTS_STATUS_CMD_SIZE_ERROR: Command contains an invalid payload.
|
|
|
|
-+ * @IPTS_STATUS_NOT_READY: Buffer addresses have not been set.
|
|
|
|
-+ * @IPTS_STATUS_REQUEST_OUTSTANDING: There is an outstanding command of the same type.
|
|
|
|
-+ * The host must wait for a response before sending another
|
|
|
|
-+ * command of the same type.
|
|
|
|
-+ * @IPTS_STATUS_NO_SENSOR_FOUND: No sensor could be found. Either no sensor is connected, it
|
|
|
|
-+ * has not been initialized yet, or the system is improperly
|
|
|
|
-+ * configured.
|
|
|
|
-+ * @IPTS_STATUS_OUT_OF_MEMORY: Not enough free memory for requested operation.
|
|
|
|
-+ * @IPTS_STATUS_INTERNAL_ERROR: An unexpected error occurred.
|
|
|
|
-+ * @IPTS_STATUS_SENSOR_DISABLED: The sensor has been disabled and must be reinitialized.
|
|
|
|
-+ * @IPTS_STATUS_COMPAT_CHECK_FAIL: Compatibility revision check between sensor and ME failed.
|
|
|
|
-+ * The host can ignore this error and attempt to continue.
|
|
|
|
-+ * @IPTS_STATUS_SENSOR_EXPECTED_RESET: The sensor went through a reset initiated by ME or host.
|
|
|
|
-+ * @IPTS_STATUS_SENSOR_UNEXPECTED_RESET: The sensor went through an unexpected reset.
|
|
|
|
-+ * @IPTS_STATUS_RESET_FAILED: Requested sensor reset failed to complete.
|
|
|
|
-+ * @IPTS_STATUS_TIMEOUT: The operation timed out.
|
|
|
|
-+ * @IPTS_STATUS_TEST_MODE_FAIL: Test mode pattern did not match expected values.
|
|
|
|
-+ * @IPTS_STATUS_SENSOR_FAIL_FATAL: The sensor reported a fatal error during reset sequence.
|
|
|
|
-+ * Further progress is not possible.
|
|
|
|
-+ * @IPTS_STATUS_SENSOR_FAIL_NONFATAL: The sensor reported a fatal error during reset sequence.
|
|
|
|
-+ * The host can attempt to continue.
|
|
|
|
-+ * @IPTS_STATUS_INVALID_DEVICE_CAPS: The device reported invalid capabilities.
|
|
|
|
-+ * @IPTS_STATUS_QUIESCE_IO_IN_PROGRESS: Command cannot be completed until Quiesce IO is done.
|
|
|
|
-+ */
|
|
|
|
-+enum ipts_status {
|
|
|
|
-+ IPTS_STATUS_SUCCESS = 0,
|
|
|
|
-+ IPTS_STATUS_INVALID_PARAMS = 1,
|
|
|
|
-+ IPTS_STATUS_ACCESS_DENIED = 2,
|
|
|
|
-+ IPTS_STATUS_CMD_SIZE_ERROR = 3,
|
|
|
|
-+ IPTS_STATUS_NOT_READY = 4,
|
|
|
|
-+ IPTS_STATUS_REQUEST_OUTSTANDING = 5,
|
|
|
|
-+ IPTS_STATUS_NO_SENSOR_FOUND = 6,
|
|
|
|
-+ IPTS_STATUS_OUT_OF_MEMORY = 7,
|
|
|
|
-+ IPTS_STATUS_INTERNAL_ERROR = 8,
|
|
|
|
-+ IPTS_STATUS_SENSOR_DISABLED = 9,
|
|
|
|
-+ IPTS_STATUS_COMPAT_CHECK_FAIL = 10,
|
|
|
|
-+ IPTS_STATUS_SENSOR_EXPECTED_RESET = 11,
|
|
|
|
-+ IPTS_STATUS_SENSOR_UNEXPECTED_RESET = 12,
|
|
|
|
-+ IPTS_STATUS_RESET_FAILED = 13,
|
|
|
|
-+ IPTS_STATUS_TIMEOUT = 14,
|
|
|
|
-+ IPTS_STATUS_TEST_MODE_FAIL = 15,
|
|
|
|
-+ IPTS_STATUS_SENSOR_FAIL_FATAL = 16,
|
|
|
|
-+ IPTS_STATUS_SENSOR_FAIL_NONFATAL = 17,
|
|
|
|
-+ IPTS_STATUS_INVALID_DEVICE_CAPS = 18,
|
|
|
|
-+ IPTS_STATUS_QUIESCE_IO_IN_PROGRESS = 19,
|
|
|
|
-+};
|
|
|
|
|
|
++ return ipts_control_send_feedback(ipts, buffer);
|
|
|
|
++}
|
|
+
|
|
+
|
|
+/*
|
|
+/*
|
|
-+ * The amount of buffers that is used for IPTS
|
|
|
|
|
|
++ * ipts_control_start() - Initialized the device and starts the data flow.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
+ */
|
|
+ */
|
|
-+#define IPTS_BUFFERS 16
|
|
|
|
|
|
++int ipts_control_start(struct ipts_context *ipts);
|
|
+
|
|
+
|
|
+/*
|
|
+/*
|
|
-+ * The special buffer ID that is used for direct host2me feedback.
|
|
|
|
|
|
++ * ipts_control_stop() - Stops the data flow and resets the device.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
+ */
|
|
+ */
|
|
-+#define IPTS_HOST2ME_BUFFER IPTS_BUFFERS
|
|
|
|
|
|
++int ipts_control_stop(struct ipts_context *ipts);
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * enum ipts_mode - Operation mode for IPTS hardware
|
|
|
|
-+ * @IPTS_MODE_SINGLETOUCH: Fallback that supports only one finger and no stylus.
|
|
|
|
-+ * The data is received as a HID report with ID 64.
|
|
|
|
-+ * @IPTS_MODE_MULTITOUCH: The "proper" operation mode for IPTS. It will return
|
|
|
|
-+ * stylus data as well as capacitive heatmap touch data.
|
|
|
|
-+ * This data needs to be processed in userspace.
|
|
|
|
|
|
++/*
|
|
|
|
++ * ipts_control_restart() - Stops the device and starts it again.
|
|
|
|
++ * @ipts: The IPTS driver context.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
+ */
|
|
+ */
|
|
-+enum ipts_mode {
|
|
|
|
-+ IPTS_MODE_SINGLETOUCH = 0,
|
|
|
|
-+ IPTS_MODE_MULTITOUCH = 1,
|
|
|
|
-+};
|
|
|
|
|
|
++int ipts_control_restart(struct ipts_context *ipts);
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * struct ipts_set_mode_cmd - Payload for the SET_MODE command.
|
|
|
|
-+ * @mode: The mode that IPTS should operate in.
|
|
|
|
|
|
++#endif /* IPTS_CONTROL_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/desc.h b/drivers/hid/ipts/desc.h
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..c058974a03a1
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/desc.h
|
|
|
|
+@@ -0,0 +1,81 @@
|
|
|
|
++/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2022-2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
-+struct ipts_set_mode_cmd {
|
|
|
|
-+ enum ipts_mode mode;
|
|
|
|
-+ u8 reserved[12];
|
|
|
|
-+} __packed;
|
|
|
|
+
|
|
+
|
|
-+#define IPTS_WORKQUEUE_SIZE 8192
|
|
|
|
-+#define IPTS_WORKQUEUE_ITEM_SIZE 16
|
|
|
|
|
|
++#ifndef IPTS_DESC_H
|
|
|
|
++#define IPTS_DESC_H
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * struct ipts_set_mem_window_cmd - Payload for the SET_MEM_WINDOW command.
|
|
|
|
-+ * @data_buffer_addr_lower: Lower 32 bits of the data buffer addresses.
|
|
|
|
-+ * @data_buffer_addr_upper: Upper 32 bits of the data buffer addresses.
|
|
|
|
-+ * @workqueue_addr_lower: Lower 32 bits of the workqueue buffer address.
|
|
|
|
-+ * @workqueue_addr_upper: Upper 32 bits of the workqueue buffer address.
|
|
|
|
-+ * @doorbell_addr_lower: Lower 32 bits of the doorbell buffer address.
|
|
|
|
-+ * @doorbell_addr_upper: Upper 32 bits of the doorbell buffer address.
|
|
|
|
-+ * @feedback_buffer_addr_lower: Lower 32 bits of the feedback buffer addresses.
|
|
|
|
-+ * @feedback_buffer_addr_upper: Upper 32 bits of the feedback buffer addresses.
|
|
|
|
-+ * @host2me_addr_lower: Lower 32 bits of the host2me buffer address.
|
|
|
|
-+ * @host2me_addr_upper: Upper 32 bits of the host2me buffer address.
|
|
|
|
-+ * @workqueue_item_size: Magic value. (IPTS_WORKQUEUE_ITEM_SIZE)
|
|
|
|
-+ * @workqueue_size: Magic value. (IPTS_WORKQUEUE_SIZE)
|
|
|
|
-+ *
|
|
|
|
-+ * The data buffers are buffers that get filled with touch data by the ME.
|
|
|
|
-+ * The doorbell buffer is a u32 that gets incremented by the ME once a data
|
|
|
|
-+ * buffer has been filled with new data.
|
|
|
|
-+ *
|
|
|
|
-+ * The other buffers are required for using GuC submission with binary
|
|
|
|
-+ * firmware. Since support for GuC submission has been dropped from i915,
|
|
|
|
-+ * they are not used anymore, but they need to be allocated and passed,
|
|
|
|
-+ * otherwise the hardware will refuse to start.
|
|
|
|
-+ */
|
|
|
|
-+struct ipts_set_mem_window_cmd {
|
|
|
|
-+ u32 data_buffer_addr_lower[IPTS_BUFFERS];
|
|
|
|
-+ u32 data_buffer_addr_upper[IPTS_BUFFERS];
|
|
|
|
-+ u32 workqueue_addr_lower;
|
|
|
|
-+ u32 workqueue_addr_upper;
|
|
|
|
-+ u32 doorbell_addr_lower;
|
|
|
|
-+ u32 doorbell_addr_upper;
|
|
|
|
-+ u32 feedback_buffer_addr_lower[IPTS_BUFFERS];
|
|
|
|
-+ u32 feedback_buffer_addr_upper[IPTS_BUFFERS];
|
|
|
|
-+ u32 host2me_addr_lower;
|
|
|
|
-+ u32 host2me_addr_upper;
|
|
|
|
-+ u32 host2me_size;
|
|
|
|
-+ u8 reserved1;
|
|
|
|
-+ u8 workqueue_item_size;
|
|
|
|
-+ u16 workqueue_size;
|
|
|
|
-+ u8 reserved[32];
|
|
|
|
-+} __packed;
|
|
|
|
|
|
++#include <linux/types.h>
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * struct ipts_feedback_cmd - Payload for the FEEDBACK command.
|
|
|
|
-+ * @buffer: The buffer that the ME should refill.
|
|
|
|
|
|
++#define IPTS_HID_REPORT_SINGLETOUCH 64
|
|
|
|
++#define IPTS_HID_REPORT_DATA 65
|
|
|
|
++#define IPTS_HID_REPORT_SET_MODE 66
|
|
|
|
++
|
|
|
|
++#define IPTS_HID_REPORT_DATA_SIZE 7485
|
|
|
|
++
|
|
|
|
++/*
|
|
|
|
++ * HID descriptor for singletouch data.
|
|
|
|
++ * This descriptor should be present on all IPTS devices.
|
|
+ */
|
|
+ */
|
|
-+struct ipts_feedback_cmd {
|
|
|
|
-+ u32 buffer;
|
|
|
|
-+ u8 reserved[12];
|
|
|
|
-+} __packed;
|
|
|
|
|
|
++static const u8 ipts_singletouch_descriptor[] = {
|
|
|
|
++ 0x05, 0x0D, /* Usage Page (Digitizer), */
|
|
|
|
++ 0x09, 0x04, /* Usage (Touchscreen), */
|
|
|
|
++ 0xA1, 0x01, /* Collection (Application), */
|
|
|
|
++ 0x85, 0x40, /* Report ID (64), */
|
|
|
|
++ 0x09, 0x42, /* Usage (Tip Switch), */
|
|
|
|
++ 0x15, 0x00, /* Logical Minimum (0), */
|
|
|
|
++ 0x25, 0x01, /* Logical Maximum (1), */
|
|
|
|
++ 0x75, 0x01, /* Report Size (1), */
|
|
|
|
++ 0x95, 0x01, /* Report Count (1), */
|
|
|
|
++ 0x81, 0x02, /* Input (Variable), */
|
|
|
|
++ 0x95, 0x07, /* Report Count (7), */
|
|
|
|
++ 0x81, 0x03, /* Input (Constant, Variable), */
|
|
|
|
++ 0x05, 0x01, /* Usage Page (Desktop), */
|
|
|
|
++ 0x09, 0x30, /* Usage (X), */
|
|
|
|
++ 0x75, 0x10, /* Report Size (16), */
|
|
|
|
++ 0x95, 0x01, /* Report Count (1), */
|
|
|
|
++ 0xA4, /* Push, */
|
|
|
|
++ 0x55, 0x0E, /* Unit Exponent (14), */
|
|
|
|
++ 0x65, 0x11, /* Unit (Centimeter), */
|
|
|
|
++ 0x46, 0x76, 0x0B, /* Physical Maximum (2934), */
|
|
|
|
++ 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
|
|
|
|
++ 0x81, 0x02, /* Input (Variable), */
|
|
|
|
++ 0x09, 0x31, /* Usage (Y), */
|
|
|
|
++ 0x46, 0x74, 0x06, /* Physical Maximum (1652), */
|
|
|
|
++ 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
|
|
|
|
++ 0x81, 0x02, /* Input (Variable), */
|
|
|
|
++ 0xB4, /* Pop, */
|
|
|
|
++ 0xC0, /* End Collection */
|
|
|
|
++};
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * enum ipts_feedback_cmd_type - Commands that can be executed on the sensor through feedback.
|
|
|
|
|
|
++/*
|
|
|
|
++ * Fallback HID descriptor for older devices that do not have
|
|
|
|
++ * the ability to query their HID descriptor.
|
|
+ */
|
|
+ */
|
|
-+enum ipts_feedback_cmd_type {
|
|
|
|
-+ IPTS_FEEDBACK_CMD_TYPE_NONE = 0,
|
|
|
|
-+ IPTS_FEEDBACK_CMD_TYPE_SOFT_RESET = 1,
|
|
|
|
-+ IPTS_FEEDBACK_CMD_TYPE_GOTO_ARMED = 2,
|
|
|
|
-+ IPTS_FEEDBACK_CMD_TYPE_GOTO_SENSING = 3,
|
|
|
|
-+ IPTS_FEEDBACK_CMD_TYPE_GOTO_SLEEP = 4,
|
|
|
|
-+ IPTS_FEEDBACK_CMD_TYPE_GOTO_DOZE = 5,
|
|
|
|
-+ IPTS_FEEDBACK_CMD_TYPE_HARD_RESET = 6,
|
|
|
|
|
|
++static const u8 ipts_fallback_descriptor[] = {
|
|
|
|
++ 0x05, 0x0D, /* Usage Page (Digitizer), */
|
|
|
|
++ 0x09, 0x0F, /* Usage (Capacitive Hm Digitizer), */
|
|
|
|
++ 0xA1, 0x01, /* Collection (Application), */
|
|
|
|
++ 0x85, 0x41, /* Report ID (65), */
|
|
|
|
++ 0x09, 0x56, /* Usage (Scan Time), */
|
|
|
|
++ 0x95, 0x01, /* Report Count (1), */
|
|
|
|
++ 0x75, 0x10, /* Report Size (16), */
|
|
|
|
++ 0x81, 0x02, /* Input (Variable), */
|
|
|
|
++ 0x09, 0x61, /* Usage (Gesture Char Quality), */
|
|
|
|
++ 0x75, 0x08, /* Report Size (8), */
|
|
|
|
++ 0x96, 0x3D, 0x1D, /* Report Count (7485), */
|
|
|
|
++ 0x81, 0x03, /* Input (Constant, Variable), */
|
|
|
|
++ 0x85, 0x42, /* Report ID (66), */
|
|
|
|
++ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
|
|
|
|
++ 0x09, 0xC8, /* Usage (C8h), */
|
|
|
|
++ 0x75, 0x08, /* Report Size (8), */
|
|
|
|
++ 0x95, 0x01, /* Report Count (1), */
|
|
|
|
++ 0xB1, 0x02, /* Feature (Variable), */
|
|
|
|
++ 0xC0, /* End Collection, */
|
|
+};
|
|
+};
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * enum ipts_feedback_data_type - Describes the data that a feedback buffer contains.
|
|
|
|
-+ * @IPTS_FEEDBACK_DATA_TYPE_VENDOR: The buffer contains vendor specific feedback.
|
|
|
|
-+ * @IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES: The buffer contains a HID set features command.
|
|
|
|
-+ * @IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES: The buffer contains a HID get features command.
|
|
|
|
-+ * @IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT: The buffer contains a HID output report.
|
|
|
|
-+ * @IPTS_FEEDBACK_DATA_TYPE_STORE_DATA: The buffer contains calibration data for the sensor.
|
|
|
|
|
|
++#endif /* IPTS_DESC_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/hid.c b/drivers/hid/ipts/hid.c
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..6782394e8dde
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/hid.c
|
|
|
|
+@@ -0,0 +1,348 @@
|
|
|
|
++// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2022-2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
-+enum ipts_feedback_data_type {
|
|
|
|
-+ IPTS_FEEDBACK_DATA_TYPE_VENDOR = 0,
|
|
|
|
-+ IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES = 1,
|
|
|
|
-+ IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES = 2,
|
|
|
|
-+ IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT = 3,
|
|
|
|
-+ IPTS_FEEDBACK_DATA_TYPE_STORE_DATA = 4,
|
|
|
|
|
|
++
|
|
|
|
++#include <linux/completion.h>
|
|
|
|
++#include <linux/gfp.h>
|
|
|
|
++#include <linux/hid.h>
|
|
|
|
++#include <linux/mutex.h>
|
|
|
|
++#include <linux/slab.h>
|
|
|
|
++#include <linux/types.h>
|
|
|
|
++
|
|
|
|
++#include "context.h"
|
|
|
|
++#include "control.h"
|
|
|
|
++#include "desc.h"
|
|
|
|
++#include "hid.h"
|
|
|
|
++#include "spec-data.h"
|
|
|
|
++#include "spec-device.h"
|
|
|
|
++#include "spec-hid.h"
|
|
|
|
++
|
|
|
|
++static int ipts_hid_start(struct hid_device *hid)
|
|
|
|
++{
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void ipts_hid_stop(struct hid_device *hid)
|
|
|
|
++{
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_hid_switch_mode(struct ipts_context *ipts, enum ipts_mode mode)
|
|
|
|
++{
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (ipts->mode == mode)
|
|
|
|
++ return 0;
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * This is only allowed on older devices.
|
|
|
|
++ */
|
|
|
|
++ if (ipts->info.intf_eds > 1)
|
|
|
|
++ return 0;
|
|
|
|
++
|
|
|
|
++ ipts->mode = mode;
|
|
|
|
++ return ipts_control_restart(ipts);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_hid_parse(struct hid_device *hid)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_context *ipts = NULL;
|
|
|
|
++
|
|
|
|
++ bool has_native_descriptor = false;
|
|
|
|
++
|
|
|
|
++ u8 *buffer = NULL;
|
|
|
|
++ size_t size = 0;
|
|
|
|
++
|
|
|
|
++ if (!hid)
|
|
|
|
++ return -ENODEV;
|
|
|
|
++
|
|
|
|
++ ipts = hid->driver_data;
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ size = sizeof(ipts_singletouch_descriptor);
|
|
|
|
++ has_native_descriptor = ipts->descriptor.address && ipts->descriptor.size > 0;
|
|
|
|
++
|
|
|
|
++ if (has_native_descriptor)
|
|
|
|
++ size += ipts->descriptor.size;
|
|
|
|
++ else
|
|
|
|
++ size += sizeof(ipts_fallback_descriptor);
|
|
|
|
++
|
|
|
|
++ buffer = kzalloc(size, GFP_KERNEL);
|
|
|
|
++ if (!buffer)
|
|
|
|
++ return -ENOMEM;
|
|
|
|
++
|
|
|
|
++ memcpy(buffer, ipts_singletouch_descriptor, sizeof(ipts_singletouch_descriptor));
|
|
|
|
++
|
|
|
|
++ if (has_native_descriptor) {
|
|
|
|
++ memcpy(&buffer[sizeof(ipts_singletouch_descriptor)], ipts->descriptor.address,
|
|
|
|
++ ipts->descriptor.size);
|
|
|
|
++ } else {
|
|
|
|
++ memcpy(&buffer[sizeof(ipts_singletouch_descriptor)], ipts_fallback_descriptor,
|
|
|
|
++ sizeof(ipts_fallback_descriptor));
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = hid_parse_report(hid, buffer, size);
|
|
|
|
++ kfree(buffer);
|
|
|
|
++
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to parse HID descriptor: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_hid_get_feature(struct ipts_context *ipts, unsigned char reportnum, __u8 *buf,
|
|
|
|
++ size_t size, enum ipts_feedback_data_type type)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!buf)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ mutex_lock(&ipts->feature_lock);
|
|
|
|
++
|
|
|
|
++ memset(buf, 0, size);
|
|
|
|
++ buf[0] = reportnum;
|
|
|
|
++
|
|
|
|
++ memset(&ipts->feature_report, 0, sizeof(ipts->feature_report));
|
|
|
|
++ reinit_completion(&ipts->feature_event);
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE, type, buf, size);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to send hid2me feedback: %d\n", ret);
|
|
|
|
++ goto out;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = wait_for_completion_timeout(&ipts->feature_event, msecs_to_jiffies(5000));
|
|
|
|
++ if (ret == 0) {
|
|
|
|
++ dev_warn(ipts->dev, "GET_FEATURES timed out!\n");
|
|
|
|
++ ret = -EIO;
|
|
|
|
++ goto out;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (!ipts->feature_report.address) {
|
|
|
|
++ ret = -EFAULT;
|
|
|
|
++ goto out;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (ipts->feature_report.size > size) {
|
|
|
|
++ ret = -ETOOSMALL;
|
|
|
|
++ goto out;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts->feature_report.size;
|
|
|
|
++ memcpy(buf, ipts->feature_report.address, ipts->feature_report.size);
|
|
|
|
++
|
|
|
|
++out:
|
|
|
|
++ mutex_unlock(&ipts->feature_lock);
|
|
|
|
++ return ret;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_hid_set_feature(struct ipts_context *ipts, unsigned char reportnum, __u8 *buf,
|
|
|
|
++ size_t size, enum ipts_feedback_data_type type)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!buf)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ buf[0] = reportnum;
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE, type, buf, size);
|
|
|
|
++ if (ret)
|
|
|
|
++ dev_err(ipts->dev, "Failed to send hid2me feedback: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ return ret;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_hid_raw_request(struct hid_device *hid, unsigned char reportnum, __u8 *buf,
|
|
|
|
++ size_t size, unsigned char rtype, int reqtype)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_context *ipts = NULL;
|
|
|
|
++
|
|
|
|
++ enum ipts_feedback_data_type type = IPTS_FEEDBACK_DATA_TYPE_VENDOR;
|
|
|
|
++
|
|
|
|
++ if (!hid)
|
|
|
|
++ return -ENODEV;
|
|
|
|
++
|
|
|
|
++ ipts = hid->driver_data;
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!buf)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT)
|
|
|
|
++ type = IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT;
|
|
|
|
++ else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT)
|
|
|
|
++ type = IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES;
|
|
|
|
++ else if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT)
|
|
|
|
++ type = IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES;
|
|
|
|
++ else
|
|
|
|
++ return -EIO;
|
|
|
|
++
|
|
|
|
++ // Implemente mode switching report for older devices without native HID support
|
|
|
|
++ if (type == IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES && reportnum == IPTS_HID_REPORT_SET_MODE) {
|
|
|
|
++ ret = ipts_hid_switch_mode(ipts, buf[1]);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to switch modes: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (reqtype == HID_REQ_GET_REPORT)
|
|
|
|
++ return ipts_hid_get_feature(ipts, reportnum, buf, size, type);
|
|
|
|
++ else
|
|
|
|
++ return ipts_hid_set_feature(ipts, reportnum, buf, size, type);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_hid_output_report(struct hid_device *hid, __u8 *data, size_t size)
|
|
|
|
++{
|
|
|
|
++ struct ipts_context *ipts = NULL;
|
|
|
|
++
|
|
|
|
++ if (!hid)
|
|
|
|
++ return -ENODEV;
|
|
|
|
++
|
|
|
|
++ ipts = hid->driver_data;
|
|
|
|
++
|
|
|
|
++ return ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE,
|
|
|
|
++ IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT, data, size);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static struct hid_ll_driver ipts_hid_driver = {
|
|
|
|
++ .start = ipts_hid_start,
|
|
|
|
++ .stop = ipts_hid_stop,
|
|
|
|
++ .open = ipts_hid_start,
|
|
|
|
++ .close = ipts_hid_stop,
|
|
|
|
++ .parse = ipts_hid_parse,
|
|
|
|
++ .raw_request = ipts_hid_raw_request,
|
|
|
|
++ .output_report = ipts_hid_output_report,
|
|
+};
|
|
+};
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * struct ipts_feedback_buffer - The contents of an IPTS feedback buffer.
|
|
|
|
-+ * @cmd_type: A command that should be executed on the sensor.
|
|
|
|
-+ * @size: The size of the payload to be written.
|
|
|
|
-+ * @buffer: The ID of the buffer that contains this feedback data.
|
|
|
|
-+ * @protocol: The protocol version of the EDS.
|
|
|
|
-+ * @data_type: The type of payload that the buffer contains.
|
|
|
|
-+ * @spi_offset: The offset at which to write the payload data.
|
|
|
|
-+ * @payload: Payload for the feedback command, or 0 if no payload is sent.
|
|
|
|
-+ */
|
|
|
|
-+struct ipts_feedback_buffer {
|
|
|
|
-+ enum ipts_feedback_cmd_type cmd_type;
|
|
|
|
-+ u32 size;
|
|
|
|
-+ u32 buffer;
|
|
|
|
-+ u32 protocol;
|
|
|
|
-+ enum ipts_feedback_data_type data_type;
|
|
|
|
-+ u32 spi_offset;
|
|
|
|
-+ u8 reserved[40];
|
|
|
|
-+ u8 payload[];
|
|
|
|
-+} __packed;
|
|
|
|
|
|
++int ipts_hid_input_data(struct ipts_context *ipts, u32 buffer)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ u8 *temp = NULL;
|
|
|
|
++ struct ipts_hid_header *frame = NULL;
|
|
|
|
++ struct ipts_data_header *header = NULL;
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * enum ipts_reset_type - Possible ways of resetting the touch sensor
|
|
|
|
-+ * @IPTS_RESET_TYPE_HARD: Perform hardware reset using GPIO pin.
|
|
|
|
-+ * @IPTS_RESET_TYPE_SOFT: Perform software reset using SPI interface.
|
|
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!ipts->hid)
|
|
|
|
++ return -ENODEV;
|
|
|
|
++
|
|
|
|
++ header = (struct ipts_data_header *)ipts->resources.data[buffer].address;
|
|
|
|
++
|
|
|
|
++ if (!header)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (header->size == 0)
|
|
|
|
++ return 0;
|
|
|
|
++
|
|
|
|
++ if (header->type == IPTS_DATA_TYPE_HID)
|
|
|
|
++ return hid_input_report(ipts->hid, HID_INPUT_REPORT, header->data, header->size, 1);
|
|
|
|
++
|
|
|
|
++ if (header->type == IPTS_DATA_TYPE_GET_FEATURES) {
|
|
|
|
++ ipts->feature_report.address = header->data;
|
|
|
|
++ ipts->feature_report.size = header->size;
|
|
|
|
++
|
|
|
|
++ complete_all(&ipts->feature_event);
|
|
|
|
++ return 0;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (header->type != IPTS_DATA_TYPE_FRAME)
|
|
|
|
++ return 0;
|
|
|
|
++
|
|
|
|
++ if (header->size + 3 + sizeof(struct ipts_hid_header) > IPTS_HID_REPORT_DATA_SIZE)
|
|
|
|
++ return -ERANGE;
|
|
|
|
++
|
|
|
|
++ temp = kzalloc(IPTS_HID_REPORT_DATA_SIZE, GFP_KERNEL);
|
|
|
|
++ if (!temp)
|
|
|
|
++ return -ENOMEM;
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * Synthesize a HID report matching the devices that natively send HID reports
|
|
|
|
++ */
|
|
|
|
++ temp[0] = IPTS_HID_REPORT_DATA;
|
|
|
|
++
|
|
|
|
++ frame = (struct ipts_hid_header *)&temp[3];
|
|
|
|
++ frame->type = IPTS_HID_FRAME_TYPE_RAW;
|
|
|
|
++ frame->size = header->size + sizeof(*frame);
|
|
|
|
++
|
|
|
|
++ memcpy(frame->data, header->data, header->size);
|
|
|
|
++
|
|
|
|
++ ret = hid_input_report(ipts->hid, HID_INPUT_REPORT, temp, IPTS_HID_REPORT_DATA_SIZE, 1);
|
|
|
|
++ kfree(temp);
|
|
|
|
++
|
|
|
|
++ return ret;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_hid_init(struct ipts_context *ipts, struct ipts_device_info info)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (ipts->hid)
|
|
|
|
++ return 0;
|
|
|
|
++
|
|
|
|
++ ipts->hid = hid_allocate_device();
|
|
|
|
++ if (IS_ERR(ipts->hid)) {
|
|
|
|
++ int err = PTR_ERR(ipts->hid);
|
|
|
|
++
|
|
|
|
++ dev_err(ipts->dev, "Failed to allocate HID device: %d\n", err);
|
|
|
|
++ return err;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ipts->hid->driver_data = ipts;
|
|
|
|
++ ipts->hid->dev.parent = ipts->dev;
|
|
|
|
++ ipts->hid->ll_driver = &ipts_hid_driver;
|
|
|
|
++
|
|
|
|
++ ipts->hid->vendor = info.vendor;
|
|
|
|
++ ipts->hid->product = info.product;
|
|
|
|
++ ipts->hid->group = HID_GROUP_MULTITOUCH;
|
|
|
|
++
|
|
|
|
++ snprintf(ipts->hid->name, sizeof(ipts->hid->name), "IPTS %04X:%04X", info.vendor,
|
|
|
|
++ info.product);
|
|
|
|
++
|
|
|
|
++ ret = hid_add_device(ipts->hid);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to add HID device: %d\n", ret);
|
|
|
|
++ ipts_hid_free(ipts);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_hid_free(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!ipts->hid)
|
|
|
|
++ return 0;
|
|
|
|
++
|
|
|
|
++ hid_destroy_device(ipts->hid);
|
|
|
|
++ ipts->hid = NULL;
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
+diff --git a/drivers/hid/ipts/hid.h b/drivers/hid/ipts/hid.h
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..62bf3cd48608
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/hid.h
|
|
|
|
+@@ -0,0 +1,22 @@
|
|
|
|
++/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2022-2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
-+enum ipts_reset_type {
|
|
|
|
-+ IPTS_RESET_TYPE_HARD = 0,
|
|
|
|
-+ IPTS_RESET_TYPE_SOFT = 1,
|
|
|
|
-+};
|
|
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * struct ipts_reset_sensor_cmd - Payload for the RESET_SENSOR command.
|
|
|
|
-+ * @type: What type of reset should be performed.
|
|
|
|
-+ */
|
|
|
|
-+struct ipts_reset_sensor_cmd {
|
|
|
|
-+ enum ipts_reset_type type;
|
|
|
|
-+ u8 reserved[4];
|
|
|
|
-+} __packed;
|
|
|
|
|
|
++#ifndef IPTS_HID_H
|
|
|
|
++#define IPTS_HID_H
|
|
|
|
++
|
|
|
|
++#include <linux/types.h>
|
|
|
|
++
|
|
|
|
++#include "context.h"
|
|
|
|
++#include "spec-device.h"
|
|
|
|
++
|
|
|
|
++int ipts_hid_input_data(struct ipts_context *ipts, u32 buffer);
|
|
|
|
++
|
|
|
|
++int ipts_hid_init(struct ipts_context *ipts, struct ipts_device_info info);
|
|
|
|
++int ipts_hid_free(struct ipts_context *ipts);
|
|
|
|
++
|
|
|
|
++#endif /* IPTS_HID_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/main.c b/drivers/hid/ipts/main.c
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..0f20c6c08c38
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/main.c
|
|
|
|
+@@ -0,0 +1,127 @@
|
|
|
|
++// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
|
|
++ */
|
|
|
|
++
|
|
|
|
++#include <linux/completion.h>
|
|
|
|
++#include <linux/delay.h>
|
|
|
|
++#include <linux/device.h>
|
|
|
|
++#include <linux/dma-mapping.h>
|
|
|
|
++#include <linux/mei_cl_bus.h>
|
|
|
|
++#include <linux/mod_devicetable.h>
|
|
|
|
++#include <linux/module.h>
|
|
|
|
++#include <linux/mutex.h>
|
|
|
|
++#include <linux/slab.h>
|
|
|
|
++#include <linux/stddef.h>
|
|
|
|
++#include <linux/types.h>
|
|
|
|
++
|
|
|
|
++#include "context.h"
|
|
|
|
++#include "control.h"
|
|
|
|
++#include "mei.h"
|
|
|
|
++#include "receiver.h"
|
|
|
|
++#include "spec-device.h"
|
|
|
|
++
|
|
|
|
++/*
|
|
|
|
++ * The MEI client ID for IPTS functionality.
|
|
|
|
++ */
|
|
|
|
++#define IPTS_ID UUID_LE(0x3e8d0870, 0x271a, 0x4208, 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04)
|
|
|
|
++
|
|
|
|
++static int ipts_set_dma_mask(struct mei_cl_device *cldev)
|
|
|
|
++{
|
|
|
|
++ if (!cldev)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64)))
|
|
|
|
++ return 0;
|
|
|
|
++
|
|
|
|
++ return dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(32));
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_context *ipts = NULL;
|
|
|
|
++
|
|
|
|
++ if (!cldev)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ ret = ipts_set_dma_mask(cldev);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(&cldev->dev, "Failed to set DMA mask for IPTS: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = mei_cldev_enable(cldev);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(&cldev->dev, "Failed to enable MEI device: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ipts = devm_kzalloc(&cldev->dev, sizeof(*ipts), GFP_KERNEL);
|
|
|
|
++ if (!ipts) {
|
|
|
|
++ mei_cldev_disable(cldev);
|
|
|
|
++ return -ENOMEM;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_mei_init(&ipts->mei, cldev);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(&cldev->dev, "Failed to init MEI bus logic: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ipts->dev = &cldev->dev;
|
|
|
|
++ ipts->mode = IPTS_MODE_EVENT;
|
|
|
|
++
|
|
|
|
++ mutex_init(&ipts->feature_lock);
|
|
|
|
++ init_completion(&ipts->feature_event);
|
|
|
|
++
|
|
|
|
++ mei_cldev_set_drvdata(cldev, ipts);
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_start(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(&cldev->dev, "Failed to start IPTS: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void ipts_remove(struct mei_cl_device *cldev)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_context *ipts = NULL;
|
|
|
|
++
|
|
|
|
++ if (!cldev) {
|
|
|
|
++ pr_err("MEI device is NULL!");
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ipts = mei_cldev_get_drvdata(cldev);
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_stop(ipts);
|
|
|
|
++ if (ret)
|
|
|
|
++ dev_err(&cldev->dev, "Failed to stop IPTS: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ mei_cldev_disable(cldev);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static struct mei_cl_device_id ipts_device_id_table[] = {
|
|
|
|
++ { .uuid = IPTS_ID, .version = MEI_CL_VERSION_ANY },
|
|
|
|
++ {},
|
|
|
|
++};
|
|
|
|
++MODULE_DEVICE_TABLE(mei, ipts_device_id_table);
|
|
|
|
++
|
|
|
|
++static struct mei_cl_driver ipts_driver = {
|
|
|
|
++ .id_table = ipts_device_id_table,
|
|
|
|
++ .name = "ipts",
|
|
|
|
++ .probe = ipts_probe,
|
|
|
|
++ .remove = ipts_remove,
|
|
|
|
++};
|
|
|
|
++module_mei_cl_driver(ipts_driver);
|
|
|
|
++
|
|
|
|
++MODULE_DESCRIPTION("IPTS touchscreen driver");
|
|
|
|
++MODULE_AUTHOR("Dorian Stoll <dorian.stoll@tmsp.io>");
|
|
|
|
++MODULE_LICENSE("GPL");
|
|
|
|
+diff --git a/drivers/hid/ipts/mei.c b/drivers/hid/ipts/mei.c
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..26666fd99b0c
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/mei.c
|
|
|
|
+@@ -0,0 +1,189 @@
|
|
|
|
++// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
|
|
++ */
|
|
|
|
++
|
|
|
|
++#include <linux/device.h>
|
|
|
|
++#include <linux/errno.h>
|
|
|
|
++#include <linux/jiffies.h>
|
|
|
|
++#include <linux/list.h>
|
|
|
|
++#include <linux/mei_cl_bus.h>
|
|
|
|
++#include <linux/printk.h>
|
|
|
|
++#include <linux/rwsem.h>
|
|
|
|
++#include <linux/types.h>
|
|
|
|
++#include <linux/wait.h>
|
|
|
|
++
|
|
|
|
++#include "context.h"
|
|
|
|
++#include "mei.h"
|
|
|
|
++
|
|
|
|
++static void locked_list_add(struct list_head *new, struct list_head *head,
|
|
|
|
++ struct rw_semaphore *lock)
|
|
|
|
++{
|
|
|
|
++ down_write(lock);
|
|
|
|
++ list_add(new, head);
|
|
|
|
++ up_write(lock);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void locked_list_del(struct list_head *entry, struct rw_semaphore *lock)
|
|
|
|
++{
|
|
|
|
++ down_write(lock);
|
|
|
|
++ list_del(entry);
|
|
|
|
++ up_write(lock);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void ipts_mei_incoming(struct mei_cl_device *cldev)
|
|
|
|
++{
|
|
|
|
++ ssize_t ret = 0;
|
|
|
|
++ struct ipts_mei_message *entry = NULL;
|
|
|
|
++ struct ipts_context *ipts = NULL;
|
|
|
|
++
|
|
|
|
++ if (!cldev) {
|
|
|
|
++ pr_err("MEI device is NULL!");
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ipts = mei_cldev_get_drvdata(cldev);
|
|
|
|
++ if (!ipts) {
|
|
|
|
++ pr_err("IPTS driver context is NULL!");
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ entry = devm_kzalloc(ipts->dev, sizeof(*entry), GFP_KERNEL);
|
|
|
|
++ if (!entry)
|
|
|
|
++ return;
|
|
|
|
++
|
|
|
|
++ INIT_LIST_HEAD(&entry->list);
|
|
|
|
++
|
|
|
|
++ do {
|
|
|
|
++ ret = mei_cldev_recv(cldev, (u8 *)&entry->rsp, sizeof(entry->rsp));
|
|
|
|
++ } while (ret == -EINTR);
|
|
|
|
++
|
|
|
|
++ if (ret < 0) {
|
|
|
|
++ dev_err(ipts->dev, "Error while reading response: %ld\n", ret);
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ if (ret == 0) {
|
|
|
|
++ dev_err(ipts->dev, "Received empty response\n");
|
|
|
|
++ return;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ locked_list_add(&entry->list, &ipts->mei.messages, &ipts->mei.message_lock);
|
|
|
|
++ wake_up_all(&ipts->mei.message_queue);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_mei_search(struct ipts_mei *mei, enum ipts_command_code code,
|
|
|
|
++ struct ipts_response *rsp)
|
|
|
|
++{
|
|
|
|
++ struct ipts_mei_message *entry = NULL;
|
|
|
|
++
|
|
|
|
++ if (!mei)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!rsp)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ down_read(&mei->message_lock);
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * Iterate over the list of received messages, and check if there is one
|
|
|
|
++ * matching the requested command code.
|
|
|
|
++ */
|
|
|
|
++ list_for_each_entry(entry, &mei->messages, list) {
|
|
|
|
++ if (entry->rsp.cmd == code)
|
|
|
|
++ break;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ up_read(&mei->message_lock);
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * If entry is not the list head, this means that the loop above has been stopped early,
|
|
|
|
++ * and that we found a matching element. We drop the message from the list and return it.
|
|
|
|
++ */
|
|
|
|
++ if (!list_entry_is_head(entry, &mei->messages, list)) {
|
|
|
|
++ locked_list_del(&entry->list, &mei->message_lock);
|
|
|
|
++
|
|
|
|
++ *rsp = entry->rsp;
|
|
|
|
++ devm_kfree(&mei->cldev->dev, entry);
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return -EAGAIN;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_mei_recv(struct ipts_mei *mei, enum ipts_command_code code, struct ipts_response *rsp,
|
|
|
|
++ u64 timeout)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++
|
|
|
|
++ if (!mei)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * A timeout of 0 means check and return immideately.
|
|
|
|
++ */
|
|
|
|
++ if (timeout == 0)
|
|
|
|
++ return ipts_mei_search(mei, code, rsp);
|
|
|
|
++
|
|
|
|
++ /*
|
|
|
|
++ * A timeout of less than 0 means to wait forever.
|
|
|
|
++ */
|
|
|
|
++ if (timeout < 0) {
|
|
|
|
++ wait_event(mei->message_queue, ipts_mei_search(mei, code, rsp) == 0);
|
|
|
|
++ return 0;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = wait_event_timeout(mei->message_queue, ipts_mei_search(mei, code, rsp) == 0,
|
|
|
|
++ msecs_to_jiffies(timeout));
|
|
|
|
++
|
|
|
|
++ if (ret > 0)
|
|
|
|
++ return 0;
|
|
|
|
++
|
|
|
|
++ return -EAGAIN;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_mei_send(struct ipts_mei *mei, void *data, size_t length)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++
|
|
|
|
++ if (!mei)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!mei->cldev)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!data)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ do {
|
|
|
|
++ ret = mei_cldev_send(mei->cldev, (u8 *)data, length);
|
|
|
|
++ } while (ret == -EINTR);
|
|
|
|
++
|
|
|
|
++ if (ret < 0)
|
|
|
|
++ return ret;
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++int ipts_mei_init(struct ipts_mei *mei, struct mei_cl_device *cldev)
|
|
|
|
++{
|
|
|
|
++ if (!mei)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ if (!cldev)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ mei->cldev = cldev;
|
|
|
|
++
|
|
|
|
++ INIT_LIST_HEAD(&mei->messages);
|
|
|
|
++ init_waitqueue_head(&mei->message_queue);
|
|
|
|
++ init_rwsem(&mei->message_lock);
|
|
|
|
++
|
|
|
|
++ mei_cldev_register_rx_cb(cldev, ipts_mei_incoming);
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
+diff --git a/drivers/hid/ipts/mei.h b/drivers/hid/ipts/mei.h
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..eadacae54c40
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/mei.h
|
|
|
|
+@@ -0,0 +1,67 @@
|
|
|
|
++/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
|
|
++ */
|
|
|
|
++
|
|
|
|
++#ifndef IPTS_MEI_H
|
|
|
|
++#define IPTS_MEI_H
|
|
|
|
++
|
|
|
|
++#include <linux/list.h>
|
|
|
|
++#include <linux/mei_cl_bus.h>
|
|
|
|
++#include <linux/rwsem.h>
|
|
|
|
++#include <linux/types.h>
|
|
|
|
++#include <linux/wait.h>
|
|
|
|
++
|
|
|
|
++#include "spec-device.h"
|
|
|
|
++
|
|
|
|
++struct ipts_mei_message {
|
|
|
|
++ struct list_head list;
|
|
|
|
++ struct ipts_response rsp;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++struct ipts_mei {
|
|
|
|
++ struct mei_cl_device *cldev;
|
|
|
|
++
|
|
|
|
++ struct list_head messages;
|
|
|
|
++
|
|
|
|
++ wait_queue_head_t message_queue;
|
|
|
|
++ struct rw_semaphore message_lock;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++/*
|
|
|
|
++ * ipts_mei_recv() - Receive data from a MEI device.
|
|
|
|
++ * @mei: The IPTS MEI device context.
|
|
|
|
++ * @code: The IPTS command code to look for.
|
|
|
|
++ * @rsp: The address that the received data will be copied to.
|
|
|
|
++ * @timeout: How many milliseconds the function will wait at most.
|
|
|
|
++ *
|
|
|
|
++ * A negative timeout means to wait forever.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received.
|
|
|
|
++ */
|
|
|
|
++int ipts_mei_recv(struct ipts_mei *mei, enum ipts_command_code code, struct ipts_response *rsp,
|
|
|
|
++ u64 timeout);
|
|
|
|
++
|
|
|
|
++/*
|
|
|
|
++ * ipts_mei_send() - Send data to a MEI device.
|
|
|
|
++ * @ipts: The IPTS MEI device context.
|
|
|
|
++ * @data: The data to send.
|
|
|
|
++ * @size: The size of the data.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
|
|
++ */
|
|
|
|
++int ipts_mei_send(struct ipts_mei *mei, void *data, size_t length);
|
|
|
|
++
|
|
|
|
++/*
|
|
|
|
++ * ipts_mei_init() - Initialize the MEI device context.
|
|
|
|
++ * @mei: The MEI device context to initialize.
|
|
|
|
++ * @cldev: The MEI device the context will be bound to.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
|
|
++ */
|
|
|
|
++int ipts_mei_init(struct ipts_mei *mei, struct mei_cl_device *cldev);
|
|
|
|
++
|
|
|
|
++#endif /* IPTS_MEI_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/receiver.c b/drivers/hid/ipts/receiver.c
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..77234f9e0e17
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/receiver.c
|
|
|
|
+@@ -0,0 +1,249 @@
|
|
|
|
++// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
|
|
++ */
|
|
|
|
++
|
|
|
|
++#include <linux/delay.h>
|
|
|
|
++#include <linux/err.h>
|
|
|
|
++#include <linux/kthread.h>
|
|
|
|
++#include <linux/time64.h>
|
|
|
|
++#include <linux/timekeeping.h>
|
|
|
|
++#include <linux/types.h>
|
|
|
|
++
|
|
|
|
++#include "cmd.h"
|
|
|
|
++#include "context.h"
|
|
|
|
++#include "control.h"
|
|
|
|
++#include "hid.h"
|
|
|
|
++#include "resources.h"
|
|
|
|
++#include "spec-device.h"
|
|
|
|
++#include "thread.h"
|
|
|
|
++
|
|
|
|
++static void ipts_receiver_next_doorbell(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ u32 *doorbell = (u32 *)ipts->resources.doorbell.address;
|
|
|
|
++ *doorbell = *doorbell + 1;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static u32 ipts_receiver_current_doorbell(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ u32 *doorbell = (u32 *)ipts->resources.doorbell.address;
|
|
|
|
++ return *doorbell;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static void ipts_receiver_backoff(time64_t last, u32 n)
|
|
|
|
++{
|
|
|
|
++ /*
|
|
|
|
++ * If the last change was less than n seconds ago,
|
|
|
|
++ * sleep for a shorter period so that new data can be
|
|
|
|
++ * processed quickly. If there was no change for more than
|
|
|
|
++ * n seconds, sleep longer to avoid wasting CPU cycles.
|
|
|
|
++ */
|
|
|
|
++ if (last + n > ktime_get_seconds())
|
|
|
|
++ msleep(20);
|
|
|
|
++ else
|
|
|
|
++ msleep(200);
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_receiver_event_loop(struct ipts_thread *thread)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ u32 buffer = 0;
|
|
|
|
++
|
|
|
|
++ struct ipts_context *ipts = NULL;
|
|
|
|
++ time64_t last = ktime_get_seconds();
|
|
|
|
++
|
|
|
|
++ if (!thread)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ ipts = thread->data;
|
|
|
|
++
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ dev_info(ipts->dev, "IPTS running in event mode\n");
|
|
|
|
++
|
|
|
|
++ while (!ipts_thread_should_stop(thread)) {
|
|
|
|
++ for (int i = 0; i < IPTS_BUFFERS; i++) {
|
|
|
|
++ ret = ipts_control_wait_data(ipts, false);
|
|
|
|
++ if (ret == -EAGAIN)
|
|
|
|
++ break;
|
|
|
|
++
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to wait for data: %d\n", ret);
|
|
|
|
++ continue;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ buffer = ipts_receiver_current_doorbell(ipts) % IPTS_BUFFERS;
|
|
|
|
++ ipts_receiver_next_doorbell(ipts);
|
|
|
|
++
|
|
|
|
++ ret = ipts_hid_input_data(ipts, buffer);
|
|
|
|
++ if (ret)
|
|
|
|
++ dev_err(ipts->dev, "Failed to process buffer: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_refill_buffer(ipts, buffer);
|
|
|
|
++ if (ret)
|
|
|
|
++ dev_err(ipts->dev, "Failed to send feedback: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_request_data(ipts);
|
|
|
|
++ if (ret)
|
|
|
|
++ dev_err(ipts->dev, "Failed to request data: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ last = ktime_get_seconds();
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ipts_receiver_backoff(last, 5);
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_request_flush(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to request flush: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_wait_data(ipts, true);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to wait for data: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ if (ret != -EAGAIN)
|
|
|
|
++ return ret;
|
|
|
|
++ else
|
|
|
|
++ return 0;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ ret = ipts_control_wait_flush(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to wait for flush: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ if (ret != -EAGAIN)
|
|
|
|
++ return ret;
|
|
|
|
++ else
|
|
|
|
++ return 0;
|
|
|
|
++ }
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
|
|
++
|
|
|
|
++static int ipts_receiver_doorbell_loop(struct ipts_thread *thread)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
|
|
++ u32 buffer = 0;
|
|
|
|
++
|
|
|
|
++ u32 doorbell = 0;
|
|
|
|
++ u32 lastdb = 0;
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * struct ipts_command - A message sent from the host to the ME.
|
|
|
|
-+ * @code: The message code describing the command. (see IPTS_CMD_*)
|
|
|
|
-+ * @payload: Payload for the command, or 0 if no payload is required.
|
|
|
|
-+ */
|
|
|
|
-+struct ipts_command {
|
|
|
|
-+ u32 code;
|
|
|
|
-+ u8 payload[320];
|
|
|
|
-+} __packed;
|
|
|
|
|
|
++ struct ipts_context *ipts = NULL;
|
|
|
|
++ time64_t last = ktime_get_seconds();
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * struct ipts_device_info - Payload for the GET_DEVICE_INFO response.
|
|
|
|
-+ * @vendor_id: Vendor ID of the touch sensor.
|
|
|
|
-+ * @device_id: Device ID of the touch sensor.
|
|
|
|
-+ * @hw_rev: Hardware revision of the touch sensor.
|
|
|
|
-+ * @fw_rev: Firmware revision of the touch sensor.
|
|
|
|
-+ * @data_size: Required size of one data buffer.
|
|
|
|
-+ * @feedback_size: Required size of one feedback buffer.
|
|
|
|
-+ * @mode: Current operation mode of IPTS.
|
|
|
|
-+ * @max_contacts: The amount of concurrent touches supported by the sensor.
|
|
|
|
-+ */
|
|
|
|
-+struct ipts_get_device_info_rsp {
|
|
|
|
-+ u16 vendor_id;
|
|
|
|
-+ u16 device_id;
|
|
|
|
-+ u32 hw_rev;
|
|
|
|
-+ u32 fw_rev;
|
|
|
|
-+ u32 data_size;
|
|
|
|
-+ u32 feedback_size;
|
|
|
|
-+ enum ipts_mode mode;
|
|
|
|
-+ u8 max_contacts;
|
|
|
|
-+ u8 reserved[19];
|
|
|
|
-+} __packed;
|
|
|
|
|
|
++ if (!thread)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * struct ipts_feedback_rsp - Payload for the FEEDBACK response.
|
|
|
|
-+ * @buffer: The buffer that has received feedback.
|
|
|
|
-+ */
|
|
|
|
-+struct ipts_feedback_rsp {
|
|
|
|
-+ u32 buffer;
|
|
|
|
-+} __packed;
|
|
|
|
|
|
++ ipts = thread->data;
|
|
+
|
|
+
|
|
-+/**
|
|
|
|
-+ * struct ipts_response - A message sent from the ME to the host.
|
|
|
|
-+ * @code: The message code describing the response. (see IPTS_RSP_*)
|
|
|
|
-+ * @status: The status code returned by the command.
|
|
|
|
-+ * @payload: Payload returned by the command.
|
|
|
|
-+ */
|
|
|
|
-+struct ipts_response {
|
|
|
|
-+ u32 code;
|
|
|
|
-+ enum ipts_status status;
|
|
|
|
-+ u8 payload[80];
|
|
|
|
-+} __packed;
|
|
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+#endif /* _IPTS_PROTOCOL_H_ */
|
|
|
|
-diff --git a/drivers/misc/ipts/receiver.c b/drivers/misc/ipts/receiver.c
|
|
|
|
-new file mode 100644
|
|
|
|
-index 000000000000..23dca13c2139
|
|
|
|
---- /dev/null
|
|
|
|
-+++ b/drivers/misc/ipts/receiver.c
|
|
|
|
-@@ -0,0 +1,224 @@
|
|
|
|
-+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
-+/*
|
|
|
|
-+ * Copyright (c) 2016 Intel Corporation
|
|
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
-+ *
|
|
|
|
-+ * Linux driver for Intel Precise Touch & Stylus
|
|
|
|
-+ */
|
|
|
|
|
|
++ dev_info(ipts->dev, "IPTS running in doorbell mode\n");
|
|
+
|
|
+
|
|
-+#include <linux/mei_cl_bus.h>
|
|
|
|
-+#include <linux/moduleparam.h>
|
|
|
|
-+#include <linux/types.h>
|
|
|
|
|
|
++ while (true) {
|
|
|
|
++ if (ipts_thread_should_stop(thread)) {
|
|
|
|
++ ret = ipts_control_request_flush(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to request flush: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
|
|
++ }
|
|
+
|
|
+
|
|
-+#include "context.h"
|
|
|
|
-+#include "control.h"
|
|
|
|
-+#include "protocol.h"
|
|
|
|
-+#include "resources.h"
|
|
|
|
|
|
++ doorbell = ipts_receiver_current_doorbell(ipts);
|
|
+
|
|
+
|
|
-+/*
|
|
|
|
-+ * Temporary parameter to guard gen7 multitouch mode.
|
|
|
|
-+ * Remove once gen7 has stable iptsd support.
|
|
|
|
-+ */
|
|
|
|
-+static bool gen7mt;
|
|
|
|
-+module_param(gen7mt, bool, 0644);
|
|
|
|
|
|
++ /*
|
|
|
|
++ * After filling up one of the data buffers, IPTS will increment
|
|
|
|
++ * the doorbell. The value of the doorbell stands for the *next*
|
|
|
|
++ * buffer that IPTS is going to fill.
|
|
|
|
++ */
|
|
|
|
++ while (lastdb != doorbell) {
|
|
|
|
++ buffer = lastdb % IPTS_BUFFERS;
|
|
+
|
|
+
|
|
-+static int ipts_receiver_handle_get_device_info(struct ipts_context *ipts,
|
|
|
|
-+ struct ipts_response *rsp)
|
|
|
|
-+{
|
|
|
|
-+ struct ipts_set_mode_cmd cmd;
|
|
|
|
|
|
++ ret = ipts_hid_input_data(ipts, buffer);
|
|
|
|
++ if (ret)
|
|
|
|
++ dev_err(ipts->dev, "Failed to process buffer: %d\n", ret);
|
|
+
|
|
+
|
|
-+ memcpy(&ipts->device_info, rsp->payload,
|
|
|
|
-+ sizeof(struct ipts_get_device_info_rsp));
|
|
|
|
|
|
++ ret = ipts_control_refill_buffer(ipts, buffer);
|
|
|
|
++ if (ret)
|
|
|
|
++ dev_err(ipts->dev, "Failed to send feedback: %d\n", ret);
|
|
+
|
|
+
|
|
-+ memset(&cmd, 0, sizeof(struct ipts_set_mode_cmd));
|
|
|
|
-+ cmd.mode = IPTS_MODE_MULTITOUCH;
|
|
|
|
|
|
++ last = ktime_get_seconds();
|
|
|
|
++ lastdb++;
|
|
|
|
++ }
|
|
+
|
|
+
|
|
-+ return ipts_control_send(ipts, IPTS_CMD_SET_MODE, &cmd,
|
|
|
|
-+ sizeof(struct ipts_set_mode_cmd));
|
|
|
|
-+}
|
|
|
|
|
|
++ if (ipts_thread_should_stop(thread))
|
|
|
|
++ break;
|
|
+
|
|
+
|
|
-+static int ipts_receiver_handle_set_mode(struct ipts_context *ipts)
|
|
|
|
-+{
|
|
|
|
-+ int i, ret;
|
|
|
|
-+ struct ipts_set_mem_window_cmd cmd;
|
|
|
|
|
|
++ ipts_receiver_backoff(last, 5);
|
|
|
|
++ }
|
|
+
|
|
+
|
|
-+ ret = ipts_resources_alloc(ipts);
|
|
|
|
|
|
++ ret = ipts_control_wait_data(ipts, true);
|
|
+ if (ret) {
|
|
+ if (ret) {
|
|
-+ dev_err(ipts->dev, "Failed to allocate resources\n");
|
|
|
|
-+ return ret;
|
|
|
|
|
|
++ dev_err(ipts->dev, "Failed to wait for data: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ if (ret != -EAGAIN)
|
|
|
|
++ return ret;
|
|
|
|
++ else
|
|
|
|
++ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
-+ memset(&cmd, 0, sizeof(struct ipts_set_mem_window_cmd));
|
|
|
|
|
|
++ ret = ipts_control_wait_flush(ipts);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to wait for flush: %d\n", ret);
|
|
|
|
++
|
|
|
|
++ if (ret != -EAGAIN)
|
|
|
|
++ return ret;
|
|
|
|
++ else
|
|
|
|
++ return 0;
|
|
|
|
++ }
|
|
+
|
|
+
|
|
-+ for (i = 0; i < IPTS_BUFFERS; i++) {
|
|
|
|
-+ cmd.data_buffer_addr_lower[i] =
|
|
|
|
-+ lower_32_bits(ipts->data[i].dma_address);
|
|
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
+
|
|
+
|
|
-+ cmd.data_buffer_addr_upper[i] =
|
|
|
|
-+ upper_32_bits(ipts->data[i].dma_address);
|
|
|
|
|
|
++int ipts_receiver_start(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
+
|
|
+
|
|
-+ cmd.feedback_buffer_addr_lower[i] =
|
|
|
|
-+ lower_32_bits(ipts->feedback[i].dma_address);
|
|
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+ cmd.feedback_buffer_addr_upper[i] =
|
|
|
|
-+ upper_32_bits(ipts->feedback[i].dma_address);
|
|
|
|
|
|
++ if (ipts->mode == IPTS_MODE_EVENT) {
|
|
|
|
++ ret = ipts_thread_start(&ipts->receiver_loop, ipts_receiver_event_loop, ipts,
|
|
|
|
++ "ipts_event");
|
|
|
|
++ } else if (ipts->mode == IPTS_MODE_DOORBELL) {
|
|
|
|
++ ret = ipts_thread_start(&ipts->receiver_loop, ipts_receiver_doorbell_loop, ipts,
|
|
|
|
++ "ipts_doorbell");
|
|
|
|
++ } else {
|
|
|
|
++ ret = -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
-+ cmd.workqueue_addr_lower = lower_32_bits(ipts->workqueue.dma_address);
|
|
|
|
-+ cmd.workqueue_addr_upper = upper_32_bits(ipts->workqueue.dma_address);
|
|
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to start receiver loop: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
+
|
|
+
|
|
-+ cmd.doorbell_addr_lower = lower_32_bits(ipts->doorbell.dma_address);
|
|
|
|
-+ cmd.doorbell_addr_upper = upper_32_bits(ipts->doorbell.dma_address);
|
|
|
|
|
|
++ return 0;
|
|
|
|
++}
|
|
+
|
|
+
|
|
-+ cmd.host2me_addr_lower = lower_32_bits(ipts->host2me.dma_address);
|
|
|
|
-+ cmd.host2me_addr_upper = upper_32_bits(ipts->host2me.dma_address);
|
|
|
|
|
|
++int ipts_receiver_stop(struct ipts_context *ipts)
|
|
|
|
++{
|
|
|
|
++ int ret = 0;
|
|
+
|
|
+
|
|
-+ cmd.workqueue_size = IPTS_WORKQUEUE_SIZE;
|
|
|
|
-+ cmd.workqueue_item_size = IPTS_WORKQUEUE_ITEM_SIZE;
|
|
|
|
|
|
++ if (!ipts)
|
|
|
|
++ return -EFAULT;
|
|
|
|
++
|
|
|
|
++ ret = ipts_thread_stop(&ipts->receiver_loop);
|
|
|
|
++ if (ret) {
|
|
|
|
++ dev_err(ipts->dev, "Failed to stop receiver loop: %d\n", ret);
|
|
|
|
++ return ret;
|
|
|
|
++ }
|
|
+
|
|
+
|
|
-+ return ipts_control_send(ipts, IPTS_CMD_SET_MEM_WINDOW, &cmd,
|
|
|
|
-+ sizeof(struct ipts_set_mem_window_cmd));
|
|
|
|
|
|
++ return 0;
|
|
+}
|
|
+}
|
|
|
|
+diff --git a/drivers/hid/ipts/receiver.h b/drivers/hid/ipts/receiver.h
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..96070f34fbca
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/receiver.h
|
|
|
|
+@@ -0,0 +1,17 @@
|
|
|
|
++/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
|
|
++ */
|
|
+
|
|
+
|
|
-+static int ipts_receiver_handle_set_mem_window(struct ipts_context *ipts)
|
|
|
|
-+{
|
|
|
|
-+ int ret;
|
|
|
|
|
|
++#ifndef IPTS_RECEIVER_H
|
|
|
|
++#define IPTS_RECEIVER_H
|
|
+
|
|
+
|
|
-+ dev_info(ipts->dev, "Device %04hX:%04hX ready\n",
|
|
|
|
-+ ipts->device_info.vendor_id, ipts->device_info.device_id);
|
|
|
|
-+ ipts->status = IPTS_HOST_STATUS_STARTED;
|
|
|
|
|
|
++#include "context.h"
|
|
+
|
|
+
|
|
-+ ret = ipts_control_send(ipts, IPTS_CMD_READY_FOR_DATA, NULL, 0);
|
|
|
|
-+ if (ret)
|
|
|
|
-+ return ret;
|
|
|
|
|
|
++int ipts_receiver_start(struct ipts_context *ipts);
|
|
|
|
++int ipts_receiver_stop(struct ipts_context *ipts);
|
|
+
|
|
+
|
|
-+ if (!gen7mt)
|
|
|
|
-+ return 0;
|
|
|
|
|
|
++#endif /* IPTS_RECEIVER_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/resources.c b/drivers/hid/ipts/resources.c
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..80ba5885bb55
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/resources.c
|
|
|
|
+@@ -0,0 +1,108 @@
|
|
|
|
++// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
|
|
++ */
|
|
+
|
|
+
|
|
-+ return ipts_control_set_feature(ipts, 0x5, 0x1);
|
|
|
|
-+}
|
|
|
|
|
|
++#include <linux/dma-mapping.h>
|
|
|
|
++#include <linux/types.h>
|
|
+
|
|
+
|
|
-+static int ipts_receiver_handle_feedback(struct ipts_context *ipts,
|
|
|
|
-+ struct ipts_response *rsp)
|
|
|
|
|
|
++#include "resources.h"
|
|
|
|
++#include "spec-device.h"
|
|
|
|
++
|
|
|
|
++static int ipts_resources_alloc_buffer(struct ipts_buffer *buffer, struct device *dev, size_t size)
|
|
+{
|
|
+{
|
|
-+ struct ipts_feedback_rsp feedback;
|
|
|
|
|
|
++ if (!buffer)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+ if (ipts->status != IPTS_HOST_STATUS_STOPPING)
|
|
|
|
|
|
++ if (buffer->address)
|
|
+ return 0;
|
|
+ return 0;
|
|
+
|
|
+
|
|
-+ memcpy(&feedback, rsp->payload, sizeof(feedback));
|
|
|
|
|
|
++ buffer->address = dma_alloc_coherent(dev, size, &buffer->dma_address, GFP_KERNEL);
|
|
|
|
++
|
|
|
|
++ if (!buffer->address)
|
|
|
|
++ return -ENOMEM;
|
|
+
|
|
+
|
|
-+ if (feedback.buffer < IPTS_BUFFERS - 1)
|
|
|
|
-+ return ipts_control_send_feedback(ipts, feedback.buffer + 1);
|
|
|
|
|
|
++ buffer->size = size;
|
|
|
|
++ buffer->device = dev;
|
|
+
|
|
+
|
|
-+ return ipts_control_send(ipts, IPTS_CMD_CLEAR_MEM_WINDOW, NULL, 0);
|
|
|
|
|
|
++ return 0;
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+static int ipts_receiver_handle_clear_mem_window(struct ipts_context *ipts)
|
|
|
|
|
|
++static void ipts_resources_free_buffer(struct ipts_buffer *buffer)
|
|
+{
|
|
+{
|
|
-+ ipts->status = IPTS_HOST_STATUS_STOPPED;
|
|
|
|
|
|
++ if (!buffer->address)
|
|
|
|
++ return;
|
|
+
|
|
+
|
|
-+ if (ipts->restart)
|
|
|
|
-+ return ipts_control_start(ipts);
|
|
|
|
|
|
++ dma_free_coherent(buffer->device, buffer->size, buffer->address, buffer->dma_address);
|
|
+
|
|
+
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
|
|
++ buffer->address = NULL;
|
|
|
|
++ buffer->size = 0;
|
|
+
|
|
+
|
|
-+static bool ipts_receiver_sensor_was_reset(u32 status)
|
|
|
|
-+{
|
|
|
|
-+ return status == IPTS_STATUS_SENSOR_EXPECTED_RESET ||
|
|
|
|
-+ status == IPTS_STATUS_SENSOR_UNEXPECTED_RESET;
|
|
|
|
|
|
++ buffer->dma_address = 0;
|
|
|
|
++ buffer->device = NULL;
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+static bool ipts_receiver_handle_error(struct ipts_context *ipts,
|
|
|
|
-+ struct ipts_response *rsp)
|
|
|
|
|
|
++int ipts_resources_init(struct ipts_resources *res, struct device *dev, size_t ds, size_t fs)
|
|
+{
|
|
+{
|
|
-+ bool error;
|
|
|
|
-+
|
|
|
|
-+ switch (rsp->status) {
|
|
|
|
-+ case IPTS_STATUS_SUCCESS:
|
|
|
|
-+ case IPTS_STATUS_COMPAT_CHECK_FAIL:
|
|
|
|
-+ error = false;
|
|
|
|
-+ break;
|
|
|
|
-+ case IPTS_STATUS_INVALID_PARAMS:
|
|
|
|
-+ error = rsp->code != IPTS_RSP_FEEDBACK;
|
|
|
|
-+ break;
|
|
|
|
-+ case IPTS_STATUS_SENSOR_DISABLED:
|
|
|
|
-+ error = ipts->status != IPTS_HOST_STATUS_STOPPING;
|
|
|
|
-+ break;
|
|
|
|
-+ default:
|
|
|
|
-+ error = true;
|
|
|
|
-+ break;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ if (!error)
|
|
|
|
-+ return false;
|
|
|
|
|
|
++ int ret = 0;
|
|
+
|
|
+
|
|
-+ dev_err(ipts->dev, "Command 0x%08x failed: %d\n", rsp->code,
|
|
|
|
-+ rsp->status);
|
|
|
|
|
|
++ if (!res)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+ if (ipts_receiver_sensor_was_reset(rsp->status)) {
|
|
|
|
-+ dev_err(ipts->dev, "Sensor was reset\n");
|
|
|
|
|
|
++ for (int i = 0; i < IPTS_BUFFERS; i++) {
|
|
|
|
++ ret = ipts_resources_alloc_buffer(&res->data[i], dev, ds);
|
|
|
|
++ if (ret)
|
|
|
|
++ goto err;
|
|
|
|
++ }
|
|
+
|
|
+
|
|
-+ if (ipts_control_restart(ipts))
|
|
|
|
-+ dev_err(ipts->dev, "Failed to restart IPTS\n");
|
|
|
|
|
|
++ for (int i = 0; i < IPTS_BUFFERS; i++) {
|
|
|
|
++ ret = ipts_resources_alloc_buffer(&res->feedback[i], dev, fs);
|
|
|
|
++ if (ret)
|
|
|
|
++ goto err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
-+ return true;
|
|
|
|
-+}
|
|
|
|
|
|
++ ret = ipts_resources_alloc_buffer(&res->doorbell, dev, sizeof(u32));
|
|
|
|
++ if (ret)
|
|
|
|
++ goto err;
|
|
+
|
|
+
|
|
-+static void ipts_receiver_handle_response(struct ipts_context *ipts,
|
|
|
|
-+ struct ipts_response *rsp)
|
|
|
|
-+{
|
|
|
|
-+ int ret;
|
|
|
|
|
|
++ ret = ipts_resources_alloc_buffer(&res->workqueue, dev, sizeof(u32));
|
|
|
|
++ if (ret)
|
|
|
|
++ goto err;
|
|
+
|
|
+
|
|
-+ if (ipts_receiver_handle_error(ipts, rsp))
|
|
|
|
-+ return;
|
|
|
|
|
|
++ ret = ipts_resources_alloc_buffer(&res->hid2me, dev, fs);
|
|
|
|
++ if (ret)
|
|
|
|
++ goto err;
|
|
+
|
|
+
|
|
-+ switch (rsp->code) {
|
|
|
|
-+ case IPTS_RSP_GET_DEVICE_INFO:
|
|
|
|
-+ ret = ipts_receiver_handle_get_device_info(ipts, rsp);
|
|
|
|
-+ break;
|
|
|
|
-+ case IPTS_RSP_SET_MODE:
|
|
|
|
-+ ret = ipts_receiver_handle_set_mode(ipts);
|
|
|
|
-+ break;
|
|
|
|
-+ case IPTS_RSP_SET_MEM_WINDOW:
|
|
|
|
-+ ret = ipts_receiver_handle_set_mem_window(ipts);
|
|
|
|
-+ break;
|
|
|
|
-+ case IPTS_RSP_FEEDBACK:
|
|
|
|
-+ ret = ipts_receiver_handle_feedback(ipts, rsp);
|
|
|
|
-+ break;
|
|
|
|
-+ case IPTS_RSP_CLEAR_MEM_WINDOW:
|
|
|
|
-+ ret = ipts_receiver_handle_clear_mem_window(ipts);
|
|
|
|
-+ break;
|
|
|
|
-+ default:
|
|
|
|
-+ ret = 0;
|
|
|
|
-+ break;
|
|
|
|
-+ }
|
|
|
|
-+
|
|
|
|
-+ if (!ret)
|
|
|
|
-+ return;
|
|
|
|
|
|
++ ret = ipts_resources_alloc_buffer(&res->descriptor, dev, ds + 8);
|
|
|
|
++ if (ret)
|
|
|
|
++ goto err;
|
|
|
|
++
|
|
|
|
++ return 0;
|
|
+
|
|
+
|
|
-+ dev_err(ipts->dev, "Error while handling response 0x%08x: %d\n",
|
|
|
|
-+ rsp->code, ret);
|
|
|
|
|
|
++err:
|
|
+
|
|
+
|
|
-+ if (ipts_control_stop(ipts))
|
|
|
|
-+ dev_err(ipts->dev, "Failed to stop IPTS\n");
|
|
|
|
|
|
++ ipts_resources_free(res);
|
|
|
|
++ return ret;
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+void ipts_receiver_callback(struct mei_cl_device *cldev)
|
|
|
|
|
|
++int ipts_resources_free(struct ipts_resources *res)
|
|
+{
|
|
+{
|
|
-+ int ret;
|
|
|
|
-+ struct ipts_response rsp;
|
|
|
|
-+ struct ipts_context *ipts;
|
|
|
|
|
|
++ if (!res)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+ ipts = mei_cldev_get_drvdata(cldev);
|
|
|
|
|
|
++ for (int i = 0; i < IPTS_BUFFERS; i++)
|
|
|
|
++ ipts_resources_free_buffer(&res->data[i]);
|
|
+
|
|
+
|
|
-+ ret = mei_cldev_recv(cldev, (u8 *)&rsp, sizeof(struct ipts_response));
|
|
|
|
-+ if (ret <= 0) {
|
|
|
|
-+ dev_err(ipts->dev, "Error while reading response: %d\n", ret);
|
|
|
|
-+ return;
|
|
|
|
-+ }
|
|
|
|
|
|
++ for (int i = 0; i < IPTS_BUFFERS; i++)
|
|
|
|
++ ipts_resources_free_buffer(&res->feedback[i]);
|
|
|
|
++
|
|
|
|
++ ipts_resources_free_buffer(&res->doorbell);
|
|
|
|
++ ipts_resources_free_buffer(&res->workqueue);
|
|
|
|
++ ipts_resources_free_buffer(&res->hid2me);
|
|
|
|
++ ipts_resources_free_buffer(&res->descriptor);
|
|
+
|
|
+
|
|
-+ ipts_receiver_handle_response(ipts, &rsp);
|
|
|
|
|
|
++ return 0;
|
|
+}
|
|
+}
|
|
-diff --git a/drivers/misc/ipts/receiver.h b/drivers/misc/ipts/receiver.h
|
|
|
|
|
|
+diff --git a/drivers/hid/ipts/resources.h b/drivers/hid/ipts/resources.h
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..7f075afa7ef8
|
|
|
|
|
|
+index 000000000000..6cbb24a8a054
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/receiver.h
|
|
|
|
-@@ -0,0 +1,16 @@
|
|
|
|
|
|
++++ b/drivers/hid/ipts/resources.h
|
|
|
|
+@@ -0,0 +1,39 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/*
|
|
+/*
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
+ *
|
|
+ *
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
+
|
|
+
|
|
-+#ifndef _IPTS_RECEIVER_H_
|
|
|
|
-+#define _IPTS_RECEIVER_H_
|
|
|
|
|
|
++#ifndef IPTS_RESOURCES_H
|
|
|
|
++#define IPTS_RESOURCES_H
|
|
+
|
|
+
|
|
-+#include <linux/mei_cl_bus.h>
|
|
|
|
|
|
++#include <linux/device.h>
|
|
|
|
++#include <linux/types.h>
|
|
|
|
++
|
|
|
|
++#include "spec-device.h"
|
|
|
|
++
|
|
|
|
++struct ipts_buffer {
|
|
|
|
++ u8 *address;
|
|
|
|
++ size_t size;
|
|
|
|
++
|
|
|
|
++ dma_addr_t dma_address;
|
|
|
|
++ struct device *device;
|
|
|
|
++};
|
|
+
|
|
+
|
|
-+void ipts_receiver_callback(struct mei_cl_device *cldev);
|
|
|
|
|
|
++struct ipts_resources {
|
|
|
|
++ struct ipts_buffer data[IPTS_BUFFERS];
|
|
|
|
++ struct ipts_buffer feedback[IPTS_BUFFERS];
|
|
+
|
|
+
|
|
-+#endif /* _IPTS_RECEIVER_H_ */
|
|
|
|
-diff --git a/drivers/misc/ipts/resources.c b/drivers/misc/ipts/resources.c
|
|
|
|
|
|
++ struct ipts_buffer doorbell;
|
|
|
|
++ struct ipts_buffer workqueue;
|
|
|
|
++ struct ipts_buffer hid2me;
|
|
|
|
++
|
|
|
|
++ struct ipts_buffer descriptor;
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++int ipts_resources_init(struct ipts_resources *res, struct device *dev, size_t ds, size_t fs);
|
|
|
|
++int ipts_resources_free(struct ipts_resources *res);
|
|
|
|
++
|
|
|
|
++#endif /* IPTS_RESOURCES_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/spec-data.h b/drivers/hid/ipts/spec-data.h
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..8e3a2409e438
|
|
|
|
|
|
+index 000000000000..e8dd98895a7e
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/resources.c
|
|
|
|
-@@ -0,0 +1,128 @@
|
|
|
|
-+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
++++ b/drivers/hid/ipts/spec-data.h
|
|
|
|
+@@ -0,0 +1,100 @@
|
|
|
|
++/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/*
|
|
+/*
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
+ *
|
|
+ *
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
+
|
|
+
|
|
-+#include <linux/dma-mapping.h>
|
|
|
|
|
|
++#ifndef IPTS_SPEC_DATA_H
|
|
|
|
++#define IPTS_SPEC_DATA_H
|
|
+
|
|
+
|
|
-+#include "context.h"
|
|
|
|
|
|
++#include <linux/build_bug.h>
|
|
|
|
++#include <linux/types.h>
|
|
+
|
|
+
|
|
-+void ipts_resources_free(struct ipts_context *ipts)
|
|
|
|
-+{
|
|
|
|
-+ int i;
|
|
|
|
-+ struct ipts_buffer_info *buffers;
|
|
|
|
|
|
++/**
|
|
|
|
++ * enum ipts_feedback_cmd_type - Commands that can be executed on the sensor through feedback.
|
|
|
|
++ */
|
|
|
|
++enum ipts_feedback_cmd_type {
|
|
|
|
++ IPTS_FEEDBACK_CMD_TYPE_NONE = 0,
|
|
|
|
++ IPTS_FEEDBACK_CMD_TYPE_SOFT_RESET = 1,
|
|
|
|
++ IPTS_FEEDBACK_CMD_TYPE_GOTO_ARMED = 2,
|
|
|
|
++ IPTS_FEEDBACK_CMD_TYPE_GOTO_SENSING = 3,
|
|
|
|
++ IPTS_FEEDBACK_CMD_TYPE_GOTO_SLEEP = 4,
|
|
|
|
++ IPTS_FEEDBACK_CMD_TYPE_GOTO_DOZE = 5,
|
|
|
|
++ IPTS_FEEDBACK_CMD_TYPE_HARD_RESET = 6,
|
|
|
|
++};
|
|
+
|
|
+
|
|
-+ u32 data_buffer_size = ipts->device_info.data_size;
|
|
|
|
-+ u32 feedback_buffer_size = ipts->device_info.feedback_size;
|
|
|
|
|
|
++/**
|
|
|
|
++ * enum ipts_feedback_data_type - Defines what data a feedback buffer contains.
|
|
|
|
++ * @IPTS_FEEDBACK_DATA_TYPE_VENDOR: The buffer contains vendor specific feedback.
|
|
|
|
++ * @IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES: The buffer contains a HID set features report.
|
|
|
|
++ * @IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES: The buffer contains a HID get features report.
|
|
|
|
++ * @IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT: The buffer contains a HID output report.
|
|
|
|
++ * @IPTS_FEEDBACK_DATA_TYPE_STORE_DATA: The buffer contains calibration data for the sensor.
|
|
|
|
++ */
|
|
|
|
++enum ipts_feedback_data_type {
|
|
|
|
++ IPTS_FEEDBACK_DATA_TYPE_VENDOR = 0,
|
|
|
|
++ IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES = 1,
|
|
|
|
++ IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES = 2,
|
|
|
|
++ IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT = 3,
|
|
|
|
++ IPTS_FEEDBACK_DATA_TYPE_STORE_DATA = 4,
|
|
|
|
++};
|
|
+
|
|
+
|
|
-+ buffers = ipts->data;
|
|
|
|
-+ for (i = 0; i < IPTS_BUFFERS; i++) {
|
|
|
|
-+ if (!buffers[i].address)
|
|
|
|
-+ continue;
|
|
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_feedback_header - Header that is prefixed to the data in a feedback buffer.
|
|
|
|
++ * @cmd_type: A command that should be executed on the sensor.
|
|
|
|
++ * @size: The size of the payload to be written.
|
|
|
|
++ * @buffer: The ID of the buffer that contains this feedback data.
|
|
|
|
++ * @protocol: The protocol version of the EDS.
|
|
|
|
++ * @data_type: The type of data that the buffer contains.
|
|
|
|
++ * @spi_offset: The offset at which to write the payload data to the sensor.
|
|
|
|
++ * @payload: Payload for the feedback command, or 0 if no payload is sent.
|
|
|
|
++ */
|
|
|
|
++struct ipts_feedback_header {
|
|
|
|
++ enum ipts_feedback_cmd_type cmd_type;
|
|
|
|
++ u32 size;
|
|
|
|
++ u32 buffer;
|
|
|
|
++ u32 protocol;
|
|
|
|
++ enum ipts_feedback_data_type data_type;
|
|
|
|
++ u32 spi_offset;
|
|
|
|
++ u8 reserved[40];
|
|
|
|
++ u8 payload[];
|
|
|
|
++} __packed;
|
|
+
|
|
+
|
|
-+ dma_free_coherent(ipts->dev, data_buffer_size,
|
|
|
|
-+ buffers[i].address, buffers[i].dma_address);
|
|
|
|
|
|
++static_assert(sizeof(struct ipts_feedback_header) == 64);
|
|
+
|
|
+
|
|
-+ buffers[i].address = NULL;
|
|
|
|
-+ buffers[i].dma_address = 0;
|
|
|
|
-+ }
|
|
|
|
|
|
++/**
|
|
|
|
++ * enum ipts_data_type - Defines what type of data a buffer contains.
|
|
|
|
++ * @IPTS_DATA_TYPE_FRAME: Raw data frame.
|
|
|
|
++ * @IPTS_DATA_TYPE_ERROR: Error data.
|
|
|
|
++ * @IPTS_DATA_TYPE_VENDOR: Vendor specific data.
|
|
|
|
++ * @IPTS_DATA_TYPE_HID: A HID report.
|
|
|
|
++ * @IPTS_DATA_TYPE_GET_FEATURES: The response to a GET_FEATURES HID2ME command.
|
|
|
|
++ */
|
|
|
|
++enum ipts_data_type {
|
|
|
|
++ IPTS_DATA_TYPE_FRAME = 0x00,
|
|
|
|
++ IPTS_DATA_TYPE_ERROR = 0x01,
|
|
|
|
++ IPTS_DATA_TYPE_VENDOR = 0x02,
|
|
|
|
++ IPTS_DATA_TYPE_HID = 0x03,
|
|
|
|
++ IPTS_DATA_TYPE_GET_FEATURES = 0x04,
|
|
|
|
++ IPTS_DATA_TYPE_DESCRIPTOR = 0x05,
|
|
|
|
++};
|
|
+
|
|
+
|
|
-+ buffers = ipts->feedback;
|
|
|
|
-+ for (i = 0; i < IPTS_BUFFERS; i++) {
|
|
|
|
-+ if (!buffers[i].address)
|
|
|
|
-+ continue;
|
|
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_data_header - Header that is prefixed to the data in a data buffer.
|
|
|
|
++ * @type: What data the buffer contains.
|
|
|
|
++ * @size: How much data the buffer contains.
|
|
|
|
++ * @buffer: Which buffer the data is in.
|
|
|
|
++ */
|
|
|
|
++struct ipts_data_header {
|
|
|
|
++ enum ipts_data_type type;
|
|
|
|
++ u32 size;
|
|
|
|
++ u32 buffer;
|
|
|
|
++ u8 reserved[52];
|
|
|
|
++ u8 data[];
|
|
|
|
++} __packed;
|
|
+
|
|
+
|
|
-+ dma_free_coherent(ipts->dev, feedback_buffer_size,
|
|
|
|
-+ buffers[i].address, buffers[i].dma_address);
|
|
|
|
|
|
++static_assert(sizeof(struct ipts_data_header) == 64);
|
|
+
|
|
+
|
|
-+ buffers[i].address = NULL;
|
|
|
|
-+ buffers[i].dma_address = 0;
|
|
|
|
-+ }
|
|
|
|
|
|
++#endif /* IPTS_SPEC_DATA_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/spec-device.h b/drivers/hid/ipts/spec-device.h
|
|
|
|
+new file mode 100644
|
|
|
|
+index 000000000000..93f673d981f7
|
|
|
|
+--- /dev/null
|
|
|
|
++++ b/drivers/hid/ipts/spec-device.h
|
|
|
|
+@@ -0,0 +1,285 @@
|
|
|
|
++/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
++/*
|
|
|
|
++ * Copyright (c) 2016 Intel Corporation
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
|
|
++ *
|
|
|
|
++ * Linux driver for Intel Precise Touch & Stylus
|
|
|
|
++ */
|
|
+
|
|
+
|
|
-+ if (ipts->doorbell.address) {
|
|
|
|
-+ dma_free_coherent(ipts->dev, sizeof(u32),
|
|
|
|
-+ ipts->doorbell.address,
|
|
|
|
-+ ipts->doorbell.dma_address);
|
|
|
|
|
|
++#ifndef IPTS_SPEC_DEVICE_H
|
|
|
|
++#define IPTS_SPEC_DEVICE_H
|
|
+
|
|
+
|
|
-+ ipts->doorbell.address = NULL;
|
|
|
|
-+ ipts->doorbell.dma_address = 0;
|
|
|
|
-+ }
|
|
|
|
|
|
++#include <linux/build_bug.h>
|
|
|
|
++#include <linux/types.h>
|
|
|
|
++
|
|
|
|
++/*
|
|
|
|
++ * The amount of buffers that IPTS can use for data transfer.
|
|
|
|
++ */
|
|
|
|
++#define IPTS_BUFFERS 16
|
|
+
|
|
+
|
|
-+ if (ipts->workqueue.address) {
|
|
|
|
-+ dma_free_coherent(ipts->dev, sizeof(u32),
|
|
|
|
-+ ipts->workqueue.address,
|
|
|
|
-+ ipts->workqueue.dma_address);
|
|
|
|
|
|
++/*
|
|
|
|
++ * The buffer ID that is used for HID2ME feedback
|
|
|
|
++ */
|
|
|
|
++#define IPTS_HID2ME_BUFFER IPTS_BUFFERS
|
|
+
|
|
+
|
|
-+ ipts->workqueue.address = NULL;
|
|
|
|
-+ ipts->workqueue.dma_address = 0;
|
|
|
|
-+ }
|
|
|
|
|
|
++/**
|
|
|
|
++ * enum ipts_command - Commands that can be sent to the IPTS hardware.
|
|
|
|
++ * @IPTS_CMD_GET_DEVICE_INFO: Retrieves vendor information from the device.
|
|
|
|
++ * @IPTS_CMD_SET_MODE: Changes the mode that the device will operate in.
|
|
|
|
++ * @IPTS_CMD_SET_MEM_WINDOW: Configures memory buffers for passing data between device and driver.
|
|
|
|
++ * @IPTS_CMD_QUIESCE_IO: Stops the data flow from the device to the driver.
|
|
|
|
++ * @IPTS_CMD_READY_FOR_DATA: Informs the device that the driver is ready to receive data.
|
|
|
|
++ * @IPTS_CMD_FEEDBACK: Informs the device that a buffer was processed and can be refilled.
|
|
|
|
++ * @IPTS_CMD_CLEAR_MEM_WINDOW: Stops the data flow and clears the buffer addresses on the device.
|
|
|
|
++ * @IPTS_CMD_RESET_SENSOR: Resets the sensor to its default state.
|
|
|
|
++ * @IPTS_CMD_GET_DESCRIPTOR: Retrieves the HID descriptor of the device.
|
|
|
|
++ */
|
|
|
|
++enum ipts_command_code {
|
|
|
|
++ IPTS_CMD_GET_DEVICE_INFO = 0x01,
|
|
|
|
++ IPTS_CMD_SET_MODE = 0x02,
|
|
|
|
++ IPTS_CMD_SET_MEM_WINDOW = 0x03,
|
|
|
|
++ IPTS_CMD_QUIESCE_IO = 0x04,
|
|
|
|
++ IPTS_CMD_READY_FOR_DATA = 0x05,
|
|
|
|
++ IPTS_CMD_FEEDBACK = 0x06,
|
|
|
|
++ IPTS_CMD_CLEAR_MEM_WINDOW = 0x07,
|
|
|
|
++ IPTS_CMD_RESET_SENSOR = 0x0B,
|
|
|
|
++ IPTS_CMD_GET_DESCRIPTOR = 0x0F,
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++/**
|
|
|
|
++ * enum ipts_status - Possible status codes returned by the IPTS device.
|
|
|
|
++ * @IPTS_STATUS_SUCCESS: Operation completed successfully.
|
|
|
|
++ * @IPTS_STATUS_INVALID_PARAMS: Command contained an invalid payload.
|
|
|
|
++ * @IPTS_STATUS_ACCESS_DENIED: ME could not validate a buffer address.
|
|
|
|
++ * @IPTS_STATUS_CMD_SIZE_ERROR: Command contains an invalid payload.
|
|
|
|
++ * @IPTS_STATUS_NOT_READY: Buffer addresses have not been set.
|
|
|
|
++ * @IPTS_STATUS_REQUEST_OUTSTANDING: There is an outstanding command of the same type.
|
|
|
|
++ * @IPTS_STATUS_NO_SENSOR_FOUND: No sensor could be found.
|
|
|
|
++ * @IPTS_STATUS_OUT_OF_MEMORY: Not enough free memory for requested operation.
|
|
|
|
++ * @IPTS_STATUS_INTERNAL_ERROR: An unexpected error occurred.
|
|
|
|
++ * @IPTS_STATUS_SENSOR_DISABLED: The sensor has been disabled and must be reinitialized.
|
|
|
|
++ * @IPTS_STATUS_COMPAT_CHECK_FAIL: Compatibility revision check between sensor and ME failed.
|
|
|
|
++ * The host can ignore this error and attempt to continue.
|
|
|
|
++ * @IPTS_STATUS_SENSOR_EXPECTED_RESET: The sensor went through a reset initiated by the driver.
|
|
|
|
++ * @IPTS_STATUS_SENSOR_UNEXPECTED_RESET: The sensor went through an unexpected reset.
|
|
|
|
++ * @IPTS_STATUS_RESET_FAILED: Requested sensor reset failed to complete.
|
|
|
|
++ * @IPTS_STATUS_TIMEOUT: The operation timed out.
|
|
|
|
++ * @IPTS_STATUS_TEST_MODE_FAIL: Test mode pattern did not match expected values.
|
|
|
|
++ * @IPTS_STATUS_SENSOR_FAIL_FATAL: The sensor reported an error during reset sequence.
|
|
|
|
++ * Further progress is not possible.
|
|
|
|
++ * @IPTS_STATUS_SENSOR_FAIL_NONFATAL: The sensor reported an error during reset sequence.
|
|
|
|
++ * The driver can attempt to continue.
|
|
|
|
++ * @IPTS_STATUS_INVALID_DEVICE_CAPS: The device reported invalid capabilities.
|
|
|
|
++ * @IPTS_STATUS_QUIESCE_IO_IN_PROGRESS: Command cannot be completed until Quiesce IO is done.
|
|
|
|
++ */
|
|
|
|
++enum ipts_status {
|
|
|
|
++ IPTS_STATUS_SUCCESS = 0x00,
|
|
|
|
++ IPTS_STATUS_INVALID_PARAMS = 0x01,
|
|
|
|
++ IPTS_STATUS_ACCESS_DENIED = 0x02,
|
|
|
|
++ IPTS_STATUS_CMD_SIZE_ERROR = 0x03,
|
|
|
|
++ IPTS_STATUS_NOT_READY = 0x04,
|
|
|
|
++ IPTS_STATUS_REQUEST_OUTSTANDING = 0x05,
|
|
|
|
++ IPTS_STATUS_NO_SENSOR_FOUND = 0x06,
|
|
|
|
++ IPTS_STATUS_OUT_OF_MEMORY = 0x07,
|
|
|
|
++ IPTS_STATUS_INTERNAL_ERROR = 0x08,
|
|
|
|
++ IPTS_STATUS_SENSOR_DISABLED = 0x09,
|
|
|
|
++ IPTS_STATUS_COMPAT_CHECK_FAIL = 0x0A,
|
|
|
|
++ IPTS_STATUS_SENSOR_EXPECTED_RESET = 0x0B,
|
|
|
|
++ IPTS_STATUS_SENSOR_UNEXPECTED_RESET = 0x0C,
|
|
|
|
++ IPTS_STATUS_RESET_FAILED = 0x0D,
|
|
|
|
++ IPTS_STATUS_TIMEOUT = 0x0E,
|
|
|
|
++ IPTS_STATUS_TEST_MODE_FAIL = 0x0F,
|
|
|
|
++ IPTS_STATUS_SENSOR_FAIL_FATAL = 0x10,
|
|
|
|
++ IPTS_STATUS_SENSOR_FAIL_NONFATAL = 0x11,
|
|
|
|
++ IPTS_STATUS_INVALID_DEVICE_CAPS = 0x12,
|
|
|
|
++ IPTS_STATUS_QUIESCE_IO_IN_PROGRESS = 0x13,
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_command - Message that is sent to the device for calling a command.
|
|
|
|
++ * @cmd: The command that will be called.
|
|
|
|
++ * @payload: Payload containing parameters for the called command.
|
|
|
|
++ */
|
|
|
|
++struct ipts_command {
|
|
|
|
++ enum ipts_command_code cmd;
|
|
|
|
++ u8 payload[320];
|
|
|
|
++} __packed;
|
|
|
|
++
|
|
|
|
++static_assert(sizeof(struct ipts_command) == 324);
|
|
|
|
++
|
|
|
|
++/**
|
|
|
|
++ * enum ipts_mode - Configures what data the device produces and how its sent.
|
|
|
|
++ * @IPTS_MODE_EVENT: The device will send an event once a buffer was filled.
|
|
|
|
++ * Older devices will return singletouch data in this mode.
|
|
|
|
++ * @IPTS_MODE_DOORBELL: The device will notify the driver by incrementing the doorbell value.
|
|
|
|
++ * Older devices will return multitouch data in this mode.
|
|
|
|
++ */
|
|
|
|
++enum ipts_mode {
|
|
|
|
++ IPTS_MODE_EVENT = 0x00,
|
|
|
|
++ IPTS_MODE_DOORBELL = 0x01,
|
|
|
|
++};
|
|
|
|
++
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_set_mode - Payload for the SET_MODE command.
|
|
|
|
++ * @mode: Changes the mode that IPTS will operate in.
|
|
|
|
++ */
|
|
|
|
++struct ipts_set_mode {
|
|
|
|
++ enum ipts_mode mode;
|
|
|
|
++ u8 reserved[12];
|
|
|
|
++} __packed;
|
|
|
|
++
|
|
|
|
++static_assert(sizeof(struct ipts_set_mode) == 16);
|
|
|
|
++
|
|
|
|
++#define IPTS_WORKQUEUE_SIZE 8192
|
|
|
|
++#define IPTS_WORKQUEUE_ITEM_SIZE 16
|
|
+
|
|
+
|
|
-+ if (ipts->host2me.address) {
|
|
|
|
-+ dma_free_coherent(ipts->dev, feedback_buffer_size,
|
|
|
|
-+ ipts->host2me.address,
|
|
|
|
-+ ipts->host2me.dma_address);
|
|
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_mem_window - Payload for the SET_MEM_WINDOW command.
|
|
|
|
++ * @data_addr_lower: Lower 32 bits of the data buffer addresses.
|
|
|
|
++ * @data_addr_upper: Upper 32 bits of the data buffer addresses.
|
|
|
|
++ * @workqueue_addr_lower: Lower 32 bits of the workqueue buffer address.
|
|
|
|
++ * @workqueue_addr_upper: Upper 32 bits of the workqueue buffer address.
|
|
|
|
++ * @doorbell_addr_lower: Lower 32 bits of the doorbell buffer address.
|
|
|
|
++ * @doorbell_addr_upper: Upper 32 bits of the doorbell buffer address.
|
|
|
|
++ * @feedbackaddr_lower: Lower 32 bits of the feedback buffer addresses.
|
|
|
|
++ * @feedbackaddr_upper: Upper 32 bits of the feedback buffer addresses.
|
|
|
|
++ * @hid2me_addr_lower: Lower 32 bits of the hid2me buffer address.
|
|
|
|
++ * @hid2me_addr_upper: Upper 32 bits of the hid2me buffer address.
|
|
|
|
++ * @hid2me_size: Size of the hid2me feedback buffer.
|
|
|
|
++ * @workqueue_item_size: Magic value. Must be 16.
|
|
|
|
++ * @workqueue_size: Magic value. Must be 8192.
|
|
|
|
++ *
|
|
|
|
++ * The workqueue related items in this struct are required for using
|
|
|
|
++ * GuC submission with binary processing firmware. Since this driver does
|
|
|
|
++ * not use GuC submission and instead exports raw data to userspace, these
|
|
|
|
++ * items are not actually used, but they need to be allocated and passed
|
|
|
|
++ * to the device, otherwise initialization will fail.
|
|
|
|
++ */
|
|
|
|
++struct ipts_mem_window {
|
|
|
|
++ u32 data_addr_lower[IPTS_BUFFERS];
|
|
|
|
++ u32 data_addr_upper[IPTS_BUFFERS];
|
|
|
|
++ u32 workqueue_addr_lower;
|
|
|
|
++ u32 workqueue_addr_upper;
|
|
|
|
++ u32 doorbell_addr_lower;
|
|
|
|
++ u32 doorbell_addr_upper;
|
|
|
|
++ u32 feedback_addr_lower[IPTS_BUFFERS];
|
|
|
|
++ u32 feedback_addr_upper[IPTS_BUFFERS];
|
|
|
|
++ u32 hid2me_addr_lower;
|
|
|
|
++ u32 hid2me_addr_upper;
|
|
|
|
++ u32 hid2me_size;
|
|
|
|
++ u8 reserved1;
|
|
|
|
++ u8 workqueue_item_size;
|
|
|
|
++ u16 workqueue_size;
|
|
|
|
++ u8 reserved[32];
|
|
|
|
++} __packed;
|
|
+
|
|
+
|
|
-+ ipts->host2me.address = NULL;
|
|
|
|
-+ ipts->host2me.dma_address = 0;
|
|
|
|
-+ }
|
|
|
|
-+}
|
|
|
|
|
|
++static_assert(sizeof(struct ipts_mem_window) == 320);
|
|
+
|
|
+
|
|
-+int ipts_resources_alloc(struct ipts_context *ipts)
|
|
|
|
-+{
|
|
|
|
-+ int i;
|
|
|
|
-+ struct ipts_buffer_info *buffers;
|
|
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_quiesce_io - Payload for the QUIESCE_IO command.
|
|
|
|
++ */
|
|
|
|
++struct ipts_quiesce_io {
|
|
|
|
++ u8 reserved[12];
|
|
|
|
++} __packed;
|
|
+
|
|
+
|
|
-+ u32 data_buffer_size = ipts->device_info.data_size;
|
|
|
|
-+ u32 feedback_buffer_size = ipts->device_info.feedback_size;
|
|
|
|
|
|
++static_assert(sizeof(struct ipts_quiesce_io) == 12);
|
|
+
|
|
+
|
|
-+ buffers = ipts->data;
|
|
|
|
-+ for (i = 0; i < IPTS_BUFFERS; i++) {
|
|
|
|
-+ buffers[i].address =
|
|
|
|
-+ dma_alloc_coherent(ipts->dev, data_buffer_size,
|
|
|
|
-+ &buffers[i].dma_address, GFP_KERNEL);
|
|
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_feedback - Payload for the FEEDBACK command.
|
|
|
|
++ * @buffer: The buffer that the device should refill.
|
|
|
|
++ */
|
|
|
|
++struct ipts_feedback {
|
|
|
|
++ u32 buffer;
|
|
|
|
++ u8 reserved[12];
|
|
|
|
++} __packed;
|
|
+
|
|
+
|
|
-+ if (!buffers[i].address)
|
|
|
|
-+ goto release_resources;
|
|
|
|
-+ }
|
|
|
|
|
|
++static_assert(sizeof(struct ipts_feedback) == 16);
|
|
+
|
|
+
|
|
-+ buffers = ipts->feedback;
|
|
|
|
-+ for (i = 0; i < IPTS_BUFFERS; i++) {
|
|
|
|
-+ buffers[i].address =
|
|
|
|
-+ dma_alloc_coherent(ipts->dev, feedback_buffer_size,
|
|
|
|
-+ &buffers[i].dma_address, GFP_KERNEL);
|
|
|
|
|
|
++/**
|
|
|
|
++ * enum ipts_reset_type - Possible ways of resetting the device.
|
|
|
|
++ * @IPTS_RESET_TYPE_HARD: Perform hardware reset using GPIO pin.
|
|
|
|
++ * @IPTS_RESET_TYPE_SOFT: Perform software reset using SPI command.
|
|
|
|
++ */
|
|
|
|
++enum ipts_reset_type {
|
|
|
|
++ IPTS_RESET_TYPE_HARD = 0x00,
|
|
|
|
++ IPTS_RESET_TYPE_SOFT = 0x01,
|
|
|
|
++};
|
|
+
|
|
+
|
|
-+ if (!buffers[i].address)
|
|
|
|
-+ goto release_resources;
|
|
|
|
-+ }
|
|
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_reset - Payload for the RESET_SENSOR command.
|
|
|
|
++ * @type: How the device should get reset.
|
|
|
|
++ */
|
|
|
|
++struct ipts_reset_sensor {
|
|
|
|
++ enum ipts_reset_type type;
|
|
|
|
++ u8 reserved[4];
|
|
|
|
++} __packed;
|
|
+
|
|
+
|
|
-+ ipts->doorbell.address =
|
|
|
|
-+ dma_alloc_coherent(ipts->dev, sizeof(u32),
|
|
|
|
-+ &ipts->doorbell.dma_address, GFP_KERNEL);
|
|
|
|
|
|
++static_assert(sizeof(struct ipts_reset_sensor) == 8);
|
|
+
|
|
+
|
|
-+ if (!ipts->doorbell.address)
|
|
|
|
-+ goto release_resources;
|
|
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_get_descriptor - Payload for the GET_DESCRIPTOR command.
|
|
|
|
++ * @addr_lower: The lower 32 bits of the descriptor buffer address.
|
|
|
|
++ * @addr_upper: The upper 32 bits of the descriptor buffer address.
|
|
|
|
++ * @magic: A magic value. Must be 8.
|
|
|
|
++ */
|
|
|
|
++struct ipts_get_descriptor {
|
|
|
|
++ u32 addr_lower;
|
|
|
|
++ u32 addr_upper;
|
|
|
|
++ u32 magic;
|
|
|
|
++ u8 reserved[12];
|
|
|
|
++} __packed;
|
|
+
|
|
+
|
|
-+ ipts->workqueue.address =
|
|
|
|
-+ dma_alloc_coherent(ipts->dev, sizeof(u32),
|
|
|
|
-+ &ipts->workqueue.dma_address, GFP_KERNEL);
|
|
|
|
|
|
++static_assert(sizeof(struct ipts_get_descriptor) == 24);
|
|
+
|
|
+
|
|
-+ if (!ipts->workqueue.address)
|
|
|
|
-+ goto release_resources;
|
|
|
|
|
|
++/*
|
|
|
|
++ * The type of a response is indicated by a
|
|
|
|
++ * command code, with the most significant bit flipped to 1.
|
|
|
|
++ */
|
|
|
|
++#define IPTS_RSP_BIT BIT(31)
|
|
+
|
|
+
|
|
-+ ipts->host2me.address =
|
|
|
|
-+ dma_alloc_coherent(ipts->dev, feedback_buffer_size,
|
|
|
|
-+ &ipts->host2me.dma_address, GFP_KERNEL);
|
|
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_response - Data returned from the device in response to a command.
|
|
|
|
++ * @cmd: The command that this response answers (IPTS_RSP_BIT will be 1).
|
|
|
|
++ * @status: The return code of the command.
|
|
|
|
++ * @payload: The data that was produced by the command.
|
|
|
|
++ */
|
|
|
|
++struct ipts_response {
|
|
|
|
++ enum ipts_command_code cmd;
|
|
|
|
++ enum ipts_status status;
|
|
|
|
++ u8 payload[80];
|
|
|
|
++} __packed;
|
|
+
|
|
+
|
|
-+ if (!ipts->workqueue.address)
|
|
|
|
-+ goto release_resources;
|
|
|
|
|
|
++static_assert(sizeof(struct ipts_response) == 88);
|
|
+
|
|
+
|
|
-+ return 0;
|
|
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_device_info - Vendor information of the IPTS device.
|
|
|
|
++ * @vendor: Vendor ID of this device.
|
|
|
|
++ * @product: Product ID of this device.
|
|
|
|
++ * @hw_version: Hardware revision of this device.
|
|
|
|
++ * @fw_version: Firmware revision of this device.
|
|
|
|
++ * @data_size: Requested size for a data buffer.
|
|
|
|
++ * @feedback_size: Requested size for a feedback buffer.
|
|
|
|
++ * @mode: Mode that the device currently operates in.
|
|
|
|
++ * @max_contacts: Maximum amount of concurrent touches the sensor can process.
|
|
|
|
++ */
|
|
|
|
++struct ipts_device_info {
|
|
|
|
++ u16 vendor;
|
|
|
|
++ u16 product;
|
|
|
|
++ u32 hw_version;
|
|
|
|
++ u32 fw_version;
|
|
|
|
++ u32 data_size;
|
|
|
|
++ u32 feedback_size;
|
|
|
|
++ enum ipts_mode mode;
|
|
|
|
++ u8 max_contacts;
|
|
|
|
++ u8 reserved1[3];
|
|
|
|
++ u8 sensor_min_eds;
|
|
|
|
++ u8 sensor_maj_eds;
|
|
|
|
++ u8 me_min_eds;
|
|
|
|
++ u8 me_maj_eds;
|
|
|
|
++ u8 intf_eds;
|
|
|
|
++ u8 reserved2[11];
|
|
|
|
++} __packed;
|
|
+
|
|
+
|
|
-+release_resources:
|
|
|
|
|
|
++static_assert(sizeof(struct ipts_device_info) == 44);
|
|
+
|
|
+
|
|
-+ ipts_resources_free(ipts);
|
|
|
|
-+ return -ENOMEM;
|
|
|
|
-+}
|
|
|
|
-diff --git a/drivers/misc/ipts/resources.h b/drivers/misc/ipts/resources.h
|
|
|
|
|
|
++#endif /* IPTS_SPEC_DEVICE_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/spec-hid.h b/drivers/hid/ipts/spec-hid.h
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..fdac0eee9156
|
|
|
|
|
|
+index 000000000000..ea70f29ff00c
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/resources.h
|
|
|
|
-@@ -0,0 +1,17 @@
|
|
|
|
|
|
++++ b/drivers/hid/ipts/spec-hid.h
|
|
|
|
+@@ -0,0 +1,35 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/*
|
|
+/*
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
|
|
++ * Copyright (c) 2020-2023 Dorian Stoll
|
|
+ *
|
|
+ *
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
+
|
|
+
|
|
-+#ifndef _IPTS_RESOURCES_H_
|
|
|
|
-+#define _IPTS_RESOURCES_H_
|
|
|
|
|
|
++#ifndef IPTS_SPEC_HID_H
|
|
|
|
++#define IPTS_SPEC_HID_H
|
|
+
|
|
+
|
|
-+#include "context.h"
|
|
|
|
|
|
++#include <linux/build_bug.h>
|
|
|
|
++#include <linux/types.h>
|
|
|
|
++
|
|
|
|
++/*
|
|
|
|
++ * Made-up type for passing raw IPTS data in a HID report.
|
|
|
|
++ */
|
|
|
|
++#define IPTS_HID_FRAME_TYPE_RAW 0xEE
|
|
|
|
++
|
|
|
|
++/**
|
|
|
|
++ * struct ipts_hid_frame - Header that is prefixed to raw IPTS data wrapped in a HID report.
|
|
|
|
++ * @size: Size of the data inside the report, including this header.
|
|
|
|
++ * @type: What type of data does this report contain.
|
|
|
|
++ */
|
|
|
|
++struct ipts_hid_header {
|
|
|
|
++ u32 size;
|
|
|
|
++ u8 reserved1;
|
|
|
|
++ u8 type;
|
|
|
|
++ u8 reserved2;
|
|
|
|
++ u8 data[];
|
|
|
|
++} __packed;
|
|
+
|
|
+
|
|
-+int ipts_resources_alloc(struct ipts_context *ipts);
|
|
|
|
-+void ipts_resources_free(struct ipts_context *ipts);
|
|
|
|
|
|
++static_assert(sizeof(struct ipts_hid_header) == 7);
|
|
+
|
|
+
|
|
-+#endif /* _IPTS_RESOURCES_H_ */
|
|
|
|
-diff --git a/drivers/misc/ipts/uapi.c b/drivers/misc/ipts/uapi.c
|
|
|
|
|
|
++#endif /* IPTS_SPEC_HID_H */
|
|
|
|
+diff --git a/drivers/hid/ipts/thread.c b/drivers/hid/ipts/thread.c
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..598f0710ad64
|
|
|
|
|
|
+index 000000000000..8b46f775c107
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/uapi.c
|
|
|
|
-@@ -0,0 +1,208 @@
|
|
|
|
|
|
++++ b/drivers/hid/ipts/thread.c
|
|
|
|
+@@ -0,0 +1,85 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+/*
|
|
+/*
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
|
|
++ * Copyright (c) 2023 Dorian Stoll
|
|
+ *
|
|
+ *
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
+
|
|
+
|
|
-+#include <linux/cdev.h>
|
|
|
|
-+#include <linux/delay.h>
|
|
|
|
-+#include <linux/device.h>
|
|
|
|
-+#include <linux/fs.h>
|
|
|
|
-+#include <linux/types.h>
|
|
|
|
-+#include <linux/uaccess.h>
|
|
|
|
-+
|
|
|
|
-+#include "context.h"
|
|
|
|
-+#include "control.h"
|
|
|
|
-+#include "protocol.h"
|
|
|
|
-+#include "uapi.h"
|
|
|
|
|
|
++#include <linux/completion.h>
|
|
|
|
++#include <linux/err.h>
|
|
|
|
++#include <linux/kthread.h>
|
|
|
|
++#include <linux/mutex.h>
|
|
+
|
|
+
|
|
-+struct ipts_uapi uapi;
|
|
|
|
|
|
++#include "thread.h"
|
|
+
|
|
+
|
|
-+static ssize_t ipts_uapi_read(struct file *file, char __user *buf, size_t count,
|
|
|
|
-+ loff_t *offset)
|
|
|
|
|
|
++bool ipts_thread_should_stop(struct ipts_thread *thread)
|
|
+{
|
|
+{
|
|
-+ int buffer;
|
|
|
|
-+ int maxbytes;
|
|
|
|
-+ struct ipts_context *ipts = uapi.ipts;
|
|
|
|
-+
|
|
|
|
-+ buffer = MINOR(file->f_path.dentry->d_inode->i_rdev);
|
|
|
|
-+
|
|
|
|
-+ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED)
|
|
|
|
-+ return -ENODEV;
|
|
|
|
-+
|
|
|
|
-+ maxbytes = ipts->device_info.data_size - *offset;
|
|
|
|
-+ if (maxbytes <= 0 || count > maxbytes)
|
|
|
|
-+ return -EINVAL;
|
|
|
|
-+
|
|
|
|
-+ if (copy_to_user(buf, ipts->data[buffer].address + *offset, count))
|
|
|
|
-+ return -EFAULT;
|
|
|
|
|
|
++ if (!thread)
|
|
|
|
++ return false;
|
|
+
|
|
+
|
|
-+ return count;
|
|
|
|
|
|
++ return READ_ONCE(thread->should_stop);
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+static long ipts_uapi_ioctl_get_device_ready(struct ipts_context *ipts,
|
|
|
|
-+ unsigned long arg)
|
|
|
|
|
|
++static int ipts_thread_runner(void *data)
|
|
+{
|
|
+{
|
|
-+ void __user *buffer = (void __user *)arg;
|
|
|
|
-+ u8 ready = 0;
|
|
|
|
|
|
++ int ret = 0;
|
|
|
|
++ struct ipts_thread *thread = data;
|
|
+
|
|
+
|
|
-+ if (ipts)
|
|
|
|
-+ ready = ipts->status == IPTS_HOST_STATUS_STARTED;
|
|
|
|
-+
|
|
|
|
-+ if (copy_to_user(buffer, &ready, sizeof(u8)))
|
|
|
|
|
|
++ if (!thread)
|
|
+ return -EFAULT;
|
|
+ return -EFAULT;
|
|
+
|
|
+
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static long ipts_uapi_ioctl_get_device_info(struct ipts_context *ipts,
|
|
|
|
-+ unsigned long arg)
|
|
|
|
-+{
|
|
|
|
-+ struct ipts_device_info info;
|
|
|
|
-+ void __user *buffer = (void __user *)arg;
|
|
|
|
-+
|
|
|
|
-+ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED)
|
|
|
|
-+ return -ENODEV;
|
|
|
|
-+
|
|
|
|
-+ info.vendor = ipts->device_info.vendor_id;
|
|
|
|
-+ info.product = ipts->device_info.device_id;
|
|
|
|
-+ info.version = ipts->device_info.fw_rev;
|
|
|
|
-+ info.buffer_size = ipts->device_info.data_size;
|
|
|
|
-+ info.max_contacts = ipts->device_info.max_contacts;
|
|
|
|
-+
|
|
|
|
-+ if (copy_to_user(buffer, &info, sizeof(struct ipts_device_info)))
|
|
|
|
|
|
++ if (!thread->threadfn)
|
|
+ return -EFAULT;
|
|
+ return -EFAULT;
|
|
+
|
|
+
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static long ipts_uapi_ioctl_get_doorbell(struct ipts_context *ipts,
|
|
|
|
-+ unsigned long arg)
|
|
|
|
-+{
|
|
|
|
-+ void __user *buffer = (void __user *)arg;
|
|
|
|
-+
|
|
|
|
-+ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED)
|
|
|
|
-+ return -ENODEV;
|
|
|
|
-+
|
|
|
|
-+ if (copy_to_user(buffer, ipts->doorbell.address, sizeof(u32)))
|
|
|
|
-+ return -EFAULT;
|
|
|
|
|
|
++ ret = thread->threadfn(thread);
|
|
|
|
++ complete_all(&thread->done);
|
|
+
|
|
+
|
|
-+ return 0;
|
|
|
|
|
|
++ return ret;
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+static long ipts_uapi_ioctl_send_feedback(struct ipts_context *ipts,
|
|
|
|
-+ struct file *file)
|
|
|
|
|
|
++int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread),
|
|
|
|
++ void *data, const char *name)
|
|
+{
|
|
+{
|
|
-+ int ret;
|
|
|
|
-+ u32 buffer;
|
|
|
|
-+
|
|
|
|
-+ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED)
|
|
|
|
-+ return -ENODEV;
|
|
|
|
-+
|
|
|
|
-+ buffer = MINOR(file->f_path.dentry->d_inode->i_rdev);
|
|
|
|
-+
|
|
|
|
-+ ret = ipts_control_send_feedback(ipts, buffer);
|
|
|
|
-+ if (ret)
|
|
|
|
|
|
++ if (!thread)
|
|
+ return -EFAULT;
|
|
+ return -EFAULT;
|
|
+
|
|
+
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static long ipts_uapi_ioctl_send_reset(struct ipts_context *ipts)
|
|
|
|
-+{
|
|
|
|
-+ int ret;
|
|
|
|
-+ struct ipts_reset_sensor_cmd cmd;
|
|
|
|
-+
|
|
|
|
-+ if (!ipts || ipts->status != IPTS_HOST_STATUS_STARTED)
|
|
|
|
-+ return -ENODEV;
|
|
|
|
-+
|
|
|
|
-+ memset(&cmd, 0, sizeof(struct ipts_reset_sensor_cmd));
|
|
|
|
-+ cmd.type = IPTS_RESET_TYPE_SOFT;
|
|
|
|
-+
|
|
|
|
-+ ret = ipts_control_send(ipts, IPTS_CMD_RESET_SENSOR, &cmd,
|
|
|
|
-+ sizeof(struct ipts_reset_sensor_cmd));
|
|
|
|
-+
|
|
|
|
-+ if (ret)
|
|
|
|
|
|
++ if (!threadfn)
|
|
+ return -EFAULT;
|
|
+ return -EFAULT;
|
|
+
|
|
+
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static long ipts_uapi_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
-+ unsigned long arg)
|
|
|
|
-+{
|
|
|
|
-+ struct ipts_context *ipts = uapi.ipts;
|
|
|
|
-+
|
|
|
|
-+ switch (cmd) {
|
|
|
|
-+ case IPTS_IOCTL_GET_DEVICE_READY:
|
|
|
|
-+ return ipts_uapi_ioctl_get_device_ready(ipts, arg);
|
|
|
|
-+ case IPTS_IOCTL_GET_DEVICE_INFO:
|
|
|
|
-+ return ipts_uapi_ioctl_get_device_info(ipts, arg);
|
|
|
|
-+ case IPTS_IOCTL_GET_DOORBELL:
|
|
|
|
-+ return ipts_uapi_ioctl_get_doorbell(ipts, arg);
|
|
|
|
-+ case IPTS_IOCTL_SEND_FEEDBACK:
|
|
|
|
-+ return ipts_uapi_ioctl_send_feedback(ipts, file);
|
|
|
|
-+ case IPTS_IOCTL_SEND_RESET:
|
|
|
|
-+ return ipts_uapi_ioctl_send_reset(ipts);
|
|
|
|
-+ default:
|
|
|
|
-+ return -ENOTTY;
|
|
|
|
-+ }
|
|
|
|
-+}
|
|
|
|
-+
|
|
|
|
-+static const struct file_operations ipts_uapi_fops = {
|
|
|
|
-+ .owner = THIS_MODULE,
|
|
|
|
-+ .read = ipts_uapi_read,
|
|
|
|
-+ .unlocked_ioctl = ipts_uapi_ioctl,
|
|
|
|
-+#ifdef CONFIG_COMPAT
|
|
|
|
-+ .compat_ioctl = ipts_uapi_ioctl,
|
|
|
|
-+#endif
|
|
|
|
-+};
|
|
|
|
|
|
++ init_completion(&thread->done);
|
|
+
|
|
+
|
|
-+void ipts_uapi_link(struct ipts_context *ipts)
|
|
|
|
-+{
|
|
|
|
-+ uapi.ipts = ipts;
|
|
|
|
-+}
|
|
|
|
|
|
++ thread->data = data;
|
|
|
|
++ thread->should_stop = false;
|
|
|
|
++ thread->threadfn = threadfn;
|
|
+
|
|
+
|
|
-+void ipts_uapi_unlink(void)
|
|
|
|
-+{
|
|
|
|
-+ uapi.ipts = NULL;
|
|
|
|
|
|
++ thread->thread = kthread_run(ipts_thread_runner, thread, name);
|
|
|
|
++ return PTR_ERR_OR_ZERO(thread->thread);
|
|
+}
|
|
+}
|
|
+
|
|
+
|
|
-+int ipts_uapi_init(void)
|
|
|
|
|
|
++int ipts_thread_stop(struct ipts_thread *thread)
|
|
+{
|
|
+{
|
|
-+ int i, major;
|
|
|
|
-+
|
|
|
|
-+ alloc_chrdev_region(&uapi.dev, 0, IPTS_BUFFERS, "ipts");
|
|
|
|
-+ uapi.class = class_create(THIS_MODULE, "ipts");
|
|
|
|
-+
|
|
|
|
-+ major = MAJOR(uapi.dev);
|
|
|
|
-+
|
|
|
|
-+ cdev_init(&uapi.cdev, &ipts_uapi_fops);
|
|
|
|
-+ uapi.cdev.owner = THIS_MODULE;
|
|
|
|
-+ cdev_add(&uapi.cdev, MKDEV(major, 0), IPTS_BUFFERS);
|
|
|
|
|
|
++ int ret = 0;
|
|
+
|
|
+
|
|
-+ for (i = 0; i < IPTS_BUFFERS; i++) {
|
|
|
|
-+ device_create(uapi.class, NULL, MKDEV(major, i), NULL,
|
|
|
|
-+ "ipts/%d", i);
|
|
|
|
-+ }
|
|
|
|
|
|
++ if (!thread)
|
|
|
|
++ return -EFAULT;
|
|
+
|
|
+
|
|
-+ return 0;
|
|
|
|
-+}
|
|
|
|
|
|
++ if (!thread->thread)
|
|
|
|
++ return 0;
|
|
+
|
|
+
|
|
-+void ipts_uapi_free(void)
|
|
|
|
-+{
|
|
|
|
-+ int i;
|
|
|
|
-+ int major;
|
|
|
|
|
|
++ WRITE_ONCE(thread->should_stop, true);
|
|
+
|
|
+
|
|
-+ major = MAJOR(uapi.dev);
|
|
|
|
|
|
++ /*
|
|
|
|
++ * Make sure that the write has gone through before waiting.
|
|
|
|
++ */
|
|
|
|
++ wmb();
|
|
+
|
|
+
|
|
-+ for (i = 0; i < IPTS_BUFFERS; i++)
|
|
|
|
-+ device_destroy(uapi.class, MKDEV(major, i));
|
|
|
|
|
|
++ wait_for_completion(&thread->done);
|
|
|
|
++ ret = kthread_stop(thread->thread);
|
|
+
|
|
+
|
|
-+ cdev_del(&uapi.cdev);
|
|
|
|
|
|
++ thread->thread = NULL;
|
|
|
|
++ thread->data = NULL;
|
|
|
|
++ thread->threadfn = NULL;
|
|
+
|
|
+
|
|
-+ unregister_chrdev_region(MKDEV(major, 0), MINORMASK);
|
|
|
|
-+ class_destroy(uapi.class);
|
|
|
|
|
|
++ return ret;
|
|
+}
|
|
+}
|
|
-diff --git a/drivers/misc/ipts/uapi.h b/drivers/misc/ipts/uapi.h
|
|
|
|
|
|
+diff --git a/drivers/hid/ipts/thread.h b/drivers/hid/ipts/thread.h
|
|
new file mode 100644
|
|
new file mode 100644
|
|
-index 000000000000..53fb86a88f97
|
|
|
|
|
|
+index 000000000000..a314843599fc
|
|
--- /dev/null
|
|
--- /dev/null
|
|
-+++ b/drivers/misc/ipts/uapi.h
|
|
|
|
-@@ -0,0 +1,47 @@
|
|
|
|
|
|
++++ b/drivers/hid/ipts/thread.h
|
|
|
|
+@@ -0,0 +1,60 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/*
|
|
+/*
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
+ * Copyright (c) 2016 Intel Corporation
|
|
-+ * Copyright (c) 2020 Dorian Stoll
|
|
|
|
|
|
++ * Copyright (c) 2023 Dorian Stoll
|
|
+ *
|
|
+ *
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ * Linux driver for Intel Precise Touch & Stylus
|
|
+ */
|
|
+ */
|
|
+
|
|
+
|
|
-+#ifndef _IPTS_UAPI_H_
|
|
|
|
-+#define _IPTS_UAPI_H_
|
|
|
|
-+
|
|
|
|
-+#include <linux/types.h>
|
|
|
|
|
|
++#ifndef IPTS_THREAD_H
|
|
|
|
++#define IPTS_THREAD_H
|
|
+
|
|
+
|
|
-+#include "context.h"
|
|
|
|
|
|
++#include <linux/completion.h>
|
|
|
|
++#include <linux/mutex.h>
|
|
|
|
++#include <linux/sched.h>
|
|
+
|
|
+
|
|
-+struct ipts_uapi {
|
|
|
|
-+ dev_t dev;
|
|
|
|
-+ struct class *class;
|
|
|
|
-+ struct cdev cdev;
|
|
|
|
|
|
++/*
|
|
|
|
++ * This wrapper over kthread is necessary, because calling kthread_stop makes it impossible
|
|
|
|
++ * to issue MEI commands from that thread while it shuts itself down. By using a custom
|
|
|
|
++ * boolean variable and a completion object, we can call kthread_stop only when the thread
|
|
|
|
++ * already finished all of its work and has returned.
|
|
|
|
++ */
|
|
|
|
++struct ipts_thread {
|
|
|
|
++ struct task_struct *thread;
|
|
+
|
|
+
|
|
-+ struct ipts_context *ipts;
|
|
|
|
-+};
|
|
|
|
|
|
++ bool should_stop;
|
|
|
|
++ struct completion done;
|
|
+
|
|
+
|
|
-+struct ipts_device_info {
|
|
|
|
-+ __u16 vendor;
|
|
|
|
-+ __u16 product;
|
|
|
|
-+ __u32 version;
|
|
|
|
-+ __u32 buffer_size;
|
|
|
|
-+ __u8 max_contacts;
|
|
|
|
-+
|
|
|
|
-+ /* For future expansion */
|
|
|
|
-+ __u8 reserved[19];
|
|
|
|
|
|
++ void *data;
|
|
|
|
++ int (*threadfn)(struct ipts_thread *thread);
|
|
+};
|
|
+};
|
|
+
|
|
+
|
|
-+#define IPTS_IOCTL_GET_DEVICE_READY _IOR(0x86, 0x01, __u8)
|
|
|
|
-+#define IPTS_IOCTL_GET_DEVICE_INFO _IOR(0x86, 0x02, struct ipts_device_info)
|
|
|
|
-+#define IPTS_IOCTL_GET_DOORBELL _IOR(0x86, 0x03, __u32)
|
|
|
|
-+#define IPTS_IOCTL_SEND_FEEDBACK _IO(0x86, 0x04)
|
|
|
|
-+#define IPTS_IOCTL_SEND_RESET _IO(0x86, 0x05)
|
|
|
|
-+
|
|
|
|
-+void ipts_uapi_link(struct ipts_context *ipts);
|
|
|
|
-+void ipts_uapi_unlink(void);
|
|
|
|
-+
|
|
|
|
-+int ipts_uapi_init(void);
|
|
|
|
-+void ipts_uapi_free(void);
|
|
|
|
-+
|
|
|
|
-+#endif /* _IPTS_UAPI_H_ */
|
|
|
|
---
|
|
|
|
-2.39.1
|
|
|
|
-
|
|
|
|
-From 93797032614d485639d977647d64ff6a2448280a Mon Sep 17 00:00:00 2001
|
|
|
|
-From: Liban Hannan <liban.p@gmail.com>
|
|
|
|
-Date: Tue, 12 Apr 2022 23:31:12 +0100
|
|
|
|
-Subject: [PATCH] iommu: ipts: use IOMMU passthrough mode for IPTS
|
|
|
|
-
|
|
|
|
-Adds a quirk so that IOMMU uses passthrough mode for the IPTS device.
|
|
|
|
-Otherwise, when IOMMU is enabled, IPTS produces DMAR errors like:
|
|
|
|
-
|
|
|
|
-DMAR: [DMA Read NO_PASID] Request device [00:16.4] fault addr
|
|
|
|
-0x104ea3000 [fault reason 0x06] PTE Read access is not set
|
|
|
|
-
|
|
|
|
-This is very similar to the bug described at:
|
|
|
|
-https://bugs.launchpad.net/bugs/1958004
|
|
|
|
-
|
|
|
|
-Fixed with the following patch which this patch basically copies:
|
|
|
|
-https://launchpadlibrarian.net/586396847/43255ca.diff
|
|
|
|
-Patchset: ipts
|
|
|
|
----
|
|
|
|
- drivers/iommu/intel/iommu.c | 24 ++++++++++++++++++++++++
|
|
|
|
- 1 file changed, 24 insertions(+)
|
|
|
|
-
|
|
|
|
-diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
|
|
|
|
-index 644ca49e8cf8..408c321b929a 100644
|
|
|
|
---- a/drivers/iommu/intel/iommu.c
|
|
|
|
-+++ b/drivers/iommu/intel/iommu.c
|
|
|
|
-@@ -37,6 +37,8 @@
|
|
|
|
- #define IS_GFX_DEVICE(pdev) ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
|
|
|
|
- #define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB)
|
|
|
|
- #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA)
|
|
|
|
-+#define IS_IPTS(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \
|
|
|
|
-+ ((pdev)->device == 0x9d3e))
|
|
|
|
- #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e)
|
|
|
|
-
|
|
|
|
- #define IOAPIC_RANGE_START (0xfee00000)
|
|
|
|
-@@ -286,12 +288,14 @@ int intel_iommu_enabled = 0;
|
|
|
|
- EXPORT_SYMBOL_GPL(intel_iommu_enabled);
|
|
|
|
-
|
|
|
|
- static int dmar_map_gfx = 1;
|
|
|
|
-+static int dmar_map_ipts = 1;
|
|
|
|
- static int intel_iommu_superpage = 1;
|
|
|
|
- static int iommu_identity_mapping;
|
|
|
|
- static int iommu_skip_te_disable;
|
|
|
|
-
|
|
|
|
- #define IDENTMAP_GFX 2
|
|
|
|
- #define IDENTMAP_AZALIA 4
|
|
|
|
-+#define IDENTMAP_IPTS 16
|
|
|
|
-
|
|
|
|
- const struct iommu_ops intel_iommu_ops;
|
|
|
|
-
|
|
|
|
-@@ -2630,6 +2634,9 @@ static int device_def_domain_type(struct device *dev)
|
|
|
|
-
|
|
|
|
- if ((iommu_identity_mapping & IDENTMAP_GFX) && IS_GFX_DEVICE(pdev))
|
|
|
|
- return IOMMU_DOMAIN_IDENTITY;
|
|
|
|
-+
|
|
|
|
-+ if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev))
|
|
|
|
-+ return IOMMU_DOMAIN_IDENTITY;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-@@ -3019,6 +3026,9 @@ static int __init init_dmars(void)
|
|
|
|
- if (!dmar_map_gfx)
|
|
|
|
- iommu_identity_mapping |= IDENTMAP_GFX;
|
|
|
|
-
|
|
|
|
-+ if (!dmar_map_ipts)
|
|
|
|
-+ iommu_identity_mapping |= IDENTMAP_IPTS;
|
|
|
|
-+
|
|
|
|
- check_tylersburg_isoch();
|
|
|
|
-
|
|
|
|
- ret = si_domain_init(hw_pass_through);
|
|
|
|
-@@ -4774,6 +4784,17 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
|
|
|
|
- dmar_map_gfx = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-+static void quirk_iommu_ipts(struct pci_dev *dev)
|
|
|
|
-+{
|
|
|
|
-+ if (!IS_IPTS(dev))
|
|
|
|
-+ return;
|
|
|
|
|
|
++/*
|
|
|
|
++ * ipts_thread_should_stop() - Returns true if the thread is asked to terminate.
|
|
|
|
++ * @thread: The current thread.
|
|
|
|
++ *
|
|
|
|
++ * Returns: true if the thread should stop, false if not.
|
|
|
|
++ */
|
|
|
|
++bool ipts_thread_should_stop(struct ipts_thread *thread);
|
|
+
|
|
+
|
|
-+ if (risky_device(dev))
|
|
|
|
-+ return;
|
|
|
|
|
|
++/*
|
|
|
|
++ * ipts_thread_start() - Starts an IPTS thread.
|
|
|
|
++ * @thread: The thread to initialize and start.
|
|
|
|
++ * @threadfn: The function to execute.
|
|
|
|
++ * @data: An argument that will be passed to threadfn.
|
|
|
|
++ * @name: The name of the new thread.
|
|
|
|
++ *
|
|
|
|
++ * Returns: 0 on success, <0 on error.
|
|
|
|
++ */
|
|
|
|
++int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread),
|
|
|
|
++ void *data, const char name[]);
|
|
+
|
|
+
|
|
-+ pci_info(dev, "Passthrough IOMMU for IPTS\n");
|
|
|
|
-+ dmar_map_ipts = 0;
|
|
|
|
-+}
|
|
|
|
- /* G4x/GM45 integrated gfx dmar support is totally busted. */
|
|
|
|
- DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx);
|
|
|
|
- DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx);
|
|
|
|
-@@ -4809,6 +4830,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
|
|
|
|
- DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx);
|
|
|
|
- DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx);
|
|
|
|
-
|
|
|
|
-+/* disable IPTS dmar support */
|
|
|
|
-+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9D3E, quirk_iommu_ipts);
|
|
|
|
|
|
++/*
|
|
|
|
++ * ipts_thread_stop() - Asks the thread to terminate and waits until it has finished.
|
|
|
|
++ * @thread: The thread that should stop.
|
|
|
|
++ *
|
|
|
|
++ * Returns: The return value of the thread function.
|
|
|
|
++ */
|
|
|
|
++int ipts_thread_stop(struct ipts_thread *thread);
|
|
+
|
|
+
|
|
- static void quirk_iommu_rwbf(struct pci_dev *dev)
|
|
|
|
- {
|
|
|
|
- if (risky_device(dev))
|
|
|
|
|
|
++#endif /* IPTS_THREAD_H */
|
|
--
|
|
--
|
|
2.39.1
|
|
2.39.1
|
|
|
|
|