diff --git a/.gitignore b/.gitignore index 839586ec..c661a348 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ xcuserdata xcshareddata project.xcworkspace VoodooInput +Lilu.kext diff --git a/.travis.yml b/.travis.yml index 3025b1b4..fd9de90a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ matrix: compiler: clang script: + - src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/Lilu/master/Lilu/Scripts/bootstrap.sh) && eval "$src" || exit 1 - src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/VoodooInput/master/VoodooInput/Scripts/bootstrap.sh) && eval "$src" || exit 1 - xcodebuild -configuration Debug - xcodebuild -configuration Release @@ -28,6 +29,7 @@ matrix: compiler: clang script: + - src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/Lilu/master/Lilu/Scripts/bootstrap.sh) && eval "$src" || exit 1 - src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/VoodooInput/master/VoodooInput/Scripts/bootstrap.sh) && eval "$src" || exit 1 - xcodebuild analyze -quiet -scheme VoodooPS2Controller -configuration Debug CLANG_ANALYZER_OUTPUT=plist-html CLANG_ANALYZER_OUTPUT_DIR="$(pwd)/clang-analyze" && [ "$(find clang-analyze -name "*.html")" = "" ] - xcodebuild analyze -quiet -scheme VoodooPS2Controller -configuration Release CLANG_ANALYZER_OUTPUT=plist-html CLANG_ANALYZER_OUTPUT_DIR="$(pwd)/clang-analyze" && [ "$(find clang-analyze -name "*.html")" = "" ] diff --git a/Changelog.md b/Changelog.md index cf696167..3264b86f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,12 +1,15 @@ VoodooPS2 Changelog ============================ -- Fix Command key being pressed after disabling the keyboard and trackpad with Command-PrtScr key combo -- Added a message to allow other kexts to disable the keyboard +#### v2.1.7 +- Added ability for native brightness keys discovery with `Lilu` API, please be aware of this new dependecy and drop SSDT modification to corresponding `_QXX`. +- Added constants for 11.0 support #### v2.1.6 - Upgraded to VoodooInput 1.0.7 - Fixed swiping desktops when holding a dragged item by improving thumb detection - Fixed keyboard timeout error on some laptop configurations +- Fix Command key being pressed after disabling the keyboard and trackpad with Command-PrtScr key combo +- Added a message to allow other kexts to disable the keyboard #### v2.1.5 - Upgraded to VoodooInput 1.0.6 diff --git a/README.md b/README.md index b4702bde..4413300c 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,12 @@ In addition this kext supports **Force Touch** emulation (*configured in `Info.p * **Mode 4** (*by @Tarik02*) – pressure is passed to the system using the following formula: ![formula](Docs/force_touch.png) The parameters in the formula are configured using `ForceTouchCustomUpThreshold`, `ForceTouchCustomDownThreshold` and `ForceTouchCustomPower` keys in `Info.plist` or configuration SSDT. Note that `ForceTouchCustomDownThreshold` is the *upper* limit on the pressure value and vice versa, because it corresponds to the touchpad being fully pressed *down*. +For Elan touchpad, only mode 0 and mode 1 are supported. + ## Installation and compilation +For native brightness keys discovery, `Lilu` is required to probe graphics devices. + For VoodooPS2Trackpad.kext to work multitouch interface engine, named VoodooInput.kext, is required. - For released binaries a compatible version of VoodooInput.kext is already included in the PlugIns directory. @@ -55,8 +59,11 @@ In addition, for 2-in-1 systems that do not support disabling the keyboard in ha * VoodooPS2Controller etc. – turbo, mackerintel, @RehabMan, nhand42, phb, Chunnan, jape, bumby (see RehabMan's repository). * Magic Trackpad 2 reverse engineering and implementation – https://github.com/alexandred/VoodooI2C project team. * VoodooPS2Trackpad integration – @kprinssu. -* Force Touch emulation and finger renumbering algorithm** - @usr-sse2. +* Force Touch emulation and finger renumbering algorithm** – @usr-sse2. +* Elan touchpad driver – @BAndysc and @hieplpvip \* On my touchpad this gesture was practically impossible to perform with the old VoodooPS2Trackpad. Now it works well. + \*\* Due to the limitations of PS/2 bus, Synaptics touchpad reports only the number of fingers and coordinates of two of them to the computer. When there are two fingers on the touchpad and third finger is added, a 'jump' may happen, because the coordinates of one of the fingers are replaced with the coordinates of the added finger. Finger renumbering algorithm estimates the distance from old coordinates to new ones in order to hide this 'jump' from the OS ~~and to calculate approximate position of the 'hidden' finger, in assumption that fingers move together in parallel to each other~~. Now third and fourth fingers are reported at the same position as one of the first two fingers. It allows Launchpad/Show desktop gesture to work more reliably. + \*\*\* The touchpad reports both finger width (ranged from 4 to 15) and pressure (ranged from 0 to 255), but in practice the measured width is almost always 4, and the reported pressure depends more on actual touch width than on actual pressure. diff --git a/VoodooPS2Controller.xcodeproj/project.pbxproj b/VoodooPS2Controller.xcodeproj/project.pbxproj index bcbd3eeb..130ff3e7 100644 --- a/VoodooPS2Controller.xcodeproj/project.pbxproj +++ b/VoodooPS2Controller.xcodeproj/project.pbxproj @@ -217,6 +217,7 @@ 84167814161B55B2002C60E6 /* Products */, ); sourceTree = ""; + usesTabs = 0; }; 84167814161B55B2002C60E6 /* Products */ = { isa = PBXGroup; @@ -301,13 +302,13 @@ 356B895D23007F1A0042F30F /* VoodooInput Headers */, 84833FAC161B62A900845294 /* VoodooPS2ALPSGlidePoint.h */, 84833FAB161B62A900845294 /* VoodooPS2ALPSGlidePoint.cpp */, + 9828A92E24A2B6C200550FAA /* VoodooPS2Elan.h */, + 9828A92D24A2B6C200550FAA /* VoodooPS2Elan.cpp */, 84833FAE161B62A900845294 /* VoodooPS2SentelicFSP.h */, 84833FAD161B62A900845294 /* VoodooPS2SentelicFSP.cpp */, 84833FB0161B62A900845294 /* VoodooPS2SynapticsTouchPad.h */, 84833FAF161B62A900845294 /* VoodooPS2SynapticsTouchPad.cpp */, 84167857161B56C4002C60E6 /* Supporting Files */, - 9828A92D24A2B6C200550FAA /* VoodooPS2Elan.cpp */, - 9828A92E24A2B6C200550FAA /* VoodooPS2Elan.h */, ); path = VoodooPS2Trackpad; sourceTree = ""; @@ -531,7 +532,7 @@ 84167808161B55B2002C60E6 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1140; + LastUpgradeCheck = 1200; ORGANIZATIONNAME = Acidanthera; }; buildConfigurationList = 8416780B161B55B2002C60E6 /* Build configuration list for PBXProject "VoodooPS2Controller" */; @@ -760,6 +761,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++1y"; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; @@ -805,7 +807,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; "LLVM_LTO[arch=x86_64]" = YES; MACOSX_DEPLOYMENT_TARGET = 10.11; - MODULE_VERSION = 2.1.6; + MODULE_VERSION = 2.1.7; ONLY_ACTIVE_ARCH = YES; "OTHER_LDFLAGS[arch=x86_64]" = "-dead_strip"; PRODUCT_NAME = VoodooPS2Controller; @@ -819,6 +821,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++1y"; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; @@ -857,7 +860,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; "LLVM_LTO[arch=x86_64]" = YES; MACOSX_DEPLOYMENT_TARGET = 10.11; - MODULE_VERSION = 2.1.6; + MODULE_VERSION = 2.1.7; "OTHER_LDFLAGS[arch=x86_64]" = "-dead_strip"; PRODUCT_NAME = VoodooPS2Controller; SDKROOT = macosx; @@ -913,6 +916,7 @@ COMBINE_HIDPI_IMAGES = YES; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Lilu.kext/Contents/Resources"; INFOPLIST_FILE = "VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -934,6 +938,7 @@ COMBINE_HIDPI_IMAGES = YES; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Lilu.kext/Contents/Resources"; INFOPLIST_FILE = "VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", diff --git a/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme index d9cf0273..53cfca6d 100644 --- a/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme +++ b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme @@ -1,6 +1,6 @@ OSBundleLibraries + com.apple.iokit.IOACPIFamily + 1.0.0d1 com.apple.iokit.IOHIDFamily 1.0.0b1 com.apple.kpi.bsd @@ -583,6 +585,8 @@ 8.0.0 as.acidanthera.voodoo.driver.PS2Controller ${MODULE_VERSION} + as.vit9696.Lilu + 1.2.0 OSBundleRequired Console diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp b/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp index 08718119..f96b3eee 100644 --- a/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp @@ -43,6 +43,7 @@ #include "ApplePS2ToADBMap.h" #include "AppleACPIPS2Nub.h" #include +#include // Constants for Info.plist settings @@ -65,6 +66,11 @@ #define kMacroTranslation "Macro Translation" #define kMaxMacroTime "MaximumMacroTime" +// Constants for brightness keys + +#define kBrightnessDevice "BrightnessDevice" +#define kBrightnessKey "BrightnessKey" + // Definitions for Macro Inversion data format //REVIEW: This should really be defined as some sort of structure #define kIgnoreBytes 2 // first two bytes of macro data are ignored (always 0xffff) @@ -176,6 +182,12 @@ bool ApplePS2Keyboard::init(OSDictionary * dict) _keysSpecial = 0; _f12ejectdelay = 250; // default is 250 ms + // initialize ACPI support for brightness key + _panel = 0; + _panelNotified = false; + _panelPrompt = false; + _panelNotifiers = 0; + // initialize ACPI support for keyboard backlight/screen brightness _provider = 0; _brightnessLevels = 0; @@ -336,6 +348,64 @@ ApplePS2Keyboard* ApplePS2Keyboard::probe(IOService * provider, SInt32 * score) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +IORegistryEntry* ApplePS2Keyboard::getDevicebyAddress(IORegistryEntry *parent, int address) { + IORegistryEntry* child = NULL; + auto iter = parent->getChildIterator(gIODTPlane); + if (iter) { + IORegistryEntry* dev; + int addr; + while ((dev = (IORegistryEntry*)iter->getNextObject())) { + auto location = dev->getLocation(); + // The device need to be present in ACPI scope and follow the naming convention ('A'-'Z', '_') + auto name = dev->getName(); + if (location && name && name [0] <= '_' && + sscanf(dev->getLocation(), "%x", &addr) == 1 && addr == address) { + child = dev; + break; + } + } + } + OSSafeRelease(iter); + return child; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +IOACPIPlatformDevice* ApplePS2Keyboard::getBrightnessPanel() { + IOACPIPlatformDevice *panel = nullptr; + + auto info = DeviceInfo::create(); + + auto getAcpiDevice = [](IORegistryEntry *dev) -> IOACPIPlatformDevice * { + if (dev == nullptr) + return nullptr; + + auto path = OSDynamicCast(OSString, dev->getProperty("acpi-path")); + if (path != nullptr) { + auto p = IORegistryEntry::fromPath(path->getCStringNoCopy()); + auto r = OSDynamicCast(IOACPIPlatformDevice, p); + if (r) return r; + OSSafeRelease(p); + } + return nullptr; + }; + + if (info) { + if (info->videoBuiltin != nullptr) + panel = getAcpiDevice(getDevicebyAddress(info->videoBuiltin, 0x400)); + + if (panel == nullptr) + for (size_t i = 0; panel == nullptr && i < info->videoExternal.size(); ++i) + panel = getAcpiDevice(getDevicebyAddress(info->videoExternal[i].video, 0x110)); + + DeviceInfo::deleter(info); + } + + return panel; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + bool ApplePS2Keyboard::start(IOService * provider) { DEBUG_LOG("ApplePS2Keyboard::start entered...\n"); @@ -379,6 +449,15 @@ bool ApplePS2Keyboard::start(IOService * provider) return false; } + // get IOACPIPlatformDevice for built-in panel + _panel = getBrightnessPanel(); + if (_panel != nullptr) { + if ((_panelNotifiers = _panel->registerInterest(gIOGeneralInterest, _panelNotification, this))) + setProperty(kBrightnessDevice, _panel->getName()); + else + IOLog("ps2br: unable to register interest for GFX notifications\n"); + } + // get IOACPIPlatformDevice for Device (PS2K) //REVIEW: should really look at the parent chain for IOACPIPlatformDevice instead. _provider = (IOACPIPlatformDevice*)IORegistryEntry::fromPath("IOService:/AppleACPIPlatformExpert/PS2K"); @@ -950,8 +1029,11 @@ void ApplePS2Keyboard::stop(IOService * provider) OSSafeReleaseNULL(_device); // - // Release ACPI provider for PS2K ACPI device + // Release ACPI provider for panel and PS2K ACPI device // + if (_panel && _panelNotifiers) + _panelNotifiers->remove(); + OSSafeReleaseNULL(_panel); OSSafeReleaseNULL(_provider); // @@ -1809,6 +1891,19 @@ bool ApplePS2Keyboard::dispatchKeyboardEventWithPacket(const UInt8* packet) // special cases switch (adbKeyCode) { + case BRIGHTNESS_UP: + case BRIGHTNESS_DOWN: + if (_panelNotified) { + eatKey = true; + if (!_panelPrompt) { + _panelPrompt = true; + IOLog("%s: Already got brightness key from GFX device, please revert DSDT modification.\n", getName()); + } + } else if (!_panel && !_panelPrompt) { + _panelPrompt = true; + IOLog("%s: Unrecognized GFX device, please consider report your case.\n", getName()); + } + break; case 0x90: case 0x91: if (_brightnessLevels) @@ -1920,6 +2015,64 @@ bool ApplePS2Keyboard::dispatchKeyboardEventWithPacket(const UInt8* packet) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +IOReturn ApplePS2Keyboard::_panelNotification(void *target, void *refCon, UInt32 messageType, IOService *provider, void *messageArgument, vm_size_t argSize) { + if (messageType == kIOACPIMessageDeviceNotification) { + if (NULL == target) { + DEBUG_LOG("%s kIOACPIMessageDeviceNotification target is null\n", provider->getName()); + return kIOReturnError; + } + + auto self = OSDynamicCast(ApplePS2Keyboard, reinterpret_cast(target)); + if (NULL == self) { + DEBUG_LOG("%s kIOACPIMessageDeviceNotification target is not a ApplePS2Keyboard\n", provider->getName()); + return kIOReturnError; + } + + if (NULL != messageArgument) { + uint64_t now_abs; + UInt32 arg = *static_cast(messageArgument); + switch (arg) { + case kIOACPIMessageBrightnessUp: + clock_get_uptime(&now_abs); + self->dispatchKeyboardEventX(BRIGHTNESS_UP, true, now_abs); + clock_get_uptime(&now_abs); + self->dispatchKeyboardEventX(BRIGHTNESS_UP, false, now_abs); + DEBUG_LOG("%s ACPI brightness up\n", self->getName()); + break; + + case kIOACPIMessageBrightnessDown: + clock_get_uptime(&now_abs); + self->dispatchKeyboardEventX(BRIGHTNESS_DOWN, true, now_abs); + clock_get_uptime(&now_abs); + self->dispatchKeyboardEventX(BRIGHTNESS_DOWN, false, now_abs); + DEBUG_LOG("%s ACPI brightness down\n", self->getName()); + break; + + case kIOACPIMessageBrightnessCycle: + case kIOACPIMessageBrightnessZero: + case kIOACPIMessageBrightnessOff: + DEBUG_LOG("%s ACPI brightness operation 0x%02x not implemented\n", self->getName(), *((UInt32 *) messageArgument)); + return kIOReturnSuccess; + + default: + DEBUG_LOG("%s unknown ACPI notification 0x%04x\n", self->getName(), *((UInt32 *) messageArgument)); + return kIOReturnSuccess; + } + if (!self->_panelNotified) { + self->_panelNotified = true; + self->setProperty(kBrightnessKey, "ACPI"); + } + } else { + DEBUG_LOG("%s %s received unknown kIOACPIMessageDeviceNotification\n", self->getName(), provider->getName()); + } + } else { + DEBUG_LOG("%s received %08X\n", provider->getName(), messageType); + } + return kIOReturnSuccess; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + void ApplePS2Keyboard::setAlphaLockFeedback(bool locked) { // diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard.h b/VoodooPS2Keyboard/VoodooPS2Keyboard.h index c69dde74..20b4adfa 100644 --- a/VoodooPS2Keyboard/VoodooPS2Keyboard.h +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard.h @@ -30,6 +30,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winconsistent-missing-override" #include +#include #pragma clang diagnostic pop #include @@ -106,7 +107,12 @@ class EXPORT ApplePS2Keyboard : public IOHIKeyboard IOTimerEventSource* _sleepEjectTimer; UInt32 _maxsleeppresstime; - // ACPI support for screen brightness + // ACPI support for panel brightness + IOACPIPlatformDevice * _panel; + bool _panelNotified; + bool _panelPrompt; + IONotifier * _panelNotifiers; + IOACPIPlatformDevice * _provider; int * _brightnessLevels; int _brightnessCount; @@ -138,6 +144,9 @@ class EXPORT ApplePS2Keyboard : public IOHIKeyboard virtual void setKeyboardEnable(bool enable); virtual void initKeyboard(); virtual void setDevicePowerState(UInt32 whatToDo); + IORegistryEntry* getDevicebyAddress(IORegistryEntry *parent, int address); + IOACPIPlatformDevice* getBrightnessPanel(); + static IOReturn _panelNotification(void *target, void *refCon, UInt32 messageType, IOService *provider, void *messageArgument, vm_size_t argSize); void modifyKeyboardBacklight(int adbKeyCode, bool goingDown); void modifyScreenBrightness(int adbKeyCode, bool goingDown); inline bool checkModifierState(UInt16 mask) diff --git a/VoodooPS2Trackpad/VoodooPS2Elan.cpp b/VoodooPS2Trackpad/VoodooPS2Elan.cpp index e2bc9df9..21fb408d 100644 --- a/VoodooPS2Trackpad/VoodooPS2Elan.cpp +++ b/VoodooPS2Trackpad/VoodooPS2Elan.cpp @@ -10,20 +10,12 @@ // generally one cannot IOLog from interrupt context, it eventually leads to kernel panic // but it is useful sometimes -#ifdef PACKET_DEBUG +#if 0 #define INTERRUPT_LOG(args...) do { IOLog(args); } while (0) #else #define INTERRUPT_LOG(args...) do { } while (0) #endif -// enable for trackpad debugging -#ifdef DEBUG_MSG -#define DEBUG_VERBOSE -//#define PACKET_DEBUG -#endif - -#define kTPDN "TPDN" // Trackpad Disable Notification - #include "LegacyIOService.h" #pragma clang diagnostic push @@ -41,23 +33,6 @@ #include "VoodooInputMultitouch/VoodooInputTransducer.h" #include "VoodooInputMultitouch/VoodooInputMessages.h" - -#define kIOFBTransformKey "IOFBTransform" - -enum { - // transforms - kIOFBRotateFlags = 0x0000000f, - - kIOFBSwapAxes = 0x00000001, - kIOFBInvertX = 0x00000002, - kIOFBInvertY = 0x00000004, - - kIOFBRotate0 = 0x00000000, - kIOFBRotate90 = kIOFBSwapAxes | kIOFBInvertX, - kIOFBRotate180 = kIOFBInvertX | kIOFBInvertY, - kIOFBRotate270 = kIOFBSwapAxes | kIOFBInvertY -}; - // ============================================================================= // ApplePS2Elan Class Implementation // @@ -72,93 +47,82 @@ UInt32 ApplePS2Elan::interfaceID() #define abs(x) ((x) < 0 ? -(x) : (x)) -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool ApplePS2Elan::init(OSDictionary * dict) -{ - // +bool ApplePS2Elan::init(OSDictionary *dict) { // Initialize this object's minimal state. This is invoked right after this // object is instantiated. - // - - if (!super::init(dict)) + + if (!super::init(dict)) { return false; + } // announce version - extern kmod_info_t kmod_info; + extern kmod_info_t kmod_info; DEBUG_LOG("VoodooPS2Elan: Version %s starting on OS X Darwin %d.%d.\n", kmod_info.version, version_major, version_minor); - setProperty ("Revision", 24, 32); - return true; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Elan::injectVersionDependentProperties(OSDictionary *config) -{ +void ApplePS2Elan::injectVersionDependentProperties(OSDictionary *config) { // inject properties specific to the version of Darwin that is runnning... char buf[32]; - OSDictionary* dict = NULL; - do - { + OSDictionary *dict = NULL; + do { // check for "Darwin major.minor" snprintf(buf, sizeof(buf), "Darwin %d.%d", version_major, version_minor); - if ((dict = OSDynamicCast(OSDictionary, config->getObject(buf)))) + if ((dict = OSDynamicCast(OSDictionary, config->getObject(buf)))) { break; + } + // check for "Darwin major.x" snprintf(buf, sizeof(buf), "Darwin %d.x", version_major); - if ((dict = OSDynamicCast(OSDictionary, config->getObject(buf)))) + if ((dict = OSDynamicCast(OSDictionary, config->getObject(buf)))) { break; + } + // check for "Darwin 16+" (this is what is used currently, other formats are for future) - if (version_major >= 16 && (dict = OSDynamicCast(OSDictionary, config->getObject("Darwin 16+")))) + if (version_major >= 16 && (dict = OSDynamicCast(OSDictionary, config->getObject("Darwin 16+")))) { break; + } } while (0); - if (dict) - { + if (dict) { // found version specific properties above, inject... - if (OSCollectionIterator* iter = OSCollectionIterator::withCollection(dict)) - { + if (OSCollectionIterator *iter = OSCollectionIterator::withCollection(dict)) { // Note: OSDictionary always contains OSSymbol* - while (const OSSymbol* key = static_cast(iter->getNextObject())) - { - if (OSObject* value = dict->getObject(key)) + while (const OSSymbol *key = static_cast(iter->getNextObject())) { + if (OSObject *value = dict->getObject(key)) { setProperty(key, value); + } } iter->release(); } } } -ApplePS2Elan* ApplePS2Elan::probe(IOService * provider, SInt32 * score) -{ +ApplePS2Elan *ApplePS2Elan::probe(IOService *provider, SInt32 *score) { DEBUG_LOG("ApplePS2Elan::probe entered...\n"); - - // + // The driver has been instructed to verify the presence of the actual // hardware we represent. We are guaranteed by the controller that the // mouse clock is enabled and the mouse itself is disabled (thus it // won't send any asynchronous mouse data that may mess up the // responses expected by the commands we send it). - // - - if (!super::probe(provider, score)) + + if (!super::probe(provider, score)) { return 0; + } - _device = (ApplePS2MouseDevice*)provider; + _device = (ApplePS2MouseDevice*)provider; // find config specific to Platform Profile - OSDictionary* list = OSDynamicCast(OSDictionary, getProperty(kPlatformProfile)); - OSDictionary* config = _device->getController()->makeConfigurationNode(list, "Elantech TouchPad"); - if (config) - { + OSDictionary *list = OSDynamicCast(OSDictionary, getProperty(kPlatformProfile)); + OSDictionary *config = _device->getController()->makeConfigurationNode(list, "Elantech TouchPad"); + if (config) { // if DisableDevice is Yes, then do not load at all... - OSBoolean* disable = OSDynamicCast(OSBoolean, config->getObject(kDisableDevice)); - if (disable && disable->isTrue()) - { + OSBoolean *disable = OSDynamicCast(OSBoolean, config->getObject(kDisableDevice)); + if (disable && disable->isTrue()) { config->release(); - _device = 0; + _device = 0; return 0; } #ifdef DEBUG @@ -166,29 +130,28 @@ ApplePS2Elan* ApplePS2Elan::probe(IOService * provider, SInt32 * score) setProperty(kMergedConfiguration, config); #endif - // load settings specific to Platform Profile - setParamPropertiesGated(config); - injectVersionDependentProperties(config); - OSSafeReleaseNULL(config); + // load settings specific to Platform Profile + setParamPropertiesGated(config); + injectVersionDependentProperties(config); + OSSafeReleaseNULL(config); } - + resetMouse(); - IOLog("VoodooPS2Elan: send magic knock to the device.\n"); + DEBUG_LOG("VoodooPS2Elan: send magic knock to the device.\n"); // send magic knock to the device if (elantechDetect()) { - IOLog("VoodooPS2Elan: elantouchpad not detected\n"); + DEBUG_LOG("VoodooPS2Elan: elan touchpad not detected\n"); return NULL; } - + resetMouse(); - - if (elantechQueryInfo()) - { + + if (elantechQueryInfo()) { IOLog("VoodooPS2Elan: query info failed\n"); return NULL; } - + IOLog("VoodooPS2Elan: capabilities: %x %x %x\n", info.capabilities[0], info.capabilities[1], info.capabilities[2]); IOLog("VoodooPS2Elan: samples: %x %x %x\n", info.capabilities[0], info.capabilities[1], info.capabilities[2]); IOLog("VoodooPS2Elan: hw_version: %x\n", info.hw_version); @@ -203,7 +166,6 @@ ApplePS2Elan* ApplePS2Elan::probe(IOService * provider, SInt32 * score) IOLog("VoodooPS2Elan: y_traces: %d\n", info.y_traces); IOLog("VoodooPS2Elan: width: %d\n", info.width); IOLog("VoodooPS2Elan: bus: %d\n", info.bus); - IOLog("VoodooPS2Elan: paritycheck: %d\n", info.paritycheck); IOLog("VoodooPS2Elan: jumpy_cursor: %d\n", info.jumpy_cursor); IOLog("VoodooPS2Elan: reports_pressure: %d\n", info.reports_pressure); @@ -211,17 +173,11 @@ ApplePS2Elan* ApplePS2Elan::probe(IOService * provider, SInt32 * score) IOLog("VoodooPS2Elan: set_hw_resolution: %d\n", info.set_hw_resolution); IOLog("VoodooPS2Elan: has_trackpoint: %d\n", info.has_trackpoint); IOLog("VoodooPS2Elan: has_middle_button: %d\n", info.has_middle_button); - - if (info.hw_version <= 2) - { - IOLog("VoodooPS2Elan: Unsupported ELAN PS2 Touchpad version. Currently only v4 and v3 version is supported. You have: %d\n", info.hw_version); - return nullptr; - } - + IOLog("VoodooPS2Elan: elan touchpad detected. Probing finished.\n"); - + _device = nullptr; - + return this; } @@ -241,263 +197,207 @@ void ApplePS2Elan::handleClose(IOService *forClient, IOOptionBits options) { super::handleClose(forClient, options); } -bool ApplePS2Elan::start(IOService* provider) -{ - // +bool ApplePS2Elan::start(IOService *provider) { // The driver has been instructed to start. This is called after a // successful probe and match. - // - if (!super::start(provider)) + if (!super::start(provider)) { return false; + } - // // Maintain a pointer to and retain the provider object. - // - - _device = (ApplePS2MouseDevice *) provider; + _device = (ApplePS2MouseDevice *)provider; _device->retain(); - - // - // Announce hardware properties. - // + // Announce hardware properties. char buf[128]; snprintf(buf, sizeof(buf), "Elan v %d, fw: %x, bus: %d", info.hw_version, info.fw_version, info.bus); setProperty("RM,TrackpadInfo", buf); - if (info.bus == ETP_BUS_PS2_ONLY) + if (info.bus == ETP_BUS_PS2_ONLY) { setProperty("Bus", "ETP_BUS_PS2_ONLY"); - else if (info.bus == ETP_BUS_SMB_ALERT_ONLY) + } else if (info.bus == ETP_BUS_SMB_ALERT_ONLY) { setProperty("Bus", "ETP_BUS_SMB_ALERT_ONLY"); - else if (info.bus == ETP_BUS_SMB_HST_NTFY_ONLY) + } else if (info.bus == ETP_BUS_SMB_HST_NTFY_ONLY) { setProperty("Bus", "ETP_BUS_SMB_HST_NTFY_ONLY"); - else if (info.bus == ETP_BUS_PS2_SMB_ALERT) + } else if (info.bus == ETP_BUS_PS2_SMB_ALERT) { setProperty("Bus", "ETP_BUS_PS2_SMB_ALERT"); - else if (info.bus == ETP_BUS_PS2_SMB_HST_NTFY) + } else if (info.bus == ETP_BUS_PS2_SMB_HST_NTFY) { setProperty("Bus", "ETP_BUS_PS2_SMB_HST_NTFY"); - + } + if (info.bus == ETP_BUS_SMB_HST_NTFY_ONLY || info.bus == ETP_BUS_PS2_SMB_HST_NTFY || - ETP_NEW_IC_SMBUS_HOST_NOTIFY(info.fw_version)) + ETP_NEW_IC_SMBUS_HOST_NOTIFY(info.fw_version)) { setProperty("SMBus NOTE", "It looks like your touchpad is supported by VoodooSMBus kext, which gives better multitouch experience. We recommend you to try it."); - else if (info.bus == ETP_BUS_PS2_ONLY) + } else if (info.bus == ETP_BUS_PS2_ONLY) { setProperty("SMBus NOTE", "It looks like your touchpad does not support SMBus protocol."); + } - // // Advertise the current state of the tapping feature. // // Must add this property to let our superclass know that it should handle // trackpad acceleration settings from user space. Without this, tracking // speed adjustments from the mouse prefs panel have no effect. - // - setProperty(kIOHIDPointerAccelerationTypeKey, kIOHIDTrackpadAccelerationType); setProperty(kIOHIDScrollAccelerationTypeKey, kIOHIDTrackpadScrollAccelerationKey); - setProperty(kIOHIDScrollResolutionKey, _scrollresolution << 16, 32); - // added for Sierra precise scrolling (credit usr-sse2) + setProperty(kIOHIDScrollResolutionKey, _scrollresolution << 16, 32); + // added for Sierra precise scrolling (credit @usr-sse2) setProperty("HIDScrollResolutionX", _scrollresolution << 16, 32); setProperty("HIDScrollResolutionY", _scrollresolution << 16, 32); - - // + // Setup workloop with command gate for thread syncronization... - // - IOWorkLoop* pWorkLoop = getWorkLoop(); + IOWorkLoop *pWorkLoop = getWorkLoop(); _cmdGate = IOCommandGate::commandGate(this); - if (!pWorkLoop || !_cmdGate) - { - _device->release(); - _device = nullptr; + if (!pWorkLoop || !_cmdGate) { + OSSafeReleaseNULL(_device); return false; } - - // + // Lock the controller during initialization - // - _device->lock(); - + attachedHIDPointerDevices = OSSet::withCapacity(1); registerHIDPointerNotifications(); - + pWorkLoop->addEventSource(_cmdGate); - + elantechSetupPS2(); - // // Install our driver's interrupt handler, for asynchronous data delivery. - // - _device->installInterruptAction(this, - OSMemberFunctionCast(PS2InterruptAction,this,&ApplePS2Elan::interruptOccurred), + OSMemberFunctionCast(PS2InterruptAction, this, &ApplePS2Elan::interruptOccurred), OSMemberFunctionCast(PS2PacketAction, this, &ApplePS2Elan::packetReady)); _interruptHandlerInstalled = true; - - elantechTouchpadEnable(true); - - // now safe to allow other threads + + // Enable the touchpad + setTouchPadEnable(true); + + // Now it is safe to allow other threads _device->unlock(); - - // - // Install our power control handler. - // - - _device->installPowerControlAction( this, - OSMemberFunctionCast(PS2PowerControlAction, this, &ApplePS2Elan::setDevicePowerState) ); - _powerControlHandlerInstalled = true; - - // + + // Install our power control handler + _device->installPowerControlAction(this, OSMemberFunctionCast(PS2PowerControlAction, this, &ApplePS2Elan::setDevicePowerState)); + _powerControlHandlerInstalled = true; + // Request message registration for keyboard to trackpad communication - // //setProperty(kDeliverNotifications, true); - - // get IOACPIPlatformDevice for Device (PS2M) - //REVIEW: should really look at the parent chain for IOACPIPlatformDevice instead. - _provider = (IOACPIPlatformDevice*)IORegistryEntry::fromPath("IOService:/AppleACPIPlatformExpert/PS2M"); - if (_provider && kIOReturnSuccess != _provider->validateObject(kTPDN)) - { - _provider->release(); - _provider = NULL; - } - + return true; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Elan::stop( IOService * provider ) -{ +void ApplePS2Elan::stop(IOService *provider) { DEBUG_LOG("%s: stop called\n", getName()); - - // - // The driver has been instructed to stop. Note that we must break all + + // The driver has been instructed to stop. Note that we must break all // connections to other service objects now (ie. no registered actions, // no pointers and retains to objects, etc), if any. - // assert(_device == provider); unregisterHIDPointerNotifications(); OSSafeReleaseNULL(attachedHIDPointerDevices); - - // - // Disable the mouse itself, so that it may stop reporting mouse events. - // - elantechTouchpadEnable(false); + // Disable the touchpad + setTouchPadEnable(false); - // free up timer for scroll momentum - IOWorkLoop* pWorkLoop = getWorkLoop(); - if (pWorkLoop) - { - if (_cmdGate) - { + // Release command gate + IOWorkLoop *pWorkLoop = getWorkLoop(); + if (pWorkLoop) { + if (_cmdGate) { pWorkLoop->removeEventSource(_cmdGate); - _cmdGate->release(); - _cmdGate = 0; + OSSafeReleaseNULL(_cmdGate); } } - - // - // Uninstall the interrupt handler. - // - if (_interruptHandlerInstalled) - { + // Uninstall the interrupt handler + if (_interruptHandlerInstalled) { _device->uninstallInterruptAction(); _interruptHandlerInstalled = false; } - // - // Uninstall the power control handler. - // - - if (_powerControlHandlerInstalled) - { + // Uninstall the power control handler + if (_powerControlHandlerInstalled) { _device->uninstallPowerControlAction(); _powerControlHandlerInstalled = false; } - - // + // Release the pointer to the provider object. - // - OSSafeReleaseNULL(_device); - - // - // Release ACPI provider for PS2M ACPI device - // - OSSafeReleaseNULL(_provider); - - super::stop(provider); + + super::stop(provider); } -void ApplePS2Elan::setParamPropertiesGated(OSDictionary * config) -{ - if (NULL == config) - return; - - const struct {const char *name; int *var;} int32vars[]={ - {"WakeDelay", &wakedelay}, - {"ScrollResolution", &_scrollresolution}, - {"TrackpointMultiplierX", &_trackpointMultiplierX}, - {"TrackpointMultiplierY", &_trackpointMultiplierY}, - {"TrackpointDividerX", &_trackpointDividerX}, - {"TrackpointDividerY", &_trackpointDividerY}, - {"MouseResolution", &_mouseResolution}, - {"MouseSampleRate", &_mouseSampleRate}, - }; - const struct {const char *name; int *var;} boolvars[]={ - {"ProcessUSBMouseStopsTrackpad", &_processusbmouse}, +void ApplePS2Elan::setParamPropertiesGated(OSDictionary *config) { + if (NULL == config) { + return; + } + + const struct {const char *name; int *var;} int32vars[] = { + {"WakeDelay", &wakedelay}, + {"ScrollResolution", &_scrollresolution}, + {"TrackpointMultiplierX", &_trackpointMultiplierX}, + {"TrackpointMultiplierY", &_trackpointMultiplierY}, + {"TrackpointDividerX", &_trackpointDividerX}, + {"TrackpointDividerY", &_trackpointDividerY}, + {"MouseResolution", &_mouseResolution}, + {"MouseSampleRate", &_mouseSampleRate}, + {"ForceTouchMode", (int*)&_forceTouchMode}, + }; + + const struct {const char *name; uint64_t *var;} int64vars[] = { + {"QuietTimeAfterTyping", &maxaftertyping}, + }; + + const struct {const char *name; bool *var;} boolvars[] = { + {"ProcessUSBMouseStopsTrackpad", &_processusbmouse}, {"ProcessBluetoothMouseStopsTrackpad", &_processbluetoothmouse}, - {"SetHwResolution", &_set_hw_resolution} - }; - const struct {const char* name; bool* var;} lowbitvars[]={ - {"USBMouseStopsTrackpad", &usb_mouse_stops_trackpad}, + {"SetHwResolution", &_set_hw_resolution}, }; - const struct {const char* name; uint64_t* var; } int64vars[]={ + + const struct {const char *name; bool *var;} lowbitvars[] = { + {"USBMouseStopsTrackpad", &usb_mouse_stops_trackpad}, }; - + + OSBoolean *bl; + OSNumber *num; + // highrate? - OSBoolean *bl; - if ((bl=OSDynamicCast (OSBoolean, config->getObject ("UseHighRate")))) - { + if ((bl = OSDynamicCast(OSBoolean, config->getObject("UseHighRate")))) { setProperty("UseHighRate", bl->isTrue()); } - - OSNumber *num; + + // 32-bit config items + for (int i = 0; i < countof(int32vars); i++) { + if ((num = OSDynamicCast(OSNumber, config->getObject(int32vars[i].name)))) { + *int32vars[i].var = num->unsigned32BitValue(); + setProperty(int32vars[i].name, *int32vars[i].var, 32); + } + } + // 64-bit config items - for (int i = 0; i < countof(int64vars); i++) - if ((num=OSDynamicCast(OSNumber, config->getObject(int64vars[i].name)))) - { + for (int i = 0; i < countof(int64vars); i++) { + if ((num = OSDynamicCast(OSNumber, config->getObject(int64vars[i].name)))) { *int64vars[i].var = num->unsigned64BitValue(); setProperty(int64vars[i].name, *int64vars[i].var, 64); } + } + // boolean config items - for (int i = 0; i < countof(boolvars); i++) - if ((bl=OSDynamicCast (OSBoolean,config->getObject (boolvars[i].name)))) - { - *boolvars[i].var = bl->isTrue(); + for (int i = 0; i < countof(boolvars); i++) { + if ((bl = OSDynamicCast(OSBoolean, config->getObject(boolvars[i].name)))) { + *boolvars[i].var = bl->isTrue(); setProperty(boolvars[i].name, *boolvars[i].var ? kOSBooleanTrue : kOSBooleanFalse); } - // 32-bit config items - for (int i = 0; i < countof(int32vars);i++) - if ((num=OSDynamicCast (OSNumber,config->getObject (int32vars[i].name)))) - { - *int32vars[i].var = num->unsigned32BitValue(); - setProperty(int32vars[i].name, *int32vars[i].var, 32); - } + } + // lowbit config items - for (int i = 0; i < countof(lowbitvars); i++) - { - if ((num=OSDynamicCast (OSNumber,config->getObject(lowbitvars[i].name)))) - { - *lowbitvars[i].var = (num->unsigned32BitValue()&0x1)?true:false; + for (int i = 0; i < countof(lowbitvars); i++) { + if ((num = OSDynamicCast(OSNumber, config->getObject(lowbitvars[i].name)))) { + *lowbitvars[i].var = (num->unsigned32BitValue() & 0x1) ? true : false; setProperty(lowbitvars[i].name, *lowbitvars[i].var ? 1 : 0, 32); - } - //REVIEW: are these items ever carried in a boolean? - else if ((bl=OSDynamicCast(OSBoolean, config->getObject(lowbitvars[i].name)))) - { + } else if ((bl = OSDynamicCast(OSBoolean, config->getObject(lowbitvars[i].name)))) { + // REVIEW: are these items ever carried in a boolean? *lowbitvars[i].var = bl->isTrue(); setProperty(lowbitvars[i].name, *lowbitvars[i].var ? kOSBooleanTrue : kOSBooleanFalse); } @@ -509,96 +409,130 @@ void ApplePS2Elan::setParamPropertiesGated(OSDictionary * config) } } -IOReturn ApplePS2Elan::setParamProperties(OSDictionary* dict) -{ - ////IOReturn result = super::IOHIDevice::setParamProperties(dict); - if (_cmdGate) - { +IOReturn ApplePS2Elan::setParamProperties(OSDictionary *dict) { + if (_cmdGate) { // syncronize through workloop... - ////_cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Elan::setParamPropertiesGated), dict); + //_cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Elan::setParamPropertiesGated), dict); setParamPropertiesGated(dict); } - + return super::setParamProperties(dict); - ////return result; } -IOReturn ApplePS2Elan::setProperties(OSObject *props) -{ - OSDictionary *dict = OSDynamicCast(OSDictionary, props); - if (dict && _cmdGate) - { +IOReturn ApplePS2Elan::setProperties(OSObject *props) { + OSDictionary *dict = OSDynamicCast(OSDictionary, props); + if (dict && _cmdGate) { // synchronize through workloop... _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Elan::setParamPropertiesGated), dict); } - - return super::setProperties(props); + + return super::setProperties(props); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +IOReturn ApplePS2Elan::message(UInt32 type, IOService* provider, void* argument) { + // Here is where we receive messages from the keyboard driver + // + // This allows for the keyboard driver to enable/disable the trackpad + // when a certain keycode is pressed. + // + // It also allows the trackpad driver to learn the last time a key + // has been pressed, so it can implement various "ignore trackpad + // input while typing" options. + switch (type) { + case kPS2M_getDisableTouchpad: + { + bool* pResult = (bool*)argument; + *pResult = !ignoreall; + break; + } + + case kPS2M_setDisableTouchpad: + { + bool enable = *((bool*)argument); + ignoreall = !enable; + break; + } + + case kPS2M_resetTouchpad: + { + int *reqCode = (int *)argument; + IOLog("VoodooPS2Elan::kPS2M_resetTouchpad reqCode: %d\n", *reqCode); + if (*reqCode == 1) { + setTouchPadEnable(false); + IOSleep(wakedelay); + + ignoreall = false; + _packetByteCount = 0; + _ringBuffer.reset(); + + resetMouse(); + elantechSetupPS2(); + setTouchPadEnable(true); + } + break; + } -void ApplePS2Elan::setDevicePowerState( UInt32 whatToDo ) -{ - switch ( whatToDo ) - { + case kPS2M_notifyKeyPressed: + { + // just remember last time key pressed... this can be used in + // interrupt handler to detect unintended input while typing + PS2KeyInfo* pInfo = (PS2KeyInfo*)argument; + keytime = pInfo->time; + break; + } + } + + return kIOReturnSuccess; +} + +void ApplePS2Elan::setDevicePowerState(UInt32 whatToDo) { + switch (whatToDo) { case kPS2C_DisableDevice: - // - // Disable touchpad (synchronous). - // - - elantechTouchpadEnable(false); + // Disable the touchpad + setTouchPadEnable(false); break; case kPS2C_EnableDevice: - // // Must not issue any commands before the device has - // completed its power-on self-test and calibration. - // - + // completed its power-on self-test and calibration IOSleep(wakedelay); - - // Reset and enable the touchpad. - // Clear packet buffer pointer to avoid issues caused by - // stale packet fragments. - // - elantechSetupPS2(); - + // Clear packet buffer pointer to avoid issues caused by stale packet fragments _packetByteCount = 0; _ringBuffer.reset(); - - elantechTouchpadEnable(true); + + // Reset and enable the touchpad + resetMouse(); + elantechSetupPS2(); + setTouchPadEnable(true); break; } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Elan::registerHIDPointerNotifications() -{ +void ApplePS2Elan::registerHIDPointerNotifications() { IOServiceMatchingNotificationHandler notificationHandler = OSMemberFunctionCast(IOServiceMatchingNotificationHandler, this, &ApplePS2Elan::notificationHIDAttachedHandler); - + // Determine if we should listen for USB mouse attach events as per configuration if (_processusbmouse) { // USB mouse HID description as per USB spec: http://www.usb.org/developers/hidpage/HID1_11.pdf - OSDictionary* matchingDictionary = serviceMatching("IOUSBInterface"); - + OSDictionary *matchingDictionary = serviceMatching("IOUSBInterface"); + propertyMatching(OSSymbol::withCString(kUSBHostMatchingPropertyInterfaceClass), OSNumber::withNumber(kUSBHIDInterfaceClass, 8), matchingDictionary); propertyMatching(OSSymbol::withCString(kUSBHostMatchingPropertyInterfaceSubClass), OSNumber::withNumber(kUSBHIDBootInterfaceSubClass, 8), matchingDictionary); propertyMatching(OSSymbol::withCString(kUSBHostMatchingPropertyInterfaceProtocol), OSNumber::withNumber(kHIDMouseInterfaceProtocol, 8), matchingDictionary); - + // Register for future services usb_hid_publish_notify = addMatchingNotification(gIOFirstPublishNotification, matchingDictionary, notificationHandler, this, NULL, 10000); usb_hid_terminate_notify = addMatchingNotification(gIOTerminatedNotification, matchingDictionary, notificationHandler, this, NULL, 10000); OSSafeReleaseNULL(matchingDictionary); } - + // Determine if we should listen for bluetooth mouse attach events as per configuration if (_processbluetoothmouse) { // Bluetooth HID devices - OSDictionary* matchingDictionary = serviceMatching("IOBluetoothHIDDriver"); + OSDictionary *matchingDictionary = serviceMatching("IOBluetoothHIDDriver"); propertyMatching(OSSymbol::withCString(kIOHIDVirtualHIDevice), kOSBooleanFalse, matchingDictionary); - + // Register for future services bluetooth_hid_publish_notify = addMatchingNotification(gIOFirstPublishNotification, matchingDictionary, notificationHandler, this, NULL, 10000); bluetooth_hid_terminate_notify = addMatchingNotification(gIOTerminatedNotification, matchingDictionary, notificationHandler, this, NULL, 10000); @@ -606,60 +540,59 @@ void ApplePS2Elan::registerHIDPointerNotifications() } } -void ApplePS2Elan::unregisterHIDPointerNotifications() -{ +void ApplePS2Elan::unregisterHIDPointerNotifications() { // Free device matching notifiers // remove() releases them - if (usb_hid_publish_notify) + + if (usb_hid_publish_notify) { usb_hid_publish_notify->remove(); + } - if (usb_hid_terminate_notify) + if (usb_hid_terminate_notify) { usb_hid_terminate_notify->remove(); + } - if (bluetooth_hid_publish_notify) + if (bluetooth_hid_publish_notify) { bluetooth_hid_publish_notify->remove(); + } - if (bluetooth_hid_terminate_notify) + if (bluetooth_hid_terminate_notify) { bluetooth_hid_terminate_notify->remove(); + } attachedHIDPointerDevices->flushCollection(); } -void ApplePS2Elan::notificationHIDAttachedHandlerGated(IOService * newService, - IONotifier * notifier) -{ +void ApplePS2Elan::notificationHIDAttachedHandlerGated(IOService *newService, IONotifier *notifier) { char path[256]; int len = 255; memset(path, 0, len); newService->getPath(path, &len, gIOServicePlane); - + if (notifier == usb_hid_publish_notify) { attachedHIDPointerDevices->setObject(newService); DEBUG_LOG("%s: USB pointer HID device published: %s, # devices: %d\n", getName(), path, attachedHIDPointerDevices->getCount()); } - + if (notifier == usb_hid_terminate_notify) { attachedHIDPointerDevices->removeObject(newService); DEBUG_LOG("%s: USB pointer HID device terminated: %s, # devices: %d\n", getName(), path, attachedHIDPointerDevices->getCount()); } - + if (notifier == bluetooth_hid_publish_notify) { - // Filter on specific CoD (Class of Device) bluetooth devices only - OSNumber* propDeviceClass = OSDynamicCast(OSNumber, newService->getProperty("ClassOfDevice")); - + OSNumber *propDeviceClass = OSDynamicCast(OSNumber, newService->getProperty("ClassOfDevice")); + if (propDeviceClass != NULL) { - - long classOfDevice = propDeviceClass->unsigned32BitValue(); - - long deviceClassMajor = (classOfDevice & 0x1F00) >> 8; - long deviceClassMinor = (classOfDevice & 0xFF) >> 2; - + UInt32 classOfDevice = propDeviceClass->unsigned32BitValue(); + + UInt32 deviceClassMajor = (classOfDevice & 0x1F00) >> 8; + UInt32 deviceClassMinor = (classOfDevice & 0xFF) >> 2; + if (deviceClassMajor == kBluetoothDeviceClassMajorPeripheral) { // Bluetooth peripheral devices - - long deviceClassMinor1 = (deviceClassMinor) & 0x30; - long deviceClassMinor2 = (deviceClassMinor) & 0x0F; - + UInt32 deviceClassMinor1 = (deviceClassMinor) & 0x30; + UInt32 deviceClassMinor2 = (deviceClassMinor) & 0x0F; + if (deviceClassMinor1 == kBluetoothDeviceClassMinorPeripheral1Pointing || // Seperate pointing device deviceClassMinor1 == kBluetoothDeviceClassMinorPeripheral1Combo) // Combo bluetooth keyboard/touchpad { @@ -667,7 +600,6 @@ void ApplePS2Elan::notificationHIDAttachedHandlerGated(IOService * newService, deviceClassMinor2 == kBluetoothDeviceClassMinorPeripheral2DigitizerTablet || // Magic Touchpad deviceClassMinor2 == kBluetoothDeviceClassMinorPeripheral2DigitalPen) // Wacom Tablet { - attachedHIDPointerDevices->setObject(newService); DEBUG_LOG("%s: Bluetooth pointer HID device published: %s, # devices: %d\n", getName(), path, attachedHIDPointerDevices->getCount()); } @@ -675,19 +607,19 @@ void ApplePS2Elan::notificationHIDAttachedHandlerGated(IOService * newService, } } } - + if (notifier == bluetooth_hid_terminate_notify) { attachedHIDPointerDevices->removeObject(newService); DEBUG_LOG("%s: Bluetooth pointer HID device terminated: %s, # devices: %d\n", getName(), path, attachedHIDPointerDevices->getCount()); } - + if (notifier == usb_hid_publish_notify || notifier == bluetooth_hid_publish_notify) { if (usb_mouse_stops_trackpad && attachedHIDPointerDevices->getCount() > 0) { // One or more USB or Bluetooth pointer devices attached, disable trackpad ignoreall = true; } } - + if (notifier == usb_hid_terminate_notify || notifier == bluetooth_hid_terminate_notify) { if (usb_mouse_stops_trackpad && attachedHIDPointerDevices->getCount() == 0) { // No USB or bluetooth pointer devices attached, re-enable trackpad @@ -696,36 +628,32 @@ void ApplePS2Elan::notificationHIDAttachedHandlerGated(IOService * newService, } } -bool ApplePS2Elan::notificationHIDAttachedHandler(void * refCon, - IOService * newService, - IONotifier * notifier) -{ - if (_cmdGate) { // defensive +bool ApplePS2Elan::notificationHIDAttachedHandler(void *refCon, IOService *newService, IONotifier *notifier) { + if (_cmdGate) { _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Elan::notificationHIDAttachedHandlerGated), newService, notifier); } return true; } - -/// elantech.c port - +// elantech.c port template -int ApplePS2Elan::ps2_command(UInt8* params, unsigned int command) -{ +int ApplePS2Elan::ps2_command(UInt8 *params, unsigned int command) { TPS2Request<1 + I> request; request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; request.commands[0].inOrOut = command; - for (int i = 0; i < I; ++i) + for (int i = 0; i < I; i++) { request.commands[1 + i].command = kPS2C_ReadDataPort; + } request.commandsCount = 1 + I; assert(request.commandsCount <= countof(request.commands)); _device->submitRequestAndBlock(&request); - - for (int i = 0; i < I; ++i) + + for (int i = 0; i < I; i++) { params[i] = request.commands[i + 1].inOrOut; + } return request.commandsCount != 1 + I; } @@ -734,22 +662,23 @@ int ApplePS2Elan::ps2_command(UInt8* params, unsigned int command) * A retrying version of ps2_command */ template -int ApplePS2Elan::elantech_ps2_command(unsigned char *param, int command) -{ +int ApplePS2Elan::elantech_ps2_command(unsigned char *param, int command) { int rc; int tries = ETP_PS2_COMMAND_TRIES; do { rc = ps2_command(param, command); - if (rc == 0) + if (rc == 0) { break; + } tries--; IOLog("VoodooPS2Elan: retrying ps2 command 0x%02x (%d).\n", command, tries); IOSleep(ETP_PS2_COMMAND_DELAY); } while (tries > 0); - if (rc) + if (rc) { IOLog("VoodooPS2Elan: ps2 command 0x%02x failed.\n", command); + } return rc; } @@ -761,28 +690,25 @@ int ApplePS2Elan::elantech_ps2_command(unsigned char *param, int command) * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu * is the command. */ - -int ApplePS2Elan::ps2_sliced_command(UInt8 command) -{ +int ApplePS2Elan::ps2_sliced_command(UInt8 command) { int j = 0; - + TPS2Request<> request; request.commands[j].command = kPS2C_SendMouseCommandAndCompareAck; request.commands[j++].inOrOut = kDP_SetMouseScaling1To1; - for (int i = 6; i >= 0; i -= 2) { UInt8 d = (command >> i) & 3; request.commands[j].command = kPS2C_SendMouseCommandAndCompareAck; request.commands[j++].inOrOut = kDP_SetMouseResolution; - + request.commands[j].command = kPS2C_SendMouseCommandAndCompareAck; request.commands[j++].inOrOut = d; } - + request.commandsCount = j; _device->submitRequestAndBlock(&request); - + return request.commandsCount != j; } @@ -790,24 +716,20 @@ int ApplePS2Elan::ps2_sliced_command(UInt8 command) * Send a Synaptics style sliced query command */ template -int ApplePS2Elan::synaptics_send_cmd(unsigned char c, unsigned char *param) -{ - if (ps2_sliced_command(c) || - ps2_command(param, kDP_GetMouseInformation)) { - IOLog("VoodooPS2Elan: query 0x%02x failed.\n", c); +int ApplePS2Elan::synaptics_send_cmd(unsigned char c, unsigned char *param) { + if (ps2_sliced_command(c) || ps2_command(param, kDP_GetMouseInformation)) { + IOLog("VoodooPS2Elan: query 0x%02x failed.\n", c); return -1; } return 0; } - /* * V3 and later support this fast command */ - template -int ApplePS2Elan::elantech_send_cmd(unsigned char c, unsigned char* param) -{ +template +int ApplePS2Elan::elantech_send_cmd(unsigned char c, unsigned char *param) { if (ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || ps2_command<0>(NULL, c) || ps2_command(param, kDP_GetMouseInformation)) { @@ -818,28 +740,37 @@ int ApplePS2Elan::elantech_send_cmd(unsigned char c, unsigned char* param) return 0; } -bool ApplePS2Elan::elantech_is_signature_valid(const unsigned char *param) -{ +template +int ApplePS2Elan::send_cmd(unsigned char c, unsigned char *param) { + if (info.hw_version >= 3) { + return elantech_send_cmd(c, param); + } else { + return synaptics_send_cmd(c, param); + } +} + +bool ApplePS2Elan::elantech_is_signature_valid(const unsigned char *param) { static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10 }; - int i; - if (param[0] == 0) + if (param[0] == 0) { return false; + } - if (param[1] == 0) + if (param[1] == 0) { return true; + } - /* - * Some hw_version >= 4 models have a revision higher then 20. Meaning - * that param[2] may be 10 or 20, skip the rates check for these. - */ - if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f && - param[2] < 40) + // Some hw_version >= 4 models have a revision higher then 20. + // Meaning that param[2] may be 10 or 20, skip the rates check for these. + if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f && param[2] < 40) { return true; + } - for (i = 0; i < sizeof(rates)/sizeof(*rates); i++) - if (param[2] == rates[i]) + for (int i = 0; i < sizeof(rates) / sizeof(*rates); i++) { + if (param[2] == rates[i]) { return false; + } + } return true; } @@ -848,20 +779,16 @@ bool ApplePS2Elan::elantech_is_signature_valid(const unsigned char *param) * (value from firmware) * 10 + 790 = dpi * we also have to convert dpi to dots/mm (*10/254 to avoid floating point) */ -static unsigned int elantech_convert_res(unsigned int val) -{ +unsigned int ApplePS2Elan::elantech_convert_res(unsigned int val) { return (val * 10 + 790) * 10 / 254; } - -int ApplePS2Elan::elantech_get_resolution_v4(unsigned int *x_res, - unsigned int *y_res, - unsigned int *bus) -{ +int ApplePS2Elan::elantech_get_resolution_v4(unsigned int *x_res, unsigned int *y_res, unsigned int *bus) { unsigned char param[3]; - if (elantech_send_cmd<3>(ETP_RESOLUTION_QUERY, param)) + if (elantech_send_cmd<3>(ETP_RESOLUTION_QUERY, param)) { return -1; + } *x_res = elantech_convert_res(param[1] & 0x0f); *y_res = elantech_convert_res((param[1] & 0xf0) >> 4); @@ -870,47 +797,32 @@ int ApplePS2Elan::elantech_get_resolution_v4(unsigned int *x_res, return 0; } -template -int ApplePS2Elan::send_cmd(unsigned char c, unsigned char *param) -{ - if (info.hw_version >= 3) - return elantech_send_cmd(c, param); - else - return synaptics_send_cmd(c, param); -} - /* * Use magic knock to detect Elantech touchpad */ -int ApplePS2Elan::elantechDetect() -{ +int ApplePS2Elan::elantechDetect() { unsigned char param[3]; if (ps2_command<0>(NULL, kDP_SetDefaults) || - ps2_command<0>( NULL, kDP_SetDefaultsAndDisable) || - ps2_command<0>( NULL, kDP_SetMouseScaling1To1) || - ps2_command<0>( NULL, kDP_SetMouseScaling1To1) || - ps2_command<0>( NULL, kDP_SetMouseScaling1To1) || + ps2_command<0>(NULL, kDP_SetDefaultsAndDisable) || + ps2_command<0>(NULL, kDP_SetMouseScaling1To1) || + ps2_command<0>(NULL, kDP_SetMouseScaling1To1) || + ps2_command<0>(NULL, kDP_SetMouseScaling1To1) || ps2_command<3>(param, kDP_GetMouseInformation)) { - IOLog("VoodooPS2Elan: sending Elantech magic knock failed.\n"); + DEBUG_LOG("VoodooPS2Elan: sending Elantech magic knock failed.\n"); return -1; } - /* - * Report this in case there are Elantech models that use a different - * set of magic numbers - */ - if (param[0] != 0x3c || param[1] != 0x03 || - (param[2] != 0xc8 && param[2] != 0x00)) { + // Report this in case there are Elantech models that use a different + // set of magic numbers + if (param[0] != 0x3c || param[1] != 0x03 || (param[2] != 0xc8 && param[2] != 0x00)) { IOLog("VoodooPS2Elan: unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", param[0], param[1], param[2]); return -1; } - /* - * Query touchpad's firmware version and see if it reports known - * value to avoid mis-detection. Logitech mice are known to respond - * to Elantech magic knock and there might be more. - */ + // Query touchpad's firmware version and see if it reports known + // value to avoid mis-detection. Logitech mice are known to respond + // to Elantech magic knock and there might be more. if (synaptics_send_cmd<3>(ETP_FW_VERSION_QUERY, param)) { IOLog("VoodooPS2Elan: failed to query firmware version.\n"); return -1; @@ -926,76 +838,16 @@ int ApplePS2Elan::elantechDetect() return 0; } -/* - * determine hardware version and set some properties according to it. - */ -int ApplePS2Elan::elantechSetProperties() -{ - /* This represents the version of IC body. */ - int ver = (info.fw_version & 0x0f0000) >> 16; - - /* Early version of Elan touchpads doesn't obey the rule. */ - if (info.fw_version < 0x020030 || info.fw_version == 0x020600) - info.hw_version = 1; - else { - switch (ver) { - case 2: - case 4: - info.hw_version = 2; - break; - case 5: - info.hw_version = 3; - break; - case 6 ... 15: - info.hw_version = 4; - break; - default: - return -1; - } - } - - /* Turn on packet checking by default */ - info.paritycheck = 1; - - /* - * This firmware suffers from misreporting coordinates when - * a touch action starts causing the mouse cursor or scrolled page - * to jump. Enable a workaround. - */ - info.jumpy_cursor = (info.fw_version == 0x020022 || info.fw_version == 0x020600); - - if (info.hw_version > 1) { - /* For now show extra debug information */ - info.debug = 1; - - if (info.fw_version >= 0x020800) - info.reports_pressure = true; - } - - /* - * The signatures of v3 and v4 packets change depending on the - * value of this hardware flag. - */ - info.crc_enabled = (info.fw_version & 0x4000) == 0x4000; - - /* Enable real hardware resolution on hw_version 3 ? */ - info.set_hw_resolution = _set_hw_resolution;//!dmi_check_system(no_hw_res_dmi_table); - - return 0; -} - int ApplePS2Elan::elantechQueryInfo() { unsigned char param[3]; unsigned char traces; - /* - * Do the version query again so we can store the result - */ + // Do the version query again so we can store the result if (synaptics_send_cmd<3>(ETP_FW_VERSION_QUERY, param)) { IOLog("VoodooPS2Elan: failed to query firmware version.\n"); return -1; } - + info.fw_version = (param[0] << 16) | (param[1] << 8) | param[2]; if (elantechSetProperties()) { @@ -1011,7 +863,7 @@ int ApplePS2Elan::elantechQueryInfo() { return -1; } - IOLog("VoodooPS2Elan: Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", + IOLog("VoodooPS2Elan: Elan capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", info.capabilities[0], info.capabilities[1], info.capabilities[2]); @@ -1027,38 +879,33 @@ int ApplePS2Elan::elantechQueryInfo() { } if (info.samples[1] == 0x74 && info.hw_version == 0x03) { - /* - * This module has a bug which makes absolute mode - * unusable, so let's abort so we'll be using standard - * PS/2 protocol. - */ + // This module has a bug which makes absolute mode unusable, + // so let's abort so we'll be using standard PS/2 protocol. IOLog("VoodooPS2Elan: absolute mode broken, forcing standard PS/2 protocol\n"); return -1; } - /* The MSB indicates the presence of the trackpoint */ + // The MSB indicates the presence of the trackpoint info.has_trackpoint = (info.capabilities[0] & 0x80) == 0x80; info.x_res = 31; info.y_res = 31; if (info.hw_version == 4) { - if (elantech_get_resolution_v4(&info.x_res, - &info.y_res, - &info.bus)) { + if (elantech_get_resolution_v4(&info.x_res, &info.y_res, &info.bus)) { IOLog("VoodooPS2Elan: failed to query resolution data.\n"); } } - /* query range information */ + // query range information switch (info.hw_version) { - case 1: + case 1: info.x_min = ETP_XMIN_V1; info.y_min = ETP_YMIN_V1; info.x_max = ETP_XMAX_V1; info.y_max = ETP_YMAX_V1; - break; + break; - case 2: + case 2: if (info.fw_version == 0x020800 || info.fw_version == 0x020b00 || info.fw_version == 0x020030) { @@ -1066,212 +913,156 @@ int ApplePS2Elan::elantechQueryInfo() { info.y_min = ETP_YMIN_V2; info.x_max = ETP_XMAX_V2; info.y_max = ETP_YMAX_V2; - } else { - int i; - int fixed_dpi; - - i = (info.fw_version > 0x020800 && - info.fw_version < 0x020900) ? 1 : 2; - - if (send_cmd<3>(ETP_FW_ID_QUERY, param)) - return -1; + } else { + if (send_cmd<3>(ETP_FW_ID_QUERY, param)) { + return -1; + } - fixed_dpi = param[1] & 0x10; + int i = (info.fw_version > 0x020800 && info.fw_version < 0x020900) ? 1 : 2; + int fixed_dpi = param[1] & 0x10; - if (((info.fw_version >> 16) == 0x14) && fixed_dpi) { - if (send_cmd<3>(ETP_SAMPLE_QUERY, param)) - return -1; + if (((info.fw_version >> 16) == 0x14) && fixed_dpi) { + if (send_cmd<3>(ETP_SAMPLE_QUERY, param)) { + return -1; + } - info.x_max = (info.capabilities[1] - i) * param[1] / 2; - info.y_max = (info.capabilities[2] - i) * param[2] / 2; - } else if (info.fw_version == 0x040216) { - info.x_max = 819; - info.y_max = 405; - } else if (info.fw_version == 0x040219 || info.fw_version == 0x040215) { - info.x_max = 900; - info.y_max = 500; - } else { - info.x_max = (info.capabilities[1] - i) * 64; - info.y_max = (info.capabilities[2] - i) * 64; + info.x_max = (info.capabilities[1] - i) * param[1] / 2; + info.y_max = (info.capabilities[2] - i) * param[2] / 2; + } else if (info.fw_version == 0x040216) { + info.x_max = 819; + info.y_max = 405; + } else if (info.fw_version == 0x040219 || info.fw_version == 0x040215) { + info.x_max = 900; + info.y_max = 500; + } else { + info.x_max = (info.capabilities[1] - i) * 64; + info.y_max = (info.capabilities[2] - i) * 64; + } } - } - break; + break; - case 3: - if (send_cmd<3>(ETP_FW_ID_QUERY, param)) - return -1; + case 3: + if (send_cmd<3>(ETP_FW_ID_QUERY, param)) { + return -1; + } info.x_max = (0x0f & param[0]) << 8 | param[1]; info.y_max = (0xf0 & param[0]) << 4 | param[2]; - break; + break; - case 4: - if (send_cmd<3>(ETP_FW_ID_QUERY, param)) - return -1; + case 4: + if (send_cmd<3>(ETP_FW_ID_QUERY, param)) { + return -1; + } info.x_max = (0x0f & param[0]) << 8 | param[1]; info.y_max = (0xf0 & param[0]) << 4 | param[2]; traces = info.capabilities[1]; - if ((traces < 2) || (traces > info.x_max)) - return -1; + if ((traces < 2) || (traces > info.x_max)) { + return -1; + } info.width = info.x_max / (traces - 1); - /* column number of traces */ + // column number of traces info.x_traces = traces; - /* row number of traces */ + // row number of traces traces = info.capabilities[2]; - if ((traces >= 2) && (traces <= info.y_max)) + if ((traces >= 2) && (traces <= info.y_max)) { info.y_traces = traces; + } - break; + break; } - - /* check for the middle button: DMI matching or new v4 firmwares */ - //info.has_middle_button = dmi_check_system(elantech_dmi_has_middle_button) || - // (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info.fw_version) && - // !elantech_is_buttonpad(info)); - return 0; -} + // check if device has buttonpad + info.is_buttonpad = (info.fw_version & 0x001000) != 0; -void ApplePS2Elan::resetMouse() { - UInt8 params[2]; - ps2_command<2>(params, kDP_Reset); - - if (params[0] != 0xaa && params[1] != 0x00) { - IOLog("VoodooPS2Elan: failed resetting.\n"); - } + // check for the middle button + info.has_middle_button = ETP_NEW_IC_SMBUS_HOST_NOTIFY(info.fw_version) && !info.is_buttonpad; + + return 0; } /* - * Send an Elantech style special command to read a value from a register + * determine hardware version and set some properties according to it. */ -int ApplePS2Elan::elantechReadReg(unsigned char reg, unsigned char *val) { - unsigned char param[3]; - int rc = 0; +int ApplePS2Elan::elantechSetProperties() { + // This represents the version of IC body + int ver = (info.fw_version & 0x0f0000) >> 16; - if (reg < 0x07 || reg > 0x26) - return -1; + // Early version of Elan touchpads doesn't obey the rule + if (info.fw_version < 0x020030 || info.fw_version == 0x020600) { + info.hw_version = 1; + } else { + switch (ver) { + case 2: + case 4: + info.hw_version = 2; + break; + case 5: + info.hw_version = 3; + break; + case 6 ... 15: + info.hw_version = 4; + break; + default: + return -1; + } + } - if (reg > 0x11 && reg < 0x20) - return -1; + // Turn on packet checking by default + info.paritycheck = 1; - switch (info.hw_version) { - case 1: - if (ps2_sliced_command(ETP_REGISTER_READ) || - ps2_sliced_command(reg) || - ps2_command<3>(param, kDP_GetMouseInformation)) { - rc = -1; - } - break; + // This firmware suffers from misreporting coordinates when + // a touch action starts causing the mouse cursor or scrolled page + // to jump. Enable a workaround. + info.jumpy_cursor = (info.fw_version == 0x020022 || info.fw_version == 0x020600); - case 2: - if (elantech_ps2_command<0>( NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>( NULL, ETP_REGISTER_READ) || - elantech_ps2_command<0>( NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>( NULL, reg) || - elantech_ps2_command<3>(param, kDP_GetMouseInformation)) { - rc = -1; - } - break; + if (info.hw_version > 1) { + // For now show extra debug information + info.debug = 1; - case 3 ... 4: - if (elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, ETP_REGISTER_READWRITE) || - elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, reg) || - elantech_ps2_command<3>(param, kDP_GetMouseInformation)) { - rc = -1; - } - break; + if (info.fw_version >= 0x020800) { + info.reports_pressure = true; + } } - if (rc) - IOLog("VoodooPS2Elan: failed to read register 0x%02x.\n", reg); - else if (info.hw_version != 4) - *val = param[0]; - else - *val = param[1]; + // The signatures of v3 and v4 packets change depending on the + // value of this hardware flag. + info.crc_enabled = (info.fw_version & 0x4000) == 0x4000; - return rc; + // Enable real hardware resolution on hw_version 3 ? + info.set_hw_resolution = _set_hw_resolution; + + // Set packet length (4 for v1, 6 for v2 and newer) + _packetLength = (info.hw_version == 1) ? 4 : 6; + + return 0; } /* - * Send an Elantech style special command to write a register with a value + * Set the appropriate event bits for the input subsystem */ -int ApplePS2Elan::elantechWriteReg(unsigned char reg, unsigned char val) -{ - int rc = 0; - - if (reg < 0x07 || reg > 0x26) - return -1; - - if (reg > 0x11 && reg < 0x20) - return -1; +int ApplePS2Elan::elantechSetInputParams() { + setProperty(VOODOO_INPUT_LOGICAL_MAX_X_KEY, info.x_max - info.x_min, 32); + setProperty(VOODOO_INPUT_LOGICAL_MAX_Y_KEY, info.y_max - info.y_min, 32); - switch (info.hw_version) { - case 1: - if (ps2_sliced_command(ETP_REGISTER_WRITE) || - ps2_sliced_command(reg) || - ps2_sliced_command(val) || - ps2_command<0>(NULL, kDP_SetMouseScaling1To1)) { - rc = -1; - } - break; - - case 2: - if (elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, ETP_REGISTER_WRITE) || - elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, reg) || - elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, val) || - elantech_ps2_command<0>(NULL, kDP_SetMouseScaling1To1)) { - rc = -1; - } - break; - - case 3: - if (elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, ETP_REGISTER_READWRITE) || - elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, reg) || - elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, val) || - elantech_ps2_command<0>(NULL, kDP_SetMouseScaling1To1)) { - rc = -1; - } - break; - - case 4: - if (elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, ETP_REGISTER_READWRITE) || - elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, reg) || - elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, ETP_REGISTER_READWRITE) || - elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || - elantech_ps2_command<0>(NULL, val) || - elantech_ps2_command<0>(NULL, kDP_SetMouseScaling1To1)) { - rc = -1; - } - break; - } + setProperty(VOODOO_INPUT_PHYSICAL_MAX_X_KEY, (info.x_max + 1) * 100 / info.x_res, 32); + setProperty(VOODOO_INPUT_PHYSICAL_MAX_Y_KEY, (info.y_max + 1) * 100 / info.y_res, 32); - if (rc) - IOLog("VoodooPS2Elan: failed to write register 0x%02x with value 0x%02x.\n", - reg, val); + setProperty("IOFBTransform", 0ull, 32); + setProperty("VoodooInputSupported", kOSBooleanTrue); + registerService(); - return rc; + return 0; } /* * Put the touchpad into absolute mode */ -int ApplePS2Elan::elantechSetAbsoluteMode() -{ +int ApplePS2Elan::elantechSetAbsoluteMode() { unsigned char val; int tries = ETP_READ_BACK_TRIES; int rc = 0; @@ -1287,10 +1078,10 @@ int ApplePS2Elan::elantechSetAbsoluteMode() break; case 2: - /* Windows driver values */ + // Windows driver values etd.reg_10 = 0x54; - etd.reg_11 = 0x88; /* 0x8a */ - etd.reg_21 = 0x60; /* 0x00 */ + etd.reg_11 = 0x88; // 0x8a + etd.reg_21 = 0x60; // 0x00 if (elantechWriteReg(0x10, etd.reg_10) || elantechWriteReg(0x11, etd.reg_11) || elantechWriteReg(0x21, etd.reg_21)) { @@ -1299,35 +1090,37 @@ int ApplePS2Elan::elantechSetAbsoluteMode() break; case 3: - if (info.set_hw_resolution) + if (info.set_hw_resolution) { etd.reg_10 = 0x0b; - else + } else { etd.reg_10 = 0x01; + } - if (elantechWriteReg(0x10, etd.reg_10)) + if (elantechWriteReg(0x10, etd.reg_10)) { rc = -1; + } break; case 4: etd.reg_07 = 0x01; - if (elantechWriteReg(0x07, etd.reg_07)) + if (elantechWriteReg(0x07, etd.reg_07)) { rc = -1; + } - goto skip_readback_reg_10; /* v4 has no reg 0x10 to read */ + goto skip_readback_reg_10; // v4 has no reg 0x10 to read } if (rc == 0) { - /* - * Read back reg 0x10. For hardware version 1 we must make - * sure the absolute mode bit is set. For hardware version 2 - * the touchpad is probably initializing and not ready until - * we read back the value we just wrote. - */ + // Read back reg 0x10. For hardware version 1 we must make + // sure the absolute mode bit is set. For hardware version 2 + // the touchpad is probably initializing and not ready until + // we read back the value we just wrote. do { rc = elantechReadReg(0x10, &val); - if (rc == 0) + if (rc == 0) { break; + } tries--; IOLog("VoodooPS2Elan: retrying read (%d).\n", tries); IOSleep(ETP_READ_BACK_DELAY); @@ -1335,47 +1128,26 @@ int ApplePS2Elan::elantechSetAbsoluteMode() if (rc) { IOLog("VoodooPS2Elan: failed to read back register 0x10.\n"); - } else if (info.hw_version == 1 && - !(val & ETP_R10_ABSOLUTE_MODE)) { + } else if (info.hw_version == 1 && !(val & ETP_R10_ABSOLUTE_MODE)) { IOLog("VoodooPS2Elan: touchpad refuses to switch to absolute mode.\n"); rc = -1; } } - skip_readback_reg_10: - if (rc) +skip_readback_reg_10: + if (rc) { IOLog("VoodooPS2Elan: failed to initialise registers.\n"); + } return rc; } -/* - * Set the appropriate event bits for the input subsystem - */ -int ApplePS2Elan::elantechSetInputParams() -{ - setProperty(VOODOO_INPUT_LOGICAL_MAX_X_KEY, info.x_max - info.x_min, 32); - setProperty(VOODOO_INPUT_LOGICAL_MAX_Y_KEY, info.y_max - info.y_min, 32); - - setProperty(VOODOO_INPUT_PHYSICAL_MAX_X_KEY, (info.x_max + 1) * 100 / info.x_res, 32); - setProperty(VOODOO_INPUT_PHYSICAL_MAX_Y_KEY, (info.y_max + 1) * 100 / info.y_res, 32); - - setProperty("IOFBTransform", 0ull, 32); - setProperty("VoodooInputSupported", kOSBooleanTrue); - registerService(); - - return 0; -} - /* * Initialize the touchpad */ -int ApplePS2Elan::elantechSetupPS2() -{ - int i; - +int ApplePS2Elan::elantechSetupPS2() { etd.parity[0] = 1; - for (i = 1; i < 256; i++) + for (int i = 1; i < 256; i++) etd.parity[i] = etd.parity[i & (i - 1)] ^ 1; if (elantechSetAbsoluteMode()) { @@ -1383,16 +1155,18 @@ int ApplePS2Elan::elantechSetupPS2() return -1; } + /* if (info.fw_version == 0x381f17) { - //etd.original_set_rate = psmouse->set_rate; - //psmouse->set_rate = elantech_set_rate_restore_reg_07; + etd.original_set_rate = psmouse->set_rate; + psmouse->set_rate = elantech_set_rate_restore_reg_07; } + */ if (elantechSetInputParams()) { IOLog("VoodooPS2: failed to query touchpad range.\n"); return -1; } - + // set resolution and dpi TPS2Request<> request; request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; @@ -1400,122 +1174,541 @@ int ApplePS2Elan::elantechSetupPS2() request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; request.commands[1].inOrOut = kDP_SetMouseSampleRate; // 0xF3 request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[2].inOrOut = _mouseSampleRate ; //0x64 * 2; // 200 dpi + request.commands[2].inOrOut = _mouseSampleRate; // 200 dpi request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; request.commands[3].inOrOut = kDP_SetMouseResolution; // 0xE8 request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[4].inOrOut = _mouseResolution; // 0x03; // 0x03 = 8 counts/mm + request.commands[4].inOrOut = _mouseResolution; // 0x03 = 8 counts/mm request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; request.commands[5].inOrOut = kDP_SetMouseScaling1To1; // 0xE6 request.commands[6].command = kPS2C_SendMouseCommandAndCompareAck; request.commands[6].inOrOut = kDP_Enable; // 0xF4, Enable Data Reporting request.commandsCount = 7; _device->submitRequestAndBlock(&request); - + return 0; } -PS2InterruptResult ApplePS2Elan::interruptOccurred(UInt8 data) { - UInt8* packet = _ringBuffer.head(); - packet[_packetByteCount++] = data; - - if (_packetByteCount == kPacketLengthMax) - { - _ringBuffer.advanceHead(kPacketLengthMax); - _packetByteCount = 0; - return kPS2IR_packetReady; +/* + * Send an Elantech style special command to read a value from a register + */ +int ApplePS2Elan::elantechReadReg(unsigned char reg, unsigned char *val) { + unsigned char param[3]; + int rc = 0; + + if (reg < 0x07 || reg > 0x26) { + return -1; + } + + if (reg > 0x11 && reg < 0x20) { + return -1; + } + + switch (info.hw_version) { + case 1: + if (ps2_sliced_command(ETP_REGISTER_READ) || + ps2_sliced_command(reg) || + ps2_command<3>(param, kDP_GetMouseInformation)) { + rc = -1; + } + break; + + case 2: + if (elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, ETP_REGISTER_READ) || + elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, reg) || + elantech_ps2_command<3>(param, kDP_GetMouseInformation)) { + rc = -1; + } + break; + + case 3 ... 4: + if (elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, ETP_REGISTER_READWRITE) || + elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, reg) || + elantech_ps2_command<3>(param, kDP_GetMouseInformation)) { + rc = -1; + } + break; + } + + if (rc) { + IOLog("VoodooPS2Elan: failed to read register 0x%02x.\n", reg); + } else if (info.hw_version != 4) { + *val = param[0]; + } else { + *val = param[1]; + } + + return rc; +} + +/* + * Send an Elantech style special command to write a register with a value + */ +int ApplePS2Elan::elantechWriteReg(unsigned char reg, unsigned char val) { + int rc = 0; + + if (reg < 0x07 || reg > 0x26) { + return -1; + } + + if (reg > 0x11 && reg < 0x20) { + return -1; + } + + switch (info.hw_version) { + case 1: + if (ps2_sliced_command(ETP_REGISTER_WRITE) || + ps2_sliced_command(reg) || + ps2_sliced_command(val) || + ps2_command<0>(NULL, kDP_SetMouseScaling1To1)) { + rc = -1; + } + break; + + case 2: + if (elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, ETP_REGISTER_WRITE) || + elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, reg) || + elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, val) || + elantech_ps2_command<0>(NULL, kDP_SetMouseScaling1To1)) { + rc = -1; + } + break; + + case 3: + if (elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, ETP_REGISTER_READWRITE) || + elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, reg) || + elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, val) || + elantech_ps2_command<0>(NULL, kDP_SetMouseScaling1To1)) { + rc = -1; + } + break; + + case 4: + if (elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, ETP_REGISTER_READWRITE) || + elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, reg) || + elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, ETP_REGISTER_READWRITE) || + elantech_ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + elantech_ps2_command<0>(NULL, val) || + elantech_ps2_command<0>(NULL, kDP_SetMouseScaling1To1)) { + rc = -1; + } + break; + } + + if (rc) { + IOLog("VoodooPS2Elan: failed to write register 0x%02x with value 0x%02x.\n", reg, val); + } + + return rc; +} + +int ApplePS2Elan::elantechDebounceCheckV2() { + // When we encounter packet that matches this exactly, it means the + // hardware is in debounce status. Just ignore the whole packet. + static const uint8_t debounce_packet[] = { + 0x84, 0xff, 0xff, 0x02, 0xff, 0xff + }; + + unsigned char *packet = _ringBuffer.tail(); + + return !memcmp(packet, debounce_packet, sizeof(debounce_packet)); +} + +int ApplePS2Elan::elantechPacketCheckV1() { + unsigned char *packet = _ringBuffer.tail(); + unsigned char p1, p2, p3; + + // Parity bits are placed differently + if (info.fw_version < 0x020000) { + // byte 0: D U p1 p2 1 p3 R L + p1 = (packet[0] & 0x20) >> 5; + p2 = (packet[0] & 0x10) >> 4; + } else { + // byte 0: n1 n0 p2 p1 1 p3 R L + p1 = (packet[0] & 0x10) >> 4; + p2 = (packet[0] & 0x20) >> 5; + } + + p3 = (packet[0] & 0x04) >> 2; + + return etd.parity[packet[1]] == p1 && + etd.parity[packet[2]] == p2 && + etd.parity[packet[3]] == p3; +} + +int ApplePS2Elan::elantechPacketCheckV2() { + unsigned char *packet = _ringBuffer.tail(); + + // V2 hardware has two flavors. Older ones that do not report pressure, + // and newer ones that reports pressure and width. With newer ones, all + // packets (1, 2, 3 finger touch) have the same constant bits. With + // older ones, 1/3 finger touch packets and 2 finger touch packets + // have different constant bits. + // With all three cases, if the constant bits are not exactly what I + // expected, I consider them invalid. + + if (info.reports_pressure) { + return (packet[0] & 0x0c) == 0x04 && (packet[3] & 0x0f) == 0x02; + } + + if ((packet[0] & 0xc0) == 0x80) { + return (packet[0] & 0x0c) == 0x0c && (packet[3] & 0x0e) == 0x08; + } + + return (packet[0] & 0x3c) == 0x3c && + (packet[1] & 0xf0) == 0x00 && + (packet[3] & 0x3e) == 0x38 && + (packet[4] & 0xf0) == 0x00; +} + +int ApplePS2Elan::elantechPacketCheckV3() { + static const uint8_t debounce_packet[] = { + 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff + }; + + unsigned char *packet = _ringBuffer.tail(); + + // check debounce first, it has the same signature in byte 0 + // and byte 3 as PACKET_V3_HEAD. + if (!memcmp(packet, debounce_packet, sizeof(debounce_packet))) { + return PACKET_DEBOUNCE; + } + + // If the hardware flag 'crc_enabled' is set the packets have different signatures. + if (info.crc_enabled) { + if ((packet[3] & 0x09) == 0x08) { + return PACKET_V3_HEAD; + } + + if ((packet[3] & 0x09) == 0x09) { + return PACKET_V3_TAIL; + } + } else { + if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02) { + return PACKET_V3_HEAD; + } + + if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c) { + return PACKET_V3_TAIL; + } + + if ((packet[3] & 0x0f) == 0x06) { + return PACKET_TRACKPOINT; + } + } + + return PACKET_UNKNOWN; +} + +int ApplePS2Elan::elantechPacketCheckV4() { + unsigned char *packet = _ringBuffer.tail(); + unsigned char packet_type = packet[3] & 0x03; + unsigned int ic_version; + bool sanity_check; + + INTERRUPT_LOG("VoodooPS2Elan: Packet dump (%04x, %04x, %04x, %04x, %04x, %04x)\n", packet[0], packet[1], packet[2], packet[3], packet[4], packet[5]); + + if (info.has_trackpoint && (packet[3] & 0x0f) == 0x06) { + return PACKET_TRACKPOINT; + } + + // This represents the version of IC body. + ic_version = (info.fw_version & 0x0f0000) >> 16; + + INTERRUPT_LOG("VoodooPS2Elan: icVersion(%d), crc(%d), samples[1](%d) \n", ic_version, info.crc_enabled, info.samples[1]); + + // Sanity check based on the constant bits of a packet. + // The constant bits change depending on the value of + // the hardware flag 'crc_enabled' and the version of + // the IC body, but are the same for every packet, + // regardless of the type. + if (info.crc_enabled) { + sanity_check = ((packet[3] & 0x08) == 0x00); + } else if (ic_version == 7 && info.samples[1] == 0x2A) { + sanity_check = ((packet[3] & 0x1c) == 0x10); + } else { + sanity_check = ((packet[0] & 0x08) == 0x00 && (packet[3] & 0x1c) == 0x10); + } + + if (!sanity_check) { + return PACKET_UNKNOWN; + } + + switch (packet_type) { + case 0: + return PACKET_V4_STATUS; + + case 1: + return PACKET_V4_HEAD; + + case 2: + return PACKET_V4_MOTION; + } + + return PACKET_UNKNOWN; +} + +void ApplePS2Elan::elantechReportAbsoluteV1() { + unsigned char *packet = _ringBuffer.tail(); + unsigned int fingers = 0, x = 0, y = 0; + + if (info.fw_version < 0x020000) { + // byte 0: D U p1 p2 1 p3 R L + // byte 1: f 0 th tw x9 x8 y9 y8 + fingers = ((packet[1] & 0x80) >> 7) + ((packet[1] & 0x30) >> 4); + } else { + // byte 0: n1 n0 p2 p1 1 p3 R L + // byte 1: 0 0 0 0 x9 x8 y9 y8 + fingers = (packet[0] & 0xc0) >> 6; + } + + if (info.jumpy_cursor) { + if (fingers != 1) { + etd.single_finger_reports = 0; + } else if (etd.single_finger_reports < 2) { + // Discard first 2 reports of one finger, bogus + etd.single_finger_reports++; + INTERRUPT_LOG("VoodooPS2Elan: discarding packet\n"); + return; + } + } + + // byte 2: x7 x6 x5 x4 x3 x2 x1 x0 + // byte 3: y7 y6 y5 y4 y3 y2 y1 y0 + x = ((packet[1] & 0x0c) << 6) | packet[2]; + y = info.y_max - (((packet[1] & 0x03) << 8) | packet[3]); + + virtualFinger[0].touch = false; + virtualFinger[1].touch = false; + virtualFinger[2].touch = false; + + leftButton = packet[0] & 0x01; + rightButton = packet[0] & 0x02; + + if (fingers == 1) { + virtualFinger[0].touch = true; + virtualFinger[0].button = packet[0] & 0x03; + virtualFinger[0].prev = virtualFinger[0].now; + virtualFinger[0].now.x = x; + virtualFinger[0].now.y = y; + if (lastFingers != 1) { + virtualFinger[0].prev = virtualFinger[0].now; + } + } + + if (fingers == 2) { + virtualFinger[0].touch = virtualFinger[1].touch = true; + virtualFinger[0].button = virtualFinger[1].button = packet[0] & 0x03; + virtualFinger[0].prev = virtualFinger[0].now; + virtualFinger[1].prev = virtualFinger[1].now; + + int h = 100; + int dy = (int)(sin30deg * h); + int dx = (int)(cos30deg * h); + + virtualFinger[0].now.x = x; + virtualFinger[0].now.y = y - h; + + virtualFinger[1].now.x = x + dx; + virtualFinger[1].now.y = y + dy; + + if (lastFingers != 2) { + virtualFinger[0].prev = virtualFinger[0].now; + virtualFinger[1].prev = virtualFinger[1].now; + } + } + + if (fingers == 3) { + virtualFinger[0].touch = virtualFinger[1].touch = virtualFinger[2].touch = true; + virtualFinger[0].button = virtualFinger[1].button = virtualFinger[2].button = packet[0] & 0x03; + virtualFinger[0].prev = virtualFinger[0].now; + virtualFinger[1].prev = virtualFinger[1].now; + virtualFinger[2].prev = virtualFinger[2].now; + + int h = 100; + int dy = (int)(sin30deg * h); + int dx = (int)(cos30deg * h); + + virtualFinger[0].now.x = x; + virtualFinger[0].now.y = y - h; + + virtualFinger[1].now.x = x - dx; + virtualFinger[1].now.y = y + dy; + + virtualFinger[2].now.x = x + dx; + virtualFinger[2].now.y = y + dy; + + if (lastFingers != 3) { + virtualFinger[0].prev = virtualFinger[0].now; + virtualFinger[1].prev = virtualFinger[1].now; + virtualFinger[2].prev = virtualFinger[2].now; + } + } + + lastFingers = fingers; + sendTouchData(); +} + +void ApplePS2Elan::elantechReportAbsoluteV2() { + unsigned char *packet = _ringBuffer.tail(); + unsigned int fingers = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0; + unsigned int width = 0, pres = 0; + + // byte 0: n1 n0 . . . . R L + fingers = (packet[0] & 0xc0) >> 6; + + switch (fingers) { + case 3: + case 1: + // byte 1: . . . . x11 x10 x9 x8 + // byte 2: x7 x6 x5 x4 x4 x2 x1 x0 + x1 = ((packet[1] & 0x0f) << 8) | packet[2]; + + // byte 4: . . . . y11 y10 y9 y8 + // byte 5: y7 y6 y5 y4 y3 y2 y1 y0 + y1 = info.y_max - (((packet[4] & 0x0f) << 8) | packet[5]); + + pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); + width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); + break; + + case 2: + // The coordinate of each finger is reported separately + // with a lower resolution for two finger touches: + + // byte 0: . . ay8 ax8 . . . . + // byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 + x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2; + + // byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 + y1 = info.y_max - ((((packet[0] & 0x20) << 3) | packet[2]) << 2); + + // byte 3: . . by8 bx8 . . . . + // byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 + x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2; + + // byte 5: by7 by8 by5 by4 by3 by2 by1 by0 + y2 = info.y_max - ((((packet[3] & 0x20) << 3) | packet[5]) << 2); + + // Unknown so just report sensible values + pres = 127; + width = 7; + break; + } + + virtualFinger[0].touch = false; + virtualFinger[1].touch = false; + virtualFinger[2].touch = false; + + leftButton = packet[0] & 0x01; + rightButton = packet[0] & 0x02; + + if (fingers == 1 || fingers == 2) { + virtualFinger[0].touch = true; + virtualFinger[0].button = packet[0] & 0x03; + virtualFinger[0].prev = virtualFinger[0].now; + virtualFinger[0].now.x = x1; + virtualFinger[0].now.y = y1; + if (lastFingers != 1 && lastFingers != 2) { + virtualFinger[0].prev = virtualFinger[0].now; + } + } + + if (fingers == 2) { + virtualFinger[1].touch = true; + virtualFinger[1].button = packet[0] & 0x03; + virtualFinger[1].prev = virtualFinger[1].now; + virtualFinger[1].now.x = x2; + virtualFinger[1].now.y = y2; + if (lastFingers != 2) { + virtualFinger[1].prev = virtualFinger[1].now; + } } - return kPS2IR_packetBuffering; -} - -int ApplePS2Elan::elantechPacketCheckV3() -{ - static const uint8_t debounce_packet[] = { - 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff - }; + if (fingers == 3) { + virtualFinger[0].touch = virtualFinger[1].touch = virtualFinger[2].touch = true; + virtualFinger[0].button = virtualFinger[1].button = virtualFinger[2].button = packet[0] & 0x03; + virtualFinger[0].prev = virtualFinger[0].now; + virtualFinger[1].prev = virtualFinger[1].now; + virtualFinger[2].prev = virtualFinger[2].now; - unsigned char *packet = _ringBuffer.tail(); + int h = 100; + int dy = (int)(sin30deg * h); + int dx = (int)(cos30deg * h); - /* - * check debounce first, it has the same signature in byte 0 - * and byte 3 as PACKET_V3_HEAD. - */ - if (!memcmp(packet, debounce_packet, sizeof(debounce_packet))) - return PACKET_DEBOUNCE; + virtualFinger[0].now.x = x1; + virtualFinger[0].now.y = y1 - h; - /* - * If the hardware flag 'crc_enabled' is set the packets have - * different signatures. - */ - if (info.crc_enabled) { - if ((packet[3] & 0x09) == 0x08) - return PACKET_V3_HEAD; + virtualFinger[1].now.x = x1 - dx; + virtualFinger[1].now.y = y1 + dy; - if ((packet[3] & 0x09) == 0x09) - return PACKET_V3_TAIL; - } else { - if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02) - return PACKET_V3_HEAD; + virtualFinger[2].now.x = x1 + dx; + virtualFinger[2].now.y = y1 + dy; - if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c) - return PACKET_V3_TAIL; - if ((packet[3] & 0x0f) == 0x06) - return PACKET_TRACKPOINT; + if (lastFingers != 3) { + virtualFinger[0].prev = virtualFinger[0].now; + virtualFinger[1].prev = virtualFinger[1].now; + virtualFinger[2].prev = virtualFinger[2].now; + } } - return PACKET_UNKNOWN; + lastFingers = fingers; + sendTouchData(); } -void ApplePS2Elan::elantechReportAbsoluteV3(int packetType) -{ +void ApplePS2Elan::elantechReportAbsoluteV3(int packetType) { unsigned char *packet = _ringBuffer.tail(); unsigned int fingers = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0; unsigned int width = 0, pres = 0; - /* byte 0: n1 n0 . . . . R L */ + // byte 0: n1 n0 . . . . R L fingers = (packet[0] & 0xc0) >> 6; switch (fingers) { - case 3: - case 1: - /* - * byte 1: . . . . x11 x10 x9 x8 - * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 - */ - x1 = ((packet[1] & 0x0f) << 8) | packet[2]; - /* - * byte 4: . . . . y11 y10 y9 y8 - * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 - */ + case 3: + case 1: + // byte 1: . . . . x11 x10 x9 x8 + // byte 2: x7 x6 x5 x4 x4 x2 x1 x0 + x1 = ((packet[1] & 0x0f) << 8) | packet[2]; + + // byte 4: . . . . y11 y10 y9 y8 + // byte 5: y7 y6 y5 y4 y3 y2 y1 y0 y1 = info.y_max - (((packet[4] & 0x0f) << 8) | packet[5]); - break; - - case 2: - if (packetType == PACKET_V3_HEAD) { - /* - * byte 1: . . . . ax11 ax10 ax9 ax8 - * byte 2: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 - */ - etd.mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2]; - /* - * byte 4: . . . . ay11 ay10 ay9 ay8 - * byte 5: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 - */ - etd.mt[0].y = info.y_max - - (((packet[4] & 0x0f) << 8) | packet[5]); - /* - * wait for next packet - */ - return; - } + break; - /* packet_type == PACKET_V3_TAIL */ + case 2: + if (packetType == PACKET_V3_HEAD) { + // byte 1: . . . . ax11 ax10 ax9 ax8 + // byte 2: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 + etd.mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2]; + + // byte 4: . . . . ay11 ay10 ay9 ay8 + // byte 5: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 + etd.mt[0].y = info.y_max - (((packet[4] & 0x0f) << 8) | packet[5]); + + // wait for next packet + return; + } + + // packet_type == PACKET_V3_TAIL x1 = etd.mt[0].x; y1 = etd.mt[0].y; x2 = ((packet[1] & 0x0f) << 8) | packet[2]; y2 = info.y_max - (((packet[4] & 0x0f) << 8) | packet[5]); - break; + break; } pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); @@ -1524,113 +1717,153 @@ void ApplePS2Elan::elantechReportAbsoluteV3(int packetType) virtualFinger[0].touch = false; virtualFinger[1].touch = false; virtualFinger[2].touch = false; - + leftButton = packet[0] & 0x01; rightButton = packet[0] & 0x02; - - if (fingers == 1 || fingers == 2) - { + + if (fingers == 1 || fingers == 2) { virtualFinger[0].touch = true; virtualFinger[0].button = packet[0] & 0x03; virtualFinger[0].prev = virtualFinger[0].now; virtualFinger[0].now.x = x1; virtualFinger[0].now.y = y1; - if (lastFingersV3 != 1 && lastFingersV3 != 2) + if (lastFingers != 1 && lastFingers != 2) { virtualFinger[0].prev = virtualFinger[0].now; + } } - if (fingers == 2) - { + + if (fingers == 2) { virtualFinger[1].touch = true; virtualFinger[1].button = packet[0] & 0x03; virtualFinger[1].prev = virtualFinger[1].now; virtualFinger[1].now.x = x2; virtualFinger[1].now.y = y2; - if (lastFingersV3 != 2) + if (lastFingers != 2) { virtualFinger[1].prev = virtualFinger[1].now; + } } - if (fingers == 3) - { + + if (fingers == 3) { virtualFinger[0].touch = virtualFinger[1].touch = virtualFinger[2].touch = true; virtualFinger[0].button = virtualFinger[1].button = virtualFinger[2].button = packet[0] & 0x03; virtualFinger[0].prev = virtualFinger[0].now; virtualFinger[1].prev = virtualFinger[1].now; virtualFinger[2].prev = virtualFinger[2].now; - - float sin30deg = 0.5f; - float cos30deg = 0.86602540378f; - + int h = 100; int dy = (int)(sin30deg * h); int dx = (int)(cos30deg * h); - + virtualFinger[0].now.x = x1; virtualFinger[0].now.y = y1 - h; - + virtualFinger[1].now.x = x1 - dx; virtualFinger[1].now.y = y1 + dy; - + virtualFinger[2].now.x = x1 + dx; virtualFinger[2].now.y = y1 + dy; - - if (lastFingersV3 != 3) - { + + if (lastFingers != 3) { virtualFinger[0].prev = virtualFinger[0].now; virtualFinger[1].prev = virtualFinger[1].now; virtualFinger[2].prev = virtualFinger[2].now; } } - - lastFingersV3 = fingers; - elantechInputSyncV4(); + + lastFingers = fingers; + sendTouchData(); } -int ApplePS2Elan::elantechPacketCheckV4() -{ - unsigned char *packet = _ringBuffer.tail(); - unsigned char packet_type = packet[3] & 0x03; - unsigned int ic_version; - bool sanity_check; - - INTERRUPT_LOG("VoodooPS2Elan: Packet dump (%04x, %04x, %04x, %04x, %04x, %04x)\n", packet[0], packet[1], packet[2], packet[3], packet[4], packet[5] ); +void ApplePS2Elan::elantechReportAbsoluteV4(int packetType) { + AbsoluteTime timestamp; + clock_get_uptime(×tamp); - if ((packet[3] & 0x0f) == 0x06) - return PACKET_TRACKPOINT; + inputEvent.timestamp = timestamp; - /* This represents the version of IC body. */ - ic_version = (info.fw_version & 0x0f0000) >> 16; - - INTERRUPT_LOG("VoodooPS2Elan: icVersion(%d), crc(%d), samples[1](%d) \n", ic_version, info.crc_enabled, info.samples[1]); + switch (packetType) { + case PACKET_V4_STATUS: + INTERRUPT_LOG("VoodooPS2Elan: Got status packet\n"); + processPacketStatusV4(); + break; - /* - * Sanity check based on the constant bits of a packet. - * The constant bits change depending on the value of - * the hardware flag 'crc_enabled' and the version of - * the IC body, but are the same for every packet, - * regardless of the type. - */ - if (info.crc_enabled) - sanity_check = ((packet[3] & 0x08) == 0x00); - else if (ic_version == 7 && info.samples[1] == 0x2A) - sanity_check = ((packet[3] & 0x1c) == 0x10); - else - sanity_check = ((packet[0] & 0x08) == 0x00 && - (packet[3] & 0x1c) == 0x10); + case PACKET_V4_HEAD: + INTERRUPT_LOG("VoodooPS2Elan: Got head packet\n"); + processPacketHeadV4(); + break; - if (!sanity_check) - return PACKET_UNKNOWN; + case PACKET_V4_MOTION: + INTERRUPT_LOG("VoodooPS2Elan: Got motion packet\n"); + processPacketMotionV4(); + break; - switch (packet_type) { - case 0: - return PACKET_V4_STATUS; + case PACKET_UNKNOWN: + default: + // impossible to get here + break; + } +} - case 1: - return PACKET_V4_HEAD; +void ApplePS2Elan::elantechReportTrackpoint() { + // byte 0: 0 0 sx sy 0 M R L + // byte 1: ~sx 0 0 0 0 0 0 0 + // byte 2: ~sy 0 0 0 0 0 0 0 + // byte 3: 0 0 ~sy ~sx 0 1 1 0 + // byte 4: x7 x6 x5 x4 x3 x2 x1 x0 + // byte 5: y7 y6 y5 y4 y3 y2 y1 y0 + // + // x and y are written in two's complement spread + // over 9 bits with sx/sy the relative top bit and + // x7..x0 and y7..y0 the lower bits. + // ~sx is the inverse of sx, ~sy is the inverse of sy. + // The sign of y is opposite to what the input driver + // expects for a relative movement + + UInt32 *t = (UInt32 *)_ringBuffer.tail(); + UInt32 signature = *t & ~7U; + if (signature != 0x06000030U && + signature != 0x16008020U && + signature != 0x26800010U && + signature != 0x36808000U) { + INTERRUPT_LOG("VoodooPS2Elan: unexpected trackpoint packet skipped\n"); + return; + } - case 2: - return PACKET_V4_MOTION; + unsigned char *packet = _ringBuffer.tail(); + + int trackpointLeftButton = packet[0] & 0x1; + int trackpointRightButton = packet[0] & 0x2; + int trackpointMiddleButton = packet[0] & 0x4; + + int dx = packet[4] - (int)((packet[1] ^ 0x80) << 1); + int dy = (int)((packet[2] ^ 0x80) << 1) - packet[5]; + + dx = dx * _trackpointMultiplierX / _trackpointDividerX; + dy = dy * _trackpointMultiplierY / _trackpointDividerY; + + // enable trackpoint scroll mode when middle button was pressed and the trackpoint moved + if (trackpointMiddleButton == 4 && (dx != 0 || dy != 0)) { + trackpointScrolling = true; } - return PACKET_UNKNOWN; + // disable trackpoint scrolling mode when middle button is released + if (trackpointScrolling && trackpointMiddleButton == 0) { + trackpointScrolling = false; + } + + AbsoluteTime timestamp; + clock_get_uptime(×tamp); + + // remember last time trackpoint was used. this can be used in + // interrupt handler to detect unintended input + uint64_t timestamp_ns; + absolutetime_to_nanoseconds(timestamp, ×tamp_ns); + keytime = timestamp_ns; + + if (trackpointScrolling) { + dispatchScrollWheelEvent(dx, dy, 0, timestamp); + } else { + dispatchRelativePointerEvent(dx, dy, trackpointRightButton | trackpointLeftButton | trackpointMiddleButton, timestamp); + } } void ApplePS2Elan::processPacketStatusV4() { @@ -1639,7 +1872,7 @@ void ApplePS2Elan::processPacketStatusV4() { leftButton = packet[0] & 0x1; rightButton = packet[0] & 0x2; - /* notify finger state change */ + // notify finger state change fingers = packet[1] & 0x1f; int count = 0; for (int i = 0; i < ETP_MAX_FINGERS; i++) { @@ -1647,81 +1880,35 @@ void ApplePS2Elan::processPacketStatusV4() { // finger has been lifted off the touchpad INTERRUPT_LOG("VoodooPS2Elan: %d finger has been lifted off the touchpad\n", i); virtualFinger[i].touch = false; - } - else - { + } else { virtualFinger[i].touch = true; INTERRUPT_LOG("VoodooPS2Elan: %d finger has been touched the touchpad\n", i); count++; } } - + heldFingers = count; - - //reportLeft(packet[0] & 1, 0, true); - //reportRight(packet[0] & 2, 0, true); - //reportMiddle(packet[0] & 4, 0, true); - + headPacketsCount = 0; - - // if count > 0 - // then we wait for HEAD packets to report - // so that we report all fingers at once. - // if count == 0, we have to report the fact fingers are taken off, because there won't be any HEAD packets - if (count == 0) - elantechInputSyncV4(); -} -void ApplePS2Elan::reportLeft(int state, int finger, bool status) -{ - if (leftButtons[finger] != state) - { - auto str = status ? "STATUS" : "HEAD/MOTION"; - if (state == 0) - IOLog("VoodooPS2ElanButtons: [%s] left button, finger %d lifted (fingers on touchpad: %d)\n", str, finger, heldFingers); - else - IOLog("VoodooPS2ElanButtons: [%s] left button, finger %d pressed (fingers on touchpad: %d)\n", str, finger, heldFingers); - leftButtons[finger] = state; - } -} -/* -void ApplePS2Elan::reportMiddle(int state, int finger) -{ - if (middleButtons[finger] != state) - { - if (state == 0) - IOLog("VoodooPS2ElanButtons: middle button, finger %d lifted (fingers on touchpad: %d)\n", finger, heldFingers); - else - IOLog("VoodooPS2ElanButtons: middle button, finger %d pressed (fingers on touchpad: %d)\n", finger, heldFingers); - middleButtons[finger] = state; - } -}*/ - -void ApplePS2Elan::reportRight(int state, int finger, bool status) -{ - if (rightButtons[finger] != state) - { - auto str = status ? "STATUS" : "HEAD/MOTION"; - - if (state == 0) - IOLog("VoodooPS2ElanButtons: [%s] right button, finger %d lifted (fingers on touchpad: %d)\n", str, finger, heldFingers); - else - IOLog("VoodooPS2ElanButtons: [%s] right button, finger %d pressed (fingers on touchpad: %d)\n", str, finger, heldFingers); - rightButtons[finger] = state; + // if count > 0, we wait for HEAD packets to report so that we report all fingers at once. + // if count == 0, we have to report the fact fingers are taken off, because there won't be any HEAD packets + if (count == 0) { + sendTouchData(); } } void ApplePS2Elan::processPacketHeadV4() { unsigned char *packet = _ringBuffer.tail(); - + leftButton = packet[0] & 0x1; rightButton = packet[0] & 0x2; - + int id = ((packet[3] & 0xe0) >> 5) - 1; int pres, traces; headPacketsCount++; - + if (id < 0) { INTERRUPT_LOG("VoodooPS2Elan: invalid id, aborting\n"); return; @@ -1735,29 +1922,20 @@ void ApplePS2Elan::processPacketHeadV4() { pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); traces = (packet[0] & 0xf0) >> 4; - + INTERRUPT_LOG("VoodooPS2Elan: pres: %d, traces: %d, width: %d\n", pres, traces, etd.width); - - //reportLeft(packet[0] & 1, 0, false); - //reportRight(packet[0] & 2, 0, false); - //reportMiddle(packet[0] & 4, 0); - - //reportLeft(packet[0] & 1, id+1); - //reportRight(packet[0] & 2, id+1); - //reportMiddle(packet[0] & 4, id+1); - - virtualFinger[id].button = (packet[0] & 1); + virtualFinger[id].button = (packet[0] & 0x3); virtualFinger[id].prev = virtualFinger[id].now; - virtualFinger[id].now.pressure = pres; - virtualFinger[id].now.width = traces; - + virtualFinger[id].pressure = pres; + virtualFinger[id].width = traces; + virtualFinger[id].now.x = x; virtualFinger[id].now.y = y; - + if (headPacketsCount == heldFingers) { headPacketsCount = 0; - elantechInputSyncV4(); + sendTouchData(); } } @@ -1768,80 +1946,40 @@ void ApplePS2Elan::processPacketMotionV4() { leftButton = packet[0] & 0x1; rightButton = packet[0] & 0x2; - + id = ((packet[0] & 0xe0) >> 5) - 1; if (id < 0) { INTERRUPT_LOG("VoodooPS2Elan: invalid id, aborting\n"); return; } - + sid = ((packet[3] & 0xe0) >> 5) - 1; weight = (packet[0] & 0x10) ? ETP_WEIGHT_VALUE : 1; - /* - * Motion packets give us the delta of x, y values of specific fingers, - * but in two's complement. Let the compiler do the conversion for us. - * Also _enlarge_ the numbers to int, in case of overflow. - */ + + // Motion packets give us the delta of x, y values of specific fingers, + // but in two's complement. Let the compiler do the conversion for us. + // Also _enlarge_ the numbers to int, in case of overflow. delta_x1 = (signed char)packet[1]; delta_y1 = (signed char)packet[2]; delta_x2 = (signed char)packet[4]; delta_y2 = (signed char)packet[5]; - - //reportLeft(packet[0] & 1, 0, false); - //reportRight(packet[0] & 2, 0, false); - //reportMiddle(packet[0] & 4, 0); - - //reportLeft(packet[0] & 1, id+1); - //reportRight(packet[0] & 2, id+1); - //reportMiddle(packet[0] & 4, id+1); - - - virtualFinger[id].button = (packet[0] & 1); + + virtualFinger[id].button = (packet[0] & 0x3); virtualFinger[id].prev = virtualFinger[id].now; virtualFinger[id].now.x += delta_x1 * weight; virtualFinger[id].now.y -= delta_y1 * weight; - + if (sid >= 0) { - virtualFinger[sid].button = (packet[0] & 1); + virtualFinger[sid].button = (packet[0] & 0x3); virtualFinger[sid].prev = virtualFinger[sid].now; virtualFinger[sid].now.x += delta_x2 * weight; virtualFinger[sid].now.y -= delta_y2 * weight; - - //reportLeft(packet[3] & 1, 0); - //reportRight(packet[3] & 2, 0); - //reportMiddle(packet[3] & 4, 0); - - //reportLeft(packet[3] & 1, sid+1); - //reportRight(packet[3] & 2, sid+1); - //reportMiddle(packet[3] & 4, sid+1); - } - - elantechInputSyncV4(); -} - -void ApplePS2Elan::elantechReportTrackpoint() { - unsigned char *packet = _ringBuffer.tail(); - - trackpointLeftButton = packet[0] & 0x1; - trackpointRightButton = packet[0] & 0x2; - //middleButton = packet[0] & 0x4; - - int dx = packet[4]; - int dy = packet[5]; - - dx = packet[4] - (int)((packet[1]^0x80)<<1); - dy = (int)((packet[2]^0x80)<<1) - packet[5]; - - dx = dx * _trackpointMultiplierX / _trackpointDividerX; - dy = dy * _trackpointMultiplierY / _trackpointDividerY; - - AbsoluteTime timestamp; - clock_get_uptime(×tamp); + } - dispatchRelativePointerEvent(dx, dy, trackpointRightButton | trackpointLeftButton /*| middleButton*/, timestamp); + sendTouchData(); } -static MT2FingerType GetBestFingerType(int i) { +MT2FingerType ApplePS2Elan::GetBestFingerType(int i) { switch (i) { case 0: return kMT2FingerTypeIndexFinger; case 1: return kMT2FingerTypeMiddleFinger; @@ -1855,79 +1993,94 @@ static MT2FingerType GetBestFingerType(int i) { return kMT2FingerTypeIndexFinger; } -void ApplePS2Elan::elantechInputSyncV4() { +void ApplePS2Elan::sendTouchData() { AbsoluteTime timestamp; clock_get_uptime(×tamp); - - bool is_buttonpad = elantech_is_buttonpad(); - - int count = 0; - for (int i = 0; i < 5; ++i){ - if (!virtualFinger[i].touch) + uint64_t timestamp_ns; + absolutetime_to_nanoseconds(timestamp, ×tamp_ns); + + // Ignore input for specified time after keyboard/trackpoint usage + if (timestamp_ns - keytime < maxaftertyping) { + return; + } + + static_assert(VOODOO_INPUT_MAX_TRANSDUCERS >= ETP_MAX_FINGERS, "Trackpad supports too many fingers"); + + int transducers_count = 0; + for (int i = 0; i < ETP_MAX_FINGERS; i++) { + const auto &state = virtualFinger[i]; + if (!state.touch) { continue; - - inputEvent.transducers[count].currentCoordinates = virtualFinger[i].now; - inputEvent.transducers[count].previousCoordinates = virtualFinger[i].prev; - - - inputEvent.transducers[count].isValid = true; - inputEvent.transducers[count].isPhysicalButtonDown = is_buttonpad && virtualFinger[i].button; - inputEvent.transducers[count].isTransducerActive = true; - - inputEvent.transducers[count].secondaryId = count; - inputEvent.transducers[count].fingerType = GetBestFingerType(count); - inputEvent.transducers[count].type = FINGER; - + } + + auto &transducer = inputEvent.transducers[transducers_count]; + + transducer.currentCoordinates = state.now; + transducer.previousCoordinates = state.prev; + transducer.timestamp = timestamp; + + transducer.isValid = true; + transducer.isPhysicalButtonDown = info.is_buttonpad && state.button; + transducer.isTransducerActive = true; + + transducer.secondaryId = i; + transducer.fingerType = GetBestFingerType(transducers_count); + transducer.type = FINGER; + // it looks like Elan PS2 pressure and width is very inaccurate // it is better to leave it that way - inputEvent.transducers[count].supportsPressure = false; - - inputEvent.transducers[count].timestamp = timestamp; - - count++; - } - - // set the thumb to improve 4F pinch and spread gesture - if (count == 4) { - // simple thumb detection: to find the lowest finger touch. - SInt16 y_min = info.y_max / 2; - int thumb_index = 0; + transducer.supportsPressure = false; + + // Force Touch emulation + // Physical button is translated into force touch instead of click + if (_forceTouchMode == FORCE_TOUCH_BUTTON && transducer.isPhysicalButtonDown) { + transducer.supportsPressure = true; + transducer.isPhysicalButtonDown = false; + transducer.currentCoordinates.pressure = 255; + transducer.currentCoordinates.width = 10; + } + + transducers_count++; + } + + // set the thumb to improve 4F pinch and spread gesture and cross-screen dragging + if (transducers_count >= 4) { + // simple thumb detection: find the lowest finger touch in the vertical direction + // note: the origin is top left corner, so lower finger means higher y coordinate + UInt32 maxY = 0; + int newThumbIndex = 0; int currentThumbIndex = 0; - for (int i = 0; i < count; i++) { - if (inputEvent.transducers[i].currentCoordinates.y > y_min) { - y_min = inputEvent.transducers[i].currentCoordinates.y; - thumb_index = i; + for (int i = 0; i < transducers_count; i++) { + if (inputEvent.transducers[i].currentCoordinates.y > maxY) { + maxY = inputEvent.transducers[i].currentCoordinates.y; + newThumbIndex = i; } - if (inputEvent.transducers[i].fingerType == kMT2FingerTypeThumb) + if (inputEvent.transducers[i].fingerType == kMT2FingerTypeThumb) { currentThumbIndex = i; + } } - inputEvent.transducers[currentThumbIndex].fingerType = inputEvent.transducers[thumb_index].fingerType; - inputEvent.transducers[thumb_index].fingerType = kMT2FingerTypeThumb; + inputEvent.transducers[currentThumbIndex].fingerType = inputEvent.transducers[newThumbIndex].fingerType; + inputEvent.transducers[newThumbIndex].fingerType = kMT2FingerTypeThumb; } - - for (int i = count; i < VOODOO_INPUT_MAX_TRANSDUCERS; ++i) { + + for (int i = transducers_count; i < VOODOO_INPUT_MAX_TRANSDUCERS; i++) { inputEvent.transducers[i].isValid = false; inputEvent.transducers[i].isPhysicalButtonDown = false; inputEvent.transducers[i].isTransducerActive = false; } - - inputEvent.contact_count = count; + + inputEvent.contact_count = transducers_count; inputEvent.timestamp = timestamp; - - if (voodooInputInstance) - { + + if (voodooInputInstance) { super::messageClient(kIOMessageVoodooInputMessage, voodooInputInstance, &inputEvent, sizeof(VoodooInputEvent)); } - - if (!is_buttonpad) - { - if (inputEvent.contact_count == 0) - { + + if (!info.is_buttonpad) { + if (transducers_count == 0) { UInt32 buttons = leftButton | rightButton; dispatchRelativePointerEvent(0, 0, buttons, timestamp); - } - else - { + } else { UInt32 buttons = 0; bool send = false; if (lastLeftButton != leftButton) { @@ -1938,120 +2091,130 @@ void ApplePS2Elan::elantechInputSyncV4() { buttons |= rightButton; send = true; } - if (send) + if (send) { dispatchRelativePointerEvent(0, 0, buttons, timestamp); + } } lastLeftButton = leftButton; lastRightButton = rightButton; - //lastMiddleButton = middleButton; } } +PS2InterruptResult ApplePS2Elan::interruptOccurred(UInt8 data) { + UInt8 *packet = _ringBuffer.head(); + packet[_packetByteCount++] = data; -void ApplePS2Elan::elantechReportAbsoluteV4(int packetType) { - AbsoluteTime timestamp; - clock_get_uptime(×tamp); - - inputEvent.timestamp = timestamp; - - switch (packetType) { - case PACKET_V4_STATUS: - INTERRUPT_LOG("VoodooPS2Elan: Got status packet\n"); - processPacketStatusV4(); - break; - - case PACKET_V4_HEAD: - INTERRUPT_LOG("VoodooPS2Elan: Got head packet\n"); - processPacketHeadV4(); - break; - - case PACKET_V4_MOTION: - INTERRUPT_LOG("VoodooPS2Elan: Got motion packet\n"); - processPacketMotionV4(); - break; - case PACKET_UNKNOWN: - default: - /* impossible to get here */ - break; - } - - if (voodooInputInstance) { - if (changed) - { - VoodooInputDimensions d; - d.min_x = info.x_min; - d.max_x = info.x_max; - d.min_y = info.y_min; - d.max_y = info.y_max; - super::messageClient(kIOMessageVoodooInputUpdateDimensionsMessage, voodooInputInstance, &d, sizeof(VoodooInputDimensions)); - - changed = false; - } - + if (_packetByteCount == _packetLength) { + _ringBuffer.advanceHead(_packetLength); + _packetByteCount = 0; + return kPS2IR_packetReady; } - else - INTERRUPT_LOG("VoodooPS2Elan: no voodooInputInstance\n"); + + return kPS2IR_packetBuffering; } -void ApplePS2Elan::packetReady() -{ +void ApplePS2Elan::packetReady() { INTERRUPT_LOG("VoodooPS2Elan: packet ready occurred\n"); // empty the ring buffer, dispatching each packet... - while (_ringBuffer.count() >= kPacketLength) - { + while (_ringBuffer.count() >= _packetLength) { + if (ignoreall) { + _ringBuffer.advanceTail(_packetLength); + continue; + } + int packetType; switch (info.hw_version) { case 1: + if (info.paritycheck && !elantechPacketCheckV1()) { + // ignore invalid packet + INTERRUPT_LOG("VoodooPS2Elan: invalid packet received\n"); + break; + } + + INTERRUPT_LOG("VoodooPS2Elan: Handling absolute mode\n"); + elantechReportAbsoluteV1(); + break; + case 2: - INTERRUPT_LOG("VoodooPS2Elan: packet ready occurred, but unsupported version\n"); - // V1 and V2 are ancient hardware, not going to implement right away + if (elantechDebounceCheckV2()) { + // ignore debounce + break; + } + + if (info.paritycheck && !elantechPacketCheckV2()) { + // ignore invalid packet + INTERRUPT_LOG("VoodooPS2Elan: invalid packet received\n"); + break; + } + + INTERRUPT_LOG("VoodooPS2Elan: Handling absolute mode\n"); + elantechReportAbsoluteV2(); break; + case 3: packetType = elantechPacketCheckV3(); + INTERRUPT_LOG("VoodooPS2Elan: Packet Type %d\n", packetType); + switch (packetType) { case PACKET_UNKNOWN: INTERRUPT_LOG("VoodooPS2Elan: invalid packet received\n"); break; + case PACKET_DEBOUNCE: - /* ignore debounce */ + // ignore debounce break; + case PACKET_TRACKPOINT: + INTERRUPT_LOG("VoodooPS2Elan: Handling trackpoint packet\n"); elantechReportTrackpoint(); break; + default: + INTERRUPT_LOG("VoodooPS2Elan: Handling absolute mode\n"); elantechReportAbsoluteV3(packetType); break; } break; + case 4: packetType = elantechPacketCheckV4(); - INTERRUPT_LOG("VoodooPS2Elan: Packet Type %d\n", packetType); switch (packetType) { case PACKET_UNKNOWN: - INTERRUPT_LOG("VoodooPS2Elan: Handling unknown mode\n"); + INTERRUPT_LOG("VoodooPS2Elan: invalid packet received\n"); break; case PACKET_TRACKPOINT: - INTERRUPT_LOG("VoodooPS2Elan: Handling trackpoint mode\n"); + INTERRUPT_LOG("VoodooPS2Elan: Handling trackpoint packet\n"); elantechReportTrackpoint(); break; + default: INTERRUPT_LOG("VoodooPS2Elan: Handling absolute mode\n"); elantechReportAbsoluteV4(packetType); + break; } break; + default: INTERRUPT_LOG("VoodooPS2Elan: invalid packet received\n"); } - _ringBuffer.advanceTail(kPacketLength); + _ringBuffer.advanceTail(_packetLength); + } +} + +void ApplePS2Elan::resetMouse() { + UInt8 params[2]; + ps2_command<2>(params, kDP_Reset); + + if (params[0] != 0xaa && params[1] != 0x00) { + IOLog("VoodooPS2Elan: failed resetting.\n"); } } -void ApplePS2Elan::elantechTouchpadEnable(bool enable ) -{ +void ApplePS2Elan::setTouchPadEnable(bool enable) { ps2_command<0>(NULL, enable ? kDP_Enable : kDP_SetDefaultsAndDisable); } diff --git a/VoodooPS2Trackpad/VoodooPS2Elan.h b/VoodooPS2Trackpad/VoodooPS2Elan.h index a21a0bac..f1e2989d 100644 --- a/VoodooPS2Trackpad/VoodooPS2Elan.h +++ b/VoodooPS2Trackpad/VoodooPS2Elan.h @@ -22,153 +22,11 @@ #include "VoodooInputMultitouch/VoodooInputEvent.h" -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// SimpleAverage Class Declaration -// - -template -class SimpleAverage -{ -private: - T m_buffer[N]; - int m_count; - int m_sum; - int m_index; - -public: - inline SimpleAverage() { reset(); } - T filter(T data) - { - // add new entry to sum - m_sum += data; - // if full buffer, then we are overwriting, so subtract old from sum - if (m_count == N) - m_sum -= m_buffer[m_index]; - // new entry into buffer - m_buffer[m_index] = data; - // move index to next position with wrap around - if (++m_index >= N) - m_index = 0; - // keep count moving until buffer is full - if (m_count < N) - ++m_count; - // return average of current items - return m_sum / m_count; - } - inline void reset() - { - m_count = 0; - m_sum = 0; - m_index = 0; - } - inline int count() const { return m_count; } - inline int sum() const { return m_sum; } - T oldest() const - { - // undefined if nothing in here, return zero - if (m_count == 0) - return 0; - // if it is not full, oldest is at index 0 - // if full, it is right where the next one goes - if (m_count < N) - return m_buffer[0]; - else - return m_buffer[m_index]; - } - T newest() const - { - // undefined if nothing in here, return zero - if (m_count == 0) - return 0; - // newest is index - 1, with wrap - int index = m_index; - if (--index < 0) - index = m_count-1; - return m_buffer[index]; - } - T average() const - { - if (m_count == 0) - return 0; - return m_sum / m_count; - } -}; - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// DecayingAverage Class Declaration -// - -template -class DecayingAverage -{ -private: - T m_last; - bool m_lastvalid; - -public: - inline DecayingAverage() { reset(); } - T filter(T data, int fingers) - { - TT result = data; - TT last = m_last; - if (m_lastvalid) - result = (result * N1) / D + (last * N2) / D; - m_lastvalid = true; - m_last = (T)result; - return m_last; - } - inline void reset() - { - m_lastvalid = false; - } -}; - -template -class UndecayAverage -{ -private: - T m_last; - bool m_lastvalid; - -public: - inline UndecayAverage() { reset(); } - T filter(T data) - { - TT result = data; - TT last = m_last; - if (m_lastvalid) - result = (result * D) / N1 - (last * N2) / N1; - m_lastvalid = true; - m_last = (T)data; - return (T)result; - } - inline void reset() - { - m_lastvalid = false; - } -}; - -struct synaptics_hw_state { - int x; - int y; - int z; - int w; - int virtualFingerIndex; -}; - struct virtual_finger_state { - SimpleAverage x_avg; - SimpleAverage y_avg; - uint8_t pressure; - uint8_t width; - bool touch; - bool button; - MT2FingerType fingerType; -}; - -struct virtual_finger_state_2 { TouchCoordinates prev; TouchCoordinates now; + uint8_t pressure; + uint8_t width; bool touch; bool button; MT2FingerType fingerType; @@ -182,30 +40,26 @@ typedef enum { FORCE_TOUCH_CUSTOM = 4 } ForceTouchMode; -#define SYNAPTICS_MAX_FINGERS 5 - -#define kPacketLength 6 #define kPacketLengthMax 6 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // // FROM LINUX ELANTECH.C - /* * Command values for Synaptics style queries */ -#define ETP_FW_ID_QUERY 0x00 -#define ETP_FW_VERSION_QUERY 0x01 +#define ETP_FW_ID_QUERY 0x00 +#define ETP_FW_VERSION_QUERY 0x01 #define ETP_CAPABILITIES_QUERY 0x02 -#define ETP_SAMPLE_QUERY 0x03 -#define ETP_RESOLUTION_QUERY 0x04 +#define ETP_SAMPLE_QUERY 0x03 +#define ETP_RESOLUTION_QUERY 0x04 /* * Command values for register reading or writing */ -#define ETP_REGISTER_READ 0x10 -#define ETP_REGISTER_WRITE 0x11 +#define ETP_REGISTER_READ 0x10 +#define ETP_REGISTER_WRITE 0x11 #define ETP_REGISTER_READWRITE 0x00 /* @@ -216,86 +70,86 @@ typedef enum { /* * Times to retry a ps2_command and millisecond delay between tries */ -#define ETP_PS2_COMMAND_TRIES 3 -#define ETP_PS2_COMMAND_DELAY 500 +#define ETP_PS2_COMMAND_TRIES 3 +#define ETP_PS2_COMMAND_DELAY 500 /* * Times to try to read back a register and millisecond delay between tries */ -#define ETP_READ_BACK_TRIES 5 -#define ETP_READ_BACK_DELAY 2000 +#define ETP_READ_BACK_TRIES 5 +#define ETP_READ_BACK_DELAY 2000 /* * Register bitmasks for hardware version 1 */ -#define ETP_R10_ABSOLUTE_MODE 0x04 -#define ETP_R11_4_BYTE_MODE 0x02 +#define ETP_R10_ABSOLUTE_MODE 0x04 +#define ETP_R11_4_BYTE_MODE 0x02 /* * Capability bitmasks */ -#define ETP_CAP_HAS_ROCKER 0x04 +#define ETP_CAP_HAS_ROCKER 0x04 /* * One hard to find application note states that X axis range is 0 to 576 * and Y axis range is 0 to 384 for harware version 1. * Edge fuzz might be necessary because of bezel around the touchpad */ -#define ETP_EDGE_FUZZ_V1 32 +#define ETP_EDGE_FUZZ_V1 32 -#define ETP_XMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1) -#define ETP_XMAX_V1 (576 - ETP_EDGE_FUZZ_V1) -#define ETP_YMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1) -#define ETP_YMAX_V1 (384 - ETP_EDGE_FUZZ_V1) +#define ETP_XMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1) +#define ETP_XMAX_V1 (576 - ETP_EDGE_FUZZ_V1) +#define ETP_YMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1) +#define ETP_YMAX_V1 (384 - ETP_EDGE_FUZZ_V1) /* * The resolution for older v2 hardware doubled. * (newer v2's firmware provides command so we can query) */ -#define ETP_XMIN_V2 0 -#define ETP_XMAX_V2 1152 -#define ETP_YMIN_V2 0 -#define ETP_YMAX_V2 768 +#define ETP_XMIN_V2 0 +#define ETP_XMAX_V2 1152 +#define ETP_YMIN_V2 0 +#define ETP_YMAX_V2 768 // Preasure min-max -#define ETP_PMIN_V2 0 -#define ETP_PMAX_V2 255 +#define ETP_PMIN_V2 0 +#define ETP_PMAX_V2 255 // Width min-max -#define ETP_WMIN_V2 0 -#define ETP_WMAX_V2 15 +#define ETP_WMIN_V2 0 +#define ETP_WMAX_V2 15 /* * v3 hardware has 2 kinds of packet types, * v4 hardware has 3. */ -#define PACKET_UNKNOWN 0x01 -#define PACKET_DEBOUNCE 0x02 -#define PACKET_V3_HEAD 0x03 -#define PACKET_V3_TAIL 0x04 -#define PACKET_V4_HEAD 0x05 -#define PACKET_V4_MOTION 0x06 -#define PACKET_V4_STATUS 0x07 -#define PACKET_TRACKPOINT 0x08 +#define PACKET_UNKNOWN 0x01 +#define PACKET_DEBOUNCE 0x02 +#define PACKET_V3_HEAD 0x03 +#define PACKET_V3_TAIL 0x04 +#define PACKET_V4_HEAD 0x05 +#define PACKET_V4_MOTION 0x06 +#define PACKET_V4_STATUS 0x07 +#define PACKET_TRACKPOINT 0x08 /* * track up to 5 fingers for v4 hardware */ -#define ETP_MAX_FINGERS 5 +#define ETP_MAX_FINGERS 5 /* * weight value for v4 hardware */ -#define ETP_WEIGHT_VALUE 5 +#define ETP_WEIGHT_VALUE 5 /* * Bus information on 3rd byte of query ETP_RESOLUTION_QUERY(0x04) */ -#define ETP_BUS_PS2_ONLY 0 +#define ETP_BUS_PS2_ONLY 0 #define ETP_BUS_SMB_ALERT_ONLY 1 -#define ETP_BUS_SMB_HST_NTFY_ONLY 2 -#define ETP_BUS_PS2_SMB_ALERT 3 -#define ETP_BUS_PS2_SMB_HST_NTFY 4 +#define ETP_BUS_SMB_HST_NTFY_ONLY 2 +#define ETP_BUS_PS2_SMB_ALERT 3 +#define ETP_BUS_PS2_SMB_HST_NTFY 4 /* * New ICs are either using SMBus Host Notify or just plain PS2. @@ -341,12 +195,12 @@ struct elantech_device_info { bool reports_pressure; bool crc_enabled; bool set_hw_resolution; + bool is_buttonpad; bool has_trackpoint; bool has_middle_button; }; struct elantech_data { - char tp_phys[32]; unsigned char reg_07; unsigned char reg_10; unsigned char reg_11; @@ -362,122 +216,121 @@ struct elantech_data { unsigned char parity[256]; }; - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ApplePS2Elan Class Declaration // -class EXPORT ApplePS2Elan : public IOHIPointing -{ +class EXPORT ApplePS2Elan : public IOHIPointing { typedef IOHIPointing super; - OSDeclareDefaultStructors(ApplePS2Elan); + OSDeclareDefaultStructors(ApplePS2Elan); private: IOService* voodooInputInstance {nullptr}; ApplePS2MouseDevice* _device {nullptr}; - bool _interruptHandlerInstalled {false}; + bool _interruptHandlerInstalled {false}; bool _powerControlHandlerInstalled {false}; - UInt32 _packetByteCount {0}; - UInt8 _lastdata {0}; - RingBuffer _ringBuffer {}; - - IOCommandGate* _cmdGate {nullptr}; - IOACPIPlatformDevice* _provider {nullptr}; - - VoodooInputEvent inputEvent {}; - - // when trackpad has physical buttons - - UInt32 trackpointLeftButton = 0; - UInt32 trackpointRightButton = 0; - + UInt32 _packetByteCount {0}; + UInt32 _packetLength {0}; + RingBuffer _ringBuffer {}; + + IOCommandGate* _cmdGate {nullptr}; + + VoodooInputEvent inputEvent {}; + + // when trackpad has physical button UInt32 leftButton = 0; UInt32 rightButton = 0; - UInt32 lastLeftButton = 0; UInt32 lastRightButton = 0; - - UInt32 lastFingersV3 = 0; - - UInt32 leftButtons[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - UInt32 rightButtons[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - //UInt32 middleButtons[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - - void reportLeft(int state, int finger, bool status); - //void reportMiddle(int state, int finger, bool status); - void reportRight(int state, int finger, bool status); - + + const float sin30deg = 0.5f; + const float cos30deg = 0.86602540378f; + UInt32 lastFingers = 0; + + bool trackpointScrolling {false}; + int heldFingers = 0; int headPacketsCount = 0; - virtual_finger_state_2 virtualFinger[ETP_MAX_FINGERS] {}; - + virtual_finger_state virtualFinger[ETP_MAX_FINGERS] {}; + + static_assert(ETP_MAX_FINGERS <= kMT2FingerTypeLittleFinger, "Too many fingers for one hand"); + + ForceTouchMode _forceTouchMode {FORCE_TOUCH_BUTTON}; + int _scrollresolution {2300}; int wakedelay {1000}; int _trackpointMultiplierX {200}; int _trackpointMultiplierY {200}; int _trackpointDividerX {200}; int _trackpointDividerY {200}; - - int _mouseResolution { 0x3 }; - int _mouseSampleRate { 200 }; - - int _set_hw_resolution {false}; - - static_assert(SYNAPTICS_MAX_FINGERS <= kMT2FingerTypeLittleFinger, "Too many fingers for one hand"); + + int _mouseResolution {0x3}; + int _mouseSampleRate {200}; + + bool _set_hw_resolution {false}; bool ignoreall {false}; bool usb_mouse_stops_trackpad {true}; - - int _processusbmouse {true}; - int _processbluetoothmouse {true}; - - OSSet* attachedHIDPointerDevices {nullptr}; - - IONotifier* usb_hid_publish_notify {nullptr}; // Notification when an USB mouse HID device is connected - IONotifier* usb_hid_terminate_notify {nullptr}; // Notification when an USB mouse HID device is disconnected - - IONotifier* bluetooth_hid_publish_notify {nullptr}; // Notification when a bluetooth HID device is connected - IONotifier* bluetooth_hid_terminate_notify {nullptr}; // Notification when a bluetooth HID device is disconnected - - virtual PS2InterruptResult interruptOccurred(UInt8 data); + + bool _processusbmouse {true}; + bool _processbluetoothmouse {true}; + + uint64_t keytime {0}; + uint64_t maxaftertyping {500000000}; + + OSSet *attachedHIDPointerDevices {nullptr}; + + IONotifier *usb_hid_publish_notify {nullptr}; // Notification when an USB mouse HID device is connected + IONotifier *usb_hid_terminate_notify {nullptr}; // Notification when an USB mouse HID device is disconnected + + IONotifier *bluetooth_hid_publish_notify {nullptr}; // Notification when a bluetooth HID device is connected + IONotifier *bluetooth_hid_terminate_notify {nullptr}; // Notification when a bluetooth HID device is disconnected + + virtual PS2InterruptResult interruptOccurred(UInt8 data); virtual void packetReady(); virtual void setDevicePowerState(UInt32 whatToDo); - + bool handleOpen(IOService *forClient, IOOptionBits options, void *arg) override; void handleClose(IOService *forClient, IOOptionBits options) override; - - void setParamPropertiesGated(OSDictionary* dict); - void injectVersionDependentProperties(OSDictionary* dict); + + void setParamPropertiesGated(OSDictionary *dict); + void injectVersionDependentProperties(OSDictionary *dict); void registerHIDPointerNotifications(); void unregisterHIDPointerNotifications(); - - void notificationHIDAttachedHandlerGated(IOService * newService, IONotifier * notifier); - bool notificationHIDAttachedHandler(void * refCon, IOService * newService, IONotifier * notifier); - + + void notificationHIDAttachedHandlerGated(IOService *newService, IONotifier *notifier); + bool notificationHIDAttachedHandler(void *refCon, IOService *newService, IONotifier *notifier); + elantech_data etd {}; elantech_device_info info {}; int elantechDetect(); - void resetMouse(); int elantechQueryInfo(); int elantechSetProperties(); - int elantechSetupPS2(); int elantechSetAbsoluteMode(); - int elantechWriteReg(unsigned char reg, unsigned char val); - int elantechReadReg(unsigned char reg, unsigned char *val); int elantechSetInputParams(); - int elantechPacketCheckV4(); + int elantechSetupPS2(); + int elantechReadReg(unsigned char reg, unsigned char *val); + int elantechWriteReg(unsigned char reg, unsigned char val); + int elantechDebounceCheckV2(); + int elantechPacketCheckV1(); + int elantechPacketCheckV2(); int elantechPacketCheckV3(); - void elantechReportTrackpoint(); - void elantechReportAbsoluteV4(int packetType); + int elantechPacketCheckV4(); + void elantechReportAbsoluteV1(); + void elantechReportAbsoluteV2(); void elantechReportAbsoluteV3(int packetType); + void elantechReportAbsoluteV4(int packetType); + void elantechReportTrackpoint(); void processPacketStatusV4(); void processPacketHeadV4(); void processPacketMotionV4(); - void elantechInputSyncV4(); - void elantechTouchpadEnable(bool enable ); - + void sendTouchData(); + void resetMouse(); + void setTouchPadEnable(bool enable); + + static MT2FingerType GetBestFingerType(int i); + template int ps2_command(UInt8* params, unsigned int command); template @@ -487,32 +340,26 @@ class EXPORT ApplePS2Elan : public IOHIPointing int synaptics_send_cmd(unsigned char c, unsigned char *param); template int elantech_send_cmd(unsigned char c, unsigned char *param); - bool elantech_is_signature_valid(const unsigned char *param); - int elantech_get_resolution_v4(unsigned int *x_res, unsigned int *y_res, unsigned int *bus); - template int send_cmd(unsigned char c, unsigned char *param); - - bool changed = true; - - bool elantech_is_buttonpad() - { - return (info.fw_version & 0x001000) != 0; - } - + + bool elantech_is_signature_valid(const unsigned char *param); + static unsigned int elantech_convert_res(unsigned int val); + int elantech_get_resolution_v4(unsigned int *x_res, unsigned int *y_res, unsigned int *bus); + public: - bool init (OSDictionary* properties) override; - ApplePS2Elan* probe (IOService* provider, SInt32 * score) override; - bool start (IOService* provider ) override; - void stop (IOService* provider ) override; - + bool init(OSDictionary *properties) override; + ApplePS2Elan *probe(IOService *provider, SInt32 *score) override; + bool start(IOService *provider) override; + void stop(IOService *provider) override; + UInt32 deviceType() override; UInt32 interfaceID() override; - IOReturn setParamProperties(OSDictionary* dict) override; - IOReturn setProperties(OSObject *props) override; - -// IOReturn message(UInt32 type, IOService* provider, void* argument) override; + IOReturn setParamProperties(OSDictionary* dict) override; + IOReturn setProperties(OSObject *props) override; + + IOReturn message(UInt32 type, IOService* provider, void* argument) override; }; #endif /* _ApplePS2Elan_H */ diff --git a/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist b/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist index 47524ea8..3ec0ba2b 100644 --- a/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist +++ b/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist @@ -22,70 +22,6 @@ ${MODULE_VERSION} IOKitPersonalities - Elantech TouchPad - - IOProbeScore - 7000 - IOProviderClass - ApplePS2MouseDevice - IOClass - ApplePS2Elan - CFBundleIdentifier - as.acidanthera.voodoo.driver.PS2Trackpad - Platform Profile - - Default - - SetHwResolution - - MouseResolution - 3 - MouseSampleRate - 200 - TrackpointMultiplierY - 120 - TrackpointMultiplierX - 120 - ButtonCount - 3 - Darwin 16+ - - ApplePreferenceCapability - - ApplePreferenceIdentifier - com.apple.AppleMultitouchTrackpad - MT Built-in - - MTHIDDevice - - SupportsGestureScrolling - - TrackpadEmbedded - - TrackpadFourFingerGestures - - TrackpadSecondaryClickCorners - - TrackpadThreeFingerDrag - - - DisableDevice - - ProcessBluetoothMouseStopsTrackpad - - ProcessUSBMouseStopsTrackpad - - ScrollResolution - 400 - USBMouseStopsTrackpad - 0 - UseHighRate - - WakeDelay - 1000 - - - ALPS GlidePoint CFBundleIdentifier @@ -149,6 +85,76 @@ + Elantech TouchPad + + IOProbeScore + 7000 + IOProviderClass + ApplePS2MouseDevice + IOClass + ApplePS2Elan + CFBundleIdentifier + as.acidanthera.voodoo.driver.PS2Trackpad + Platform Profile + + Default + + ButtonCount + 3 + Darwin 16+ + + ApplePreferenceCapability + + ApplePreferenceIdentifier + com.apple.AppleMultitouchTrackpad + MT Built-in + + MTHIDDevice + + SupportsGestureScrolling + + TrackpadEmbedded + + TrackpadFourFingerGestures + + TrackpadSecondaryClickCorners + + TrackpadThreeFingerDrag + + + DisableDevice + + ForceTouchMode + 1 + MouseResolution + 3 + MouseSampleRate + 200 + ProcessBluetoothMouseStopsTrackpad + + ProcessUSBMouseStopsTrackpad + + QuietTimeAfterTyping + 500000000 + ScrollResolution + 400 + SetHwResolution + + TrackpointMultiplierX + 120 + TrackpointMultiplierY + 120 + USBMouseStopsTrackpad + 0 + UseHighRate + + WakeDelay + 1000 + + + RM,deliverNotifications + + Native Multitouch Engine CFBundleIdentifier