diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..68e40d7b --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,91 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + release: + types: [published] + +env: + PROJECT_TYPE: KEXT + +jobs: + build: + name: Build + runs-on: macos-latest + env: + JOB_TYPE: BUILD + steps: + - uses: actions/checkout@v5 + - uses: actions/checkout@v5 + with: + repository: acidanthera/MacKernelSDK + path: MacKernelSDK + - name: CI Bootstrap + run: | + src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/ci-bootstrap.sh) && eval "$src" || exit 1 + - name: VoodooInput Bootstrap + run: | + src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/VoodooInput/master/VoodooInput/Scripts/bootstrap.sh) && eval "$src" || exit 1 + + - run: xcodebuild -jobs 1 -configuration Debug + - run: xcodebuild -jobs 1 -configuration Release + + - name: Upload to Artifacts + uses: actions/upload-artifact@v4 + with: + name: Artifacts + path: build/*/*/*.zip + - name: Upload to Release + if: github.event_name == 'release' + uses: svenstaro/upload-release-action@81c65b7cd4de9b2570615ce3aad67a41de5b1a13 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: build/*/*/*.zip + tag: ${{ github.ref }} + file_glob: true + + analyze-clang: + name: Analyze Clang + runs-on: macos-latest + env: + JOB_TYPE: ANALYZE + steps: + - uses: actions/checkout@v5 + - uses: actions/checkout@v5 + with: + repository: acidanthera/MacKernelSDK + path: MacKernelSDK + - name: CI Bootstrap + run: | + src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/ci-bootstrap.sh) && eval "$src" || exit 1 + - name: VoodooInput Bootstrap + run: | + src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/VoodooInput/master/VoodooInput/Scripts/bootstrap.sh) && eval "$src" || exit 1 + + - run: xcodebuild analyze -quiet -scheme VoodooPS2Controller -configuration Debug CLANG_ANALYZER_OUTPUT=plist-html CLANG_ANALYZER_OUTPUT_DIR="$(pwd)/clang-analyze" && [ "$(find clang-analyze -name "*.html")" = "" ] + - run: xcodebuild analyze -quiet -scheme VoodooPS2Controller -configuration Release CLANG_ANALYZER_OUTPUT=plist-html CLANG_ANALYZER_OUTPUT_DIR="$(pwd)/clang-analyze" && [ "$(find clang-analyze -name "*.html")" = "" ] + + analyze-coverity: + name: Analyze Coverity + runs-on: macos-latest + env: + JOB_TYPE: COVERITY + if: github.repository_owner == 'acidanthera' && github.event_name != 'pull_request' + steps: + - uses: actions/checkout@v5 + - uses: actions/checkout@v5 + with: + repository: acidanthera/MacKernelSDK + path: MacKernelSDK + - name: CI Bootstrap + run: | + src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/ci-bootstrap.sh) && eval "$src" || exit 1 + - name: Run Coverity + run: | + src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/coverity/covstrap.sh) && eval "$src" || exit 1 + env: + COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + COVERITY_SCAN_EMAIL: ${{ secrets.COVERITY_SCAN_EMAIL }} + COVERITY_BUILD_COMMAND: xcodebuild -configuration Release diff --git a/.gitignore b/.gitignore index 839586ec..6de4bdb1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ xcuserdata xcshareddata project.xcworkspace VoodooInput +/MacKernelSDK diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3025b1b4..00000000 --- a/.travis.yml +++ /dev/null @@ -1,34 +0,0 @@ -language: cpp - -matrix: - include: - - os: osx - name: "Build" - osx_image: xcode10.2 - compiler: clang - - script: - - 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 - - deploy: - provider: releases - skip_cleanup: true - file: "build/*/*/*.zip" - file_glob: true - api_key: - secure: Kyxtp5f9lb+38SZBhfHuQsZB95UQ3TaSGprfCfr3enjZSQ+EHAjGj0Fek7hde6iaMG1CfdJrLItGycNPlrOafAX7ylC5WwJw2CApWXnsuJxDPju5f49P6aEmWHWitS0dYVcHp/IWBYpjacxBKNxDD9lBI9TfCd6mLvZiv15WngNyfKWz90YdY4YlgEPB3VM+XcRoYtILL1eg1Q++hlxn4fE1TEyK4pGJxrFfq0Fqvy8rQ5zYm1idk0aasC1GsS3U7L5zlZjlYs3RRnKOzSKiexLvhhcOPFsdAuQaW+IL/eofzaSEm9Slc9pCXUUlmyU5O0+hS36YPCh9cVSYv0HVVMQp+DotcbD/nKhJE5V+x+8xYRP2oIFLd15ujhJDBHWVn5QMAUu6YiGwtbLFRIUe4P8T0JkBDpUlznqZI2XzusHmpQDeDT/IzLwU1srELLqHjlS5qQQYvXrocTEgdvvUE9yMWUp7v52gk/rEm8jCH3cwYaXBwjfYC8Iy/GgoEoCYG1x6dF7BLYsv0+DYde16oEOB38Oilf++JFEK7AJb5adWjDJOwYEEIeWBhMtOKEJ5TaBiaYmlNsClM0r4CKD712cHSgHD/EDqRzVp3873R+pfSUfEfjth4vi+lVvnyqj38YCy0kTT0meVVg6M/V9c39eyTd53DiIjLowB3J3YJEk= - on: - tags: true - - - os: osx - name: "Analyze Clang" - osx_image: xcode11 - compiler: clang - - script: - - 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 8fa3399f..34156d34 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,9 +1,90 @@ VoodooPS2 Changelog ============================ +#### v2.3.7 +- Fixed multiple PS2/SMBus devices attaching +- Fixed eratic pointer in bootpicker by disabling SMBus/PS2 devices on shutdown + +#### v2.3.6 +- Lowered macOS requirements to 10.10 +- Added PS/2 stub driver for better VoodooRMI compatibility +- Removed old external reset interface originally used by VoodooRMI + +#### v2.3.5 +- Removed actAsTrackpad and related logic +- Fix Trackpoints connected to Elan Touchpads +- Use VoodooInput Trackpoint logic for Elan Touchpads + +#### v2.3.4 +- Fixed device count detection when `ps2rst=0` is set +- Fixed handleClose not being called by VoodooInput + +#### v2.3.3 +- Fixed rapidly opening pages in browsers while scrolling with the trackpoint +- Fixed buttons on various trackpads (especially those without trackpoints attached) +- Fixed DynamicEWMode problem on Lenovo ThinkPad Laptops (acidanthera/bugtracker#890) + +#### v2.3.2 +- Added `ps2kbdonly=1` argument not to disable touchpad line on reboot, thx @Kethen + +#### v2.3.1 +- Fixed disabled keyboard after reboot + +#### v2.3.0 +- Fixed variable shadowing + +#### v2.2.9 +- Improved stability of ALPS touchpads +- V8 touchpads can pass all four fingers to VoodooInput natively +- Fixed unpressing during 3 fingers gesture on ALPS V7 +- Use VoodooTrackpoint for trackstick and non-MT ALPS touchpads + +#### v2.2.8 +- Added ALPS touchpad support +- Fixed ELAN trackpoint scrolling in the wrong direction + +#### v2.2.7 +- Fixed kernel panic after S3 + +#### v2.2.6 +- Fixed some touchpads not waking after S3 + +#### v2.2.5 +- Added support for touchpads with multiplexors + +#### v2.2.4 +- Fixed incorrect command gate initialization causing panics + +#### v2.2.3 +- Added `DisableDeepSleep` to workaround ACPI S3 wakes on some Synaptics touchpads + +#### v2.2.2 +- Added NumLockSupport & NumLockOnAtBoot + +#### v2.2.1 +- Fix issue with registering of services matched by property name "RM,deliverNotifications". It solves issue with broadcasting timestamp for the last pressed key and handling of QuietTimeAfterTyping [see bug #1415](https://github.com/acidanthera/bugtracker/issues/1415) + +#### v2.2.0 +- Added VoodooRmi compatibility to allow external touchpad resets + +#### v2.1.9 +- Disabled PrntScr remap by default, see `SSDT-PrtSc-Remap.dsl` for example to re-enable it +- Disabled Command and Option remap by default, see `SSDT-Swap-CommandOption.dsl` for example to re-enable it + +#### v2.1.8 +- Added support for receiving input form other kexts +- Fixed dynamic coordinate refresh for ELAN v3 touchpads + +#### v2.1.7 +- Initial MacKernelSDK and Xcode 12 compatibility +- Added support for select ELAN touchpads by BAndysc and hieplpvip +- 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/Docs/ACPI/SSDT-AlternateSwipes.dsl b/Docs/ACPI/SSDT-AlternateSwipes.dsl index 701645d4..c03385c8 100644 --- a/Docs/ACPI/SSDT-AlternateSwipes.dsl +++ b/Docs/ACPI/SSDT-AlternateSwipes.dsl @@ -2,6 +2,8 @@ // instead of the Ctrl+Option+Arrows DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) { + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() { "Keyboard", Package() diff --git a/Docs/ACPI/SSDT-DisableDeepSleep.dsl b/Docs/ACPI/SSDT-DisableDeepSleep.dsl new file mode 100644 index 00000000..f61dbe63 --- /dev/null +++ b/Docs/ACPI/SSDT-DisableDeepSleep.dsl @@ -0,0 +1,17 @@ +// For computers with Synaptics touchpad that wake immediately after +// going to sleep deeper touchpad sleep can be disabled. +// +// This will cause the touchpad to consume more power in ACPI S3. +DefinitionBlock("", "SSDT", 2, "ACDT", "ps2", 0) +{ + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() + { + "Synaptics TouchPad", Package() + { + "DisableDeepSleep", ">y", + } + }) +} +//EOF diff --git a/Docs/ACPI/SSDT-DisableElanWakeDelay.dsl b/Docs/ACPI/SSDT-DisableElanWakeDelay.dsl new file mode 100644 index 00000000..9d5b948c --- /dev/null +++ b/Docs/ACPI/SSDT-DisableElanWakeDelay.dsl @@ -0,0 +1,16 @@ +// For some computers wake delay needs to be shorter +// for ELAN touchpad to work after wakeup. + +DefinitionBlock("", "SSDT", 2, "ACDT", "ps2", 0) +{ + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() + { + "Elantech TouchPad", Package() + { + "WakeDelay", 0, + }, + }) +} +//EOF diff --git a/Docs/ACPI/SSDT-DisableTrackpadProbe.dsl b/Docs/ACPI/SSDT-DisableTrackpadProbe.dsl index cb239b8c..df0519c8 100644 --- a/Docs/ACPI/SSDT-DisableTrackpadProbe.dsl +++ b/Docs/ACPI/SSDT-DisableTrackpadProbe.dsl @@ -5,6 +5,8 @@ // This can improve the reliability of VoodooPS2Mouse.kext and is more efficient as well. DefinitionBlock("", "SSDT", 2, "ACDT", "ps2", 0) { + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() { "Synaptics TouchPad", Package() diff --git a/Docs/ACPI/SSDT-Disable_DynamicEWMode.dsl b/Docs/ACPI/SSDT-Disable_DynamicEWMode.dsl deleted file mode 100644 index 75db07cc..00000000 --- a/Docs/ACPI/SSDT-Disable_DynamicEWMode.dsl +++ /dev/null @@ -1,11 +0,0 @@ -DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) -{ - Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() - { - "Synaptics TouchPad", Package() - { - "DynamicEWMode", ">n", - }, - }) -} -//EOF diff --git a/Docs/ACPI/SSDT-Enable_DynamicEWMode.dsl b/Docs/ACPI/SSDT-Enable_DynamicEWMode.dsl deleted file mode 100644 index 41ef52f3..00000000 --- a/Docs/ACPI/SSDT-Enable_DynamicEWMode.dsl +++ /dev/null @@ -1,11 +0,0 @@ -DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) -{ - Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() - { - "Synaptics TouchPad", Package() - { - "DynamicEWMode", ">y", - }, - }) -} -//EOF diff --git a/Docs/ACPI/SSDT-HP-FixLidSleep.dsl b/Docs/ACPI/SSDT-HP-FixLidSleep.dsl index 5af71a34..6aa4e9a3 100755 --- a/Docs/ACPI/SSDT-HP-FixLidSleep.dsl +++ b/Docs/ACPI/SSDT-HP-FixLidSleep.dsl @@ -3,6 +3,8 @@ // Make sure you verify _SB.PCI0.LPCB.PS2K ACPI path. DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) { + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() { "Keyboard", Package() diff --git a/Docs/ACPI/SSDT-KEY-DELL-WN09.dsl b/Docs/ACPI/SSDT-KEY-DELL-WN09.dsl index 1fba5296..b60dcd5f 100644 --- a/Docs/ACPI/SSDT-KEY-DELL-WN09.dsl +++ b/Docs/ACPI/SSDT-KEY-DELL-WN09.dsl @@ -7,6 +7,8 @@ DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2dell", 0) { + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + // Select Dell specific keyboard map in VoodooPS2Keyboard.kext Method(_SB.PCI0.LPCB.PS2K._DSM, 4) { diff --git a/Docs/ACPI/SSDT-MouseAsTrackpad.dsl b/Docs/ACPI/SSDT-MouseAsTrackpad.dsl deleted file mode 100644 index 99cbf9d0..00000000 --- a/Docs/ACPI/SSDT-MouseAsTrackpad.dsl +++ /dev/null @@ -1,14 +0,0 @@ -// VoodooPS2Mouse.kext has the ability to appear as a trackpad. -// Most "trackpad" related settings don't work with it, but it will -// enable a few extra features. -DefinitionBlock("", "SSDT", 2, "ACDT", "ps2", 0) -{ - Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() - { - "Mouse", Package() - { - "ActLikeTrackpad", ">y", - }, - }) -} -//EOF diff --git a/Docs/ACPI/SSDT-NumLockOnAtBoot.dsl b/Docs/ACPI/SSDT-NumLockOnAtBoot.dsl new file mode 100755 index 00000000..d9778525 --- /dev/null +++ b/Docs/ACPI/SSDT-NumLockOnAtBoot.dsl @@ -0,0 +1,15 @@ +// Set NumLock state to ON at Bootup + +DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) +{ + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() + { + "Keyboard", Package() + { + "NumLockOnAtBoot", ">y", + }, + }) +} +//EOF diff --git a/Docs/ACPI/SSDT-NumLockSupport.dsl b/Docs/ACPI/SSDT-NumLockSupport.dsl new file mode 100755 index 00000000..b6cba0b5 --- /dev/null +++ b/Docs/ACPI/SSDT-NumLockSupport.dsl @@ -0,0 +1,18 @@ +// Add Support for Num Lock key +// By Default Voodoo maps Num Lock to Clear key because of the following +// 1) On USB keyboards Num Lock is translated to the Clear key +// 2) On Apple keyboards there is no Num Lock key + +DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) +{ + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() + { + "Keyboard", Package() + { + "NumLockSupport", ">y", + }, + }) +} +//EOF diff --git a/Docs/ACPI/SSDT-PrtSc-F13.dsl b/Docs/ACPI/SSDT-PrtSc-F13.dsl deleted file mode 100755 index 8df3f3f8..00000000 --- a/Docs/ACPI/SSDT-PrtSc-F13.dsl +++ /dev/null @@ -1,18 +0,0 @@ -// This sample shows how to remap the PrtSc key to F13 -// F13, for example, could then be mapped to one of the image capture functions -// via SysPrefs->Keyboard->Shortcuts -DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) -{ - Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() - { - "Keyboard", Package() - { - "Custom PS2 Map", Package() - { - Package(){}, - "e037=64", // PrtSc=F13 - }, - }, - }) -} -//EOF diff --git a/Docs/ACPI/SSDT-PrtSc-Remap.dsl b/Docs/ACPI/SSDT-PrtSc-Remap.dsl new file mode 100755 index 00000000..3365625a --- /dev/null +++ b/Docs/ACPI/SSDT-PrtSc-Remap.dsl @@ -0,0 +1,21 @@ +// Remap PrntScr to disable touchpad/keyboard +// Supported Voodoo PrntScr Key combinations: + +// PrntScr Enable/Disable touchpad +// Windows+PrntScr Enable/Disable touchpad+keyboard +// Ctrl+Alt+PrntScr Reset and enable touchpad +// Shift+PrntScr Send SysRq scancode to the kernel + +DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) +{ + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() + { + "Keyboard", Package() + { + "RemapPrntScr", ">y", + }, + }) +} +//EOF diff --git a/Docs/ACPI/SSDT-Swap-CommandOption.dsl b/Docs/ACPI/SSDT-Swap-CommandOption.dsl new file mode 100755 index 00000000..e0fb1ced --- /dev/null +++ b/Docs/ACPI/SSDT-Swap-CommandOption.dsl @@ -0,0 +1,14 @@ +// This sample shows how to remap the Command and Option. +DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) +{ + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() + { + "Keyboard", Package() + { + "Swap command and option", ">y", + } + }) +} +//EOF diff --git a/Docs/ACPI/SSDT-Swap-LeftControlCapsLock.dsl b/Docs/ACPI/SSDT-Swap-LeftControlCapsLock.dsl index 5ad51812..afe8d6ab 100644 --- a/Docs/ACPI/SSDT-Swap-LeftControlCapsLock.dsl +++ b/Docs/ACPI/SSDT-Swap-LeftControlCapsLock.dsl @@ -4,6 +4,8 @@ // For example, a Lenovo x1 Carbon 5th Gen would be "_SB.PCI0.LPCB.KBD.RMCF" DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) { + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() { "Keyboard", Package() diff --git a/Docs/ACPI/SSDT-Swap-LeftControlCommand.dsl b/Docs/ACPI/SSDT-Swap-LeftControlCommand.dsl index e5f9ca53..560c5bec 100755 --- a/Docs/ACPI/SSDT-Swap-LeftControlCommand.dsl +++ b/Docs/ACPI/SSDT-Swap-LeftControlCommand.dsl @@ -2,6 +2,8 @@ // and Command (left Alt) to Left Control. DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) { + External (_SB_.PCI0.LPCB.PS2K, DeviceObj) + Name(_SB.PCI0.LPCB.PS2K.RMCF, Package() { "Keyboard", Package() @@ -15,4 +17,4 @@ DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) }, }) } -//EOF \ No newline at end of file +//EOF diff --git a/Docs/ACPI/SSDT-Thinkpad_Clickpad.dsl b/Docs/ACPI/SSDT-Thinkpad_Clickpad.dsl index 32d26cfd..500c8ffb 100644 --- a/Docs/ACPI/SSDT-Thinkpad_Clickpad.dsl +++ b/Docs/ACPI/SSDT-Thinkpad_Clickpad.dsl @@ -20,29 +20,19 @@ DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) { "Synaptics TouchPad", Package() { - "BogusDeltaThreshX", 800, - "BogusDeltaThreshY", 800, - "Clicking", ">y", - "DragLockTempMask", 0x40004, - "DynamicEWMode", ">n", - "FakeMiddleButton", ">n", "HWResetOnStart", ">y", - //"ForcePassThrough", ">y", - //"SkipPassThrough", ">y", - "PalmNoAction When Typing", ">y", - "ScrollResolution", 800, - "SmoothInput", ">y", - "UnsmoothInput", ">y", - "Thinkpad", ">y", - "EdgeBottom", 0, + "QuietTimeAfterTyping", 500000000, "FingerZ", 30, - "MaxTapTime", 100000000, "MouseMultiplierX", 2, "MouseMultiplierY", 2, - "MouseScrollMultiplierX", 2, - "MouseScrollMultiplierY", 2, - //"TrackpointScrollYMultiplier", 1, //Change this value to 0xFFFF in order to inverse the vertical scroll direction of the Trackpoint when holding the middle mouse button. - //"TrackpointScrollXMultiplier", 1, //Change this value to 0xFFFF in order to inverse the horizontal scroll direction of the Trackpoint when holding the middle mouse button. + "MouseDivisorX", 1, + "MouseDivisorY", 1, + // Change multipliers to 0xFFFE in order to inverse the scroll direction + // of the Trackpoint when holding the middle mouse button. + "TrackpointScrollXMultiplier", 2, + "TrackpointScrollYMultiplier", 2, + "TrackpointScrollXDivisor", 1, + "TrackpointScrollYDivisor", 1 }, }) } diff --git a/Docs/ACPI/SSDT-Thinkpad_Trackpad.dsl b/Docs/ACPI/SSDT-Thinkpad_Trackpad.dsl index f5ea83b9..89dbfd25 100644 --- a/Docs/ACPI/SSDT-Thinkpad_Trackpad.dsl +++ b/Docs/ACPI/SSDT-Thinkpad_Trackpad.dsl @@ -1,8 +1,9 @@ // Example overrides for Thinkpad models with TrackPad DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) { + External(_SB_.PCI0.LPCB.PS2K, DeviceObj) // Change _SB.PCI0.LPC.KBD if your PS2 keyboard is at a different ACPI path - External(_SB.PCI0.LPC.KBD, DeviceObj) + External(_SB_.PCI0.LPC.KBD, DeviceObj) Scope(_SB.PCI0.LPC.KBD) { // Select specific configuration in VoodooPS2Trackpad.kext @@ -20,36 +21,19 @@ DefinitionBlock ("", "SSDT", 2, "ACDT", "ps2", 0) { "Synaptics TouchPad", Package() { - "BogusDeltaThreshX", 100, - "BogusDeltaThreshY", 100, - "Clicking", ">y", - "DragLockTempMask", 0x40004, - "DynamicEWMode", ">n", - "FakeMiddleButton", ">n", "HWResetOnStart", ">y", - //"ForcePassThrough", ">y", - //"SkipPassThrough", ">y", "PalmNoAction When Typing", ">y", - "ScrollResolution", 800, - "SmoothInput", ">y", - "UnsmoothInput", ">y", - "Thinkpad", ">y", - "DivisorX", 1, - "DivisorY", 1, "FingerZ", 47, - "MaxTapTime", 100000000, - "MomentumScrollThreshY", 16, "MouseMultiplierX", 8, "MouseMultiplierY", 8, - "MouseScrollMultiplierX", 2, - "MouseScrollMultiplierY", 2, - "MultiFingerHorizontalDivisor", 4, - "MultiFingerVerticalDivisor", 4, - "Resolution", 3200, - "ScrollDeltaThreshX", 10, - "ScrollDeltaThreshY", 10, - //"TrackpointScrollYMultiplier", 1, //Change this value to 0xFFFF in order to inverse the vertical scroll direction of the Trackpoint when holding the middle mouse button. - //"TrackpointScrollXMultiplier", 1, //Change this value to 0xFFFF in order to inverse the horizontal scroll direction of the Trackpoint when holding the middle mouse button. + "MouseDivisorX", 1, + "MouseDivisorY", 1, + // Change multipliers to 0xFFFE in order to inverse the scroll direction + // of the Trackpoint when holding the middle mouse button. + "TrackpointScrollXMultiplier", 2, + "TrackpointScrollYMultiplier", 2, + "TrackpointScrollXDivisor", 1, + "TrackpointScrollYDivisor", 1 }, }) } diff --git a/Library/LegacyIOHIDDevice.h b/Library/LegacyIOHIDDevice.h deleted file mode 100644 index c5b80b07..00000000 --- a/Library/LegacyIOHIDDevice.h +++ /dev/null @@ -1,753 +0,0 @@ -/* - * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#ifndef _IOKIT_HID_IOHIDDEVICE_H -#define _IOKIT_HID_IOHIDDEVICE_H - -#include "LegacyIOService.h" -#include -#include -#include -#include -#include -#include - -class IOHIDSystem; -class IOHIDPointing; -class IOHIDKeyboard; -class IOHIDConsumer; -class IOHIDElementPrivate; -class IOHIDEventQueue; -class IOHIDInterface; -class IOHIDDeviceShim; -struct IOHIDReportHandler; -class IOHIDAsyncReportQueue; - -/*! - @typedef IOHIDCompletionAction - @abstract Function called when set/get report completes - @param target The target specified in the IOHIDCompletion struct. - @param parameter The parameter specified in the IOHIDCompletion struct. - @param status Completion status -*/ -typedef void (*IOHIDCompletionAction)( - void * target, - void * parameter, - IOReturn status, - UInt32 bufferSizeRemaining); - -#ifndef __MAC_10_15 -/*! - @typedef IOHIDCompletion - @abstract Struct spefifying action to perform when set/get report completes. - @var target The target to pass to the action function. - @var action The function to call. - @var parameter The parameter to pass to the action function. -*/ -typedef struct IOHIDCompletion { - void * target; - IOHIDCompletionAction action; - void * parameter; -} IOHIDCompletion; - -/*! - @enum IOHIDReportOption - @abstract Option bits for IOHIDDevice::handleReport, - IOHIDDevice::getReport, and IOHIDDevice::setReport - @constant kIOHIDReportOptionNotInterrupt Tells method that the report - passed was not interrupt driven. -*/ -enum -{ - kIOHIDReportOptionNotInterrupt = 0x100, - kIOHIDReportOptionVariableSize = 0x200 -}; -#endif - - -/*! @class IOHIDDevice : public IOService - @abstract IOHIDDevice defines a Human Interface Device (HID) object, - which will interact with the HID Manager by publishing static properties - in the registry, and also by reporting HID events through shared memory. - IOHIDDevice is an abstract class that must be subclassed to support a - specific type of HID devices, such as USB HID class devices. -
- Since most HID devices are expected to be USB devices, IOHIDDevice - uses the USB HID specification to define the format of the report - descriptor, and also reports that are used to communicate with the - hardware via some intervening transport layer. However, there is no - mandate that the transport layer must be restricted to USB. A subclass - may be created to support legacy ADB joysticks, and issue packets on - the ADB bus and translate those packets to USB reports, and vice versa. - IOHIDDevice does not care how those reports are generated or consumed - by the physical device, as long as the reports abide to the USB - specification. */ - -class IOHIDDevice : public IOService -{ - OSDeclareDefaultStructors( IOHIDDevice ) - - friend class IOHIDLibUserClient; - friend class IOHIDDeviceShim; - -private: - OSArray * _elementArray; - UInt32 _dataElementIndex; - IORecursiveLock * _elementLock; - IOHIDReportHandler * _reportHandlers; - IOBufferMemoryDescriptor * _elementValuesDescriptor; - bool _readyForInputReports; - UInt32 _reportCount; - UInt32 _maxInputReportSize; - UInt32 _maxOutputReportSize; - UInt32 _maxFeatureReportSize; - - struct ExpansionData { - OSSet * clientSet; - IOService * seizedClient; - AbsoluteTime eventDeadline; - OSArray * inputInterruptElementArray; - bool performTickle; - bool performWakeTickle; - OSArray * interfaceNubs; - IOHIDElementPrivate * rollOverElement; - OSArray * hierarchElements; - OSArray * interfaceElementArrays; - IOHIDAsyncReportQueue * asyncReportQueue; - IOWorkLoop * workLoop; - IOEventSource * eventSource; - IONotifier * deviceNotify; - }; - /*! @var reserved - Reserved for future use. (Internal use only) */ - ExpansionData * _reserved; - - // HID report descriptor parsing support. - - bool linkToParent( const OSArray * array, - UInt32 parentIndex, - UInt32 childIndex ); - - bool createCollectionElements( HIDPreparsedDataRef parseData, - OSArray * array, - UInt32 maxCount ); - - bool createValueElements( HIDPreparsedDataRef parseData, - OSArray * array, - UInt32 hidReportType, - IOHIDElementType elementType, - UInt32 maxCount ); - - bool createButtonElements( HIDPreparsedDataRef parseData, - OSArray * array, - UInt32 hidReportType, - IOHIDElementType elementType, - UInt32 maxCount ); - - bool createReportHandlerElements( HIDPreparsedDataRef parseData); - - bool getReportCountAndSizes( HIDPreparsedDataRef parseData ); - - bool setReportSize( UInt8 reportID, - IOHIDReportType reportType, - UInt32 bits ); - - IOReturn createElementHierarchy( HIDPreparsedDataRef parseData ); - - IOReturn parseReportDescriptor( IOMemoryDescriptor * report, - IOOptionBits options = 0 ); - - IOBufferMemoryDescriptor * createMemoryForElementValues(); - - OSNumber * newPrimaryUsageNumber(UInt32 interfaceIdx) const; - - OSNumber * newPrimaryUsagePageNumber(UInt32 interfaceIdx) const; - - OSArray * newDeviceUsagePairs(OSArray * elements, UInt32 start); - - static bool _publishDeviceNotificationHandler(void * target, - void * refCon, - IOService * newService, - IONotifier * notifier ); - -protected: - -/*! @function free - @abstract Free the IOHIDDevice object. - @discussion Release all resources that were previously allocated, - then call super::free() to propagate the call to our superclass. */ - - virtual void free() APPLE_KEXT_OVERRIDE; - -/*! @function handleOpen - @abstract Handle a client open on the interface. - @discussion This method is called by IOService::open() with the - arbitration lock held, and must return true to accept the client open. - This method will in turn call handleClientOpen() to qualify the client - requesting the open. - @param client The client object that requested the open. - @param options Options passed to IOService::open(). - @param argument Argument passed to IOService::open(). - @result true to accept the client open, false otherwise. */ - - virtual bool handleOpen(IOService * client, - IOOptionBits options, - void * argument) APPLE_KEXT_OVERRIDE; - -/*! @function handleClose - @abstract Handle a client close on the interface. - @discussion This method is called by IOService::close() with the - arbitration lock held. This method will in turn call handleClientClose() - to notify interested subclasses about the client close. If this represents - the last close, then the interface will also close the controller before - this method returns. The controllerWillClose() method will be called before - closing the controller. Subclasses should not override this method. - @param client The client object that requested the close. - @param options Options passed to IOService::close(). */ - - virtual void handleClose(IOService * client, IOOptionBits options) APPLE_KEXT_OVERRIDE; - -/*! @function handleIsOpen - @abstract Query whether a client has an open on the interface. - @discussion This method is always called by IOService with the - arbitration lock held. Subclasses should not override this method. - @result true if the specified client, or any client if none (0) is - specified, presently has an open on this object. */ - - virtual bool handleIsOpen(const IOService * client) const APPLE_KEXT_OVERRIDE; - -/*! @function handleStart - @abstract Prepare the hardware and driver to support I/O operations. - @discussion IOHIDDevice will call this method from start() before - any I/O operations are issued to the concrete subclass. Methods - such as newReportDescriptor() are only called after handleStart() - has returned true. A subclass that overrides this method should - begin its implementation by calling the version in super, and - then check the return value. - @param provider The provider argument passed to start(). - @result True on success, or false otherwise. Returning false will - cause start() to fail and return false. */ - - virtual bool handleStart( IOService * provider ); - -/*! @function handleStop - @abstract Quiesce the hardware and stop the driver. - @discussion IOHIDDevice will call this method from stop() to - signal that the hardware should be quiesced and the driver stopped. - A subclass that overrides this method should end its implementation - by calling the version in super. - @param provider The provider argument passed to stop(). */ - - virtual void handleStop( IOService * provider ); - -/*! @function newUserClient - @abstract Handle a request to create a connection for a non kernel - client. - @discussion Create a new IOUserClient, or a subclass of IOUserClient, - to service a connection to a non kernel client. This implementation - will simply call the implementation in IOService to handle the call. - @param owningTask The mach task requesting the connection. - @param security_id A token representing the access level for the task. - @param type A constant specifying the type of connection to be created. - @param properties A dictionary of additional properties for the connection. - @param handler The IOUserClient object returned. - @result The return from IOService::newUserClient() is returned. */ - - virtual IOReturn newUserClient( task_t owningTask, - void * security_id, - UInt32 type, - OSDictionary * properties, - IOUserClient ** handler ) APPLE_KEXT_OVERRIDE; - IOReturn newUserClientInternal(task_t owningTask, - void * security_id, - OSDictionary * properties, - IOUserClient ** handler ); - -/*! @function publishProperties - @abstract Publish HID properties to the I/O Kit registry. - @discussion Called by the start() method to fetch and publish all - HID properties to the I/O Kit registry. These properties will allow - the HID Manager to identify all HID device(s) in the system, by - iterating through objects that are subclasses of IOHIDDevice, and - then fetch their published property values. The implementation in - IOHIDDevice will call methods to get each individual HID property, - and subclasses will not normally need to override this method. - @param provider The provider argument passed to start(). - @result True to indicate that all properties were discovered and - published to the registry, false otherwise. Returning false will - cause start() to fail and return false. */ - - virtual bool publishProperties( IOService * provider ); - -public: - -/*! @function init - @abstract Initialize an IOHIDDevice object. - @discussion Prime the IOHIDDevice object and prepare it to support - a probe() or a start() call. This implementation will simply call - super::init(). - @param dictionary A dictionary associated with this IOHIDDevice - instance. - @result True on sucess, or false otherwise. */ - - virtual bool init( OSDictionary * dictionary = 0 ) APPLE_KEXT_OVERRIDE; - -/*! @function start - @abstract Start up the driver using the given provider. - @discussion IOHIDDevice will allocate resources, then call handleStart() - before fetching the report descriptor through newReportDescriptor(), and - publishing HID properties to the registry. Before returning true to - indicate success, registerService() is called to trigger client matching. - Subclasses are recommended to override handleStart(). - @param provider The provider that the driver was matched to, and selected - to run with. - @result True on success, or false otherwise. */ - - virtual bool start( IOService * provider ) APPLE_KEXT_OVERRIDE; - -/*! @function stop - @abstract Called by a provider (during its termination) before detaching - all its clients. - @discussion IOHIDDevice will call handleStop(), then release allocated - resources. Subclasses are recommended to override handleStop(). - @param provider The provider that the driver was started on. */ - - virtual void stop( IOService * provider ) APPLE_KEXT_OVERRIDE; - -/*! @function matchPropertyTable - @abstract Called by the provider during a match - @discussion Compare the properties in the supplied table to this - object's properties. - @param table The property table that this device will match against -*/ - - virtual bool matchPropertyTable(OSDictionary * table, SInt32 * score) APPLE_KEXT_OVERRIDE; - -/*! @function message - @abstract Receives messages delivered from an attached provider. - @discussion Handles the kIOMessageDeviceSignaledWakeup message - from a provider identifying the IOHIDDevice as the wakeup source. - @param type A type defined in IOMessage.h. - @param provider The provider from which the message originates. - @param argument An argument defined by the message type. - @result An IOReturn code defined by the message type. -*/ - - virtual IOReturn message( UInt32 type, IOService * provider, void * argument = 0 ) APPLE_KEXT_OVERRIDE; - -/*! @function newTransportString - @abstract Returns a string object that describes the transport - layer used by the HID device. - @result A string object. The caller must decrement the retain count - on the object returned. */ - - virtual OSString * newTransportString() const; - -/*! @function newManufacturerString - @abstract Returns a string object that describes the manufacturer - of the HID device. - @result A string object. The caller must decrement the retain count - on the object returned. */ - - virtual OSString * newManufacturerString() const; - -/*! @function newProductString - @abstract Returns a string object that describes the product - of the HID device. - @result A string object. The caller must decrement the retain count - on the object returned. */ - - virtual OSString * newProductString() const; - -/*! @function newVendorIDNumber - @abstract Returns a number object that describes the vendor ID - of the HID device. - @result A number object. The caller must decrement the retain count - on the object returned. */ - - virtual OSNumber * newVendorIDNumber() const; - -/*! @function newProductIDNumber - @abstract Returns a number object that describes the product ID - of the HID device. - @result A number object. The caller must decrement the retain count - on the object returned. */ - - virtual OSNumber * newProductIDNumber() const; - -/*! @function newVersionNumber - @abstract Returns a number object that describes the version number - of the HID device. - @result A number object. The caller must decrement the retain count - on the object returned. */ - - virtual OSNumber * newVersionNumber() const; - -// *** THIS HAS BEEN DEPRECATED. PLEASE USE newSerialNumberString *** -/*! @function newSerialNumber - @abstract THIS HAS BEEN DEPRECATED. PLEASE USE newSerialNumberString. - @result A number object. The caller must decrement the retain count - on the object returned. */ - - virtual OSNumber * newSerialNumber() const; - -/*! @function newPrimaryUsageNumber - @abstract Returns a number object that describes the primary usage - of the HID device. - @result A number object. The caller must decrement the retain count - on the object returned. */ - - virtual OSNumber * newPrimaryUsageNumber() const; - -/*! @function newPrimaryUsagePageNumber - @abstract Returns a number object that describes the primary usage - page of the HID device. - @result A number object. The caller must decrement the retain count - on the object returned. */ - - virtual OSNumber * newPrimaryUsagePageNumber() const; - -/*! @function newReportDescriptor - @abstract Create and return a new memory descriptor that describes the - report descriptor for the HID device. - @discussion A subclass must override this pure virtual function, and - return a memory descriptor that describes the HID report descriptor as - defined by the USB Device Class Definition for Human Interface Devices - Version 1.1 specification. - @param descriptor Pointer to the memory descriptor returned. This - memory descriptor will be released by the caller. - @result kIOReturnSuccess on success, or an error return otherwise. */ - - virtual IOReturn newReportDescriptor( - IOMemoryDescriptor ** descriptor ) const = 0; - -/*! @function handleReport - @abstract Handle an asynchronous report received from the HID device. - @param report A memory descriptor that describes the report. - @param reportType The type of report. - @param options Options to specify the request. No options are - currently defined, and the default value is 0. - @result kIOReturnSuccess on success, or an error return otherwise. */ - - virtual IOReturn handleReport( - IOMemoryDescriptor * report, - IOHIDReportType reportType = kIOHIDReportTypeInput, - IOOptionBits options = 0 ); - -/*! @function getReport - @abstract Get a report from the HID device. - @discussion A completion parameter may be added in the future. - @param report A memory descriptor that describes the memory to store - the report read from the HID device. - @param reportType The report type. - @param options The lower 8 bits will represent the Report ID. The - other 24 bits are options to specify the request. - @result kIOReturnSuccess on success, or an error return otherwise. */ - - virtual IOReturn getReport( IOMemoryDescriptor * report, - IOHIDReportType reportType, - IOOptionBits options ); - -/*! @function setReport - @abstract Send a report to the HID device. - @discussion A completion parameter may be added in the future. - @param report A memory descriptor that describes the report to send - to the HID device. - @param reportType The report type. - @param options The lower 8 bits will represent the Report ID. The - other 24 bits are options to specify the request. - @result kIOReturnSuccess on success, or an error return otherwise. */ - - virtual IOReturn setReport( IOMemoryDescriptor * report, - IOHIDReportType reportType, - IOOptionBits options = 0 ); - -/*! @function getMemoryWithCurrentElementValues - @abstract Get a reference to a memory descriptor that describes the - memory block containing the current HID element values. - @discussion Each HID element that can contribute to an input, output, - or feature report, is assigned an area of memory from a common memory - block allocated by IOHIDDevice. Each element will use its assigned - memory area to store its current value, defined by an IOHIDElementValue - structure. The memory described by the memory descriptor may be mapped - to user space to allow the HID Manager to poll the current element - value without the cost of a user-kernel transition. Subclasses should - not override this method. - @result A reference to a memory descriptor that describes the current - element values, or 0 to indicate a resource shortage. */ - - virtual IOMemoryDescriptor * getMemoryWithCurrentElementValues() const; - -/*! @function registerElement - @abstract A registration function called by a HID element to register - itself, and also to obtain an unique cookie identifier - (unique per device, not unique system-wide). - @discussion An internal data type, an IOHIDElementPrivate, is created to - represent each HID element discovered by parsing the HID report - descriptor. Each element created will call this method to register - itself with its owner (IOHIDDevice), and also to obtain an element - cookie that is used by HID Manager to specify and identify the element. - Subclasses should not override this method. - @param element The element that is requesting registration with its - owner. - @param cookie Pointer to the returned cookie assigned to this element. - @result True on success, or false otherwise. */ - - virtual bool registerElement( IOHIDElementPrivate * element, - IOHIDElementCookie * cookie ); - -/*! @function startEventDelivery - @abstract Start delivering events from a HID element to the event - queue specified. - @discussion Clients of IOHIDDevice may create an IOHIDEventQueue, and - then call this method to register for delivery of events generated by - one or more HID elements to that event queue. Subclasses should not - override this method. - @param queue The event queue that is interested in receiving events - generated by the HID element specified. The retain count on the queue - will be incremented by one. - @param cookie The cookie for a HID element published by the HID device. - @param options Options to specify the request. No options are currently - defined, and the default value is zero. - @result kIOReturnSuccess on success, or kIOReturnBadArgument if the - queue or the cookie argument specified is invalid, or kIOReturnNoMemory - if a resource shortage was encountered. */ - - virtual IOReturn startEventDelivery( IOHIDEventQueue * queue, - IOHIDElementCookie cookie, - IOOptionBits options = 0 ); - -/*! @function stopEventDelivery - @abstract Stop delivering events from one or more HID elements to the - event queue specified. - @discussion Clients that called startEventDelivery() must eventually - call this method to stop event delivery to its queue from one or more - HID elements. - @param queue The event queue that no longer wishes to receive events - generated by the HID element specified. - @param cookie The cookie for a HID element published by the HID device. - The default value of zero indicates that the queue should be removed from - the event dispatch list of all HID elements published by the HID device. - Subclasses should not override this method. - @result kIOReturnSuccess if the queue was removed from the event dispatch - list for one or more HID elements, or kIOReturnBadArgument if the queue - or the cookie argument specified is invalid, or kIOReturnNotFound if the - queue was not found. */ - - virtual IOReturn stopEventDelivery( IOHIDEventQueue * queue, - IOHIDElementCookie cookie = 0 ); - -/*! @function checkEventDelivery - @abstract Check whether events from a HID element will be delivered to - the event queue specified. - @param queue The event queue. - @param cookie The cookie for a HID element published by the HID device. - @param isActive Pointer to the return value that is set to true if events - generated by the HID element will be delivered to the queue, or false - otherwise. This return value is set only if kIOReturnSuccess is - returned. - @result kIOReturnSuccess on success, or kIOReturnBadArgument if one or - more of the arguments provided are invalid. */ - - virtual IOReturn checkEventDelivery( IOHIDEventQueue * queue, - IOHIDElementCookie cookie, - bool * isActive ); - -/*! @function updateElementValues - @abstract Updates element values from a HID device via getReport. - @discussion A completion parameter may be added in the future. - @param cookies A list of element cookies who's values need to be - set on the device. - @param cookieCount The number of element cookies. - @result kIOReturnSuccess on success, or an error return otherwise. */ - OSMetaClassDeclareReservedUsed(IOHIDDevice, 0); - virtual IOReturn updateElementValues(IOHIDElementCookie * cookies, UInt32 cookieCount = 1); - -/*! @function postElementValues - @abstract Posts element values to a HID device via setReport. - @discussion A completion parameter may be added in the future. - @param cookies A list of element cookies who's values need to be - set on the device. - @param cookieCount The number of element cookies. - @result kIOReturnSuccess on success, or an error return otherwise. */ - OSMetaClassDeclareReservedUsed(IOHIDDevice, 1); - virtual IOReturn postElementValues(IOHIDElementCookie * cookies, UInt32 cookieCount = 1); - -/*! @function newSerialNumberString - @abstract Returns a string object that describes the serial number - of the HID device. - @result A number object. The caller must decrement the retain count - on the object returned. */ - OSMetaClassDeclareReservedUsed(IOHIDDevice, 2); - virtual OSString * newSerialNumberString() const; - -/*! @function newLocationIDNumber - @abstract Returns a number object that describes the location ID - of the HID device. - @result A number object. The caller must decrement the retain count - on the object returned. */ - OSMetaClassDeclareReservedUsed(IOHIDDevice, 3); - virtual OSNumber * newLocationIDNumber() const; - -/*! @function getReport - @abstract Get a report from the HID device. - @discussion A completion parameter may be added in the future. - @param report A memory descriptor that describes the memory to store - the report read from the HID device. - @param reportType The report type. - @param options The lower 8 bits will represent the Report ID. The - other 24 bits are options to specify the request. - @param completionTimeout Specifies an amount of time (in ms) after which - the command will be aborted if the entire command has not been completed. - @param completion Function to call when request completes. If omitted then - getReport() executes synchronously, blocking until the request is complete. - @result kIOReturnSuccess on success, or an error return otherwise. */ - - OSMetaClassDeclareReservedUsed(IOHIDDevice, 4); - virtual IOReturn getReport( IOMemoryDescriptor * report, - IOHIDReportType reportType, - IOOptionBits options, - UInt32 completionTimeout, - IOHIDCompletion * completion = 0); - -/*! @function setReport - @abstract Send a report to the HID device. - @discussion A completion parameter may be added in the future. - @param report A memory descriptor that describes the report to send - to the HID device. - @param reportType The report type. - @param options The lower 8 bits will represent the Report ID. The - other 24 bits are options to specify the request. - @param completionTimeout Specifies an amount of time (in ms) after which - the command will be aborted if the entire command has not been completed. - @param completion Function to call when request completes. If omitted then - setReport() executes synchronously, blocking until the request is complete. - @result kIOReturnSuccess on success, or an error return otherwise. */ - - OSMetaClassDeclareReservedUsed(IOHIDDevice, 5); - virtual IOReturn setReport( IOMemoryDescriptor * report, - IOHIDReportType reportType, - IOOptionBits options, - UInt32 completionTimeout, - IOHIDCompletion * completion = 0); - -/*! @function newVendorIDSourceNumber - @abstract Returns a number object that describes the vendor ID - source of the HID device. - @result A number object. The caller must decrement the retain count - on the object returned. */ - OSMetaClassDeclareReservedUsed(IOHIDDevice, 6); - virtual OSNumber * newVendorIDSourceNumber() const; - -/*! @function newCountryCodeNumber - @abstract Returns a number object that describes the country code - of the HID device. - @result A number object. The caller must decrement the retain count - on the object returned. */ - OSMetaClassDeclareReservedUsed(IOHIDDevice, 7); - virtual OSNumber * newCountryCodeNumber() const; - - -/*! @function handleReportWithTime - @abstract Handle an asynchronous report received from the HID device. - @param timeStamp The timestamp of report. - @param report A memory descriptor that describes the report. - @param reportType The type of report. Currently, only - kIOHIDReportTypeInput report type is handled. - @param options Options to specify the request. No options are - currently defined, and the default value is 0. - @result kIOReturnSuccess on success, or an error return otherwise. */ - - OSMetaClassDeclareReservedUsed(IOHIDDevice, 8); - virtual IOReturn handleReportWithTime( - AbsoluteTime timeStamp, - IOMemoryDescriptor * report, - IOHIDReportType reportType = kIOHIDReportTypeInput, - IOOptionBits options = 0); - -/*! @function newReportInterval - @abstract Returns a number object that describes the actual polling - interval of the HID device in microseconds. - @result A number object. The caller must decrement the retain count - on the object returned. */ - OSMetaClassDeclareReservedUsed(IOHIDDevice, 9); - virtual OSNumber * newReportIntervalNumber() const; - - OSMetaClassDeclareReservedUsed(IOHIDDevice, 10); - virtual IOReturn handleReportWithTimeAsync( - AbsoluteTime timeStamp, - IOMemoryDescriptor * report, - IOHIDReportType reportType, - IOOptionBits options, - UInt32 completionTimeout, - IOHIDCompletion * completion); - -/*! @function newDeviceUsagePairs - @abstract Returns an array of usage dictionaries. IOHIDDevice creates - create this from the actual report descriptor, and that should be the base - for any subclass override. - @result A number object. The caller must decrement the retain count - on the object returned. */ - OSMetaClassDeclareReservedUsed(IOHIDDevice, 11); - virtual OSArray * newDeviceUsagePairs(); - -protected: - /*! @function createInterface - @abstract Creates an IOHIDInterface nub for the device to attach to. - @discussion Will create multiple interfaces, if applicable and support is - enabled. - @result true on success, false otherwise. */ - OSMetaClassDeclareReservedUnused(IOHIDDevice, 12); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 13); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 14); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 15); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 16); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 17); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 18); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 19); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 20); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 21); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 22); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 23); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 24); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 25); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 26); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 27); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 28); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 29); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 30); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 31); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 32); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 33); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 34); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 35); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 36); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 37); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 38); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 39); - OSMetaClassDeclareReservedUnused(IOHIDDevice, 40); - -}; - -#endif /* !_IOKIT_HID_IOHIDDEVICE_H */ diff --git a/Library/LegacyIOHIKeyboard.h b/Library/LegacyIOHIKeyboard.h deleted file mode 100644 index 9e40c47d..00000000 --- a/Library/LegacyIOHIKeyboard.h +++ /dev/null @@ -1,278 +0,0 @@ -/* - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -/* Copyright (c) 1992 NeXT Computer, Inc. All rights reserved. - * - * EventSrcPCKeyboard.h - PC Keyboard EventSrc subclass definition - * - * HISTORY - * 28 Aug 1992 Joe Pasqua - * Created. - */ - -#ifndef _IOHIKEYBOARD_H -#define _IOHIKEYBOARD_H - -#include "LegacyIOService.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winconsistent-missing-override" -#include -#include -#pragma clang diagnostic pop - -/* Start Action Definitions */ - -/* - * HISTORICAL NOTE: - * The following entry points were part of the IOHIKeyboardEvents - * protocol. - */ - -typedef void (*KeyboardEventAction)( OSObject * target, - /* eventFlags */ unsigned eventType, - /* flags */ unsigned flags, - /* keyCode */ unsigned key, - /* charCode */ unsigned charCode, - /* charSet */ unsigned charSet, - /* originalCharCode */ unsigned origCharCode, - /* originalCharSet */ unsigned origCharSet, - /* keyboardType */ unsigned keyboardType, - /* repeat */ bool repeat, - /* atTime */ AbsoluteTime ts); - -typedef void (*KeyboardSpecialEventAction)(OSObject * target, - /* eventType */ unsigned eventType, - /* flags */ unsigned flags, - /* keyCode */ unsigned key, - /* specialty */ unsigned flavor, - /* source id */ UInt64 guid, - /* repeat */ bool repeat, - /* atTime */ AbsoluteTime ts); - -typedef void (*UpdateEventFlagsAction)( OSObject * target, - /* flags */ unsigned flags); - -/* Event Callback Definitions */ - -typedef void (*KeyboardEventCallback)( - /* target */ OSObject * target, - /* eventFlags */ unsigned eventType, - /* flags */ unsigned flags, - /* keyCode */ unsigned key, - /* charCode */ unsigned charCode, - /* charSet */ unsigned charSet, - /* originalCharCode */ unsigned origCharCode, - /* originalCharSet */ unsigned origCharSet, - /* keyboardType */ unsigned keyboardType, - /* repeat */ bool repeat, - /* atTime */ AbsoluteTime ts, - /* sender */ OSObject * sender, - /* refcon */ void * refcon); - -typedef void (*KeyboardSpecialEventCallback)( - /* target */ OSObject * target, - /* eventType */ unsigned eventType, - /* flags */ unsigned flags, - /* keyCode */ unsigned key, - /* specialty */ unsigned flavor, - /* source id */ UInt64 guid, - /* repeat */ bool repeat, - /* atTime */ AbsoluteTime ts, - /* sender */ OSObject * sender, - /* refcon */ void * refcon); - -typedef void (*UpdateEventFlagsCallback)( - /* target */ OSObject * target, - /* flags */ unsigned flags, - /* sender */ OSObject * sender, - /* refcon */ void * refcon); - -/* End Action Definitions */ - -/* Default key repeat parameters */ -#define EV_DEFAULTINITIALREPEAT 500000000ULL // 1/2 sec in nanoseconds -#define EV_DEFAULTKEYREPEAT 83333333ULL // 1/12 sec in nanoseconds -#define EV_MINKEYREPEAT 16700000ULL // 1/60 sec - -class IOHIKeyboard : public IOHIDevice -{ - OSDeclareDefaultStructors(IOHIKeyboard); - - friend class IOHIDKeyboardDevice; - friend class IOHIDKeyboardEventDevice; - friend class IOHIDKeyboard; - friend class IOHIDConsumer; - -protected: - IOLock * _deviceLock; // Lock for all device access - IOHIKeyboardMapper * _keyMap; // KeyMap instance - - // The following fields describe the kind of keyboard - UInt32 _interfaceType; - UInt32 _deviceType; - - // The following fields describe the state of the keyboard - UInt32 * _keyState; // kbdBitVector - IOByteCount _keyStateSize; // kbdBitVector allocated size - unsigned _eventFlags; // Current eventFlags - bool _alphaLock; // true means alpha lock is on - bool _numLock; // true means num lock is on - bool _charKeyActive; // true means char gen. key active - - // The following fields are used in performing key repeats - bool _isRepeat; // true means we're generating repeat - unsigned _codeToRepeat; // What we are repeating - bool _calloutPending; // true means we've sched. a callout - AbsoluteTime _lastEventTime; // Time last event was dispatched - AbsoluteTime _downRepeatTime; // Time when we should next repeat - AbsoluteTime _keyRepeat; // Delay between key repeats - AbsoluteTime _initialKeyRepeat; // Delay before initial key repeat - UInt64 _guid; - - OSObject * _keyboardEventTarget; - KeyboardEventAction _keyboardEventAction; - OSObject * _keyboardSpecialEventTarget; - KeyboardSpecialEventAction _keyboardSpecialEventAction; - OSObject * _updateEventFlagsTarget; - UpdateEventFlagsAction _updateEventFlagsAction; - - UInt16 _lastUsagePage; - UInt16 _lastUsage; - -protected: - virtual void dispatchKeyboardEvent(unsigned int keyCode, - /* direction */ bool goingDown, - /* timeStamp */ AbsoluteTime time); - void setLastPageAndUsage(UInt16 usagePage, UInt16 usage); - void getLastPageAndUsage(UInt16 &usagePage, UInt16 &usage); - void clearLastPageAndUsage(); - -public: - virtual bool init(OSDictionary * properties = 0) override; - virtual bool start(IOService * provider) override; - virtual void stop(IOService * provider) override; - virtual void free() override; - - virtual bool open(IOService * client, - IOOptionBits options, - KeyboardEventAction keAction, - KeyboardSpecialEventAction kseAction, - UpdateEventFlagsAction uefAction); - - bool open( IOService * client, - IOOptionBits options, - void *, - KeyboardEventCallback keCallback, - KeyboardSpecialEventCallback kseCallback, - UpdateEventFlagsCallback uefCallback); - - virtual void close(IOService * client, IOOptionBits ) override; - - virtual IOReturn message( UInt32 type, IOService * provider, - void * argument = 0 ) override; - - virtual IOHIDKind hidKind() override; - virtual bool updateProperties( void ) override; - virtual IOReturn setParamProperties(OSDictionary * dict) override; - virtual IOReturn setProperties( OSObject * properties ) override; - - inline bool isRepeat() {return _isRepeat;} - -protected: // for subclasses to implement - virtual const unsigned char * defaultKeymapOfLength(UInt32 * length); - virtual void setAlphaLockFeedback(bool val); - virtual void setNumLockFeedback(bool val); - virtual UInt32 maxKeyCodes(); - - -private: - virtual bool resetKeyboard(); - virtual void scheduleAutoRepeat(); - static void _autoRepeat(void * arg, void *); - virtual void autoRepeat(); - virtual void setRepeat(unsigned eventType, unsigned keyCode); - void setRepeatMode(bool repeat); - static void _createKeyboardNub(thread_call_param_t param0, thread_call_param_t param1); - -/* - * HISTORICAL NOTE: - * The following methods were part of the KeyMapDelegate protocol; - * the declarations have now been merged directly into this class. - */ - -public: - virtual void keyboardEvent(unsigned eventType, - /* flags */ unsigned flags, - /* keyCode */ unsigned keyCode, - /* charCode */ unsigned charCode, - /* charSet */ unsigned charSet, - /* originalCharCode */ unsigned origCharCode, - /* originalCharSet */ unsigned origCharSet); - - virtual void keyboardSpecialEvent(unsigned eventType, - /* flags */ unsigned flags, - /* keyCode */ unsigned keyCode, - /* specialty */ unsigned flavor); - - virtual void updateEventFlags(unsigned flags); // Does not generate events - - virtual unsigned eventFlags(); // Global event flags - virtual unsigned deviceFlags(); // per-device event flags - virtual void setDeviceFlags(unsigned flags); // Set device event flags - virtual bool alphaLock(); // current alpha-lock state - virtual void setAlphaLock(bool val); // Set current alpha-lock state - virtual bool numLock(); - virtual void setNumLock(bool val); - virtual bool charKeyActive(); // Is a character gen. key down? - virtual void setCharKeyActive(bool val); // Note that a char gen key is down. - virtual bool doesKeyLock(unsigned key); //does key lock physically - virtual unsigned getLEDStatus(); //check hardware for LED status - -private: - static void _keyboardEvent( IOHIKeyboard * self, - unsigned eventType, - /* flags */ unsigned flags, - /* keyCode */ unsigned key, - /* charCode */ unsigned charCode, - /* charSet */ unsigned charSet, - /* originalCharCode */ unsigned origCharCode, - /* originalCharSet */ unsigned origCharSet, - /* keyboardType */ unsigned keyboardType, - /* repeat */ bool repeat, - /* atTime */ AbsoluteTime ts); - static void _keyboardSpecialEvent( - IOHIKeyboard * self, - unsigned eventType, - /* flags */ unsigned flags, - /* keyCode */ unsigned key, - /* specialty */ unsigned flavor, - /* guid */ UInt64 guid, - /* repeat */ bool repeat, - /* atTime */ AbsoluteTime ts); - - static void _updateEventFlags( IOHIKeyboard * self, - unsigned flags); /* Does not generate events */ - -}; - -#endif /* !_IOHIKEYBOARD_H */ diff --git a/Library/LegacyIOHIPointing.h b/Library/LegacyIOHIPointing.h deleted file mode 100644 index 1435e528..00000000 --- a/Library/LegacyIOHIPointing.h +++ /dev/null @@ -1,259 +0,0 @@ -/* - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2009 Apple Computer, Inc. All Rights Reserved. - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#ifndef _IOHIPOINTING_H -#define _IOHIPOINTING_H - -#include "LegacyIOService.h" -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winconsistent-missing-override" -#include -#include -#pragma clang diagnostic pop - -/* Start Action Definitions */ - -/* - * HISTORICAL NOTE: - * The following entry points were part of the IOHIPointingEvents - * protocol. - */ -typedef void (*RelativePointerEventAction)(OSObject * target, - /* buttons */ int buttons, - /* deltaX */ int dx, - /* deltaY */ int dy, - /* atTime */ AbsoluteTime ts); - -typedef void (*AbsolutePointerEventAction)(OSObject * target, - /* buttons */ int buttons, - /* at */ IOGPoint * newLoc, - /* withBounds */ IOGBounds *bounds, - /* inProximity */ bool proximity, - /* withPressure */ int pressure, - /* withAngle */ int stylusAngle, - /* atTime */ AbsoluteTime ts); - -typedef void (*ScrollWheelEventAction)(OSObject * target, - short deltaAxis1, - short deltaAxis2, - short deltaAxis3, - AbsoluteTime ts); - -/* Event Callback Definitions */ - -typedef void (*RelativePointerEventCallback)( - /* target */ OSObject * target, - /* buttons */ int buttons, - /* deltaX */ int dx, - /* deltaY */ int dy, - /* atTime */ AbsoluteTime ts, - /* sender */ OSObject * sender, - /* refcon */ void * refcon); - -typedef void (*AbsolutePointerEventCallback)( - /* target */ OSObject * target, - /* buttons */ int buttons, - /* at */ IOGPoint * newLoc, - /* withBounds */ IOGBounds *bounds, - /* inProximity */ bool proximity, - /* withPressure */ int pressure, - /* withAngle */ int stylusAngle, - /* atTime */ AbsoluteTime ts, - /* sender */ OSObject * sender, - /* refcon */ void * refcon); - -typedef void (*ScrollWheelEventCallback)( - /* target */ OSObject * target, - /* delta1 */ short deltaAxis1, - /* delta2 */ short deltaAxis2, - /* delta3 */ short deltaAxis3, - /* fixedDelta1 */ IOFixed fixedDelta1, - /* fixedDelta2 */ IOFixed fixedDelta2, - /* fixedDelta3 */ IOFixed fixedDelta3, - /* pointDelta1 */ SInt32 pointDelta1, - /* pointDelta2 */ SInt32 pointDelta2, - /* pointDelta3 */ SInt32 pointDelta3, - /* reserved */ SInt32 options, - /* atTime */ AbsoluteTime ts, - /* sender */ OSObject * sender, - /* refcon */ void * refcon); - -/* End Action Definitions */ - -/* Default accel level parameters */ -#define EV_DEFAULTPOINTERACCELLEVEL 0x0000b000 -#define EV_DEFAULTSCROLLACCELLEVEL 0x00005000 - -class IOHIDPointingDevice; -struct ScrollAccelInfo; - -class IOHIPointing : public IOHIDevice -{ - OSDeclareDefaultStructors(IOHIPointing); - - friend class IOHITablet; - friend class IOHIDPointing; - -private: - IOLock * _deviceLock; // Lock for all device access - int _buttonMode; // The "handedness" of the pointer - IOFixed _acceleration; - bool _convertAbsoluteToRelative; - bool _contactToMove; - bool _hadContact; - IOGPoint _previousLocation; - UInt8 _pressureThresholdToClick; // A scale factor of 0 to 255 to determine how much pressure is necessary to generate a primary mouse click - a value of 255 means no click will be generated - void * _scaleSegments; - IOItemCount _scaleSegCount; - IOFixed _fractX; - IOFixed _fractY; - - OSObject * _relativePointerEventTarget; - RelativePointerEventAction _relativePointerEventAction; - OSObject * _absolutePointerEventTarget; - AbsolutePointerEventAction _absolutePointerEventAction; - OSObject * _scrollWheelEventTarget; - ScrollWheelEventAction _scrollWheelEventAction; - - struct ExpansionData; - - ExpansionData * _reserved; - - void setPointingMode(UInt32 accelerateMode); - UInt32 getPointingMode (); - void setScrollType(UInt32 scrollType); - UInt32 getScrollType(); - - void dispatchScrollWheelEventWithAccelInfo( - SInt32 deltaAxis1, - SInt32 deltaAxis2, - SInt32 deltaAxis3, - ScrollAccelInfo * info, - AbsoluteTime ts); - - -protected: - virtual void dispatchRelativePointerEvent(int dx, - int dy, - UInt32 buttonState, - AbsoluteTime ts); - - virtual void dispatchAbsolutePointerEvent(IOGPoint * newLoc, - IOGBounds * bounds, - UInt32 buttonState, - bool proximity, - int pressure, - int pressureMin, - int pressureMax, - int stylusAngle, - AbsoluteTime ts); - - virtual void dispatchScrollWheelEvent(short deltaAxis1, - short deltaAxis2, - short deltaAxis3, - AbsoluteTime ts); - -public: - virtual bool init(OSDictionary * properties = 0) override; - virtual bool start(IOService * provider) override; - virtual void free() override; - - virtual bool open(IOService * client, - IOOptionBits options, - RelativePointerEventAction rpeAction, - AbsolutePointerEventAction apeAction, - ScrollWheelEventAction sweAction); - - bool open( IOService * client, - IOOptionBits options, - void *, - RelativePointerEventCallback rpeCallback, - AbsolutePointerEventCallback apeCallback, - ScrollWheelEventCallback sweCallback); - - virtual void close(IOService * client, IOOptionBits ) override; - virtual IOReturn message( UInt32 type, IOService * provider, - void * argument = 0 ) override; - - virtual IOHIDKind hidKind() override; - virtual bool updateProperties( void ) override; - virtual IOReturn setParamProperties( OSDictionary * dict ) override; - virtual IOReturn powerStateWillChangeTo( IOPMPowerFlags powerFlags, - unsigned long newState, IOService * device) override; - virtual IOReturn powerStateDidChangeTo( IOPMPowerFlags powerFlags, - unsigned long newState, IOService * device) override; - -protected: // for subclasses to implement - virtual OSData * copyAccelerationTable(); - virtual IOItemCount buttonCount(); - virtual IOFixed resolution(); - - // RY: Adding method to copy scroll wheel accel table. - // Unfortunately, we don't have any padding, so this - // is going to be non-virtual. - /*virtual*/ OSData * copyScrollAccelerationTable(); - -private: - virtual bool resetPointer(); - virtual void scalePointer(int * dxp, int * dyp); - virtual void setupForAcceleration(IOFixed accl); - - // RY: Adding methods to support scroll wheel accel. - // Unfortunately, we don't have any padding, so these - // are going to be non-virtual. - /*virtual*/ bool resetScroll(); - /*virtual*/ void setupScrollForAcceleration(IOFixed accl); - - // RY: We have to make sure that subclasses that will - // take advantage of this have their defined resolution - // in their property table. - /*virtual*/ IOFixed scrollResolutionForType(SInt32 type=-1); - /*virtual*/ IOFixed scrollReportRate(); - /*virtual*/ OSData * copyScrollAccelerationTableForType(SInt32 type=-1); - -private: - static void _relativePointerEvent( IOHIPointing * self, - int buttons, - /* deltaX */ int dx, - /* deltaY */ int dy, - /* atTime */ AbsoluteTime ts); - - /* Tablet event reporting */ - static void _absolutePointerEvent(IOHIPointing * self, - int buttons, - /* at */ IOGPoint * newLoc, - /* withBounds */ IOGBounds *bounds, - /* inProximity */ bool proximity, - /* withPressure */ int pressure, - /* withAngle */ int stylusAngle, - /* atTime */ AbsoluteTime ts); - - /* Mouse scroll wheel event reporting */ - static void _scrollWheelEvent(IOHIPointing *self, - short deltaAxis1, - short deltaAxis2, - short deltaAxis3, - AbsoluteTime ts); - -}; - -#endif /* !_IOHIPOINTING_H */ diff --git a/Library/LegacyIOService.h b/Library/LegacyIOService.h deleted file mode 100644 index fc378bd4..00000000 --- a/Library/LegacyIOService.h +++ /dev/null @@ -1,1721 +0,0 @@ -/* - * Copyright (c) 1998-2011 Apple Computer, Inc. All rights reserved. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ - */ -/* - * Copyright (c) 1998,1999 Apple Computer, Inc. All rights reserved. - * - * HISTORY - * - */ -/*! - @header - This header contains the definition of the IOService class. IOService is the sole direct subclass of IORegistryEntry and is the base class of almost all I/O Kit family superclasses. IOService defines methods that support the life cycle of I/O Kit drivers. For more information on IOService, see {@linkdoc //apple_ref/doc/uid/TP0000011 I/O Kit Fundamentals}. - - @seealso //apple_ref/doc/header/IORegistryEntry.h IORegistryEntry -*/ - -#ifndef _IOKIT_IOSERVICE_H -#define _IOKIT_IOSERVICE_H - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -extern "C" { -#include -} - -#include "LegacyLibkernMacros.h" - -#ifndef UINT64_MAX -#define UINT64_MAX 18446744073709551615ULL -#endif - -enum { - kIODefaultProbeScore = 0 -}; - -// masks for getState() -enum { - kIOServiceInactiveState = 0x00000001, - kIOServiceRegisteredState = 0x00000002, - kIOServiceMatchedState = 0x00000004, - kIOServiceFirstPublishState = 0x00000008, - kIOServiceFirstMatchState = 0x00000010 -}; - -enum { - // options for registerService() - kIOServiceExclusive = 0x00000001, - - // options for terminate() - kIOServiceRequired = 0x00000001, - kIOServiceTerminate = 0x00000004, - - // options for registerService() & terminate() - kIOServiceSynchronous = 0x00000002, - // options for registerService() - kIOServiceAsynchronous = 0x00000008 -}; - -// options for open() -enum { - kIOServiceSeize = 0x00000001, - kIOServiceFamilyOpenOptions = 0xffff0000 -}; - -// options for close() -enum { - kIOServiceFamilyCloseOptions = 0xffff0000 -}; - -typedef void * IONotificationRef; - -extern const IORegistryPlane * gIOServicePlane; -extern const IORegistryPlane * gIOPowerPlane; - -extern const OSSymbol * gIOResourcesKey; -extern const OSSymbol * gIOResourceMatchKey; -extern const OSSymbol * gIOProviderClassKey; -extern const OSSymbol * gIONameMatchKey; -extern const OSSymbol * gIONameMatchedKey; -extern const OSSymbol * gIOPropertyMatchKey; -extern const OSSymbol * gIOLocationMatchKey; -extern const OSSymbol * gIOParentMatchKey; -extern const OSSymbol * gIOPathMatchKey; -extern const OSSymbol * gIOMatchCategoryKey; -extern const OSSymbol * gIODefaultMatchCategoryKey; -extern const OSSymbol * gIOMatchedServiceCountKey; - -extern const OSSymbol * gIOUserClientClassKey; -extern const OSSymbol * gIOKitDebugKey; -extern const OSSymbol * gIOServiceKey; - -extern const OSSymbol * gIOCommandPoolSizeKey; - -extern const OSSymbol * gIOPublishNotification; -extern const OSSymbol * gIOFirstPublishNotification; -extern const OSSymbol * gIOMatchedNotification; -extern const OSSymbol * gIOFirstMatchNotification; -extern const OSSymbol * gIOTerminatedNotification; - -extern const OSSymbol * gIOGeneralInterest; -extern const OSSymbol * gIOBusyInterest; -extern const OSSymbol * gIOOpenInterest; -extern const OSSymbol * gIOAppPowerStateInterest; -extern const OSSymbol * gIOPriorityPowerStateInterest; -extern const OSSymbol * gIOConsoleSecurityInterest; - -extern const OSSymbol * gIODeviceMemoryKey; -extern const OSSymbol * gIOInterruptControllersKey; -extern const OSSymbol * gIOInterruptSpecifiersKey; - -extern SInt32 IOServiceOrdering( const OSMetaClassBase * inObj1, const OSMetaClassBase * inObj2, void * ref ); - -typedef void (*IOInterruptAction)( OSObject * target, void * refCon, - IOService * nub, int source ); - -/*! @typedef IOServiceNotificationHandler - @param target Reference supplied when the notification was registered. - @param refCon Reference constant supplied when the notification was registered. - @param newService The IOService object the notification is delivering. It is retained for the duration of the handler's invocation and doesn't need to be released by the handler. */ - -typedef bool (*IOServiceNotificationHandler)( void * target, void * refCon, - IOService * newService ); - -typedef bool (*IOServiceMatchingNotificationHandler)( void * target, void * refCon, - IOService * newService, - IONotifier * notifier ); - -/*! @typedef IOServiceInterestHandler - @param target Reference supplied when the notification was registered. - @param refCon Reference constant supplied when the notification was registered. - @param messageType Type of the message - IOKit defined in IOKit/IOMessage.h or family specific. - @param provider The IOService object who is delivering the notification. It is retained for the duration of the handler's invocation and doesn't need to be released by the handler. - @param messageArgument An argument for message, dependent on its type. - @param argSize Non zero if the argument represents a struct of that size, used when delivering messages outside the kernel. */ - -typedef IOReturn (*IOServiceInterestHandler)( void * target, void * refCon, - UInt32 messageType, IOService * provider, - void * messageArgument, vm_size_t argSize ); - -typedef void (*IOServiceApplierFunction)(IOService * service, void * context); -typedef void (*OSObjectApplierFunction)(OSObject * object, void * context); - -class IOUserClient; -class IOPlatformExpert; - -/*! @class IOService - @abstract The base class for most I/O Kit families, devices, and drivers. - @discussion The IOService base class defines APIs used to publish services, instantiate other services based on the existance of a providing service (ie. driver stacking), destroy a service and its dependent stack, notify interested parties of service state changes, and general utility functions useful across all families. - -Types of service are specified with a matching dictionary that describes properties of the service. For example, a matching dictionary might describe any IOUSBDevice (or subclass), an IOUSBDevice with a certain class code, or a IOPCIDevice with a set of matching names or device & vendor IDs. Since the matching dictionary is interpreted by the family which created the service, as well as generically by IOService, the list of properties considered for matching depends on the familiy. - -Matching dictionaries are associated with IOService classes by the catalogue, as driver property tables, and also supplied by clients of the notification APIs. - -IOService provides matching based on C++ class (via OSMetaClass dynamic casting), registry entry name, a registry path to the service (which includes device tree paths), a name assigned by BSD, or by its location (its point of attachment). - -

Driver Instantiation by IOService

- -Drivers are subclasses of IOService, and their availability is managed through the catalogue. They are instantiated based on the publication of an IOService they use (for example, an IOPCIDevice or IOUSBDevice), or when they are added to the catalogue and the IOService(s) they use are already available. - -When an IOService (the "provider") is published with the @link registerService registerService@/link method, the matching and probing process begins, which is always single threaded per provider. A list of matching dictionaries from the catalog and installed publish notification requests, that successfully match the IOService, is constructed, with ordering supplied by kIOProbeScoreKey ("IOProbeScore") property in the dictionary, or supplied with the notification. - -Each entry in the list is then processed in order - for notifications, the notification is delivered, for driver property tables a lot more happens. - -The driver class is instantiated and init() called with its property table. The new driver instance is then attached to the provider, and has its @link probe probe@/link method called with the provider as an argument. The default probe method does nothing but return success, but a driver may implement this method to interrogate the provider to make sure it can work with it. It may also modify its probe score at this time. After probe, the driver is detached and the next in the list is considered (ie. attached, probed, and detached). - -When the probing phase is complete, the list consists of successfully probed drivers, in order of their probe score (after adjustment during the @link probe probe@/link call). The list is then divided into categories based on the kIOMatchCategoryKey property ("IOMatchCategory"); drivers without a match category are all considered in one default category. Match categories allow multiple clients of a provider to be attached and started, though the provider may also enforce open/close semantics to gain active access to it. - -For each category, the highest scoring driver in that category is attached to the provider, and its @link start start@/link method called. If start is successful, the rest of the drivers in the same match category are discarded, otherwise the next highest scoring driver is started, and so on. - -The driver should only consider itself in action when the start method is called, meaning it has been selected for use on the provider, and consuming that particular match category. It should also be prepared to be allocated, probed and freed even if the probe was successful. - -After the drivers have all synchronously been started, the installed "matched" notifications that match the registered IOService are delivered. - -

Properties used by IOService

- - kIOClassKey, extern const OSSymbol * gIOClassKey, "IOClass" -
-
-Class of the driver to instantiate on matching providers. -
-
- kIOProviderClassKey, extern const OSSymbol * gIOProviderClassKey, "IOProviderClass" -
-
-Class of the provider(s) to be considered for matching, checked with OSDynamicCast so subclasses will also match. -
-
- kIOProbeScoreKey, extern const OSSymbol * gIOProbeScoreKey, "IOProbeScore" -
-
-The probe score initially used to order multiple matching drivers. -
-
- kIOMatchCategoryKey, extern const OSSymbol * gIOMatchCategoryKey, "IOMatchCategory" -
-
-A string defining the driver category for matching purposes. All drivers with no IOMatchCategory property are considered to be in the same default category. Only one driver in a category can be started on each provider. -
-
- kIONameMatchKey, extern const OSSymbol * gIONameMatchKey, "IONameMatch" -
-A string or collection of strings that match the provider's name. The comparison is implemented with the @link //apple_ref/cpp/instm/IORegistryEntry/compareNames/virtualbool/(OSObject*,OSString**) IORegistryEntry::compareNames@/link method, which supports a single string, or any collection (OSArray, OSSet, OSDictionary etc.) of strings. IOService objects with device tree properties (eg. IOPCIDevice) will also be matched based on that standard's "compatible", "name", "device_type" properties. The matching name will be left in the driver's property table in the kIONameMatchedKey property. -
-Examples -
-@textblock
-	IONameMatch
-	pci106b,7
-@/textblock
-
- -For a list of possible matching names, a serialized array of strings should used, eg. -
-@textblock
-	IONameMatch
-	
-		APPL,happy16
-		pci106b,7
-	
-@/textblock
-
- -
- kIONameMatchedKey, extern const OSSymbol * gIONameMatchedKey, "IONameMatched" -
-The name successfully matched name from the kIONameMatchKey property will be left in the driver's property table as the kIONameMatchedKey property. -
-
- kIOPropertyMatchKey, extern const OSSymbol * gIOPropertyMatchKey, "IOPropertyMatch" -
-A dictionary of properties that each must exist in the matching IOService and compare successfully with the isEqualTo method. - -
-@textblock
-	IOPropertyMatch
-	
-		APPL,happy16
-		APPL,meek8
-	
-@/textblock
-
- -
- kIOUserClientClassKey, extern const OSSymbol * gIOUserClientClassKey, "IOUserClientClass" -
-The class name that the service will attempt to allocate when a user client connection is requested. First the device nub is queried, then the nub's provider is queried by default. -
-
- kIOKitDebugKey, extern const OSSymbol * gIOKitDebugKey, "IOKitDebug" -
-Set some debug flags for logging the driver loading process. Flags are defined in IOKit/IOKitDebug.h, but 65535 works well.*/ - -class IOService : public IORegistryEntry -{ - OSDeclareDefaultStructors(IOService) - -protected: -/*! @struct ExpansionData - @discussion This structure will be used to expand the capablilties of this class in the future. - */ - struct ExpansionData { }; - -/*! @var reserved - Reserved for future use. (Internal use only) */ - ExpansionData * reserved; - -private: - IOService * __provider; - SInt32 __providerGeneration; - IOService * __owner; - IOOptionBits __state[2]; - uint64_t __timeBusy; - uint64_t __accumBusy; - IOServicePM * pwrMgt; - -protected: - // TRUE once PMinit has been called - bool initialized; - -public: - // DEPRECATED - void * pm_vars; - -public: - /* methods available in Mac OS X 10.1 or later */ -/*! @function requestTerminate - @abstract Passes a termination up the stack. - @discussion When an IOService is made inactive the default behavior is to also make any of its clients that have it as their only provider also inactive, in this way recursing the termination up the driver stack. This method allows an IOService object to override this behavior. Returning true from this method when passed a just terminated provider will cause the client to also be terminated. - @param provider The terminated provider of this object. - @param options Options originally passed to terminate, plus kIOServiceRecursing. - @result true if this object should be terminated now that its provider has been. */ - - virtual bool requestTerminate( IOService * provider, IOOptionBits options ); - -/*! @function willTerminate - @abstract Passes a termination up the stack. - @discussion Notification that a provider has been terminated, sent before recursing up the stack, in root-to-leaf order. - @param provider The terminated provider of this object. - @param options Options originally passed to terminate. - @result true. */ - - virtual bool willTerminate( IOService * provider, IOOptionBits options ); - -/*! @function didTerminate - @abstract Passes a termination up the stack. - @discussion Notification that a provider has been terminated, sent after recursing up the stack, in leaf-to-root order. - @param provider The terminated provider of this object. - @param options Options originally passed to terminate. - @param defer If there is pending I/O that requires this object to persist, and the provider is not opened by this object set defer to true and call the IOService::didTerminate() implementation when the I/O completes. Otherwise, leave defer set to its default value of false. - @result true. */ - - virtual bool didTerminate( IOService * provider, IOOptionBits options, bool * defer ); - -/*! @function nextIdleTimeout - @availability Mac OS X v10.4 and later - @abstract Allows subclasses to customize idle power management behavior. - @discussion Returns the next time that the device should idle into its next lower power state. Subclasses may override for custom idle behavior. - - A power managed driver might override this method to provide a more sophisticated idle power off algorithm than the one defined by power management. - @param currentTime The current time - @param lastActivity The time of last activity on this device - @param powerState The device's current power state. - @result Returns the next time the device should idle off (in seconds, relative to the current time). */ - - virtual SInt32 nextIdleTimeout(AbsoluteTime currentTime, - AbsoluteTime lastActivity, unsigned int powerState); - -/*! @function systemWillShutdown - @availability Mac OS X v10.5 and later - @abstract Notifies members of the power plane of system shutdown and restart. - @discussion This function is called for all members of the power plane in leaf-to-root order. If a subclass needs to wait for a pending I/O, then the call to systemWillShutdown should be postponed until the I/O completes. - - Any power managed driver (which has called @link joinPMtree joinPMtree@/link to join the power plane) interested in taking action at system shutdown or restart should override this method. - @param specifier kIOMessageSystemWillPowerOff or kIOMessageSystemWillRestart. */ - - virtual void systemWillShutdown( IOOptionBits specifier ); - -/*! @function copyClientWithCategory - @availability Mac OS X v10.6 and later - @param category An OSSymbol corresponding to an IOMatchCategory matching property. - @result Returns a reference to the IOService child with the given category. The result should be released by the caller. -*/ - - virtual IOService * copyClientWithCategory( const OSSymbol * category ); - -public: -/*! @function configureReport - * @abstract configure IOReporting channels - * @availability SPI on OS X v10.9 / iOS 7 and later - * - * @param channels - channels to configure - * @param action - enable/disable/size, etc - * @param result - action-specific returned value - * @param destination - action-specific default destination - */ - virtual IOReturn configureReport(IOReportChannelList *channels, - IOReportConfigureAction action, - void *result, - void *destination); - -/*! @function updateReport - * @abstract request current data for the specified channels - * @availability SPI on OS X 10.9 / iOS 7 and later - * - * @param channels - channels to be updated - * @param action - type/style of update - * @param result - returned details about what was updated - * @param destination - destination for this update (action-specific) - */ - virtual IOReturn updateReport(IOReportChannelList *channels, - IOReportUpdateAction action, - void *result, - void *destination); - -private: -#if __LP64__ - OSMetaClassDeclareReservedUsed(IOService, 0); - OSMetaClassDeclareReservedUsed(IOService, 1); - OSMetaClassDeclareReservedUnused(IOService, 2); - OSMetaClassDeclareReservedUnused(IOService, 3); - OSMetaClassDeclareReservedUnused(IOService, 4); - OSMetaClassDeclareReservedUnused(IOService, 5); -#else - OSMetaClassDeclareReservedUsed(IOService, 0); - OSMetaClassDeclareReservedUsed(IOService, 1); - OSMetaClassDeclareReservedUsed(IOService, 2); - OSMetaClassDeclareReservedUsed(IOService, 3); - OSMetaClassDeclareReservedUsed(IOService, 4); - OSMetaClassDeclareReservedUsed(IOService, 5); -#endif - - OSMetaClassDeclareReservedUnused(IOService, 6); - OSMetaClassDeclareReservedUnused(IOService, 7); - OSMetaClassDeclareReservedUnused(IOService, 8); - OSMetaClassDeclareReservedUnused(IOService, 9); - OSMetaClassDeclareReservedUnused(IOService, 10); - OSMetaClassDeclareReservedUnused(IOService, 11); - OSMetaClassDeclareReservedUnused(IOService, 12); - OSMetaClassDeclareReservedUnused(IOService, 13); - OSMetaClassDeclareReservedUnused(IOService, 14); - OSMetaClassDeclareReservedUnused(IOService, 15); - OSMetaClassDeclareReservedUnused(IOService, 16); - OSMetaClassDeclareReservedUnused(IOService, 17); - OSMetaClassDeclareReservedUnused(IOService, 18); - OSMetaClassDeclareReservedUnused(IOService, 19); - OSMetaClassDeclareReservedUnused(IOService, 20); - OSMetaClassDeclareReservedUnused(IOService, 21); - OSMetaClassDeclareReservedUnused(IOService, 22); - OSMetaClassDeclareReservedUnused(IOService, 23); - OSMetaClassDeclareReservedUnused(IOService, 24); - OSMetaClassDeclareReservedUnused(IOService, 25); - OSMetaClassDeclareReservedUnused(IOService, 26); - OSMetaClassDeclareReservedUnused(IOService, 27); - OSMetaClassDeclareReservedUnused(IOService, 28); - OSMetaClassDeclareReservedUnused(IOService, 29); - OSMetaClassDeclareReservedUnused(IOService, 30); - OSMetaClassDeclareReservedUnused(IOService, 31); - OSMetaClassDeclareReservedUnused(IOService, 32); - OSMetaClassDeclareReservedUnused(IOService, 33); - OSMetaClassDeclareReservedUnused(IOService, 34); - OSMetaClassDeclareReservedUnused(IOService, 35); - OSMetaClassDeclareReservedUnused(IOService, 36); - OSMetaClassDeclareReservedUnused(IOService, 37); - OSMetaClassDeclareReservedUnused(IOService, 38); - OSMetaClassDeclareReservedUnused(IOService, 39); - OSMetaClassDeclareReservedUnused(IOService, 40); - OSMetaClassDeclareReservedUnused(IOService, 41); - OSMetaClassDeclareReservedUnused(IOService, 42); - OSMetaClassDeclareReservedUnused(IOService, 43); - OSMetaClassDeclareReservedUnused(IOService, 44); - OSMetaClassDeclareReservedUnused(IOService, 45); - OSMetaClassDeclareReservedUnused(IOService, 46); - OSMetaClassDeclareReservedUnused(IOService, 47); - -public: -/*! @function getState - @abstract Accessor for IOService state bits, not normally needed or used outside IOService. - @result State bits for the IOService, eg. kIOServiceInactiveState, kIOServiceRegisteredState. */ - - virtual IOOptionBits getState( void ) const; - -/*! @function isInactive - @abstract Checks if the IOService object has been terminated, and is in the process of being destroyed. - @discussion When an IOService object is successfully terminated, it is immediately made inactive, which blocks further attach()es, matching or notifications occuring on the object. It remains inactive until the last client closes, and is then finalized and destroyed. - @result true if the IOService object has been terminated. */ - - bool isInactive( void ) const; - - /* Stack creation */ - -/*! @function registerService - @abstract Starts the registration process for a newly discovered IOService object. - @discussion This function allows an IOService subclass to be published and made available to possible clients, by starting the registration process and delivering notifications to registered clients. The object should be completely setup and ready to field requests from clients before registerService is called. - @param options The default zero options mask is recommended and should be used in most cases. The registration process is usually asynchronous, with possible driver probing and notification occurring some time later. kIOServiceSynchronous may be passed to carry out the matching and notification process for currently registered clients before returning to the caller. */ - - virtual void registerService( IOOptionBits options = 0 ); - -/*! @function probe - @abstract During an IOService object's instantiation, probes a matched service to see if it can be used. - @discussion The registration process for an IOService object (the provider) includes instantiating possible driver clients. The probe method is called in the client instance to check the matched service can be used before the driver is considered to be started. Since matching screens many possible providers, in many cases the probe method can be left unimplemented by IOService subclasses. The client is already attached to the provider when probe is called. - @param provider The registered IOService object that matches a driver personality's matching dictionary. - @param score Pointer to the current driver's probe score, which is used to order multiple matching drivers in the same match category. It defaults to the value of the IOProbeScore property in the drivers property table, or kIODefaultProbeScore if none is specified. The probe method may alter the score to affect start order. - @result An IOService instance or zero when the probe is unsuccessful. In almost all cases the value of this is returned on success. If another IOService object is returned, the probed instance is detached and freed, and the returned instance is used in its stead for start. */ - - virtual LIBKERN_RETURNS_NOT_RETAINED IOService * probe( IOService * provider, - SInt32 * score ); - -/*! @function start - @abstract During an IOService object's instantiation, starts the IOService object that has been selected to run on the provider. - @discussion The start method of an IOService instance is called by its provider when it has been selected (due to its probe score and match category) as the winning client. The client is already attached to the provider when start is called.
Implementations of start must call start on their superclass at an appropriate point. If an implementation of start has already called super::start but subsequently determines that it will fail, it must call super::stop to balance the prior call to super::start and prevent reference leaks. - @result true if the start was successful; false otherwise (which will cause the instance to be detached and usually freed). */ - - virtual bool start( IOService * provider ); - -/*! @function stop - @abstract During an IOService termination, the stop method is called in its clients before they are detached & it is destroyed. - @discussion The termination process for an IOService (the provider) will call stop in each of its clients, after they have closed the provider if they had it open, or immediately on termination. */ - - virtual void stop( IOService * provider ); - - /* Open / Close */ - -/*! @function open - @abstract Requests active access to a provider. - @discussion IOService provides generic open and close semantics to track clients of a provider that have established an active datapath. The use of open and @link close close@/link, and rules regarding ownership are family defined, and defined by the @link handleOpen handleOpen@/link and @link handleClose handleClose@/link methods in the provider. Some families will limit access to a provider based on its open state. - @param forClient Designates the client of the provider requesting the open. - @param options Options for the open. The provider family may implement options for open; IOService defines only kIOServiceSeize to request the device be withdrawn from its current owner. - @result true if the open was successful; false otherwise. */ - - virtual bool open( IOService * forClient, - IOOptionBits options = 0, - void * arg = 0 ); - -/*! @function close - @abstract Releases active access to a provider. - @discussion IOService provides generic open and close semantics to track clients of a provider that have established an active datapath. The use of @link open open@/link and close, and rules regarding ownership are family defined, and defined by the @link handleOpen handleOpen@/link and @link handleClose handleClose@/link methods in the provider. - @param forClient Designates the client of the provider requesting the close. - @param options Options available for the close. The provider family may implement options for close; IOService defines none. */ - - virtual void close( IOService * forClient, - IOOptionBits options = 0 ); - -/*! @function isOpen - @abstract Determines whether a specific, or any, client has an IOService object open. - @discussion Returns the open state of an IOService object with respect to the specified client, or when it is open by any client. - @param forClient If non-zero, isOpenisOpentrueopen. The object is locked via @link lockForArbitration lockForArbitration@/link before handleOpen is called. - @param forClient Designates the client of the provider requesting the open. - @param options Options for the open, may be interpreted by the implementor of handleOpen. - @result trueif the open was successful; false otherwise. */ - - virtual bool handleOpen( IOService * forClient, - IOOptionBits options, - void * arg ); - -/*! @function handleClose - @abstract Controls the open / close behavior of an IOService object (overrideable by subclasses). - @discussion IOService calls this method in its subclasses in response to the @link close close@/link method, so the subclass may implement the request. The default implementation provides single owner access to an IOService object via @link open open@/link. The object is locked via @link lockForArbitration lockForArbitration@/link before handleClose is called. - @param forClient Designates the client of the provider requesting the close. - @param options Options for the close, may be interpreted by the implementor of @link handleOpen handleOpen@/link. */ - - virtual void handleClose( IOService * forClient, - IOOptionBits options ); - -/*! @function handleIsOpen - @abstract Controls the open / close behavior of an IOService object (overrideable by subclasses). - @discussion IOService calls this method in its subclasses in response to the @link open open@/link method, so the subclass may implement the request. The default implementation provides single owner access to an IOService object via @link open open@/link. The object is locked via @link lockForArbitration lockForArbitration@/link before handleIsOpen is called. - @param forClient If non-zero, isOpen returns the open state for that client. If zero is passed, isOpen returns the open state for all clients. - @result true if the specific, or any, client has the IOService object open. */ - - virtual bool handleIsOpen( const IOService * forClient ) const; - - /* Stacking change */ - -/*! @function terminate - @abstract Makes an IOService object inactive and begins its destruction. - @discussion Registering an IOService object informs possible clients of its existance and instantiates drivers that may be used with it; terminate involves the opposite process of informing clients that an IOService object is no longer able to be used and will be destroyed. By default, if any client has the service open, terminate fails. If the kIOServiceRequired flag is passed however, terminate will be successful though further progress in the destruction of the IOService object will not proceed until the last client has closed it. The service will be made inactive immediately upon successful termination, and all its clients will be notified via their @link message message@/link method with a message of type kIOMessageServiceIsTerminated. Both these actions take place on the caller's thread. After the IOService object is made inactive, further matching or attach calls will fail on it. Each client has its @link stop stop@/link method called upon their close of an inactive IOService object , or on its termination if they do not have it open. After stop, @link detach detach@/link is called in each client. When all clients have been detached, the @link finalize finalize@/link method is called in the inactive service. The termination process is inherently asynchronous because it will be deferred until all clients have chosen to close. - @param options In most cases no options are needed. kIOServiceSynchronous may be passed to cause terminate to not return until the service is finalized. */ - - virtual bool terminate( IOOptionBits options = 0 ); - -/*! @function finalize - @abstract Finalizes the destruction of an IOService object. - @discussion The finalize method is called in an inactive (ie. terminated) IOService object after the last client has detached. IOService's implementation will call @link stop stop@/link, @link close close@/link, and @link detach detach@/link on each provider. When finalize returns, the object's retain count will have no references generated by IOService's registration process. - @param options The options passed to the @link terminate terminate@/link method of the IOService object are passed on to finalize. - @result true. */ - - virtual bool finalize( IOOptionBits options ); - -/*! @function free - @abstract Frees data structures that were allocated when power management was initialized on this service. */ - - virtual void free( void ) override; - -/*! @function lockForArbitration - @abstract Locks an IOService object against changes in state or ownership. - @discussion The registration, termination and open / close functions of IOService use lockForArbtration to single-thread access to an IOService object. lockForArbitration grants recursive access to the same thread. - @param isSuccessRequired If a request for access to an IOService object should be denied if it is terminated, pass false, otherwise pass true. */ - - virtual bool lockForArbitration( bool isSuccessRequired = true ); - -/*! @function unlockForArbitration - @abstract Unlocks an IOService obkect after a successful @link lockForArbitration lockForArbitration@/link. - @discussion A thread granted exclusive access to an IOService object should release it with unlockForArbitration. */ - - virtual void unlockForArbitration( void ); - -/*! @function terminateClient - @abstract Passes a termination up the stack. - @discussion When an IOService object is made inactive the default behavior is to also make any of its clients that have it as their only provider inactive, in this way recursing the termination up the driver stack. This method allows a terminated IOService object to override this behavior. Note the client may also override this behavior by overriding its @link terminate terminate@/link method. - @param client The client of the terminated provider. - @param options Options originally passed to @link terminate terminate@/link, plus kIOServiceRecursing. - @result result of the terminate request on the client. */ - - virtual bool terminateClient( IOService * client, IOOptionBits options ); - - /* Busy state indicates discovery, matching or termination is in progress */ - -/*! @function getBusyState - @abstract Returns the busyState of an IOService object. - @discussion Many activities in IOService are asynchronous. When registration, matching, or termination is in progress on an IOService object, its busyState is increased by one. Change in busyState to or from zero also changes the IOService object's provider's busyState by one, which means that an IOService object is marked busy when any of the above activities is ocurring on it or any of its clients. - @result The busyState value. */ - - virtual UInt32 getBusyState( void ); - -/*! @function adjustBusy - @abstract Adjusts the busyState of an IOService object. - @discussion Applies a delta to an IOService object's busyState. A change in the busyState to or from zero will change the IOService object's provider's busyState by one (in the same direction). - @param delta The delta to be applied to the IOService object's busyState. */ - - virtual void adjustBusy( SInt32 delta ); - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOReturn waitQuiet(mach_timespec_t * timeout) - APPLE_KEXT_DEPRECATED; - -/*! @function waitQuiet - @abstract Waits for an IOService object's busyState to be zero. - @discussion Blocks the caller until an IOService object is non busy. - @param timeout The maximum time to wait in nanoseconds. Default is to wait forever. - @result Returns an error code if Mach synchronization primitives fail, kIOReturnTimeout, or kIOReturnSuccess. */ - - IOReturn waitQuiet(uint64_t timeout = UINT64_MAX); - - /* Matching */ - -/*! @function matchPropertyTable - @abstract Allows a registered IOService object to implement family specific matching. - @discussion All matching on an IOService object will call this method to allow a family writer to implement matching in addition to the generic methods provided by IOService. The implementer should examine the matching dictionary passed to see if it contains properties the family understands for matching, and use them to match with the IOService object if so. Note that since matching is also carried out by other parts of the I/O Kit, the matching dictionary may contain properties the family does not understand - these should not be considered matching failures. - @param table The dictionary of properties to be matched against. - @param score Pointer to the current driver's probe score, which is used to order multiple matching drivers in the same match category. It defaults to the value of the IOProbeScore property in the drivers property table, or kIODefaultProbeScore if none is specified. - @result false if the family considers the matching dictionary does not match in properties it understands; true otherwise. */ - - virtual bool matchPropertyTable( OSDictionary * table, - SInt32 * score ); - - virtual bool matchPropertyTable( OSDictionary * table ); - -/*! @function matchLocation - @abstract Allows a registered IOService object to direct location matching. - @discussion By default, a location matching property will be applied to an IOService object's provider. This method allows that behavior to be overridden by families. - @param client The IOService object at which matching is taking place. - @result Returns the IOService instance to be used for location matching. */ - - virtual LIBKERN_RETURNS_NOT_RETAINED IOService * matchLocation( IOService * client ); - - /* Resource service */ - -/*! @function publishResource - @abstract Uses the resource service to publish a property. - @discussion The resource service uses IOService's matching and notification to allow objects to be published and found by any I/O Kit client by a global name. publishResource makes an object available to anyone waiting for it or looking for it in the future. - @param key An OSSymbol key that globally identifies the object. - @param value The object to be published. */ - - static void publishResource( const OSSymbol * key, OSObject * value = 0 ); - -/*! @function publishResource - @abstract Uses the resource service to publish a property. - @discussion The resource service uses IOService object's matching and notification to allow objects to be published and found by any I/O Kit client by a global name. publishResource makes an object available to anyone waiting for it or looking for it in the future. - @param key A C string key that globally identifies the object. - @param value The object to be published. */ - - static void publishResource( const char * key, OSObject * value = 0 ); - virtual bool addNeededResource( const char * key ); - - /* Notifications */ - -/*! @function addNotification - @abstract Deprecated use addMatchingNotification(). Adds a persistant notification handler to be notified of IOService events. - @discussion IOService will deliver notifications of changes in state of an IOService object to registered clients. The type of notification is specified by a symbol, for example gIOMatchedNotification or gIOTerminatedNotification, and notifications will only include IOService objects that match the supplied matching dictionary. Notifications are ordered by a priority set with addNotification. When the notification is installed, its handler will be called with each of any currently existing IOService objects that are in the correct state (eg. registered) and match the supplied matching dictionary, avoiding races between finding preexisting and new IOService events. The notification request is identified by an instance of an IONotifier object, through which it can be enabled, disabled, or removed. addNotification consumes a retain count on the matching dictionary when the notification is removed. - @param type An OSSymbol identifying the type of notification and IOService state: -
gIOPublishNotification Delivered when an IOService object is registered. -
gIOFirstPublishNotification Delivered when an IOService object is registered, but only once per IOService instance. Some IOService objects may be reregistered when their state is changed. -
gIOMatchedNotification Delivered when an IOService object has been matched with all client drivers, and they have been probed and started. -
gIOFirstMatchNotification Delivered when an IOService object has been matched with all client drivers, but only once per IOService instance. Some IOService objects may be reregistered when their state is changed. -
gIOTerminatedNotification Delivered after an IOService object has been terminated, during its finalize stage. - @param matching A matching dictionary to restrict notifications to only matching IOService objects. The dictionary will be released when the notification is removed, consuming the passed-in reference. - @param handler A C function callback to deliver notifications. - @param target An instance reference for the callback's use. - @param ref A reference constant for the callback's use. - @param priority A constant ordering all notifications of a each type. - @result An instance of an IONotifier object that can be used to control or destroy the notification request. */ - - static IONotifier * addNotification( - const OSSymbol * type, OSDictionary * matching, - IOServiceNotificationHandler handler, - void * target, void * ref = 0, - SInt32 priority = 0 ) - APPLE_KEXT_DEPRECATED; - -/*! @function addMatchingNotification - @abstract Adds a persistant notification handler to be notified of IOService events. - @discussion IOService will deliver notifications of changes in state of an IOService object to registered clients. The type of notification is specified by a symbol, for example gIOMatchedNotification or gIOTerminatedNotification, and notifications will only include IOService objects that match the supplied matching dictionary. Notifications are ordered by a priority set with addNotification. When the notification is installed, its handler will be called with each of any currently existing IOService objects that are in the correct state (eg. registered) and match the supplied matching dictionary, avoiding races between finding preexisting and new IOService events. The notification request is identified by an instance of an IONotifier object, through which it can be enabled, disabled, or removed. addMatchingNotification does not consume a reference on the matching dictionary when the notification is removed, unlike addNotification. - @param type An OSSymbol identifying the type of notification and IOService state: -
gIOPublishNotification Delivered when an IOService object is registered. -
gIOFirstPublishNotification Delivered when an IOService object is registered, but only once per IOService instance. Some IOService objects may be reregistered when their state is changed. -
gIOMatchedNotification Delivered when an IOService object has been matched with all client drivers, and they have been probed and started. -
gIOFirstMatchNotification Delivered when an IOService object has been matched with all client drivers, but only once per IOService instance. Some IOService objects may be reregistered when their state is changed. -
gIOTerminatedNotification Delivered after an IOService object has been terminated, during its finalize stage. - @param matching A matching dictionary to restrict notifications to only matching IOService objects. The dictionary is retained while the notification is installed. (Differs from addNotification). - @param handler A C function callback to deliver notifications. - @param target An instance reference for the callback's use. - @param ref A reference constant for the callback's use. - @param priority A constant ordering all notifications of a each type. - @result An instance of an IONotifier object that can be used to control or destroy the notification request. */ - - static IONotifier * addMatchingNotification( - const OSSymbol * type, OSDictionary * matching, - IOServiceMatchingNotificationHandler handler, - void * target, void * ref = 0, - SInt32 priority = 0 ); - -/*! @function waitForService - @abstract Deprecated use waitForMatchingService(). Waits for a matching to service to be published. - @discussion Provides a method of waiting for an IOService object matching the supplied matching dictionary to be registered and fully matched. - @param matching The matching dictionary describing the desired IOService object. waitForService consumes one reference of the matching dictionary. - @param timeout The maximum time to wait. - @result A published IOService object matching the supplied dictionary. */ - - static LIBKERN_RETURNS_NOT_RETAINED IOService * waitForService( LIBKERN_CONSUMED OSDictionary * matching, - mach_timespec_t * timeout = 0); - -/*! @function waitForMatchingService - @abstract Waits for a matching to service to be published. - @discussion Provides a method of waiting for an IOService object matching the supplied matching dictionary to be registered and fully matched. - @param matching The matching dictionary describing the desired IOService object. (Does not consume a reference of the matching dictionary - differs from waitForService() which does consume a reference on the matching dictionary.) - @param timeout The maximum time to wait in nanoseconds. Default is to wait forever. - @result A published IOService object matching the supplied dictionary. waitForMatchingService returns a reference to the IOService which should be released by the caller. (Differs from waitForService() which does not retain the returned object.) */ - - static IOService * waitForMatchingService( OSDictionary * matching, - uint64_t timeout = UINT64_MAX); - -/*! @function getMatchingServices - @abstract Finds the set of current published IOService objects matching a matching dictionary. - @discussion Provides a method of finding the current set of published IOService objects matching the supplied matching dictionary. - @param matching The matching dictionary describing the desired IOService objects. - @result An instance of an iterator over a set of IOService objects. To be released by the caller. */ - - static OSIterator * getMatchingServices( OSDictionary * matching ); - -/*! @function copyMatchingService - @abstract Finds one of the current published IOService objects matching a matching dictionary. - @discussion Provides a method to find one member of the set of published IOService objects matching the supplied matching dictionary. - @param matching The matching dictionary describing the desired IOService object. - @result The IOService object or NULL. To be released by the caller. */ - - static IOService * copyMatchingService( OSDictionary * matching ); - -public: - /* Helpers to make matching dictionaries for simple cases, - * they add keys to an existing dictionary, or create one. */ - -/*! @function serviceMatching - @abstract Creates a matching dictionary, or adds matching properties to an existing dictionary, that specify an IOService class match. - @discussion A very common matching criteria for IOService object is based on its class. serviceMatching creates a matching dictionary that specifies any IOService object of a class, or its subclasses. The class is specified by name, and an existing dictionary may be passed in, in which case the matching properties will be added to that dictionary rather than creating a new one. - @param className The class name, as a const C string. Class matching is successful on IOService objects of this class or any subclass. - @param table If zero, serviceMatching creates a matching dictionary and returns a reference to it, otherwise the matching properties are added to the specified dictionary. - @result The matching dictionary created, or passed in, is returned on success, or zero on failure. */ - - static OSDictionary * serviceMatching( const char * className, - OSDictionary * table = 0 ); - -/*! @function serviceMatching - @abstract Creates a matching dictionary, or adds matching properties to an existing dictionary, that specify an IOService class match. - @discussion A very common matching criteria for IOService object is based on its class. serviceMatching creates a matching dictionary that specifies any IOService of a class, or its subclasses. The class is specified by name, and an existing dictionary may be passed in, in which case the matching properties will be added to that dictionary rather than creating a new one. - @param className The class name, as an OSString (which includes OSSymbol). Class matching is successful on IOService objects of this class or any subclass. - @param table If zero, serviceMatching creates a matching dictionary and returns a reference to it, otherwise the matching properties are added to the specified dictionary. - @result The matching dictionary created, or passed in, is returned on success, or zero on failure. */ - - static OSDictionary * serviceMatching( const OSString * className, - OSDictionary * table = 0 ); - -/*! @function nameMatching - @abstract Creates a matching dictionary, or adds matching properties to an existing dictionary, that specify an IOService name match. - @discussion A very common matching criteria for IOService object is based on its name. nameMatching creates a matching dictionary that specifies any IOService object which responds successfully to the @link //apple_ref/cpp/instm/IORegistryEntry/compareName/virtualbool/(OSString*,OSString**) IORegistryEntry::compareName@/link method. An existing dictionary may be passed in, in which case the matching properties will be added to that dictionary rather than creating a new one. - @param name The service's name, as a const C string. Name matching is successful on IOService objects that respond successfully to the IORegistryEntry::compareName method. - @param table If zero, nameMatching creates a matching dictionary and returns a reference to it, otherwise the matching properties are added to the specified dictionary. - @result The matching dictionary created, or passed in, is returned on success, or zero on failure. */ - - static OSDictionary * nameMatching( const char * name, - OSDictionary * table = 0 ); - -/*! @function nameMatching - @abstract Creates a matching dictionary, or adds matching properties to an existing dictionary, that specify an IOService name match. - @discussion A very common matching criteria for IOService object is based on its name. nameMatching creates a matching dictionary that specifies any IOService object which responds successfully to the @link //apple_ref/cpp/instm/IORegistryEntry/compareName/virtualbool/(OSString*,OSString**) IORegistryEntry::compareName@/link method. An existing dictionary may be passed in, in which case the matching properties will be added to that dictionary rather than creating a new one. - @param name The service's name, as an OSString (which includes OSSymbol). Name matching is successful on IOService objects that respond successfully to the IORegistryEntry::compareName method. - @param table If zero, nameMatching creates a matching dictionary and returns a reference to it, otherwise the matching properties are added to the specified dictionary. - @result The matching dictionary created, or passed in, is returned on success, or zero on failure. */ - - static OSDictionary * nameMatching( const OSString* name, - OSDictionary * table = 0 ); - -/*! @function resourceMatching - @abstract Creates a matching dictionary, or adds matching properties to an existing dictionary, that specify a resource service match. - @discussion IOService maintains a resource service IOResources that allows objects to be published and found globally in the I/O Kit based on a name, using the standard IOService matching and notification calls. - @param name The resource name, as a const C string. Resource matching is successful when an object by that name has been published with the publishResource method. - @param table If zero, resourceMatching creates a matching dictionary and returns a reference to it, otherwise the matching properties are added to the specified dictionary. - @result The matching dictionary created, or passed in, is returned on success, or zero on failure. */ - - static OSDictionary * resourceMatching( const char * name, - OSDictionary * table = 0 ); - -/*! @function resourceMatching - @abstract Creates a matching dictionary, or adds matching properties to an existing dictionary, that specify a resource service match. - @discussion IOService maintains a resource service IOResources that allows objects to be published and found globally in the I/O Kit based on a name, using the standard IOService matching and notification calls. - @param name The resource name, as an OSString (which includes OSSymbol). Resource matching is successful when an object by that name has been published with the publishResource method. - @param table If zero, resourceMatching creates a matching dictionary and returns a reference to it, otherwise the matching properties are added to the specified dictionary. - @result The matching dictionary created, or passed in, is returned on success, or zero on failure. */ - - static OSDictionary * resourceMatching( const OSString * name, - OSDictionary * table = 0 ); - - -/*! @function propertyMatching - @abstract Creates a matching dictionary, or adds matching properties to an existing dictionary, that specify an IOService phandle match. - @discussion TODO A very common matching criteria for IOService is based on its name. nameMatching will create a matching dictionary that specifies any IOService which respond successfully to the IORegistryEntry method compareName. An existing dictionary may be passed in, in which case the matching properties will be added to that dictionary rather than creating a new one. - @param key The service's phandle, as a const UInt32. PHandle matching is successful on IOService objects that respond successfully to the IORegistryEntry method compareName. - @param value The service's phandle, as a const UInt32. PHandle matching is successful on IOService's which respond successfully to the IORegistryEntry method compareName. - @param table If zero, nameMatching will create a matching dictionary and return a reference to it, otherwise the matching properties are added to the specified dictionary. - @result The matching dictionary created, or passed in, is returned on success, or zero on failure. */ - - static OSDictionary * propertyMatching( const OSSymbol * key, const OSObject * value, - OSDictionary * table = 0 ); - -/*! @function registryEntryIDMatching - @abstract Creates a matching dictionary, or adds matching properties to an existing dictionary, that specify a IORegistryEntryID match. - @discussion registryEntryIDMatching creates a matching dictionary that specifies the IOService object with the assigned registry entry ID (returned by IORegistryEntry::getRegistryEntryID()). An existing dictionary may be passed in, in which case the matching properties will be added to that dictionary rather than creating a new one. - @param entryID The service's ID. Matching is successful on the IOService object that return that ID from the IORegistryEntry::getRegistryEntryID() method. - @param table If zero, registryEntryIDMatching creates a matching dictionary and returns a reference to it, otherwise the matching properties are added to the specified dictionary. - @result The matching dictionary created, or passed in, is returned on success, or zero on failure. */ - - static OSDictionary * registryEntryIDMatching( uint64_t entryID, - OSDictionary * table = 0 ); - - -/*! @function addLocation - @abstract Adds a location matching property to an existing dictionary. - @discussion This function creates matching properties that specify the location of a IOService object, as an embedded matching dictionary. This matching will be successful on an IOService object that attached to an IOService object which matches this location matching dictionary. - @param table The matching properties are added to the specified dictionary, which must be non-zero. - @result The location matching dictionary created is returned on success, or zero on failure. */ - - static OSDictionary * addLocation( OSDictionary * table ); - - /* Helpers for matching dictionaries. */ - -/*! @function compareProperty - @abstract Compares a property in a matching dictionary with an IOService object's property table. - @discussion This is a helper function to aid in implementing @link matchPropertyTable matchPropertyTable@/link. If the property specified by key exists in the matching dictionary, it is compared with a property of the same name in the IOService object's property table. The comparison is performed with the isEqualTo method. If the property does not exist in the matching table, success is returned. If the property exists in the matching dictionary but not the IOService property table, failure is returned. - @param matching The matching dictionary, which must be non-zero. - @param key The dictionary key specifying the property to be compared, as a C string. - @result true if the property does not exist in the matching table. If the property exists in the matching dictionary but not the IOService property table, failure is returned. Otherwise the result of calling the property from the matching dictionary's isEqualTo method with the IOService property as an argument is returned. */ - - virtual bool compareProperty( OSDictionary * matching, - const char * key ); -/*! @function compareProperty - @abstract Compares a property in a matching dictionary with an IOService object's property table. - @discussion This is a helper function to aid in implementing @link matchPropertyTable matchPropertyTable@/link. If the property specified by key exists in the matching dictionary, it is compared with a property of the same name in the IOService object's property table. The comparison is performed with the isEqualTo method. If the property does not exist in the matching table, success is returned. If the property exists in the matching dictionary but not the IOService property table, failure is returned. - @param matching The matching dictionary, which must be non-zero. - @param key The dictionary key specifying the property to be compared, as an OSString (which includes OSSymbol). - @result true if the property does not exist in the matching table. If the property exists in the matching dictionary but not the IOService property table, failure is returned. Otherwise the result of calling the property from the matching dictionary's isEqualTo method with the IOService property as an argument is returned. */ - - virtual bool compareProperty( OSDictionary * matching, - const OSString * key ); - -/*! @function compareProperties - @abstract Compares a set of properties in a matching dictionary with an IOService object's property table. - @discussion This is a helper function to aid in implementing @link matchPropertyTable matchPropertyTable@/link. A collection of dictionary keys specifies properties in a matching dictionary to be compared, with compareProperty, with an IOService object's property table, if compareProperty returns true for each key, success is returned; otherwise failure. - @param matching The matching dictionary, which must be non-zero. - @param keys A collection (eg. OSSet, OSArray, OSDictionary) which should contain OSStrings (or OSSymbols) that specify the property keys to be compared. - @result Success if compareProperty returns true for each key in the collection; otherwise failure. */ - - virtual bool compareProperties( OSDictionary * matching, - OSCollection * keys ); - - /* Client / provider accessors */ - -/*! @function attach - @abstract Attaches an IOService client to a provider in the I/O Registry. - @discussion This function called in an IOService client enters the client into the I/O Registry as a child of the provider in the service plane. The provider must be active or the attach will fail. Multiple attach calls to the same provider are no-ops and return success. A client may be attached to multiple providers. Entering an object into the I/O Registry retains both the client and provider until they are detached. - @param provider The IOService object which will serve as this object's provider. - @result false if the provider is inactive or on a resource failure; otherwise true. */ - - virtual bool attach( IOService * provider ); - -/*! @function detach - @abstract Detaches an IOService client from a provider in the I/O Registry. - @discussion This function called in an IOService client removes the client as a child of the provider in the service plane of the I/O Registry. If the provider is not a parent of the client this is a no-op, otherwise the I/O Registry releases both the client and provider. - @param provider The IOService object to detach from. */ - - virtual void detach( IOService * provider ); - -/*! @function getProvider - @abstract Returns an IOService object's primary provider. - @discussion This function called in an IOService client will return the provider to which it was first attached. Because the majority of IOService objects have only one provider, this is a useful simplification and also supports caching of the provider when the I/O Registry is unchanged. - @result The first provider of the client, or zero if the IOService object is not attached into the I/O Registry. The provider is retained while the client is attached, and should not be released by the caller. */ - - virtual IOService * getProvider( void ) const; - -/*! @function getWorkLoop - @abstract Returns the current work loop or provider->getWorkLoop. - @discussion This function returns a valid work loop that a client can use to add an IOCommandGate to. The intention is that an IOService client has data that needs to be protected but doesn't want to pay the cost of a dedicated thread. This data has to be accessed from a provider's call-out context as well. So to achieve both of these goals the client creates an IOCommandGate to lock access to his data but he registers it with the provider's work loop, i.e. the work loop which will make the completion call-outs. This avoids a potential deadlock because the work loop gate uses a recursive lock, which allows the same lock to be held multiple times by a single thread. - @result A work loop, either the current work loop or it walks up the @link getProvider getProvider@/link chain calling getWorkLoop. Eventually it will reach a valid work loop-based driver or the root of the I/O tree, where it will return a system-wide work loop. Returns 0 if it fails to find (or create) a work loop.*/ - - virtual IOWorkLoop * getWorkLoop() const; - -/*! @function getProviderIterator - @abstract Returns an iterator over an IOService object's providers. - @discussion For those few IOService objects that obtain service from multiple providers, this method supplies an iterator over a client's providers. - @result An iterator over the providers of the client, or zero if there is a resource failure. The iterator must be released when the iteration is finished. All objects returned by the iteration are retained while the iterator is valid, though they may no longer be attached during the iteration. */ - - virtual OSIterator * getProviderIterator( void ) const; - -/*! @function getOpenProviderIterator - @abstract Returns an iterator over an client's providers that are currently opened by the client. - @discussion For those few IOService objects that obtain service from multiple providers, this method supplies an iterator over a client's providers, locking each in turn with @link lockForArbitration lockForArbitration@/link and returning those that have been opened by the client. - @result An iterator over the providers the client has open, or zero if there is a resource failure. The iterator must be released when the iteration is finished. All objects returned by the iteration are retained while the iterator is valid, and the current entry in the iteration is locked with lockForArbitration, protecting it from state changes. */ - - virtual OSIterator * getOpenProviderIterator( void ) const; - -/*! @function getClient - @abstract Returns an IOService object's primary client. - @discussion This function called in an IOService provider will return the first client to attach to it. For IOService objects which have only only one client, this may be a useful simplification. - @result The first client of the provider, or zero if the IOService object is not attached into the I/O Registry. The client is retained while it is attached, and should not be released by the caller. */ - - virtual IOService * getClient( void ) const; - -/*! @function getClientIterator - @abstract Returns an iterator over an IOService object's clients. - @discussion For IOService objects that may have multiple clients, this method supplies an iterator over a provider's clients. - @result An iterator over the clients of the provider, or zero if there is a resource failure. The iterator must be released when the iteration is finished. All objects returned by the iteration are retained while the iterator is valid, though they may no longer be attached during the iteration. */ - - virtual OSIterator * getClientIterator( void ) const; - -/*! @function getOpenClientIterator - @abstract Returns an iterator over a provider's clients that currently have opened the provider. - @discussion For IOService objects that may have multiple clients, this method supplies an iterator over a provider's clients, locking each in turn with @link lockForArbitration lockForArbitration@/link and returning those that have opened the provider. - @result An iterator over the clients that have opened the provider, or zero if there is a resource failure. The iterator must be released when the iteration is finished. All objects returned by the iteration are retained while the iterator is valid, and the current entry in the iteration is locked with lockForArbitration, protecting it from state changes. */ - - virtual OSIterator * getOpenClientIterator( void ) const; - -/*! @function callPlatformFunction - @abstract Calls the platform function with the given name. - @discussion The platform expert or other drivers may implement various functions to control hardware features. callPlatformFunction allows any IOService object to access these functions. Normally callPlatformFunction is called on a service's provider. The provider services the request or passes it to its provider. The system's IOPlatformExpert subclass catches functions it knows about and redirects them into other parts of the service plane. If the IOPlatformExpert subclass cannot execute the function, the base class is called. The IOPlatformExpert base class attempts to find a service to execute the function by looking up the function name in an IOResources name space. A service may publish a service using publishResource(functionName, this). If no service can be found to execute the function an error is returned. - @param functionName Name of the function to be called. When functionName is a C string, callPlatformFunction converts the C string to an OSSymbol and calls the OSSymbol version of callPlatformFunction. This process can block and should not be used from an interrupt context. - @param waitForFunction If true, callPlatformFunction will not return until the function has been called. - @result An IOReturn code; kIOReturnSuccess if the function was successfully executed, kIOReturnUnsupported if a service to execute the function could not be found. Other return codes may be returned by the function.*/ - - virtual IOReturn callPlatformFunction( const OSSymbol * functionName, - bool waitForFunction, - void *param1, void *param2, - void *param3, void *param4 ); - - virtual IOReturn callPlatformFunction( const char * functionName, - bool waitForFunction, - void *param1, void *param2, - void *param3, void *param4 ); - - - /* Some accessors */ - -/*! @function getPlatform - @abstract Returns a pointer to the platform expert instance for the computer. - @discussion This method provides an accessor to the platform expert instance for the computer. - @result A pointer to the IOPlatformExport instance. It should not be released by the caller. */ - - static IOPlatformExpert * getPlatform( void ); - -/*! @function getPMRootDomain - @abstract Returns a pointer to the power management root domain instance for the computer. - @discussion This method provides an accessor to the power management root domain instance for the computer. - @result A pointer to the power management root domain instance. It should not be released by the caller. */ - - static class IOPMrootDomain * getPMRootDomain( void ); - -/*! @function getServiceRoot - @abstract Returns a pointer to the root of the service plane. - @discussion This method provides an accessor to the root of the service plane for the computer. - @result A pointer to the IOService instance at the root of the service plane. It should not be released by the caller. */ - - static IOService * getServiceRoot( void ); - -/*! @function getResourceService - @abstract Returns a pointer to the IOResources service. - @discussion IOService maintains a resource service IOResources that allows objects to be published and found globally in the I/O Kit based on a name, using the standard IOService matching and notification calls. - @result A pointer to the IOResources instance. It should not be released by the caller. */ - - static IOService * getResourceService( void ); - - /* Allocate resources for a matched service */ - -/*! @function getResources - @abstract Allocates any needed resources for a published IOService object before clients attach. - @discussion This method is called during the registration process for an IOService object if there are successful driver matches, before any clients attach. It allows for lazy allocation of resources to an IOService object when a matching driver is found. - @result An IOReturn code; kIOReturnSuccess is necessary for the IOService object to be successfully used, otherwise the registration process for the object is halted. */ - - virtual IOReturn getResources( void ); - - /* Device memory accessors */ - -/*! @function getDeviceMemoryCount - @abstract Returns a count of the physical memory ranges available for a device. - @discussion This method returns the count of physical memory ranges, each represented by an IODeviceMemory instance, that have been allocated for a memory mapped device. - @result An integer count of the number of ranges available. */ - - virtual IOItemCount getDeviceMemoryCount( void ); - -/*! @function getDeviceMemoryWithIndex - @abstract Returns an instance of IODeviceMemory representing one of a device's memory mapped ranges. - @discussion This method returns a pointer to an instance of IODeviceMemory for the physical memory range at the given index for a memory mapped device. - @param index An index into the array of ranges assigned to the device. - @result A pointer to an instance of IODeviceMemory, or zero if the index is beyond the count available. The IODeviceMemory is retained by the provider, so is valid while attached, or while any mappings to it exist. It should not be released by the caller. See also @link mapDeviceMemoryWithIndex mapDeviceMemoryWithIndex@/link, which creates a device memory mapping. */ - - virtual IODeviceMemory * getDeviceMemoryWithIndex( unsigned int index ); - -/*! @function mapDeviceMemoryWithIndex - @abstract Maps a physical range of a device. - @discussion This method creates a mapping for the IODeviceMemory at the given index, with IODeviceMemory::map(options). The mapping is represented by the returned instance of IOMemoryMap, which should not be released until the mapping is no longer required. - @param index An index into the array of ranges assigned to the device. - @result An instance of IOMemoryMap, or zero if the index is beyond the count available. The mapping should be released only when access to it is no longer required. */ - - virtual IOMemoryMap * mapDeviceMemoryWithIndex( unsigned int index, - IOOptionBits options = 0 ); - -/*! @function getDeviceMemory - @abstract Returns the array of IODeviceMemory objects representing a device's memory mapped ranges. - @discussion This method returns an array of IODeviceMemory objects representing the physical memory ranges allocated to a memory mapped device. - @result An OSArray of IODeviceMemory objects, or zero if none are available. The array is retained by the provider, so is valid while attached. */ - - virtual OSArray * getDeviceMemory( void ); - -/*! @function setDeviceMemory - @abstract Sets the array of IODeviceMemory objects representing a device's memory mapped ranges. - @discussion This method sets an array of IODeviceMemory objects representing the physical memory ranges allocated to a memory mapped device. - @param array An OSArray of IODeviceMemory objects, or zero if none are available. The array will be retained by the object. */ - - virtual void setDeviceMemory( OSArray * array ); - - /* Interrupt accessors */ - -/*! @function registerInterrupt - @abstract Registers a C function interrupt handler for a device supplying interrupts. - @discussion This method installs a C function interrupt handler to be called at primary interrupt time for a device's interrupt. Only one handler may be installed per interrupt source. IOInterruptEventSource provides a work loop based abstraction for interrupt delivery that may be more appropriate for work loop based drivers. - @param source The index of the interrupt source in the device. - @param target An object instance to be passed to the interrupt handler. - @param handler The C function to be called at primary interrupt time when the interrupt occurs. The handler should process the interrupt by clearing the interrupt, or by disabling the source. - @param refCon A reference constant for the handler's use. - @result An IOReturn code.
kIOReturnNoInterrupt is returned if the source is not valid; kIOReturnNoResources is returned if the interrupt already has an installed handler. */ - - virtual IOReturn registerInterrupt(int source, OSObject *target, - IOInterruptAction handler, - void *refCon = 0); - -/*! @function unregisterInterrupt - @abstract Removes a C function interrupt handler for a device supplying hardware interrupts. - @discussion This method removes a C function interrupt handler previously installed with @link registerInterrupt registerInterrupt@/link. - @param source The index of the interrupt source in the device. - @result An IOReturn code (kIOReturnNoInterrupt is returned if the source is not valid). */ - - virtual IOReturn unregisterInterrupt(int source); - -/*! @function getInterruptType - @abstract Returns the type of interrupt used for a device supplying hardware interrupts. - @param source The index of the interrupt source in the device. - @param interruptType The interrupt type for the interrupt source will be stored here by getInterruptType.
kIOInterruptTypeEdge will be returned for edge-trigggered sources.
kIOInterruptTypeLevel will be returned for level-trigggered sources. - @result An IOReturn code (kIOReturnNoInterrupt is returned if the source is not valid). */ - - virtual IOReturn getInterruptType(int source, int *interruptType); - -/*! @function enableInterrupt - @abstract Enables a device interrupt. - @discussion It is the caller's responsiblity to keep track of the enable state of the interrupt source. - @param source The index of the interrupt source in the device. - @result An IOReturn code (kIOReturnNoInterrupt is returned if the source is not valid). */ - - virtual IOReturn enableInterrupt(int source); - -/*! @function disableInterrupt - @abstract Synchronously disables a device interrupt. - @discussion If the interrupt routine is running, the call will block until the routine completes. It is the caller's responsiblity to keep track of the enable state of the interrupt source. - @param source The index of the interrupt source in the device. - @result An IOReturn code (kIOReturnNoInterrupt is returned if the source is not valid). */ - - virtual IOReturn disableInterrupt(int source); - -/*! @function causeInterrupt - @abstract Causes a device interrupt to occur. - @discussion Emulates a hardware interrupt, to be called from task level. - @param source The index of the interrupt source in the device. - @result An IOReturn code (kIOReturnNoInterrupt is returned if the source is not valid). */ - - virtual IOReturn causeInterrupt(int source); - -/*! @function requestProbe - @abstract Requests that hardware be re-scanned for devices. - @discussion For bus families that do not usually detect device addition or removal, this method represents an external request (eg. from a utility application) to rescan and publish or remove found devices. - @param options Family defined options, not interpreted by IOService. - @result An IOReturn code. */ - - virtual IOReturn requestProbe( IOOptionBits options ); - - /* Generic API for non-data-path upstream calls */ - -/*! @function message - @abstract Receives a generic message delivered from an attached provider. - @discussion A provider may deliver messages via the message method to its clients informing them of state changes, such as kIOMessageServiceIsTerminated or kIOMessageServiceIsSuspended. Certain messages are defined by the I/O Kit in IOMessage.h while others may be family dependent. This method is implemented in the client to receive messages. - @param type A type defined in IOMessage.h or defined by the provider family. - @param provider The provider from which the message originates. - @param argument An argument defined by the provider family, not used by IOService. - @result An IOReturn code defined by the message type. */ - - virtual IOReturn message( UInt32 type, IOService * provider, - void * argument = 0 ); - -/*! @function messageClient - @abstract Sends a generic message to an attached client. - @discussion A provider may deliver messages via the @link message message@/link method to its clients informing them of state changes, such as kIOMessageServiceIsTerminated or kIOMessageServiceIsSuspended. Certain messages are defined by the I/O Kit in IOMessage.h while others may be family dependent. This method may be called in the provider to send a message to the specified client, which may be useful for overrides. - @param messageType A type defined in IOMessage.h or defined by the provider family. - @param client A client of the IOService to send the message. - @param messageArgument An argument defined by the provider family, not used by IOService. - @param argSize Specifies the size of messageArgument, in bytes. If argSize is non-zero, messageArgument is treated as a pointer to argSize bytes of data. If argSize is 0 (the default), messageArgument is treated as an ordinal and passed by value. - @result The return code from the client message call. */ - - virtual IOReturn messageClient( UInt32 messageType, OSObject * client, - void * messageArgument = 0, vm_size_t argSize = 0 ); - -/*! @function messageClients - @abstract Sends a generic message to all attached clients. - @discussion A provider may deliver messages via the @link message message@/link method to its clients informing them of state changes, such as kIOMessageServiceIsTerminated or kIOMessageServiceIsSuspended. Certain messages are defined by the I/O Kit in IOMessage.h while others may be family dependent. This method may be called in the provider to send a message to all the attached clients, via the @link messageClient messageClient@/link method. - @param type A type defined in IOMessage.h or defined by the provider family. - @param argument An argument defined by the provider family, not used by IOService. - @param argSize Specifies the size of argument, in bytes. If argSize is non-zero, argument is treated as a pointer to argSize bytes of data. If argSize is 0 (the default), argument is treated as an ordinal and passed by value. - @result Any non-kIOReturnSuccess return codes returned by the clients, or kIOReturnSuccess if all return kIOReturnSuccess. */ - - virtual IOReturn messageClients( UInt32 type, - void * argument = 0, vm_size_t argSize = 0 ); - - virtual IONotifier * registerInterest( const OSSymbol * typeOfInterest, - IOServiceInterestHandler handler, - void * target, void * ref = 0 ); - - virtual void applyToProviders( IOServiceApplierFunction applier, - void * context ); - - virtual void applyToClients( IOServiceApplierFunction applier, - void * context ); - - virtual void applyToInterested( const OSSymbol * typeOfInterest, - OSObjectApplierFunction applier, - void * context ); - - virtual IOReturn acknowledgeNotification( IONotificationRef notification, - IOOptionBits response ); - - /* User client create */ - -/*! @function newUserClient - @abstract Creates a connection for a non kernel client. - @discussion A non kernel client may request a connection be opened via the @link //apple_ref/c/func/IOServiceOpen IOServiceOpen@/link library function, which will call this method in an IOService object. The rules and capabilities of user level clients are family dependent, and use the functions of the IOUserClient class for support. IOService's implementation returns kIOReturnUnsupported, so any family supporting user clients must implement this method. - @param owningTask The Mach task of the client thread in the process of opening the user client. Note that in Mac OS X, each process is based on a Mach task and one or more Mach threads. For more information on the composition of a Mach task and its relationship with Mach threads, see {@linkdoc //apple_ref/doc/uid/TP30000905-CH209-TPXREF103 "Tasks and Threads"}. - @param securityID A token representing the access level for the task. - @param type A constant specifying the type of connection to be created, specified by the caller of @link //apple_ref/c/func/IOServiceOpen IOServiceOpen@/link and interpreted only by the family. - @param handler An instance of an IOUserClient object to represent the connection, which will be released when the connection is closed, or zero if the connection was not opened. - @param properties A dictionary of additional properties for the connection. - @result A return code to be passed back to the caller of IOServiceOpen. */ - - virtual IOReturn newUserClient( task_t owningTask, void * securityID, - UInt32 type, OSDictionary * properties, - LIBKERN_RETURNS_RETAINED IOUserClient ** handler ); - - virtual IOReturn newUserClient( task_t owningTask, void * securityID, - UInt32 type, LIBKERN_RETURNS_RETAINED IOUserClient ** handler ); - - /* Return code utilities */ - -/*! @function stringFromReturn - @abstract Supplies a programmer-friendly string from an IOReturn code. - @discussion Strings are available for the standard return codes in IOReturn.h in IOService, while subclasses may implement this method to interpret family dependent return codes. - @param rtn The IOReturn code. - @result A pointer to a constant string, or zero if the return code is unknown. */ - - virtual const char * stringFromReturn( IOReturn rtn ); - -/*! @function errnoFromReturn - @abstract Translates an IOReturn code to a BSD errno. - @discussion BSD defines its own return codes for its functions in sys/errno.h, and I/O Kit families may need to supply compliant results in BSD shims. Results are available for the standard return codes in IOReturn.h in IOService, while subclasses may implement this method to interpret family dependent return codes. - @param rtn The IOReturn code. - @result The BSD errno or EIO if unknown. */ - - virtual int errnoFromReturn( IOReturn rtn ); - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* * * * * * * * * * end of IOService API * * * * * * * */ - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - /* for IOInterruptController implementors */ - - int _numInterruptSources; - IOInterruptSource *_interruptSources; - - /* overrides */ - virtual bool serializeProperties( OSSerialize * s ) const override; - - void requireMaxBusStall(UInt32 ns); - void requireMaxInterruptDelay(uint32_t ns); - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /* * * * * * * * * * * * Internals * * * * * * * * * * * */ - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - -private: - APPLE_KEXT_COMPATIBILITY_VIRTUAL - bool checkResources( void ); - APPLE_KEXT_COMPATIBILITY_VIRTUAL - bool checkResource( OSObject * matching ); - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - void probeCandidates( OSOrderedSet * matches ); - APPLE_KEXT_COMPATIBILITY_VIRTUAL - bool startCandidate( IOService * candidate ); - -public: - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOService * getClientWithCategory( const OSSymbol * category ) - APPLE_KEXT_DEPRECATED; - // copyClientWithCategory is the public replacement - -private: - APPLE_KEXT_COMPATIBILITY_VIRTUAL - bool passiveMatch( OSDictionary * matching, bool changesOK = false); - APPLE_KEXT_COMPATIBILITY_VIRTUAL - void startMatching( IOOptionBits options = 0 ); - APPLE_KEXT_COMPATIBILITY_VIRTUAL - void doServiceMatch( IOOptionBits options ); - APPLE_KEXT_COMPATIBILITY_VIRTUAL - void doServiceTerminate( IOOptionBits options ); - -private: - - bool matchPassive(OSDictionary * table, uint32_t options); - bool matchInternal(OSDictionary * table, uint32_t options, unsigned int * did); - static bool instanceMatch(const OSObject * entry, void * context); - - static OSObject * copyExistingServices( OSDictionary * matching, - IOOptionBits inState, IOOptionBits options = 0 ); - - static IONotifier * setNotification( - const OSSymbol * type, OSDictionary * matching, - IOServiceMatchingNotificationHandler handler, - void * target, void * ref, - SInt32 priority = 0 ); - - static IONotifier * doInstallNotification( - const OSSymbol * type, OSDictionary * matching, - IOServiceMatchingNotificationHandler handler, - void * target, void * ref, - SInt32 priority, OSIterator ** existing ); - - static bool syncNotificationHandler( void * target, void * ref, - IOService * newService, IONotifier * notifier ); - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - void deliverNotification( const OSSymbol * type, - IOOptionBits orNewState, IOOptionBits andNewState ); - - bool invokeNotifer( class _IOServiceNotifier * notify ); - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - void unregisterAllInterest( void ); - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOReturn waitForState( UInt32 mask, UInt32 value, - mach_timespec_t * timeout = 0 ); - - IOReturn waitForState( UInt32 mask, UInt32 value, uint64_t timeout ); - - UInt32 _adjustBusy( SInt32 delta ); - - bool terminatePhase1( IOOptionBits options = 0 ); - void scheduleTerminatePhase2( IOOptionBits options = 0 ); - void scheduleStop( IOService * provider ); - void scheduleFinalize( void ); - static void terminateThread( void * arg, wait_result_t unused ); - static void terminateWorker( IOOptionBits options ); - static void actionWillTerminate( IOService * victim, IOOptionBits options, - OSArray * doPhase2List, void*, void * ); - static void actionDidTerminate( IOService * victim, IOOptionBits options, - void *, void *, void *); - static void actionFinalize( IOService * victim, IOOptionBits options, - void *, void *, void *); - static void actionStop( IOService * client, IOService * provider, - void *, void *, void *); - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOReturn resolveInterrupt(IOService *nub, int source); - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOReturn lookupInterrupt(int source, bool resolve, LIBKERN_RETURNS_NOT_RETAINED IOInterruptController **interruptController); - - - /* power management */ -public: - -/*! @function PMinit - @abstract Initializes power management for a driver. - @discussion PMinit allocates and initializes the power management instance variables, and it should be called before accessing those variables or calling the power management methods. This method should be called inside the driver's start routine and must be paired with a call to @link PMstop PMstop@/link. - Most calls to PMinit are followed by calls to @link joinPMtree joinPMtree@/link and @link registerPowerDriver registerPowerDriver@/link. */ - - virtual void PMinit( void ); - -/*! @function PMstop - @abstract Stop power managing the driver. - @discussion Removes the driver from the power plane and stop its power management. This method is synchronous against any power management method invocations (e.g. setPowerState or setAggressiveness), so when this method returns it is guaranteed those power management methods will not be entered. Driver should not call any power management methods after this call. - Calling PMstop cleans up for the three power management initialization calls: @link PMinit PMinit@/link, @link joinPMtree joinPMtree@/link, and @link registerPowerDriver registerPowerDriver@/link. */ - - virtual void PMstop( void ); - -/*! @function joinPMtree - @abstract Joins the driver into the power plane of the I/O Registry. - @discussion A driver uses this method to call its nub when initializing (usually in its start routine after calling @link PMinit PMinit@/link), to be attached into the power management hierarchy (i.e., the power plane). A driver usually calls this method on the driver for the device that provides it power (this is frequently the nub). - Before this call returns, the caller will probably be called at @link setPowerParent setPowerParent@/link and @link setAggressiveness setAggressiveness@/link and possibly at @link addPowerChild addPowerChild@/link as it is added to the hierarchy. This method may be overridden by a nub subclass. - @param driver The driver to be added to the power plane, usually this. */ - - virtual void joinPMtree( IOService * driver ); - -/*! @function registerPowerDriver - @abstract Registers a set of power states that the driver supports. - @discussion A driver defines its array of supported power states with power management in its power management initialization (its start routine). If successful, power management will call the driver to instruct it to change its power state through @link setPowerState setPowerState@/link. - Most drivers do not need to override registerPowerDriver. A nub may override registerPowerDriver if it needs to arrange its children in the power plane differently than the default placement, but this is uncommon. - @param controllingDriver A pointer to the calling driver, usually this. - @param powerStates A driver-defined array of power states that the driver and device support. Power states are defined in pwr_mgt/IOPMpowerState.h. - @param numberOfStates The number of power states in the array. - @result IOPMNoErr. All errors are logged via kprintf. */ - - virtual IOReturn registerPowerDriver( - IOService * controllingDriver, - IOPMPowerState * powerStates, - unsigned long numberOfStates ); - -/*! @function registerInterestedDriver - @abstract Allows an IOService object to register interest in the changing power state of a power-managed IOService object. - @discussion Call registerInterestedDriver on the IOService object you are interested in receiving power state messages from, and pass a pointer to the interested driver (this) as an argument. - The interested driver is retained until the power interest is removed by calling deRegisterInterestedDriver. - The interested driver should override @link powerStateWillChangeTo powerStateWillChangeTo@/link and @link powerStateDidChangeTo powerStateDidChangeTo@/link to receive these power change messages. - Interested drivers must acknowledge power changes in powerStateWillChangeTo or powerStateDidChangeTo, either via return value or later calls to @link acknowledgePowerChange acknowledgePowerChange@/link. - @param theDriver The driver of interest adds this pointer to the list of interested drivers. It informs drivers on this list before and after the power change. - @result Flags describing the capability of the device in its current power state. If the current power state is not yet defined, zero is returned (this is the case when the driver is not yet in the power domain hierarchy or hasn't fully registered with power management yet). */ - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOPMPowerFlags registerInterestedDriver( IOService * theDriver ); - -/*! @function deRegisterInterestedDriver - @abstract De-registers power state interest from a previous call to registerInterestedDriver. - @discussion The retain from registerInterestedDriver is released. This method is synchronous against any powerStateWillChangeTo or powerStateDidChangeTo call targeting the interested driver, so when this method returns it is guaranteed those interest handlers will not be entered. - Most drivers do not need to override deRegisterInterestedDriver. - @param theDriver The interested driver previously passed into @link registerInterestedDriver registerInterestedDriver@/link. - @result A return code that can be ignored by the caller. */ - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOReturn deRegisterInterestedDriver( IOService * theDriver ); - -/*! @function acknowledgePowerChange - @abstract Acknowledges an in-progress power state change. - @discussion When power management informs an interested object (via @link powerStateWillChangeTo powerStateWillChangeTo@/link or @link powerStateDidChangeTo powerStateDidChangeTo@/link), the object can return an immediate acknowledgement via a return code, or it may return an indication that it will acknowledge later by calling acknowledgePowerChange. - Interested objects are those that have registered as interested drivers, as well as power plane children of the power changing driver. A driver that calls @link registerInterestedDriver registerInterestedDriver@/link must call acknowledgePowerChange, or use an immediate acknowledgement return from powerStateWillChangeTo or powerStateDidChangeTo. - @param whichDriver A pointer to the calling driver. The called object tracks all interested parties to ensure that all have acknowledged the power state change. - @result IOPMNoErr. */ - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOReturn acknowledgePowerChange( IOService * whichDriver ); - -/*! @function acknowledgeSetPowerState - @abstract Acknowledges the belated completion of a driver's setPowerState power state change. - @discussion After power management instructs a driver to change its state via @link setPowerState setPowerState@/link, that driver must acknowledge the change when its device has completed its transition. The acknowledgement may be immediate, via a return code from setPowerState, or delayed, via this call to acknowledgeSetPowerState. - Any driver that does not return kIOPMAckImplied from its setPowerState implementation must later call acknowledgeSetPowerState. - @result IOPMNoErr. */ - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOReturn acknowledgeSetPowerState( void ); - -/*! @function requestPowerDomainState - @abstract Tells a driver to adjust its power state. - @discussion This call is handled internally by power management. It is not intended to be overridden or called by drivers. */ - - virtual IOReturn requestPowerDomainState( - IOPMPowerFlags desiredState, - IOPowerConnection * whichChild, - unsigned long specificationFlags ); - -/*! @function makeUsable - @abstract Requests that a device become usable. - @discussion This method is called when some client of a device (or the device's own driver) is asking for the device to become usable. Power management responds by telling the object upon which this method is called to change to its highest power state. - makeUsable is implemented using @link changePowerStateToPriv changePowerStateToPriv@/link. Subsequent requests for lower power, such as from changePowerStateToPriv, will pre-empt this request. - @result A return code that can be ignored by the caller. */ - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOReturn makeUsable( void ); - -/*! @function temporaryPowerClampOn - @abstract A driver calls this method to hold itself in the highest power state until it has children. - @discussion Use temporaryPowerClampOn to hold your driver in its highest power state while waiting for child devices to attach. After children have attached, the clamp is released and the device's power state is controlled by the children's requirements. - @result A return code that can be ignored by the caller. */ - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOReturn temporaryPowerClampOn( void ); - -/*! @function changePowerStateTo - @abstract Sets a driver's power state. - @discussion This function is one of several that are used to set a driver's power state. In most circumstances, however, you should call @link changePowerStateToPriv changePowerStateToPriv@/link instead. - Calls to changePowerStateTo, changePowerStateToPriv, and a driver's power children all affect the power state of a driver. For legacy design reasons, they have overlapping functionality. Although you should call changePowerStateToPriv to change your device's power state, you might need to call changePowerStateTo in the following circumstances: -
  • If a driver will be using changePowerStateToPriv to change its power state, it should call changePowerStateTo(0) in its start routine to eliminate the influence changePowerStateTo has on power state calculations. -
  • Call changePowerStateTo in conjunction with @link setIdleTimerPeriod setIdleTimerPeriod@/link and @link activityTickle activityTickle@/link to idle a driver into a low power state. For a driver with 3 power states, for example, changePowerStateTo(1) sets a minimum level of power state 1, such that the idle timer period may not set your device's power any lower than state 1.
- @param ordinal The number of the desired power state in the power state array. - @result A return code that can be ignored by the caller. */ - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOReturn changePowerStateTo( unsigned long ordinal ); - -/*! @function currentCapability - @abstract Finds out the capability of a device's current power state. - @result A copy of the capabilityFlags field for the current power state in the power state array. */ - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - IOPMPowerFlags currentCapability( void ); - -/*! @function currentPowerConsumption - @abstract Finds out the current power consumption of a device. - @discussion Most Mac OS X power managed drivers do not report their power consumption via the staticPower field. Thus this call will not accurately reflect power consumption for most drivers. - @result A copy of the staticPower field for the current power state in the power state array. */ - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - unsigned long currentPowerConsumption( void ); - -/*! @function activityTickle - @abstract Informs power management when a power-managed device is in use, so that power management can track when it is idle and adjust its power state accordingly. - @discussion The activityTickle method is provided for objects in the system (or for the driver itself) to tell a driver that its device is being used. - The IOService superclass can manage idleness determination with a simple idle timer mechanism and this activityTickle call. To start this up, the driver calls its superclass's setIdleTimerPeriod. This starts a timer for the time interval specified in the call. When the timer expires, the superclass checks to see if there has been any activity since the last timer expiration. (It checks to see if activityTickle has been called). If there has been activity, it restarts the timer, and this process continues. When the timer expires, and there has been no device activity, the superclass lowers the device power state to the next lower state. This can continue until the device is in state zero. - After the device has been powered down by at least one power state, a subsequent call to activityTickle causes the device to be switched to a higher state required for the activity. - If the driver is managing the idleness determination totally on its own, the value of the type parameter should be kIOPMSubclassPolicy, and the driver should override the activityTickle method. The superclass IOService implementation of activityTickle does nothing with the kIOPMSubclassPolicy argument. - @param type When type is kIOPMSubclassPolicy, activityTickle is not handled in IOService and should be intercepted by the subclass. When type is kIOPMSuperclassPolicy1, an activity flag is set and the device state is checked. If the device has been powered down, it is powered up again. - @param stateNumber When type is kIOPMSuperclassPolicy1, stateNumber contains the desired power state ordinal for the activity. If the device is in a lower state, the superclass will switch it to this state. This is for devices that can handle some accesses in lower power states; the device is powered up only as far as it needs to be for the activity. - @result When type is kIOPMSuperclassPolicy1, the superclass returns true if the device is currently in the state specified by stateNumber. If the device is in a lower state and must be powered up, the superclass returns false; in this case the superclass will initiate a power change to power the device up. */ - - virtual bool activityTickle( - unsigned long type, - unsigned long stateNumber = 0 ); - -/*! @function setAggressiveness - @abstract Broadcasts an aggressiveness factor from the parent of a driver to the driver. - @discussion Implement setAggressiveness to receive a notification when an "aggressiveness Aggressiveness factors are a loose set of power management variables that contain values for system sleep timeout, display sleep timeout, whether the system is on battery or AC, and other power management features. There are several aggressiveness factors that can be broadcast and a driver may take action on whichever factors apply to it. - A driver that has joined the power plane via @link joinPMtree joinPMtree@/link will receive setAgressiveness calls when aggressiveness factors change. - A driver may override this call if it needs to do something with the new factor (such as change its idle timeout). If overridden, the driver must call its superclass's setAgressiveness method in its own setAgressiveness implementation. - Most drivers do not need to implement setAgressiveness. - @param type The aggressiveness factor type, such as kPMMinutesToDim, kPMMinutesToSpinDown, kPMMinutesToSleep, and kPMPowerSource. (Aggressiveness factors are defined in pwr_mgt/IOPM.h.) - @param newLevel The aggressiveness factor's new value. - @result IOPMNoErr. */ - - virtual IOReturn setAggressiveness( - unsigned long type, - unsigned long newLevel ); - -/*! @function getAggressiveness - @abstract Returns the current aggressiveness value for the given type. - @param type The aggressiveness factor to query. - @param currentLevel Upon successful return, contains the value of aggressiveness factor type. - @result kIOReturnSuccess upon success; an I/O Kit error code otherwise. */ - - virtual IOReturn getAggressiveness( - unsigned long type, - unsigned long * currentLevel ); - -#ifndef __LP64__ -/*! @function systemWake - @abstract Tells every driver in the power plane that the system is waking up. - @discussion This call is handled internally by power management. It is not intended to be overridden or called by drivers. */ - - virtual IOReturn systemWake( void ) - APPLE_KEXT_DEPRECATED; - -/*! @function temperatureCriticalForZone - @abstract Alerts a driver to a critical temperature in some thermal zone. - @discussion This call is unused by power management. It is not intended to be called or overridden. */ - - virtual IOReturn temperatureCriticalForZone( IOService * whichZone ) - APPLE_KEXT_DEPRECATED; - -/*! @function youAreRoot - @abstract Informs power management which IOService object is the power plane root. - @discussion This call is handled internally by power management. It is not intended to be overridden or called by drivers. */ - - virtual IOReturn youAreRoot( void ) - APPLE_KEXT_DEPRECATED; - -/*! @function setPowerParent - @abstract This call is handled internally by power management. It is not intended to be overridden or called by drivers. */ - - virtual IOReturn setPowerParent( - IOPowerConnection * parent, - bool stateKnown, - IOPMPowerFlags currentState ) - APPLE_KEXT_DEPRECATED; -#endif /* !__LP64__ */ - -/*! @function addPowerChild - @abstract Informs a driver that it has a new child. - @discussion The Platform Expert uses this method to call a driver and introduce it to a new child. This call is handled internally by power management. It is not intended to be overridden or called by drivers. - @param theChild A pointer to the child IOService object. */ - - virtual IOReturn addPowerChild( IOService * theChild ); - -/*! @function removePowerChild - @abstract Informs a power managed driver that one of its power plane childen is disappearing. - @discussion This call is handled internally by power management. It is not intended to be overridden or called by drivers. */ - - virtual IOReturn removePowerChild( IOPowerConnection * theChild ); - -#ifndef __LP64__ -/*! @function command_received - @discussion This call is handled internally by power management. It is not intended to be overridden or called by drivers. */ - - virtual void command_received( void *, void * , void * , void * ); -#endif - -/*! @function start_PM_idle_timer - @discussion This call is handled internally by power management. It is not intended to be overridden or called by drivers. */ - - APPLE_KEXT_COMPATIBILITY_VIRTUAL - void start_PM_idle_timer( void ); - -#ifndef __LP64__ -/*! @function PM_idle_timer_expiration - @discussion This call is handled internally by power management. It is not intended to be overridden or called by drivers. */ - - virtual void PM_idle_timer_expiration( void ) - APPLE_KEXT_DEPRECATED; - -/*! @function PM_Clamp_Timer_Expired - @discussion This call is handled internally by power management. It is not intended to be overridden or called by drivers. */ - - virtual void PM_Clamp_Timer_Expired( void ) - APPLE_KEXT_DEPRECATED; -#endif - -/*! @function setIdleTimerPeriod - @abstract Sets or changes the idle timer period. - @discussion A driver using the idleness determination provided by IOService calls its superclass with this method to set or change the idle timer period. See @link activityTickle activityTickle@/link for a description of this type of idleness determination. - @param period The desired idle timer period in seconds. - @result kIOReturnSuccess upon success; an I/O Kit error code otherwise. */ - - virtual IOReturn setIdleTimerPeriod( unsigned long period ); - -#ifndef __LP64__ -/*! @function getPMworkloop - @abstract Returns a pointer to the system-wide power management work loop. - @availability Deprecated in Mac OS X version 10.6. - @discussion Most drivers should create their own work loops to synchronize their code; drivers should not run arbitrary code on the power management work loop. */ - - virtual IOWorkLoop * getPMworkloop( void ) - APPLE_KEXT_DEPRECATED; -#endif - -/*! @function getPowerState - @abstract Determines a device's power state. - @discussion A device's "current power state" is updated at the end of each power state transition (e.g. transition from state 1 to state 0, or state 0 to state 2). This transition includes the time spent powering on or off any power plane children. Thus, if a child calls getPowerState on its power parent during system wake from sleep, the call will return the index to the device's off state rather than its on state. - @result The current power state's index into the device's power state array. */ - - UInt32 getPowerState( void ); - -/*! @function setPowerState - @abstract Requests a power managed driver to change the power state of its device. - @discussion A power managed driver must override setPowerState to take part in system power management. After a driver is registered with power management, the system uses setPowerState to power the device off and on for system sleep and wake. - Calls to @link PMinit PMinit@/link and @link registerPowerDriver registerPowerDriver@/link enable power management to change a device's power state using setPowerState. setPowerState is called in a clean and separate thread context. - @param powerStateOrdinal The number in the power state array of the state the driver is being instructed to switch to. - @param whatDevice A pointer to the power management object which registered to manage power for this device. In most cases, whatDevice will be equal to your driver's own this pointer. - @result The driver must return IOPMAckImplied if it has complied with the request when it returns. Otherwise if it has started the process of changing power state but not finished it, the driver should return a number of microseconds which is an upper limit of the time it will need to finish. Then, when it has completed the power switch, it should call @link acknowledgeSetPowerState acknowledgeSetPowerState@/link. */ - - virtual IOReturn setPowerState( - unsigned long powerStateOrdinal, - IOService * whatDevice ); - -#ifndef __LP64__ -/*! @function clampPowerOn - @abstract Deprecated. Do not use. */ - - virtual void clampPowerOn( unsigned long duration ); -#endif - -/*! @function maxCapabilityForDomainState - @abstract Determines a driver's highest power state possible for a given power domain state. - @discussion This happens when the power domain is changing state and power management needs to determine which state the device is capable of in the new domain state. - Most drivers do not need to implement this method, and can rely upon the default IOService implementation. The IOService implementation scans the power state array looking for the highest state whose inputPowerRequirement field exactly matches the value of the domainState parameter. If more intelligent determination is required, the driver itself should implement the method and override the superclass's implementation. - @param domainState Flags that describe the character of "domain power"; they represent the outputPowerCharacter field of a state in the power domain's power state array. - @result A state number. */ - - virtual unsigned long maxCapabilityForDomainState( IOPMPowerFlags domainState ); - -/*! @function initialPowerStateForDomainState - @abstract Determines which power state a device is in, given the current power domain state. - @discussion Power management calls this method once, when the driver is initializing power management. - Most drivers do not need to implement this method, and can rely upon the default IOService implementation. The IOService implementation scans the power state array looking for the highest state whose inputPowerRequirement field exactly matches the value of the domainState parameter. If more intelligent determination is required, the power managed driver should implement the method and override the superclass's implementation. - @param domainState Flags that describe the character of "domain power"; they represent the outputPowerCharacter field of a state in the power domain's power state array. - @result A state number. */ - - virtual unsigned long initialPowerStateForDomainState( IOPMPowerFlags domainState ); - -/*! @function powerStateForDomainState - @abstract Determines what power state the device would be in for a given power domain state. - @discussion Power management calls a driver with this method to find out what power state the device would be in for a given power domain state. This happens when the power domain is changing state and power management needs to determine the effect of the change. - Most drivers do not need to implement this method, and can rely upon the default IOService implementation. The IOService implementation scans the power state array looking for the highest state whose inputPowerRequirement field exactly matches the value of the domainState parameter. If more intelligent determination is required, the power managed driver should implement the method and override the superclass's implementation. - @param domainState Flags that describe the character of "domain power"; they represent the outputPowerCharacter field of a state in the power domain's power state array. - @result A state number. */ - - virtual unsigned long powerStateForDomainState( IOPMPowerFlags domainState ); - -/*! @function powerStateWillChangeTo - @abstract Informs interested parties that a device is about to change its power state. - @discussion Power management informs interested parties that a device is about to change to a different power state. Interested parties are those that have registered for this notification via @link registerInterestedDriver registerInterestedDriver@/link. If you have called registerInterestedDriver on a power managed driver, you must implement powerStateWillChangeTo and @link powerStateDidChangeTo powerStateDidChangeTo@/link to receive the notifications. - powerStateWillChangeTo is called in a clean and separate thread context. powerStateWillChangeTo is called before a power state transition takes place; powerStateDidChangeTo is called after the transition has completed. - @param capabilities Flags that describe the capability of the device in the new power state (they come from the capabilityFlags field of the new state in the power state array). - @param stateNumber The number of the state in the state array that the device is switching to. - @param whatDevice A pointer to the driver that is changing. It can be used by a driver that is receiving power state change notifications for multiple devices to distinguish between them. - @result The driver returns IOPMAckImplied if it has prepared for the power change when it returns. If it has started preparing but not finished, it should return a number of microseconds which is an upper limit of the time it will need to finish preparing. Then, when it has completed its preparations, it should call @link acknowledgePowerChange acknowledgePowerChange@/link. */ - - virtual IOReturn powerStateWillChangeTo( - IOPMPowerFlags capabilities, - unsigned long stateNumber, - IOService * whatDevice ); - -/*! @function powerStateDidChangeTo - @abstract Informs interested parties that a device has changed to a different power state. - @discussion Power management informs interested parties that a device has changed to a different power state. Interested parties are those that have registered for this notification via @link registerInterestedDriver registerInterestedDriver@/link. If you have called registerInterestedDriver on a power managed driver, you must implemnt @link powerStateWillChangeTo powerStateWillChangeTo@/link and powerStateDidChangeTo to receive the notifications. - powerStateDidChangeTo is called in a clean and separate thread context. powerStateWillChangeTo is called before a power state transition takes place; powerStateDidChangeTo is called after the transition has completed. - @param capabilities Flags that describe the capability of the device in the new power state (they come from the capabilityFlags field of the new state in the power state array). - @param stateNumber The number of the state in the state array that the device is switching to. - @param whatDevice A pointer to the driver that is changing. It can be used by a driver that is receiving power state change notifications for multiple devices to distinguish between them. - @result The driver returns IOPMAckImplied if it has prepared for the power change when it returns. If it has started preparing but not finished, it should return a number of microseconds which is an upper limit of the time it will need to finish preparing. Then, when it has completed its preparations, it should call @link acknowledgePowerChange acknowledgePowerChange@/link. */ - - virtual IOReturn powerStateDidChangeTo( - IOPMPowerFlags capabilities, - unsigned long stateNumber, - IOService * whatDevice ); - -#ifndef __LP64__ -/*! @function didYouWakeSystem - @abstract Asks a driver if its device is the one that just woke the system from sleep. - @availability Deprecated in Mac OS X version 10.6. - @discussion Power management calls a power managed driver with this method to ask if its device is the one that just woke the system from sleep. If a device is capable of waking the system from sleep, its driver should implement didYouWakeSystem and return true if its device was responsible for waking the system. - @result true if the driver's device woke the system and false otherwise. */ - - virtual bool didYouWakeSystem( void ) - APPLE_KEXT_DEPRECATED; - -/*! @function newTemperature - @abstract Tells a power managed driver that the temperature in the thermal zone has changed. - @discussion This call is unused by power management. It is not intended to be called or overridden. */ - - virtual IOReturn newTemperature( long currentTemp, IOService * whichZone ) - APPLE_KEXT_DEPRECATED; -#endif - - virtual bool askChangeDown( unsigned long ); - virtual bool tellChangeDown( unsigned long ); - virtual void tellNoChangeDown ( unsigned long ); - virtual void tellChangeUp( unsigned long ); - virtual IOReturn allowPowerChange( unsigned long refcon ); - virtual IOReturn cancelPowerChange( unsigned long refcon ); - -protected: -/*! @function changePowerStateToPriv - @abstract Tells a driver's superclass to change the power state of its device. - @discussion A driver uses this method to tell its superclass to change the power state of the device. This is the recommended way to change the power state of a device. - Three things affect driver power state: @link changePowerStateTo changePowerStateTo@/link, changePowerStateToPriv, and the desires of the driver's power plane children. Power management puts the device into the maximum state governed by those three entities. - Drivers may eliminate the influence of the changePowerStateTo method on power state one of two ways. See @link powerOverrideOnPriv powerOverrideOnPriv@/link to ignore the method's influence, or call changePowerStateTo(0) in the driver's start routine to remove the changePowerStateTo method's power request. - @param ordinal The number of the desired power state in the power state array. - @result A return code that can be ignored by the caller. */ - - IOReturn changePowerStateToPriv( unsigned long ordinal ); - -/*! @function powerOverrideOnPriv - @abstract Allows a driver to ignore its children's power management requests and only use changePowerStateToPriv to define its own power state. - @discussion Power management normally keeps a device at the highest state required by its requests via @link changePowerStateTo changePowerStateTo@/link, @link changePowerStateToPriv changePowerStateToPriv@/link, and its children. However, a driver may ensure a lower power state than otherwise required by itself and its children using powerOverrideOnPriv. When the override is on, power management keeps the device's power state in the state specified by changePowerStateToPriv. Turning on the override will initiate a power change if the driver's changePowerStateToPriv desired power state is different from the maximum of the changePowerStateTo desired power state and the children's desires. - @result A return code that can be ignored by the caller. */ - - IOReturn powerOverrideOnPriv( void ); - -/*! @function powerOverrideOffPriv - @abstract Allows a driver to disable a power override. - @discussion When a driver has enabled an override via @link powerOverrideOnPriv powerOverrideOnPriv@/link, it can disable it again by calling this method in its superclass. Disabling the override reverts to the default algorithm for determining a device's power state. The superclass will now keep the device at the highest state required by changePowerStateTo, changePowerStateToPriv, and its children. Turning off the override will initiate a power change if the driver's desired power state is different from the maximum of the power managed driver's desire and the children's desires. - @result A return code that can be ignored by the caller. */ - - IOReturn powerOverrideOffPriv( void ); - -/*! @function powerChangeDone - @abstract Tells a driver when a power state change is complete. - @discussion Power management uses this method to inform a driver when a power change is completely done, when all interested parties have acknowledged the @link powerStateDidChangeTo powerStateDidChangeTo@/link call. The default implementation of this method is null; the method is meant to be overridden by subclassed power managed drivers. A driver should use this method to find out if a power change it initiated is complete. - @param stateNumber The number of the state in the state array that the device has switched from. */ - - virtual void powerChangeDone( unsigned long stateNumber ); -}; - -#endif /* ! _IOKIT_IOSERVICE_H */ diff --git a/Library/LegacyLibkernMacros.h b/Library/LegacyLibkernMacros.h deleted file mode 100644 index d685ee06..00000000 --- a/Library/LegacyLibkernMacros.h +++ /dev/null @@ -1,58 +0,0 @@ -// -// LegacyLibkernMacros.h -// Lilu -// -// Copyright © 2019 vit9696. All rights reserved. -// - -#ifndef LegacyLibkernMacros_h -#define LegacyLibkernMacros_h - -// This is a compatibility header to let Lilu build with different Xcode -// versions and be able to use legacy headers and clang analyzer. - -#include - -#if !defined(LIBKERN_RETURNS_NOT_RETAINED) -#define LIBKERN_RETURNS_NOT_RETAINED -#elif defined(__clang_major__) && __clang_major__ < 11 -#undef LIBKERN_RETURNS_NOT_RETAINED -#define LIBKERN_RETURNS_NOT_RETAINED -#endif - -#if !defined(LIBKERN_RETURNS_RETAINED) -#define LIBKERN_RETURNS_RETAINED -#elif defined(__clang_major__) && __clang_major__ < 11 -#undef LIBKERN_RETURNS_RETAINED -#define LIBKERN_RETURNS_RETAINED -#endif - -#if !defined(LIBKERN_CONSUMED) -#define LIBKERN_CONSUMED -#elif defined(__clang_major__) && __clang_major__ < 11 -#undef LIBKERN_CONSUMED -#define LIBKERN_CONSUMED -#endif - -#if !defined(LIBKERN_CONSUMES_THIS) -#define LIBKERN_CONSUMES_THIS -#elif defined(__clang_major__) && __clang_major__ < 11 -#undef LIBKERN_CONSUMES_THIS -#define LIBKERN_CONSUMES_THIS -#endif - -#if !defined(LIBKERN_RETURNS_RETAINED_ON_ZERO) -#define LIBKERN_RETURNS_RETAINED_ON_ZERO -#elif defined(__clang_major__) && __clang_major__ < 11 -#undef LIBKERN_RETURNS_RETAINED_ON_ZERO -#define LIBKERN_RETURNS_RETAINED_ON_ZERO -#endif - -#if !defined(LIBKERN_RETURNS_RETAINED_ON_NONZERO) -#define LIBKERN_RETURNS_RETAINED_ON_NONZERO -#elif defined(__clang_major__) && __clang_major__ < 11 -#undef LIBKERN_RETURNS_RETAINED_ON_NONZERO -#define LIBKERN_RETURNS_RETAINED_ON_NONZERO -#endif - -#endif /* LegacyLibkernMacros_h */ diff --git a/Library/compat.cpp b/Library/compat.cpp deleted file mode 100644 index 687b6260..00000000 --- a/Library/compat.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// compat.cpp -// VoodooPS2Controller -// -// Created by user on 24/07/2019. -// Copyright © 2019 vit9696. All rights reserved. -// - -#include - -#ifdef __MAC_10_15 - -/** - * Ensure the symbol is not exported - */ -#define PRIVATE __attribute__((visibility("hidden"))) - -/** - * For private fallback symbol definition - */ -#define WEAKFUNC __attribute__((weak)) - -// macOS 10.15 adds Dispatch function to all OSObject instances and basically -// every header is now incompatible with 10.14 and earlier. -// Here we add a stub to permit older macOS versions to link. -// Note, this is done in both kern_util and plugin_start as plugins will not link -// to Lilu weak exports from vtable. - -kern_return_t WEAKFUNC PRIVATE OSObject::Dispatch(const IORPC rpc) { - (panic)("OSObject::Dispatch plugin stub called"); -} - -kern_return_t WEAKFUNC PRIVATE OSMetaClassBase::Dispatch(const IORPC rpc) { - (panic)("OSMetaClassBase::Dispatch plugin stub called"); -} - -#endif diff --git a/Library/libkmod.a b/Library/libkmod.a deleted file mode 100755 index 4bf84bb8..00000000 Binary files a/Library/libkmod.a and /dev/null differ diff --git a/README.md b/README.md index b4702bde..41846e7b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ VoodooPS2 ========= -[![Build Status](https://travis-ci.com/acidanthera/VoodooPS2.svg?branch=master)](https://travis-ci.com/acidanthera/VoodooPS2) +[![Build Status](https://github.com/acidanthera/VoodooPS2/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/acidanthera/VoodooPS2/actions) [![Scan Status](https://scan.coverity.com/projects/22190/badge.svg?flat=1)](https://scan.coverity.com/projects/22190) New **VoodooPS2Trackpad** uses VoodooInput's Magic Trackpad II emulation in order to use macOS native driver instead of handling all gestures itself. This enables the use of any from one to four finger gestures defined by Apple including: * Look up & data detectors @@ -32,6 +32,10 @@ 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. + +For ALPS touchpads, V1, V2 and V6 do not support Force Touch. V3, V4 and V5 only support mode 0, 2, 3 and 4. V7 only supports mode 0 and 1. V8 supports all modes. + ## Installation and compilation For VoodooPS2Trackpad.kext to work multitouch interface engine, named VoodooInput.kext, is required. @@ -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 – linux kernel contributors, @kprinssu, @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 6d45fd11..a4fa4a14 100644 --- a/VoodooPS2Controller.xcodeproj/project.pbxproj +++ b/VoodooPS2Controller.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 356B896323007F4F0042F30F /* VoodooInputEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 356B895F23007F4F0042F30F /* VoodooInputEvent.h */; }; 356B896423007F4F0042F30F /* VoodooInputTransducer.h in Headers */ = {isa = PBXBuildFile; fileRef = 356B896023007F4F0042F30F /* VoodooInputTransducer.h */; }; 356B896523007F4F0042F30F /* MultitouchHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 356B896123007F4F0042F30F /* MultitouchHelpers.h */; }; + 4CC27EB3251FBC7700824FE1 /* VoodooPS2TrackpadCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 4CC27EB2251FBC7700824FE1 /* VoodooPS2TrackpadCommon.h */; }; 840F104A16EFE42600E8C116 /* ApplePS2Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 840F104916EFE42600E8C116 /* ApplePS2Device.cpp */; }; 84167820161B55B2002C60E6 /* VoodooPS2Controller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8416781F161B55B2002C60E6 /* VoodooPS2Controller.cpp */; }; 84167836161B5613002C60E6 /* VoodooPS2Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84167835161B5613002C60E6 /* VoodooPS2Keyboard.cpp */; }; @@ -32,30 +33,14 @@ 84DD197C162D496E0044D061 /* AppleACPIPS2Nub.h in Headers */ = {isa = PBXBuildFile; fileRef = 84DD197A162D496E0044D061 /* AppleACPIPS2Nub.h */; }; 84EB0AE316F0AD9300016108 /* ApplePS2KeyboardDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84833F9E161B627D00845294 /* ApplePS2KeyboardDevice.cpp */; }; 84EB0AE516F0AD9600016108 /* ApplePS2MouseDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84833FA0161B627D00845294 /* ApplePS2MouseDevice.cpp */; }; - CE7F451A22E8AC61003F7971 /* libkmod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE7F451822E8AC59003F7971 /* libkmod.a */; }; - CE7F451B22E8AC65003F7971 /* libkmod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE7F451822E8AC59003F7971 /* libkmod.a */; }; - CE7F451C22E8AC69003F7971 /* libkmod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE7F451822E8AC59003F7971 /* libkmod.a */; }; - CE7F451D22E8AC6F003F7971 /* libkmod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE7F451822E8AC59003F7971 /* libkmod.a */; }; - CE86CCF122E8B0F800B32BE3 /* compat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE86CCF022E8B0F800B32BE3 /* compat.cpp */; }; - CE86CCF222E8B0F800B32BE3 /* compat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE86CCF022E8B0F800B32BE3 /* compat.cpp */; }; - CE86CCF322E8B0F800B32BE3 /* compat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE86CCF022E8B0F800B32BE3 /* compat.cpp */; }; - CE86CCF422E8B0F800B32BE3 /* compat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE86CCF022E8B0F800B32BE3 /* compat.cpp */; }; - CE86CCF622E8BC9800B32BE3 /* LegacyIOService.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCF522E8BC9800B32BE3 /* LegacyIOService.h */; }; - CE86CCF722E8BC9800B32BE3 /* LegacyIOService.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCF522E8BC9800B32BE3 /* LegacyIOService.h */; }; - CE86CCF822E8BC9800B32BE3 /* LegacyIOService.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCF522E8BC9800B32BE3 /* LegacyIOService.h */; }; - CE86CCF922E8BC9800B32BE3 /* LegacyIOService.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCF522E8BC9800B32BE3 /* LegacyIOService.h */; }; - CE86CCFB22E8BCEE00B32BE3 /* LegacyLibkernMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCFA22E8BCEE00B32BE3 /* LegacyLibkernMacros.h */; }; - CE86CCFC22E8BCEE00B32BE3 /* LegacyLibkernMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCFA22E8BCEE00B32BE3 /* LegacyLibkernMacros.h */; }; - CE86CCFD22E8BCEE00B32BE3 /* LegacyLibkernMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCFA22E8BCEE00B32BE3 /* LegacyLibkernMacros.h */; }; - CE86CCFE22E8BCEE00B32BE3 /* LegacyLibkernMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCFA22E8BCEE00B32BE3 /* LegacyLibkernMacros.h */; }; - CE86CD0122E8BDE000B32BE3 /* LegacyIOHIKeyboard.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCFF22E8BDE000B32BE3 /* LegacyIOHIKeyboard.h */; }; - CE86CD0222E8BDE000B32BE3 /* LegacyIOHIKeyboard.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCFF22E8BDE000B32BE3 /* LegacyIOHIKeyboard.h */; }; - CE86CD0322E8BDE000B32BE3 /* LegacyIOHIKeyboard.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCFF22E8BDE000B32BE3 /* LegacyIOHIKeyboard.h */; }; - CE86CD0422E8BDE000B32BE3 /* LegacyIOHIKeyboard.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CCFF22E8BDE000B32BE3 /* LegacyIOHIKeyboard.h */; }; - CE86CD0522E8BDE000B32BE3 /* LegacyIOHIPointing.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CD0022E8BDE000B32BE3 /* LegacyIOHIPointing.h */; }; - CE86CD0622E8BDE000B32BE3 /* LegacyIOHIPointing.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CD0022E8BDE000B32BE3 /* LegacyIOHIPointing.h */; }; - CE86CD0722E8BDE000B32BE3 /* LegacyIOHIPointing.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CD0022E8BDE000B32BE3 /* LegacyIOHIPointing.h */; }; - CE86CD0822E8BDE000B32BE3 /* LegacyIOHIPointing.h in Headers */ = {isa = PBXBuildFile; fileRef = CE86CD0022E8BDE000B32BE3 /* LegacyIOHIPointing.h */; }; + 9828A92F24A2B6C200550FAA /* VoodooPS2Elan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9828A92D24A2B6C200550FAA /* VoodooPS2Elan.cpp */; }; + 9828A93024A2B6C200550FAA /* VoodooPS2Elan.h in Headers */ = {isa = PBXBuildFile; fileRef = 9828A92E24A2B6C200550FAA /* VoodooPS2Elan.h */; }; + CE8DA1C5251839B3008C44E8 /* libkmod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE8DA1C4251839B2008C44E8 /* libkmod.a */; }; + CE8DA1C6251839B7008C44E8 /* libkmod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE8DA1C4251839B2008C44E8 /* libkmod.a */; }; + CE8DA1C7251839B9008C44E8 /* libkmod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE8DA1C4251839B2008C44E8 /* libkmod.a */; }; + CE8DA1CC251839BC008C44E8 /* libkmod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE8DA1C4251839B2008C44E8 /* libkmod.a */; }; + EE914A902C952F0D0023CFE0 /* VoodooPS2SMBusDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EE914A8E2C952F0D0023CFE0 /* VoodooPS2SMBusDevice.cpp */; }; + EE914A912C952F0D0023CFE0 /* VoodooPS2SMBusDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = EE914A8F2C952F0D0023CFE0 /* VoodooPS2SMBusDevice.h */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -87,6 +72,9 @@ 356B895F23007F4F0042F30F /* VoodooInputEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VoodooInputEvent.h; path = VoodooInput/Debug/VoodooInput.kext/Contents/Resources/VoodooInputMultitouch/VoodooInputEvent.h; sourceTree = SOURCE_ROOT; }; 356B896023007F4F0042F30F /* VoodooInputTransducer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VoodooInputTransducer.h; path = VoodooInput/Debug/VoodooInput.kext/Contents/Resources/VoodooInputMultitouch/VoodooInputTransducer.h; sourceTree = SOURCE_ROOT; }; 356B896123007F4F0042F30F /* MultitouchHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MultitouchHelpers.h; path = VoodooInput/Debug/VoodooInput.kext/Contents/Resources/VoodooInputMultitouch/MultitouchHelpers.h; sourceTree = SOURCE_ROOT; }; + 4CC27EB2251FBC7700824FE1 /* VoodooPS2TrackpadCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VoodooPS2TrackpadCommon.h; sourceTree = ""; }; + 73119AB725E1961D0017311C /* SSDT-NumLockOnAtBoot.dsl */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SSDT-NumLockOnAtBoot.dsl"; sourceTree = ""; }; + 73119AB825E1961D0017311C /* SSDT-NumLockSupport.dsl */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SSDT-NumLockSupport.dsl"; sourceTree = ""; }; 7B44762421D52A7100418B25 /* ApplePS2MouseDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApplePS2MouseDevice.h; sourceTree = ""; }; 8404565C161E3AAF00D74D7F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 8404565F161E3AC300D74D7F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -95,8 +83,8 @@ 840F104916EFE42600E8C116 /* ApplePS2Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ApplePS2Device.cpp; sourceTree = ""; }; 84167813161B55B2002C60E6 /* VoodooPS2Controller.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VoodooPS2Controller.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 8416781A161B55B2002C60E6 /* VoodooPS2Controller-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "VoodooPS2Controller-Info.plist"; sourceTree = ""; }; - 8416781E161B55B2002C60E6 /* VoodooPS2Controller.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = VoodooPS2Controller.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - 8416781F161B55B2002C60E6 /* VoodooPS2Controller.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = VoodooPS2Controller.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 8416781E161B55B2002C60E6 /* VoodooPS2Controller.h */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = VoodooPS2Controller.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 8416781F161B55B2002C60E6 /* VoodooPS2Controller.cpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = VoodooPS2Controller.cpp; sourceTree = ""; tabWidth = 4; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; 8416782D161B5613002C60E6 /* VoodooPS2Keyboard.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VoodooPS2Keyboard.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 84167830161B5613002C60E6 /* VoodooPS2Keyboard-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "VoodooPS2Keyboard-Info.plist"; sourceTree = ""; }; 84167834161B5613002C60E6 /* VoodooPS2Keyboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VoodooPS2Keyboard.h; sourceTree = ""; }; @@ -124,31 +112,29 @@ 84AE0F6C1BE4479200AF814A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 84DD1979162D496E0044D061 /* AppleACPIPS2Nub.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppleACPIPS2Nub.cpp; sourceTree = ""; }; 84DD197A162D496E0044D061 /* AppleACPIPS2Nub.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleACPIPS2Nub.h; sourceTree = ""; }; + 9828A92D24A2B6C200550FAA /* VoodooPS2Elan.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = VoodooPS2Elan.cpp; sourceTree = ""; }; + 9828A92E24A2B6C200550FAA /* VoodooPS2Elan.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VoodooPS2Elan.h; sourceTree = ""; }; CE39B4E122D0CCC200D344F3 /* Changelog.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Changelog.md; sourceTree = ""; }; - CE7F451122E8A8C4003F7971 /* SSDT-MouseAsTrackpad.dsl */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SSDT-MouseAsTrackpad.dsl"; sourceTree = ""; }; CE7F451222E8A8ED003F7971 /* SynapticsRevB.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = SynapticsRevB.pdf; sourceTree = ""; }; CE7F451322E8A8ED003F7971 /* voodoops2ioio.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = voodoops2ioio.sh; sourceTree = ""; }; CE7F451422E8A8F8003F7971 /* Changelog.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Changelog.md; sourceTree = SOURCE_ROOT; }; CE7F451522E8A8FE003F7971 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; CE7F451622E8A906003F7971 /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = SOURCE_ROOT; }; - CE7F451822E8AC59003F7971 /* libkmod.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libkmod.a; sourceTree = ""; }; - CE86CCF022E8B0F800B32BE3 /* compat.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = compat.cpp; sourceTree = ""; }; - CE86CCF522E8BC9800B32BE3 /* LegacyIOService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LegacyIOService.h; sourceTree = ""; }; - CE86CCFA22E8BCEE00B32BE3 /* LegacyLibkernMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LegacyLibkernMacros.h; sourceTree = ""; }; - CE86CCFF22E8BDE000B32BE3 /* LegacyIOHIKeyboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LegacyIOHIKeyboard.h; sourceTree = ""; }; - CE86CD0022E8BDE000B32BE3 /* LegacyIOHIPointing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LegacyIOHIPointing.h; sourceTree = ""; }; - CED643F022E8AF27006CDE9B /* LegacyIOHIDDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LegacyIOHIDDevice.h; sourceTree = ""; }; - ED1C95C51F9B88EF00CAFCA3 /* SSDT-PrtSc-F13.dsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "SSDT-PrtSc-F13.dsl"; sourceTree = ""; }; + CE8DA1C4251839B2008C44E8 /* libkmod.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libkmod.a; path = MacKernelSDK/Library/x86_64/libkmod.a; sourceTree = ""; }; + CEA35504257C6E3500E17556 /* SSDT-Swap-CommandOption.dsl */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SSDT-Swap-CommandOption.dsl"; sourceTree = ""; }; + CEAFF570259F8AE700693DEC /* SSDT-DisableElanWakeDelay.dsl */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SSDT-DisableElanWakeDelay.dsl"; sourceTree = ""; }; + CEF251412631A92B00A20C9A /* SSDT-DisableDeepSleep.dsl */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SSDT-DisableDeepSleep.dsl"; sourceTree = ""; }; + ED1C95C51F9B88EF00CAFCA3 /* SSDT-PrtSc-Remap.dsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "SSDT-PrtSc-Remap.dsl"; sourceTree = ""; }; ED48777A207D94BC00D6B1E8 /* SSDT-AlternateSwipes.dsl */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SSDT-AlternateSwipes.dsl"; sourceTree = ""; }; ED5759741D44FBA50069DF9F /* SSDT-KEY-DELL-WN09.dsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "SSDT-KEY-DELL-WN09.dsl"; sourceTree = ""; }; ED6BB589209E4EE8007AC3A4 /* SSDT-DisableTrackpadProbe.dsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "SSDT-DisableTrackpadProbe.dsl"; sourceTree = ""; }; - ED7E45DF1CFB35EE00ED2FB8 /* SSDT-Enable_DynamicEWMode.dsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "SSDT-Enable_DynamicEWMode.dsl"; sourceTree = ""; }; - ED7E45E01CFB35EE00ED2FB8 /* SSDT-Disable_DynamicEWMode.dsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "SSDT-Disable_DynamicEWMode.dsl"; sourceTree = ""; }; EDAD29471FE8772400A93537 /* SSDT-Swap-LeftControlCommand.dsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "SSDT-Swap-LeftControlCommand.dsl"; sourceTree = ""; }; EDB891282041BB12004DB536 /* SSDT-Swap-LeftControlCapsLock.dsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "SSDT-Swap-LeftControlCapsLock.dsl"; sourceTree = ""; }; EDD95559208E2B640031D99E /* SSDT-Thinkpad_Clickpad.dsl */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SSDT-Thinkpad_Clickpad.dsl"; sourceTree = ""; }; EDD9555A208E2E7A0031D99E /* SSDT-Thinkpad_Trackpad.dsl */ = {isa = PBXFileReference; lastKnownFileType = text; path = "SSDT-Thinkpad_Trackpad.dsl"; sourceTree = ""; }; EDD970FD1FD0B826004CCFFD /* SSDT-HP-FixLidSleep.dsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "SSDT-HP-FixLidSleep.dsl"; sourceTree = ""; }; + EE914A8E2C952F0D0023CFE0 /* VoodooPS2SMBusDevice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = VoodooPS2SMBusDevice.cpp; sourceTree = ""; }; + EE914A8F2C952F0D0023CFE0 /* VoodooPS2SMBusDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VoodooPS2SMBusDevice.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -156,7 +142,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CE7F451A22E8AC61003F7971 /* libkmod.a in Frameworks */, + CE8DA1C5251839B3008C44E8 /* libkmod.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -164,7 +150,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CE7F451B22E8AC65003F7971 /* libkmod.a in Frameworks */, + CE8DA1C6251839B7008C44E8 /* libkmod.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -172,7 +158,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CE7F451C22E8AC69003F7971 /* libkmod.a in Frameworks */, + CE8DA1C7251839B9008C44E8 /* libkmod.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -180,7 +166,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CE7F451D22E8AC6F003F7971 /* libkmod.a in Frameworks */, + CE8DA1CC251839BC008C44E8 /* libkmod.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -203,7 +189,6 @@ children = ( CE7F450F22E8A853003F7971 /* Docs */, 84833FC0161B636900845294 /* Common */, - CE7F451722E8AC45003F7971 /* Library */, CE39B4E122D0CCC200D344F3 /* Changelog.md */, 84AE0F6C1BE4479200AF814A /* README.md */, 84167818161B55B2002C60E6 /* VoodooPS2Controller */, @@ -211,8 +196,10 @@ 84167842161B56A2002C60E6 /* VoodooPS2Mouse */, 84167856161B56C4002C60E6 /* VoodooPS2Trackpad */, 84167814161B55B2002C60E6 /* Products */, + CE8DA1C3251839B2008C44E8 /* Frameworks */, ); sourceTree = ""; + usesTabs = 0; }; 84167814161B55B2002C60E6 /* Products */ = { isa = PBXGroup; @@ -297,10 +284,15 @@ 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 */, + 4CC27EB2251FBC7700824FE1 /* VoodooPS2TrackpadCommon.h */, + EE914A8F2C952F0D0023CFE0 /* VoodooPS2SMBusDevice.h */, + EE914A8E2C952F0D0023CFE0 /* VoodooPS2SMBusDevice.cpp */, 84167857161B56C4002C60E6 /* Supporting Files */, ); path = VoodooPS2Trackpad; @@ -329,14 +321,16 @@ CE7F450E22E8A842003F7971 /* ACPI */ = { isa = PBXGroup; children = ( - CE7F451122E8A8C4003F7971 /* SSDT-MouseAsTrackpad.dsl */, + 73119AB725E1961D0017311C /* SSDT-NumLockOnAtBoot.dsl */, + 73119AB825E1961D0017311C /* SSDT-NumLockSupport.dsl */, ED48777A207D94BC00D6B1E8 /* SSDT-AlternateSwipes.dsl */, - ED7E45E01CFB35EE00ED2FB8 /* SSDT-Disable_DynamicEWMode.dsl */, + CEF251412631A92B00A20C9A /* SSDT-DisableDeepSleep.dsl */, + CEAFF570259F8AE700693DEC /* SSDT-DisableElanWakeDelay.dsl */, ED6BB589209E4EE8007AC3A4 /* SSDT-DisableTrackpadProbe.dsl */, - ED7E45DF1CFB35EE00ED2FB8 /* SSDT-Enable_DynamicEWMode.dsl */, EDD970FD1FD0B826004CCFFD /* SSDT-HP-FixLidSleep.dsl */, ED5759741D44FBA50069DF9F /* SSDT-KEY-DELL-WN09.dsl */, - ED1C95C51F9B88EF00CAFCA3 /* SSDT-PrtSc-F13.dsl */, + ED1C95C51F9B88EF00CAFCA3 /* SSDT-PrtSc-Remap.dsl */, + CEA35504257C6E3500E17556 /* SSDT-Swap-CommandOption.dsl */, EDB891282041BB12004DB536 /* SSDT-Swap-LeftControlCapsLock.dsl */, EDAD29471FE8772400A93537 /* SSDT-Swap-LeftControlCommand.dsl */, EDD95559208E2B640031D99E /* SSDT-Thinkpad_Clickpad.dsl */, @@ -358,18 +352,12 @@ path = Docs; sourceTree = ""; }; - CE7F451722E8AC45003F7971 /* Library */ = { + CE8DA1C3251839B2008C44E8 /* Frameworks */ = { isa = PBXGroup; children = ( - CE7F451822E8AC59003F7971 /* libkmod.a */, - CED643F022E8AF27006CDE9B /* LegacyIOHIDDevice.h */, - CE86CCFF22E8BDE000B32BE3 /* LegacyIOHIKeyboard.h */, - CE86CD0022E8BDE000B32BE3 /* LegacyIOHIPointing.h */, - CE86CCF522E8BC9800B32BE3 /* LegacyIOService.h */, - CE86CCFA22E8BCEE00B32BE3 /* LegacyLibkernMacros.h */, - CE86CCF022E8B0F800B32BE3 /* compat.cpp */, - ); - path = Library; + CE8DA1C4251839B2008C44E8 /* libkmod.a */, + ); + name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ @@ -380,13 +368,9 @@ buildActionMask = 2147483647; files = ( 84833FA3161B627D00845294 /* ApplePS2Device.h in Headers */, - CE86CD0122E8BDE000B32BE3 /* LegacyIOHIKeyboard.h in Headers */, - CE86CCF622E8BC9800B32BE3 /* LegacyIOService.h in Headers */, 84833FA5161B627D00845294 /* ApplePS2KeyboardDevice.h in Headers */, - CE86CCFB22E8BCEE00B32BE3 /* LegacyLibkernMacros.h in Headers */, 84833FA7161B627D00845294 /* ApplePS2MouseDevice.h in Headers */, 84833FC3161B6A7E00845294 /* VoodooPS2Controller.h in Headers */, - CE86CD0522E8BDE000B32BE3 /* LegacyIOHIPointing.h in Headers */, 84DD197C162D496E0044D061 /* AppleACPIPS2Nub.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -395,11 +379,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - CE86CCFC22E8BCEE00B32BE3 /* LegacyLibkernMacros.h in Headers */, 84833FAA161B629500845294 /* ApplePS2ToADBMap.h in Headers */, - CE86CD0222E8BDE000B32BE3 /* LegacyIOHIKeyboard.h in Headers */, - CE86CCF722E8BC9800B32BE3 /* LegacyIOService.h in Headers */, - CE86CD0622E8BDE000B32BE3 /* LegacyIOHIPointing.h in Headers */, 84833FC2161B69C700845294 /* VoodooPS2Keyboard.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -409,10 +389,6 @@ buildActionMask = 2147483647; files = ( 84833FC1161B69B800845294 /* VoodooPS2Mouse.h in Headers */, - CE86CD0322E8BDE000B32BE3 /* LegacyIOHIKeyboard.h in Headers */, - CE86CCFD22E8BCEE00B32BE3 /* LegacyLibkernMacros.h in Headers */, - CE86CCF822E8BC9800B32BE3 /* LegacyIOService.h in Headers */, - CE86CD0722E8BDE000B32BE3 /* LegacyIOHIPointing.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -420,16 +396,15 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - CE86CD0422E8BDE000B32BE3 /* LegacyIOHIKeyboard.h in Headers */, 356B896323007F4F0042F30F /* VoodooInputEvent.h in Headers */, + 9828A93024A2B6C200550FAA /* VoodooPS2Elan.h in Headers */, 356B896223007F4F0042F30F /* VoodooInputMessages.h in Headers */, - CE86CCF922E8BC9800B32BE3 /* LegacyIOService.h in Headers */, - CE86CD0822E8BDE000B32BE3 /* LegacyIOHIPointing.h in Headers */, - CE86CCFE22E8BCEE00B32BE3 /* LegacyLibkernMacros.h in Headers */, + EE914A912C952F0D0023CFE0 /* VoodooPS2SMBusDevice.h in Headers */, 356B896423007F4F0042F30F /* VoodooInputTransducer.h in Headers */, 84833FB2161B62A900845294 /* VoodooPS2ALPSGlidePoint.h in Headers */, 356B896523007F4F0042F30F /* MultitouchHelpers.h in Headers */, 84833FB4161B62A900845294 /* VoodooPS2SentelicFSP.h in Headers */, + 4CC27EB3251FBC7700824FE1 /* VoodooPS2TrackpadCommon.h in Headers */, 84833FB6161B62A900845294 /* VoodooPS2SynapticsTouchPad.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -524,7 +499,7 @@ 84167808161B55B2002C60E6 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1140; + LastUpgradeCheck = 1320; ORGANIZATIONNAME = Acidanthera; }; buildConfigurationList = 8416780B161B55B2002C60E6 /* Build configuration list for PBXProject "VoodooPS2Controller" */; @@ -659,7 +634,6 @@ 840F104A16EFE42600E8C116 /* ApplePS2Device.cpp in Sources */, 84EB0AE316F0AD9300016108 /* ApplePS2KeyboardDevice.cpp in Sources */, 84EB0AE516F0AD9600016108 /* ApplePS2MouseDevice.cpp in Sources */, - CE86CCF122E8B0F800B32BE3 /* compat.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -668,7 +642,6 @@ buildActionMask = 2147483647; files = ( 84167836161B5613002C60E6 /* VoodooPS2Keyboard.cpp in Sources */, - CE86CCF222E8B0F800B32BE3 /* compat.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -677,7 +650,6 @@ buildActionMask = 2147483647; files = ( 8416784A161B56A2002C60E6 /* VoodooPS2Mouse.cpp in Sources */, - CE86CCF322E8B0F800B32BE3 /* compat.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -686,8 +658,9 @@ buildActionMask = 2147483647; files = ( 84833FB1161B62A900845294 /* VoodooPS2ALPSGlidePoint.cpp in Sources */, - CE86CCF422E8B0F800B32BE3 /* compat.cpp in Sources */, + 9828A92F24A2B6C200550FAA /* VoodooPS2Elan.cpp in Sources */, 84833FB3161B62A900845294 /* VoodooPS2SentelicFSP.cpp in Sources */, + EE914A902C952F0D0023CFE0 /* VoodooPS2SMBusDevice.cpp in Sources */, 84833FB5161B62A900845294 /* VoodooPS2SynapticsTouchPad.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -752,6 +725,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = x86_64; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++1y"; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; @@ -795,9 +769,11 @@ GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_PARAMETER = NO; GCC_WARN_UNUSED_VARIABLE = YES; + KERNEL_EXTENSION_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; + KERNEL_FRAMEWORK_HEADERS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; "LLVM_LTO[arch=x86_64]" = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MODULE_VERSION = 2.1.5; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MODULE_VERSION = 2.3.8; ONLY_ACTIVE_ARCH = YES; "OTHER_LDFLAGS[arch=x86_64]" = "-dead_strip"; PRODUCT_NAME = VoodooPS2Controller; @@ -811,6 +787,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = x86_64; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++1y"; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; @@ -847,9 +824,11 @@ GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_PARAMETER = NO; GCC_WARN_UNUSED_VARIABLE = YES; + KERNEL_EXTENSION_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; + KERNEL_FRAMEWORK_HEADERS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; "LLVM_LTO[arch=x86_64]" = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MODULE_VERSION = 2.1.5; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MODULE_VERSION = 2.3.8; "OTHER_LDFLAGS[arch=x86_64]" = "-dead_strip"; PRODUCT_NAME = VoodooPS2Controller; SDKROOT = macosx; @@ -861,18 +840,22 @@ 84167825161B55B2002C60E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ANALYZER_DEADCODE_DEADSTORES = YES; + CLANG_ANALYZER_DIVIDE_BY_ZERO = YES; + CLANG_ANALYZER_NULL_DEREFERENCE = YES; CLANG_ENABLE_OBJC_WEAK = YES; COMBINE_HIDPI_IMAGES = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; INFOPLIST_FILE = "VoodooPS2Controller/VoodooPS2Controller-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)$(LOCAL_LIBRARY_DIR)", + "$(PROJECT_DIR)/MacKernelSDK/Library/x86_64", ); MODULE_NAME = as.acidanthera.driver.VoodooPS2Controller; OTHER_CFLAGS = "-fno-stack-protector"; PRODUCT_BUNDLE_IDENTIFIER = as.acidanthera.voodoo.driver.PS2Controller; PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = "non-global"; }; @@ -887,7 +870,7 @@ INFOPLIST_FILE = "VoodooPS2Controller/VoodooPS2Controller-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)$(LOCAL_LIBRARY_DIR)", + "$(PROJECT_DIR)/MacKernelSDK/Library/x86_64", ); MODULE_NAME = as.acidanthera.driver.VoodooPS2Controller; OTHER_CFLAGS = "-fno-stack-protector"; @@ -901,6 +884,9 @@ 84167839161B5613002C60E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ANALYZER_DEADCODE_DEADSTORES = YES; + CLANG_ANALYZER_DIVIDE_BY_ZERO = YES; + CLANG_ANALYZER_NULL_DEREFERENCE = YES; CLANG_ENABLE_OBJC_WEAK = YES; COMBINE_HIDPI_IMAGES = YES; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; @@ -908,12 +894,13 @@ INFOPLIST_FILE = "VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)$(LOCAL_LIBRARY_DIR)", + "$(PROJECT_DIR)/MacKernelSDK/Library/x86_64", ); MODULE_NAME = as.acidanthera.driver.VoodooPS2Keyboard; OTHER_CFLAGS = "-fno-stack-protector"; PRODUCT_BUNDLE_IDENTIFIER = as.acidanthera.voodoo.driver.PS2Keyboard; PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = "non-global"; }; @@ -929,7 +916,7 @@ INFOPLIST_FILE = "VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)$(LOCAL_LIBRARY_DIR)", + "$(PROJECT_DIR)/MacKernelSDK/Library/x86_64", ); MODULE_NAME = as.acidanthera.driver.VoodooPS2Keyboard; OTHER_CFLAGS = "-fno-stack-protector"; @@ -943,6 +930,9 @@ 8416784D161B56A2002C60E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ANALYZER_DEADCODE_DEADSTORES = YES; + CLANG_ANALYZER_DIVIDE_BY_ZERO = YES; + CLANG_ANALYZER_NULL_DEREFERENCE = YES; CLANG_ENABLE_OBJC_WEAK = YES; COMBINE_HIDPI_IMAGES = YES; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; @@ -950,12 +940,13 @@ INFOPLIST_FILE = "VoodooPS2Mouse/VoodooPS2Mouse-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)$(LOCAL_LIBRARY_DIR)", + "$(PROJECT_DIR)/MacKernelSDK/Library/x86_64", ); MODULE_NAME = as.acidanthera.driver.VoodooPS2Mouse; OTHER_CFLAGS = "-fno-stack-protector"; PRODUCT_BUNDLE_IDENTIFIER = as.acidanthera.voodoo.driver.PS2Mouse; PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = "non-global"; }; @@ -971,7 +962,7 @@ INFOPLIST_FILE = "VoodooPS2Mouse/VoodooPS2Mouse-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)$(LOCAL_LIBRARY_DIR)", + "$(PROJECT_DIR)/MacKernelSDK/Library/x86_64", ); MODULE_NAME = as.acidanthera.driver.VoodooPS2Mouse; OTHER_CFLAGS = "-fno-stack-protector"; @@ -985,6 +976,9 @@ 84167861161B56C4002C60E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ANALYZER_DEADCODE_DEADSTORES = YES; + CLANG_ANALYZER_DIVIDE_BY_ZERO = YES; + CLANG_ANALYZER_NULL_DEREFERENCE = YES; CLANG_ENABLE_OBJC_WEAK = YES; COMBINE_HIDPI_IMAGES = YES; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/VoodooPS2Controller.kext/Contents/PlugIns"; @@ -993,12 +987,13 @@ INFOPLIST_FILE = "VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)$(LOCAL_LIBRARY_DIR)", + "$(PROJECT_DIR)/MacKernelSDK/Library/x86_64", ); MODULE_NAME = as.acidanthera.driver.VoodooPS2Trackpad; OTHER_CFLAGS = "-fno-stack-protector"; PRODUCT_BUNDLE_IDENTIFIER = as.acidanthera.voodoo.driver.PS2Trackpad; PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = "non-global"; }; @@ -1015,7 +1010,7 @@ INFOPLIST_FILE = "VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)$(LOCAL_LIBRARY_DIR)", + "$(PROJECT_DIR)/MacKernelSDK/Library/x86_64", ); MODULE_NAME = as.acidanthera.driver.VoodooPS2Trackpad; OTHER_CFLAGS = "-fno-stack-protector"; diff --git a/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme index d9cf0273..bc865335 100644 --- a/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme +++ b/VoodooPS2Controller.xcodeproj/xcshareddata/xcschemes/VoodooPS2Controller.xcscheme @@ -1,6 +1,6 @@ #include #include @@ -86,6 +86,7 @@ class EXPORT AppleACPIPS2Nub: public IOPlatformDevice public: bool start(IOService *provider) override; + void stop(IOService *provider) override; /*! @method findMouseDevice @abstract Locates the mouse nub in the IORegistry diff --git a/VoodooPS2Controller/ApplePS2Device.cpp b/VoodooPS2Controller/ApplePS2Device.cpp index 775b44ef..cb5d009e 100644 --- a/VoodooPS2Controller/ApplePS2Device.cpp +++ b/VoodooPS2Controller/ApplePS2Device.cpp @@ -29,11 +29,37 @@ OSDefineMetaClassAndStructors(ApplePS2Device, IOService); // ApplePS2Device Class Implementation // +bool ApplePS2Device::init(size_t port) +{ + _port = port; + return super::init(); +} + bool ApplePS2Device::attach(IOService * provider) { if (!super::attach(provider)) return false; + _workloop = IOWorkLoop::workLoop(); + _interruptSource = IOInterruptEventSource::interruptEventSource(this, + OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Device::packetAction)); + + if (!_interruptSource || !_workloop) + { + OSSafeReleaseNULL(_workloop); + OSSafeReleaseNULL(_interruptSource); + return false; + } + + if (_workloop->addEventSource(_interruptSource) != kIOReturnSuccess) + { + OSSafeReleaseNULL(_workloop); + OSSafeReleaseNULL(_interruptSource); + return false; + } + + setProperty(kPortKey, _port, 8); + assert(_controller == 0); _controller = (ApplePS2Controller*)provider; _controller->retain(); @@ -49,6 +75,14 @@ void ApplePS2Device::detach( IOService * provider ) _controller->release(); _controller = 0; + if (_interruptSource && _workloop) + { + _workloop->removeEventSource(_interruptSource); + } + + OSSafeReleaseNULL(_interruptSource); + OSSafeReleaseNULL(_workloop); + super::detach(provider); } @@ -70,6 +104,7 @@ void ApplePS2Device::freeRequest(PS2Request * request) bool ApplePS2Device::submitRequest(PS2Request * request) { + request->port = _port; return _controller->submitRequest(request); } @@ -77,6 +112,7 @@ bool ApplePS2Device::submitRequest(PS2Request * request) void ApplePS2Device::submitRequestAndBlock(PS2Request * request) { + request->port = _port; _controller->submitRequestAndBlock(request); } @@ -102,33 +138,38 @@ void ApplePS2Device::unlock() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ApplePS2Device::installInterruptAction(OSObject * target, - PS2InterruptAction interruptAction, - PS2PacketAction packetAction) + PS2InterruptAction interruptAction, + PS2PacketAction packetAction) { - _controller->installInterruptAction(_deviceType, target, interruptAction, packetAction); + _client = target; + _controller->installInterruptAction(_port); + _interrupt_action = interruptAction; + _packet_action = packetAction; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ApplePS2Device::uninstallInterruptAction() { - _controller->uninstallInterruptAction(_deviceType); + _controller->uninstallInterruptAction(_port); + _interrupt_action = nullptr; + _packet_action = nullptr; + _client = nullptr; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Device::installPowerControlAction( - OSObject * target, - PS2PowerControlAction action) +void ApplePS2Device::installPowerControlAction(OSObject * target, + PS2PowerControlAction action) { - _controller->installPowerControlAction(_deviceType, target, action); + _power_action = action; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ApplePS2Device::uninstallPowerControlAction() { - _controller->uninstallPowerControlAction(_deviceType); + _power_action = nullptr; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -140,7 +181,57 @@ void ApplePS2Device::dispatchMessage(int message, void *data) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +IOReturn ApplePS2Device::startSMBusCompanion(OSDictionary *companionData, UInt8 smbusAddr) +{ + return _controller->startSMBusCompanion(companionData, smbusAddr); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ApplePS2Controller* ApplePS2Device::getController() { return _controller; } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +PS2InterruptResult ApplePS2Device::interruptAction(UInt8 data) +{ + if (_client == nullptr || _interrupt_action == nullptr) + { + return kPS2IR_packetBuffering; + } + + return (*_interrupt_action)(_client, data); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::packetActionInterrupt() +{ + _interruptSource->interruptOccurred(0, 0, 0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::powerAction(UInt32 whatToDo) +{ + if (_client == nullptr || _power_action == nullptr) + { + return; + } + + (*_power_action)(_client, whatToDo); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Device::packetAction(IOInterruptEventSource *, int) +{ + if (_client == nullptr || _packet_action == nullptr) + { + return; + } + + (*_packet_action)(_client); +} diff --git a/VoodooPS2Controller/ApplePS2Device.h b/VoodooPS2Controller/ApplePS2Device.h index 9629d352..03100b5f 100644 --- a/VoodooPS2Controller/ApplePS2Device.h +++ b/VoodooPS2Controller/ApplePS2Device.h @@ -25,8 +25,9 @@ #include #include -#include "LegacyIOService.h" +#include #include +#include #include #ifdef DEBUG_MSG @@ -54,9 +55,12 @@ #define kDP_SetMouseResolution 0xE8 // (mouse) #define kDP_GetMouseInformation 0xE9 // (mouse) #define kDP_SetMouseStreamMode 0xEA // (mouse) +#define kDP_MousePoll 0xEB // (mouse) caller sets number of bytes to receive +#define kDP_MouseResetWrap 0xEC // (mouse) #define kDP_SetKeyboardLEDs 0xED // (keyboard) #define kDP_TestKeyboardEcho 0xEE // (keyboard) #define kDP_GetSetKeyboardASCs 0xF0 // (keyboard) +#define kDP_MouseSetPoll 0xF0 // (mouse) #define kDP_GetId 0xF2 // (keyboard+mouse) #define kDP_SetKeyboardTypematic 0xF3 // (keyboard) #define kDP_SetMouseSampleRate 0xF3 // (mouse) @@ -81,6 +85,7 @@ #define kCP_ReadControllerRAMBase 0x21 // #define kCP_SetCommandByte 0x60 // (keyboard+mouse) #define kCP_WriteControllerRAMBase 0x61 // +#define kCP_TransmitToMuxedMouse 0x90 // (muxed mouse) #define kCP_TestPassword 0xA4 // #define kCP_GetPassword 0xA5 // #define kCP_VerifyPassword 0xA6 // @@ -140,6 +145,7 @@ #define kSC_Resend 0xFE // request to resend keybd cmd #define kSC_Reset 0xAA // the keyboard/mouse has reset #define kSC_UpBit 0x80 // OR'd in if key below is released +#define kSC_ID 0x00 // PSMOUSE_RET_ID // // Scan Codes for some modifier keys. @@ -266,20 +272,13 @@ class RingBuffer // o Description: Writes the byte in the In Field to the data port (60h). // o In Field: Holds byte that should be written. // -// o kPS2C_WriteCommandPort: -// o Description: Writes the byte in the In Field to the command port (64h). -// o In Field: Holds byte that should be written. -// enum PS2CommandEnum { kPS2C_ReadDataPort, kPS2C_ReadDataPortAndCompare, kPS2C_WriteDataPort, - kPS2C_WriteCommandPort, - kPS2C_SendMouseCommandAndCompareAck, - kPS2C_ReadMouseDataPort, - kPS2C_ReadMouseDataPortAndCompare, + kPS2C_SendCommandAndCompareAck, kPS2C_FlushDataPort, kPS2C_SleepMS, kPS2C_ModifyCommandByte, @@ -411,6 +410,7 @@ struct PS2Request { ::operator delete(p); } public: + size_t port; UInt8 commandsCount; void * completionTarget; PS2CompletionAction completionAction; @@ -516,6 +516,11 @@ typedef void (*PS2PowerControlAction)(void * target, UInt32 whatToDo); // Published property for devices to express interest in receiving messages #define kDeliverNotifications "RM,deliverNotifications" +#define kSmbusCompanion "VoodooSMBusCompanionDevice" + +// Published property for device nub port location +#define kPortKey "Port Num" + typedef void (*PS2MessageAction)(void* target, int message, void* data); enum @@ -524,13 +529,22 @@ enum kPS2M_setDisableTouchpad = iokit_vendor_specific_msg(100), // set disable/enable touchpad (data is bool*) kPS2M_getDisableTouchpad = iokit_vendor_specific_msg(101), // get disable/enable touchpad (data is bool*) kPS2M_notifyKeyPressed = iokit_vendor_specific_msg(102), // notify of time key pressed (data is PS2KeyInfo*) + + kPS2M_notifyKeyTime = iokit_vendor_specific_msg(110), // notify of timestamp a non-modifier key was pressed (data is uint64_t*) + + kPS2M_resetTouchpad = iokit_vendor_specific_msg(151), // Force touchpad reset (data is int*) - kPS2M_notifyKeyTime = iokit_vendor_specific_msg(110) // notify of timestamp a non-modifier key was pressed (data is uint64_t*) + // from sensor (such as yoga mode indicator) to keyboard + kPS2K_setKeyboardStatus = iokit_vendor_specific_msg(200), // set disable/enable keyboard (data is bool*) + kPS2K_getKeyboardStatus = iokit_vendor_specific_msg(201), // get disable/enable keyboard (data is bool*) + + // from OEM ACPI (WMI) events to keyboard + kPS2K_notifyKeystroke = iokit_vendor_specific_msg(202), // notify of key press (data is PS2KeyInfo*), in the opposite direction of kPS2M_notifyKeyPressed }; typedef struct PS2KeyInfo { - int64_t time; + uint64_t time; UInt16 adbKeyCode; bool goingDown; bool eatKey; @@ -547,17 +561,6 @@ enum kPS2C_EnableDevice }; -// PS/2 device types. - -typedef enum -{ - kDT_Keyboard, - kDT_Mouse, -#if WATCHDOG_TIMER - kDT_Watchdog, -#endif -} PS2DeviceType; - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ApplePS2Device Class Declaration // @@ -571,9 +574,10 @@ class EXPORT ApplePS2Device : public IOService protected: ApplePS2Controller* _controller; - PS2DeviceType _deviceType; + size_t _port; public: + bool init(size_t port); bool attach(IOService * provider) override; void detach(IOService * provider) override; @@ -594,9 +598,15 @@ class EXPORT ApplePS2Device : public IOService virtual void installPowerControlAction(OSObject *, PS2PowerControlAction); virtual void uninstallPowerControlAction(); + + virtual PS2InterruptResult interruptAction(UInt8); + virtual void packetActionInterrupt(); + void packetAction(IOInterruptEventSource *, int); + virtual void powerAction(UInt32); // Messaging virtual void dispatchMessage(int message, void *data); + virtual IOReturn startSMBusCompanion(OSDictionary *companionData, UInt8 smbusAddr); // Exclusive access (command byte contention) @@ -605,6 +615,15 @@ class EXPORT ApplePS2Device : public IOService // Controller access virtual ApplePS2Controller* getController(); +private: + PS2InterruptAction _interrupt_action {nullptr}; + PS2PacketAction _packet_action {nullptr}; + PS2PowerControlAction _power_action {nullptr}; + + IOWorkLoop * _workloop {nullptr}; + IOInterruptEventSource * _interruptSource {nullptr}; + + OSObject* _client {nullptr}; }; #if 0 // Note: Now using architecture/i386/pio.h (see above) diff --git a/VoodooPS2Controller/ApplePS2KeyboardDevice.cpp b/VoodooPS2Controller/ApplePS2KeyboardDevice.cpp index 6ea687b1..f6eba164 100644 --- a/VoodooPS2Controller/ApplePS2KeyboardDevice.cpp +++ b/VoodooPS2Controller/ApplePS2KeyboardDevice.cpp @@ -28,13 +28,3 @@ // OSDefineMetaClassAndStructors(ApplePS2KeyboardDevice, ApplePS2Device); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool ApplePS2KeyboardDevice::init() -{ - bool result = super::init(); - _deviceType = kDT_Keyboard; - return result; -} - diff --git a/VoodooPS2Controller/ApplePS2KeyboardDevice.h b/VoodooPS2Controller/ApplePS2KeyboardDevice.h index 9f9435f8..6f206218 100644 --- a/VoodooPS2Controller/ApplePS2KeyboardDevice.h +++ b/VoodooPS2Controller/ApplePS2KeyboardDevice.h @@ -31,9 +31,6 @@ class EXPORT ApplePS2KeyboardDevice : public ApplePS2Device { typedef ApplePS2Device super; OSDeclareDefaultStructors(ApplePS2KeyboardDevice); - -public: - bool init() override; }; #endif /* !_APPLEPS2KEYBOARDDEVICE_H */ diff --git a/VoodooPS2Controller/ApplePS2MouseDevice.cpp b/VoodooPS2Controller/ApplePS2MouseDevice.cpp index b9fb5f28..3892629b 100644 --- a/VoodooPS2Controller/ApplePS2MouseDevice.cpp +++ b/VoodooPS2Controller/ApplePS2MouseDevice.cpp @@ -28,13 +28,3 @@ // OSDefineMetaClassAndStructors(ApplePS2MouseDevice, ApplePS2Device); - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool ApplePS2MouseDevice::init() -{ - bool result = super::init(); - _deviceType = kDT_Mouse; - return result; -} - diff --git a/VoodooPS2Controller/ApplePS2MouseDevice.h b/VoodooPS2Controller/ApplePS2MouseDevice.h index 0af67d37..4594e8bd 100644 --- a/VoodooPS2Controller/ApplePS2MouseDevice.h +++ b/VoodooPS2Controller/ApplePS2MouseDevice.h @@ -31,9 +31,6 @@ class EXPORT ApplePS2MouseDevice : public ApplePS2Device { typedef ApplePS2Device super; OSDeclareDefaultStructors(ApplePS2MouseDevice); - -public: - bool init() override; }; #endif /* !_APPLEPS2MOUSEDEVICE_H */ diff --git a/VoodooPS2Controller/VoodooPS2Controller.cpp b/VoodooPS2Controller/VoodooPS2Controller.cpp index f4a3fa34..0dc60a59 100644 --- a/VoodooPS2Controller/VoodooPS2Controller.cpp +++ b/VoodooPS2Controller/VoodooPS2Controller.cpp @@ -23,15 +23,12 @@ #define DISABLE_CLOCKS_IRQS_BEFORE_SLEEP 1 #define FULL_INIT_AFTER_WAKE 1 -#include "LegacyIOService.h" +#include #include #include #include -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winconsistent-missing-override" #include -#pragma clang diagnostic pop #include "ApplePS2KeyboardDevice.h" #include "ApplePS2MouseDevice.h" @@ -64,7 +61,7 @@ void ApplePS2Controller::interruptHandlerMouse(OSObject*, void* refCon, IOServic #if HANDLE_INTERRUPT_DATA_LATER me->_interruptSourceMouse->interruptOccurred(0, 0, 0); #else - me->handleInterrupt(kDT_Mouse); + me->handleInterrupt(); #endif } @@ -138,7 +135,7 @@ void ApplePS2Controller::interruptHandlerKeyboard(OSObject*, void* refCon, IOSer #if HANDLE_INTERRUPT_DATA_LATER me->_interruptSourceKeyboard->interruptOccurred(0, 0, 0); #else - me->handleInterrupt(kDT_Keyboard); + me->handleInterrupt(); #endif #endif //DEBUGGER_SUPPORT @@ -151,7 +148,7 @@ void ApplePS2Controller::interruptHandlerKeyboard(OSObject*, void* refCon, IOSer void ApplePS2Controller::onWatchdogTimer() { if (!_ignoreInterrupts) - handleInterrupt(kDT_Watchdog); + handleInterrupt(true); _watchdogTimer->setTimeoutMS(kWatchdogTimerInterval); } @@ -161,20 +158,19 @@ void ApplePS2Controller::onWatchdogTimer() #if !HANDLE_INTERRUPT_DATA_LATER -void ApplePS2Controller::handleInterrupt(PS2DeviceType deviceType) +void ApplePS2Controller::handleInterrupt(bool watchdog) { - ////IOLog("%s:handleInterrupt(%s)\n", getName(), deviceType == kDT_Keyboard ? "kDT_Keyboard" : deviceType == kDT_Watchdog ? "kDT_Watchdog" : "kDT_Mouse"); - // Loop only while there is data currently on the input stream. - - bool wakeMouse = false; - bool wakeKeyboard = false; + bool wakePort[kPS2MuxMaxIdx] {}; + while (1) { // while getting status and reading the port, no interrupts... bool enable = ml_set_interrupts_enabled(false); + size_t port = kPS2KbdIdx; IODelay(kDataDelay); UInt8 status = inb(kCommandPort); + if (!(status & kOutputReady)) { // no data available, so break out and return @@ -184,13 +180,13 @@ void ApplePS2Controller::handleInterrupt(PS2DeviceType deviceType) #if WATCHDOG_TIMER // do not process mouse data in watchdog timer - if (deviceType == kDT_Watchdog && (status & kMouseData)) + if (watchdog && (status & kMouseData)) { ml_set_interrupts_enabled(enable); break; } #endif - + // read the data IODelay(kDataDelay); UInt8 data = inb(kDataPort); @@ -198,59 +194,53 @@ void ApplePS2Controller::handleInterrupt(PS2DeviceType deviceType) // now ok for interrupts, we have read status, and found data... // (it does not matter [too much] if keyboard data is delivered out of order) ml_set_interrupts_enabled(enable); - + #if WATCHDOG_TIMER //REVIEW: remove this debug eventually... - if (deviceType == kDT_Watchdog) - IOLog("%s:handleInterrupt(kDT_Watchdog): %s = %02x\n", getName(), status & kMouseData ? "mouse" : "keyboard", data); + if (watchdog) + IOLog("%s:handleInterrupt(kDT_Watchdog): %s = %02x\n", getName(), port > kPS2KbdIdx ? "mouse" : "keyboard", data); #endif - if (status & kMouseData) - { - // Dispatch the data to the mouse driver. - if (kPS2IR_packetReady == _dispatchDriverInterrupt(kDT_Mouse, data)) - wakeMouse = true; - } - else + + port = getPortFromStatus(status); + if (kPS2IR_packetReady == _dispatchDriverInterrupt(port, data)) { - // Dispatch the data to the keyboard driver. - if (kPS2IR_packetReady == _dispatchDriverInterrupt(kDT_Keyboard, data)) - wakeKeyboard = true; + wakePort[port] = true; } } // while (forever) // wake up workloop based mouse interrupt source if needed - if (wakeMouse) - _interruptSourceMouse->interruptOccurred(0, 0, 0); - // wake up workloop based keyboard interrupt source if needed - if (wakeKeyboard) - _interruptSourceKeyboard->interruptOccurred(0, 0, 0); + for (size_t i = kPS2KbdIdx; i < _nubsCount; i++) { + if (wakePort[i]) + { + _devices[i]->packetActionInterrupt(); + } + } } #else // HANDLE_INTERRUPT_DATA_LATER -void ApplePS2Controller::handleInterrupt(PS2DeviceType deviceType) +void ApplePS2Controller::handleInterrupt(bool watchdog) { - ////IOLog("%s:handleInterrupt(%s)\n", getName(), deviceType == kDT_Keyboard ? "kDT_Keyboard" : deviceType == kDT_Watchdog ? "kDT_Watchdog" : "kDT_Mouse"); - // Loop only while there is data currently on the input stream. UInt8 status; + size_t port; IODelay(kDataDelay); while ((status = inb(kCommandPort)) & kOutputReady) { #if WATCHDOG_TIMER - if (deviceType == kDT_Watchdog && (status & kMouseData)) + if (watchdog && (status & kMouseData)) break; #endif - IODelay(kDataDelay); UInt8 data = inb(kDataPort); + port = getPortFromStatus(status); #if WATCHDOG_TIMER //REVIEW: remove this debug eventually... - if (deviceType == kDT_Watchdog) - IOLog("%s:handleInterrupt(kDT_Watchdog): %s = %02x\n", getName(), status & kMouseData ? "mouse" : "keyboard", data); + if (watchdog) + IOLog("%s:handleInterrupt(kDT_Watchdog): %s = %02x\n", getName(), port > kPS2KbdIdx ? "mouse" : "keyboard", data); #endif - dispatchDriverInterrupt(status & kMouseData ? kDT_Mouse : kDT_Keyboard, data); + dispatchDriverInterrupt(port, data); IODelay(kDataDelay); } } @@ -290,6 +280,10 @@ bool ApplePS2Controller::init(OSDictionary* dict) _deliverNotification = OSSymbol::withCString(kDeliverNotifications); if (_deliverNotification == NULL) return false; + + _smbusCompanion = OSSymbol::withCString(kSmbusCompanion); + if (_smbusCompanion == NULL) + return false; queue_init(&_requestQueue); @@ -379,7 +373,7 @@ IOReturn ApplePS2Controller::setProperties(OSObject* props) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Controller::resetController(void) +void ApplePS2Controller::resetController() { _suppressTimeout = true; UInt8 commandByte; @@ -387,29 +381,27 @@ void ApplePS2Controller::resetController(void) // Disable keyboard and mouse writeCommandPort(kCP_DisableKeyboardClock); writeCommandPort(kCP_DisableMouseClock); - // Flush any data - while ( inb(kCommandPort) & kOutputReady ) - { - IODelay(kDataDelay); - inb(kDataPort); - IODelay(kDataDelay); - } - writeCommandPort(kCP_EnableMouseClock); + flushDataPort(); + if (!_kbdOnly) + writeCommandPort(kCP_EnableMouseClock); writeCommandPort(kCP_EnableKeyboardClock); // Read current command writeCommandPort(kCP_GetCommandByte); - commandByte = readDataPort(kDT_Keyboard); + commandByte = readDataPort(kPS2KbdIdx); DEBUG_LOG("%s: initial commandByte = %02x\n", getName(), commandByte); // Issue Test Controller to try to reset device writeCommandPort(kCP_TestController); - readDataPort(kDT_Keyboard); - readDataPort(kDT_Mouse); + readDataPort(kPS2KbdIdx); + if (!_kbdOnly) + readDataPort(kPS2AuxIdx); // Issue Test Keyboard Port to try to reset device writeCommandPort(kCP_TestKeyboardPort); - readDataPort(kDT_Keyboard); + readDataPort(kPS2KbdIdx); // Issue Test Mouse Port to try to reset device - writeCommandPort(kCP_TestMousePort); - readDataPort(kDT_Mouse); + if (!_kbdOnly) { + writeCommandPort(kCP_TestMousePort); + readDataPort(kPS2AuxIdx); + } _suppressTimeout = false; // @@ -419,25 +411,48 @@ void ApplePS2Controller::resetController(void) // asynchronous data arrival for key/mouse events). We call the read/write // port routines directly, since no other thread will conflict with us. // - commandByte &= ~(kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ | kCB_DisableMouseClock | kCB_DisableMouseClock); - ////commandByte |= kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ; + if (!_kbdOnly) + commandByte &= ~(kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ | kCB_DisableMouseClock | kCB_DisableKeyboardClock); + else + commandByte &= ~(kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ | kCB_DisableKeyboardClock); commandByte |= kCB_TranslateMode; writeCommandPort(kCP_SetCommandByte); writeDataPort(commandByte); DEBUG_LOG("%s: new commandByte = %02x\n", getName(), commandByte); - - writeDataPort(kDP_SetDefaultsAndDisable); - readDataPort(kDT_Keyboard); // (discard acknowledge; success irrelevant) - - writeCommandPort(kCP_TransmitToMouse); +} + +// -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::resetDevices() +{ + // Reset keyboard writeDataPort(kDP_SetDefaultsAndDisable); - readDataPort(kDT_Mouse); // (discard acknowledge; success irrelevant) - - // - // Clear out garbage in the controller's input streams, before starting up - // the work loop. - // + readDataPort(kPS2KbdIdx); // (discard acknowledge; success irrelevant) + + if (!_muxPresent) + { + // Reset aux device + if(!_kbdOnly){ + writeCommandPort(kCP_TransmitToMouse); + writeDataPort(kDP_SetDefaultsAndDisable); + readDataPort(kPS2AuxIdx); // (discard acknowledge; success irrelevant) + } + return; + } + // Reset all muxed devices + for (size_t i = 0; i < PS2_MUX_PORTS; i++) + { + writeCommandPort(kCP_TransmitToMuxedMouse + i); + writeDataPort(kDP_SetDefaultsAndDisable); + readDataPort(kPS2AuxIdx + i); // (discard acknowledge; success irrelevant) + } +} + +// -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::flushDataPort() +{ while ( inb(kCommandPort) & kOutputReady ) { IODelay(kDataDelay); @@ -448,6 +463,36 @@ void ApplePS2Controller::resetController(void) // -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool ApplePS2Controller::setMuxMode(bool enable) +{ + UInt8 param = kDP_MuxCmd; + + writeCommandPort(kCP_WriteMouseOutputBuffer); + writeDataPort(param); + if (readDataPort(kPS2AuxIdx) != param) + return false; + + param = enable ? kDP_EnableMuxCmd1 : kDP_DisableMuxCmd1; + writeCommandPort(kCP_WriteMouseOutputBuffer); + writeDataPort(param); + if (readDataPort(kPS2AuxIdx) != param) + return false; + + param = enable ? kDP_GetMuxVersion : kDP_DisableMuxCmd2; + writeCommandPort(kCP_WriteMouseOutputBuffer); + writeDataPort(param); + UInt8 ver = readDataPort(kPS2AuxIdx); + + // We want the version on enable, and original command on disable + if ((enable && ver == param) || + (!enable && ver != param)) + return false; + + return true; +} + +// -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + bool ApplePS2Controller::start(IOService * provider) { DEBUG_LOG("ApplePS2Controller::start entered...\n"); @@ -474,17 +519,9 @@ bool ApplePS2Controller::start(IOService * provider) queue_enter(&_keyboardQueueUnused, &_keyboardQueueAlloc[index], KeyboardQueueElement *, chain); #endif //DEBUGGER_SUPPORT - // Note: I don't think this newIRQLayout thing is used at all - // -- our provider is PS2Nub and the PS2 nub we use does not set this flag - // -- in addition it only supports the LEGACY interrupt specifiers - // #ifdef to eliminate for now... -#ifdef NEWIRQ - if (provider->getProperty("newIRQLayout")) { // turbo - IOLog("Using new IRQ layout 0,1\n"); - _newIRQLayout = true; - } -#endif - + + PE_parse_boot_argn("ps2kbdonly", &_kbdOnly, sizeof(_kbdOnly)); + // // Reset and clean the 8042 keyboard/mouse controller. // @@ -494,6 +531,29 @@ bool ApplePS2Controller::start(IOService * provider) resetController(); } + // + // Enable "Active PS/2 Multiplexing" if it exists. + // This creates 4 Aux ports which pointing devices may connect to. + // + + if (_kbdOnly) { + _muxPresent = false; + _nubsCount = 1; + } else { + _muxPresent = setMuxMode(true); + _nubsCount = _muxPresent ? kPS2MuxMaxIdx : kPS2AuxMaxIdx; + } + + // + // Reset attached devices and clear out garbage in the controller's input streams, + // before starting up the work loop. + // + + if (_resetControllerFlag & RESET_CONTROLLER_ON_BOOT) { + resetDevices(); + flushDataPort(); + } + // // Use a spin lock to protect the client async request queue. // @@ -505,44 +565,48 @@ bool ApplePS2Controller::start(IOService * provider) // Initialize our work loop, our command gate, and our interrupt event // sources. The work loop can accept requests after this step. // + - _workLoop = IOWorkLoop::workLoop(); + _workLoop = IOWorkLoop::workLoop(); + _cmdGate = IOCommandGate::commandGate(this); + _interruptSourceQueue = IOInterruptEventSource::interruptEventSource( this, + OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::processRequestQueue)); + + + if ( !_workLoop || + !_interruptSourceQueue || + !_cmdGate) goto fail; + #if HANDLE_INTERRUPT_DATA_LATER _interruptSourceMouse = IOInterruptEventSource::interruptEventSource( this, - OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::interruptOccurred)); - _interruptSourceKeyboard = IOInterruptEventSource::interruptEventSource( this, OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::interruptOccurred)); -#else - _interruptSourceMouse = IOInterruptEventSource::interruptEventSource( this, - OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::packetReadyMouse)); _interruptSourceKeyboard = IOInterruptEventSource::interruptEventSource( this, - OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::packetReadyKeyboard)); -#endif - _interruptSourceQueue = IOInterruptEventSource::interruptEventSource( this, - OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::processRequestQueue)); - _cmdGate = IOCommandGate::commandGate(this); -#if WATCHDOG_TIMER - _watchdogTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &ApplePS2Controller::onWatchdogTimer)); - if (!_watchdogTimer) + OSMemberFunctionCast(IOInterruptEventAction, this, &ApplePS2Controller::interruptOccurred)); + + if ( !_interruptSourceMouse || + !_interruptSourceKeyboard) goto fail; + + if ( _workLoop->addEventSource(_interruptSourceMouse) != kIOReturnSuccess ) + goto fail; + if ( _workLoop->addEventSource(_interruptSourceKeyboard) != kIOReturnSuccess ) goto fail; #endif - - if ( !_workLoop || - !_interruptSourceMouse || - !_interruptSourceKeyboard || - !_interruptSourceQueue || - !_cmdGate) goto fail; if ( _workLoop->addEventSource(_interruptSourceQueue) != kIOReturnSuccess ) goto fail; if ( _workLoop->addEventSource(_cmdGate) != kIOReturnSuccess ) goto fail; - + #if WATCHDOG_TIMER + _watchdogTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &ApplePS2Controller::onWatchdogTimer)); + if (!_watchdogTimer) + goto fail; + if ( _workLoop->addEventSource(_watchdogTimer) != kIOReturnSuccess ) goto fail; _watchdogTimer->setTimeoutMS(kWatchdogTimerInterval); #endif + _interruptSourceQueue->enable(); // @@ -578,48 +642,53 @@ bool ApplePS2Controller::start(IOService * provider) // will query these nubs to determine the existence of the keyboard or mouse, // and should they exist, will attach themselves to the nub as clients. // - - _keyboardDevice = OSTypeAlloc(ApplePS2KeyboardDevice); - if ( !_keyboardDevice || - !_keyboardDevice->init() || - !_keyboardDevice->attach(this) ) + + _devices[kPS2KbdIdx] = OSTypeAlloc(ApplePS2KeyboardDevice); + if ( !_devices[kPS2KbdIdx] || + !_devices[kPS2KbdIdx]->init(kPS2KbdIdx) || + !_devices[kPS2KbdIdx]->attach(this) ) { - OSSafeReleaseNULL(_keyboardDevice); - OSSafeReleaseNULL(_interruptSourceKeyboard); + OSSafeReleaseNULL(_devices[kPS2KbdIdx]); + goto fail; } - - _mouseDevice = OSTypeAlloc(ApplePS2MouseDevice); - if ( !_mouseDevice || - !_mouseDevice->init() || - !_mouseDevice->attach(this) ) + + for (size_t i = kPS2AuxIdx; i < _nubsCount; i++) { - OSSafeReleaseNULL(_mouseDevice); - OSSafeReleaseNULL(_interruptSourceMouse); + _devices[i] = OSTypeAlloc(ApplePS2MouseDevice); + if ( !_devices[i] || + !_devices[i]->init(i) || + !_devices[i]->attach(this) ) + { + OSSafeReleaseNULL(_devices[i]); + goto fail; + } } - - if (_keyboardDevice) - _keyboardDevice->registerService(); - if (_mouseDevice) - _mouseDevice->registerService(); - + + for (size_t i = kPS2KbdIdx; i < _nubsCount; i++) + { + _devices[i]->registerService(); + } + registerService(); propertyMatch = propertyMatching(_deliverNotification, kOSBooleanTrue); if (propertyMatch != NULL) { - IOServiceMatchingNotificationHandler notificationHandler = OSMemberFunctionCast(IOServiceMatchingNotificationHandler, this, &ApplePS2Controller::notificationHandler); + IOServiceMatchingNotificationHandler publishHandler = OSMemberFunctionCast(IOServiceMatchingNotificationHandler, this, &ApplePS2Controller::notificationHandlerPublish); // // Register notifications for availability of any IOService objects wanting to consume our message events // _publishNotify = addMatchingNotification(gIOFirstPublishNotification, propertyMatch, - notificationHandler, + publishHandler, this, 0, 10000); + IOServiceMatchingNotificationHandler terminateHandler = OSMemberFunctionCast(IOServiceMatchingNotificationHandler, this, &ApplePS2Controller::notificationHandlerTerminate); + _terminateNotify = addMatchingNotification(gIOTerminatedNotification, propertyMatch, - notificationHandler, + terminateHandler, this, 0, 10000); @@ -649,8 +718,6 @@ void ApplePS2Controller::stop(IOService * provider) // Ensure that the interrupt handlers have been uninstalled (ie. no clients). assert(!_interruptInstalledKeyboard); assert(!_interruptInstalledMouse); - assert(!_powerControlInstalledKeyboard); - assert(!_powerControlInstalledMouse); // Free device matching notifiers // remove() releases them @@ -661,24 +728,30 @@ void ApplePS2Controller::stop(IOService * provider) OSSafeReleaseNULL(_notificationServices); // Free the nubs we created. - OSSafeReleaseNULL(_keyboardDevice); - OSSafeReleaseNULL(_mouseDevice); + for (size_t i = 0; i < kPS2MuxMaxIdx; i++) { + OSSafeReleaseNULL(_devices[i]); + } - // Free the event/interrupt sources. - OSSafeReleaseNULL(_interruptSourceKeyboard); - OSSafeReleaseNULL(_interruptSourceMouse); + // Free the event/interrupt sources OSSafeReleaseNULL(_interruptSourceQueue); OSSafeReleaseNULL(_cmdGate); + +#if HANDLE_INTERRUPT_DATA_LATER + OSSafeReleaseNULL(_interruptSourceMouse); + OSSafeReleaseNULL(_interruptSourceKeyboard); +#endif + #if WATCHDOG_TIMER - OSSafeReleaseNULL(_watchdogTimer); + OSSafeReleaseNULL(_watchdogTimer); #endif - + // Free the work loop. OSSafeReleaseNULL(_workLoop); // Free the RMCF configuration cache OSSafeReleaseNULL(_rmcfCache); OSSafeReleaseNULL(_deliverNotification); + OSSafeReleaseNULL(_smbusCompanion); // Free the request queue lock and empty out the request queue. if (_requestQueueLock) @@ -720,10 +793,18 @@ IOWorkLoop * ApplePS2Controller::getWorkLoop() const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Controller::installInterruptAction(PS2DeviceType deviceType, - OSObject * target, - PS2InterruptAction interruptAction, - PS2PacketAction packetAction) +void ApplePS2Controller::enableMuxPorts() +{ + for (size_t i = 0; i < PS2_MUX_PORTS; i++) + { + writeCommandPort(kCP_TransmitToMuxedMouse + i); + writeCommandPort(kCP_EnableMouseClock); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2Controller::installInterruptAction(size_t port) { // // Install the keyboard or mouse interrupt handler. @@ -735,58 +816,42 @@ void ApplePS2Controller::installInterruptAction(PS2DeviceType deviceType, // Is it the keyboard or the mouse interrupt handler that was requested? // We only install it if it is currently uninstalled. - - if (deviceType == kDT_Keyboard && !_interruptInstalledKeyboard && _interruptSourceKeyboard) + + if (port == kPS2KbdIdx && !_interruptInstalledKeyboard) { - target->retain(); - _interruptTargetKeyboard = target; - _interruptActionKeyboard = interruptAction; - _packetActionKeyboard = packetAction; - _workLoop->addEventSource(_interruptSourceKeyboard); DEBUG_LOG("%s: setCommandByte for keyboard interrupt install\n", getName()); setCommandByte(kCB_EnableKeyboardIRQ, 0); -#ifdef NEWIRQ - if (_newIRQLayout) - { // turbo - getProvider()->registerInterrupt(0,0, interruptHandlerKeyboard, this); - getProvider()->enableInterrupt(0); - } else -#endif - { - getProvider()->registerInterrupt(kIRQ_Keyboard,0, interruptHandlerKeyboard, this); - getProvider()->enableInterrupt(kIRQ_Keyboard); - } + + getProvider()->registerInterrupt(kIRQ_Keyboard,0, interruptHandlerKeyboard, this); + getProvider()->enableInterrupt(kIRQ_Keyboard); _interruptInstalledKeyboard = true; } - else if (deviceType == kDT_Mouse && !_interruptInstalledMouse && _interruptSourceMouse) + else if (port > kPS2KbdIdx) { - target->retain(); - _interruptTargetMouse = target; - _interruptActionMouse = interruptAction; - _packetActionMouse = packetAction; - _workLoop->addEventSource(_interruptSourceMouse); - DEBUG_LOG("%s: setCommandByte for mouse interrupt install\n", getName()); - setCommandByte(kCB_EnableMouseIRQ, 0); -#ifdef NEWIRQ - if (_newIRQLayout) - { // turbo - getProvider()->registerInterrupt(1, 0, interruptHandlerMouse, this); - getProvider()->enableInterrupt(1); - } else -#endif + // Only enable interrupts for the first mouse, as this interrupt is used for all of them + if (!_interruptInstalledMouse) { - getProvider()->registerInterrupt(kIRQ_Mouse, 0, interruptHandlerMouse, this); - getProvider()->enableInterrupt(kIRQ_Mouse); + DEBUG_LOG("%s: setCommandByte for mouse interrupt install\n", getName()); + if (_muxPresent) + { + enableMuxPorts(); + } + + setCommandByte(kCB_EnableMouseIRQ, 0); + + getProvider()->registerInterrupt(kIRQ_Mouse, 0, interruptHandlerMouse, this); + getProvider()->enableInterrupt(kIRQ_Mouse); } - - _interruptInstalledMouse = true; + + // Record number of mouses with interrupts + _interruptInstalledMouse++; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Controller::uninstallInterruptAction(PS2DeviceType deviceType) +void ApplePS2Controller::uninstallInterruptAction(size_t port) { // // Uninstall the keyboard or mouse interrupt handler. @@ -799,40 +864,26 @@ void ApplePS2Controller::uninstallInterruptAction(PS2DeviceType deviceType) // Is it the keyboard or the mouse interrupt handler that was requested? // We only uninstall it if it is currently installed. - if (deviceType == kDT_Keyboard && _interruptInstalledKeyboard) + if (port == kPS2KbdIdx && _interruptInstalledKeyboard) { setCommandByte(0, kCB_EnableKeyboardIRQ); -#ifdef NEWIRQ - getProvider()->disableInterrupt(0); - getProvider()->unregisterInterrupt(0); -#else getProvider()->disableInterrupt(kIRQ_Keyboard); getProvider()->unregisterInterrupt(kIRQ_Keyboard); -#endif - _workLoop->removeEventSource(_interruptSourceKeyboard); _interruptInstalledKeyboard = false; - _interruptActionKeyboard = NULL; - _packetActionKeyboard = NULL; - _interruptTargetKeyboard->release(); - _interruptTargetKeyboard = 0; } - else if (deviceType == kDT_Mouse && _interruptInstalledMouse) + else if (port > kPS2KbdIdx) { - setCommandByte(0, kCB_EnableMouseIRQ); -#ifdef NEWIRQ - getProvider()->disableInterrupt(1); - getProvider()->unregisterInterrupt(1); -#else - getProvider()->disableInterrupt(kIRQ_Mouse); - getProvider()->unregisterInterrupt(kIRQ_Mouse); -#endif - _workLoop->removeEventSource(_interruptSourceMouse); - _interruptInstalledMouse = false; - _interruptActionMouse = NULL; - _packetActionMouse = NULL; - _interruptTargetMouse->release(); - _interruptTargetMouse = 0; + assert(_interruptInstalledMouse > 0); + _interruptInstalledMouse--; + + // Only uninstall interrupt once we have no mice installed + if (_interruptInstalledMouse == 0) { + setCommandByte(0, kCB_EnableMouseIRQ); + getProvider()->disableInterrupt(kIRQ_Mouse); + getProvider()->unregisterInterrupt(kIRQ_Mouse); + } + } } @@ -896,7 +947,7 @@ void ApplePS2Controller::setCommandByteGated(PS2Request* request) UInt8 clearBits = request->commands[0].clearBits; ++_ignoreInterrupts; writeCommandPort(kCP_GetCommandByte); - UInt8 oldCommandByte = readDataPort(kDT_Keyboard); + UInt8 oldCommandByte = readDataPort(kPS2KbdIdx); --_ignoreInterrupts; DEBUG_LOG("%s: oldCommandByte = %02x\n", getName(), oldCommandByte); UInt8 newCommandByte = (oldCommandByte | setBits) & ~clearBits; @@ -913,10 +964,11 @@ void ApplePS2Controller::setCommandByteGated(PS2Request* request) bool ApplePS2Controller::submitRequest(PS2Request * request) { + assert(request->port < kPS2MuxMaxIdx); + // // Submit the request to the controller for processing, asynchronously. // - IOLockLock(_requestQueueLock); queue_enter(&_requestQueue, request, PS2Request *, chain); IOLockUnlock(_requestQueueLock); @@ -930,6 +982,8 @@ bool ApplePS2Controller::submitRequest(PS2Request * request) void ApplePS2Controller::submitRequestAndBlock(PS2Request * request) { + assert(request->port < kPS2MuxMaxIdx); + _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Controller::submitRequestAndBlockGated), request); } @@ -974,80 +1028,61 @@ void ApplePS2Controller::interruptOccurred(IOInterruptEventSource* source, int) if (dequeueKeyboardData(&status)) { unlockController(state); - dispatchDriverInterrupt(kDT_Keyboard, status); + dispatchDriverInterrupt(kPS2KbdIdx, status); lockController(&state); + continue; } // See if data is available on the mouse input stream (off real port). - else if ( (inb(kCommandPort) & (kOutputReady | kMouseData)) == - (kOutputReady | kMouseData)) + status = inb(kCommandPort); + if ( ( status & (kOutputReady | kMouseData)) != + (kOutputReady | kMouseData)) { - unlockController(state); - IODelay(kDataDelay); - dispatchDriverInterrupt(kDT_Mouse, inb(kDataPort)); - lockController(&state); + break; // out of loop } - else break; // out of loop + + unlockController(state); + IODelay(kDataDelay); + size_t port = getPortFromStatus(status); + dispatchDriverInterrupt(port, inb(kDataPort)); + lockController(&state); } unlockController(state); // (release interrupt lockout + access to queue) #else - handleInterrupt(source == _interruptSourceKeyboard ? kDT_Keyboard : kDT_Mouse); + handleInterrupt(); #endif // DEBUGGER_SUPPORT } #endif // HANDLE_INTERRUPT_DATA_LATER -#if !HANDLE_INTERRUPT_DATA_LATER -void ApplePS2Controller::packetReadyKeyboard(IOInterruptEventSource *, int) -{ - // a complete packet has arrived for the keyboard and has signaled the workloop - // -- dispatch it to the installed keyboard packet handler - if (_interruptInstalledKeyboard) - (*_packetActionKeyboard)(_interruptTargetKeyboard); -} - -void ApplePS2Controller::packetReadyMouse(IOInterruptEventSource *, int) -{ - // a complete packet has arrived for the mouse and has signaled the workloop - // -- dispatch it to the installed mouse packet handler - if (_interruptInstalledMouse) - (*_packetActionMouse)(_interruptTargetMouse); -} -#endif // !HANDLE_INTERRUPT_DATA_LATER - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -PS2InterruptResult ApplePS2Controller::_dispatchDriverInterrupt(PS2DeviceType deviceType, UInt8 data) +PS2InterruptResult ApplePS2Controller::_dispatchDriverInterrupt(size_t port, UInt8 data) { PS2InterruptResult result = kPS2IR_packetBuffering; - if (kDT_Mouse == deviceType && _interruptInstalledMouse) + + if (port >= kPS2AuxIdx && _interruptInstalledMouse) { // Dispatch the data to the mouse driver. - result = (*_interruptActionMouse)(_interruptTargetMouse, data); + result = _devices[port]->interruptAction(data); } - else if (kDT_Keyboard == deviceType && _interruptInstalledKeyboard) + else if (kPS2KbdIdx == port && _interruptInstalledKeyboard) { // Dispatch the data to the keyboard driver. - result = (*_interruptActionKeyboard)(_interruptTargetKeyboard, data); + result = _devices[kPS2KbdIdx]->interruptAction(data); } return result; } -void ApplePS2Controller::dispatchDriverInterrupt(PS2DeviceType deviceType, UInt8 data) +void ApplePS2Controller::dispatchDriverInterrupt(size_t port, UInt8 data) { - PS2InterruptResult result = _dispatchDriverInterrupt(deviceType, data); + PS2InterruptResult result = _dispatchDriverInterrupt(port, data); if (kPS2IR_packetReady == result) { #if HANDLE_INTERRUPT_DATA_LATER - if (kDT_Mouse == deviceType) - (*_packetActionMouse)(_interruptTargetMouse); - else if (kDT_Keyboard == deviceType) - (*_packetActionKeyboard)(_interruptTargetKeyboard); + _devices[port]->packetAction(nullptr, 0); #else - if (kDT_Mouse == deviceType) - _interruptSourceMouse->interruptOccurred(0, 0, 0); - else if (kDT_Keyboard == deviceType) - _interruptSourceKeyboard->interruptOccurred(0, 0, 0); + _devices[port]->packetActionInterrupt(); #endif } } @@ -1065,9 +1100,8 @@ void ApplePS2Controller::processRequest(PS2Request * request) // UInt8 byte; - PS2DeviceType deviceMode = kDT_Keyboard; + size_t devicePort = request->port; bool failed = false; - bool transmitToMouse = false; unsigned index; if (_hardwareOffline) @@ -1089,74 +1123,57 @@ void ApplePS2Controller::processRequest(PS2Request * request) switch (request->commands[index].command) { case kPS2C_ReadDataPort: - request->commands[index].inOrOut = readDataPort(deviceMode); + request->commands[index].inOrOut = readDataPort(devicePort); break; case kPS2C_ReadDataPortAndCompare: #if OUT_OF_ORDER_DATA_CORRECTION_FEATURE - byte = readDataPort(deviceMode, request->commands[index].inOrOut); + byte = readDataPort(devicePort, request->commands[index].inOrOut); #else - byte = readDataPort(deviceMode); + byte = readDataPort(devicePort); #endif failed = (byte != request->commands[index].inOrOut); request->commands[index].inOrOut = byte; break; case kPS2C_WriteDataPort: - writeDataPort(request->commands[index].inOrOut); - if (transmitToMouse) // next reads from mouse input stream - { - deviceMode = kDT_Mouse; - transmitToMouse = false; + if (devicePort >= kPS2AuxIdx) { + if (_muxPresent) { + writeCommandPort(kCP_TransmitToMuxedMouse + (devicePort - kPS2AuxIdx)); + } else { + writeCommandPort(kCP_TransmitToMouse); + } } - else - { - deviceMode = kDT_Keyboard; - } - break; - - case kPS2C_WriteCommandPort: - writeCommandPort(request->commands[index].inOrOut); - if (request->commands[index].inOrOut == kCP_TransmitToMouse) - transmitToMouse = true; // preparing to transmit data to mouse + + writeDataPort(request->commands[index].inOrOut); break; // // Send a composite mouse command that is equivalent to the following // (frequently used) command sequence: // - // 1. kPS2C_WriteCommandPort( kCP_TransmitToMouse ) - // 2. kPS2C_WriteDataPort( command ) - // 3. kPS2C_ReadDataPortAndCompare( kSC_Acknowledge ) + // 1. kPS2C_WriteDataPort( command ) + // 2. kPS2C_ReadDataPortAndCompare( kSC_Acknowledge ) // - case kPS2C_SendMouseCommandAndCompareAck: - writeCommandPort(kCP_TransmitToMouse); + case kPS2C_SendCommandAndCompareAck: + if (devicePort >= kPS2AuxIdx) { + if (_muxPresent) { + writeCommandPort(kCP_TransmitToMuxedMouse + (devicePort - kPS2AuxIdx)); + } else { + writeCommandPort(kCP_TransmitToMouse); + } + } + writeDataPort(request->commands[index].inOrOut); - deviceMode = kDT_Mouse; #if OUT_OF_ORDER_DATA_CORRECTION_FEATURE - byte = readDataPort(kDT_Mouse, kSC_Acknowledge); + byte = readDataPort(devicePort, kSC_Acknowledge); #else - byte = readDataPort(kDT_Mouse); + byte = readDataPort(devicePort); #endif failed = (byte != kSC_Acknowledge); break; - case kPS2C_ReadMouseDataPort: - deviceMode= kDT_Mouse; - request->commands[index].inOrOut = readDataPort(deviceMode); - break; - - case kPS2C_ReadMouseDataPortAndCompare: - deviceMode= kDT_Mouse; -#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE - byte = readDataPort(deviceMode, request->commands[index].inOrOut); -#else - byte = readDataPort(deviceMode); -#endif - failed = (byte != request->commands[index].inOrOut); - break; - case kPS2C_FlushDataPort: request->commands[index].inOrOut32 = 0; while ( inb(kCommandPort) & kOutputReady ) @@ -1174,7 +1191,7 @@ void ApplePS2Controller::processRequest(PS2Request * request) case kPS2C_ModifyCommandByte: writeCommandPort(kCP_GetCommandByte); - UInt8 commandByte = readDataPort(kDT_Keyboard); + UInt8 commandByte = readDataPort(kPS2KbdIdx); writeCommandPort(kCP_SetCommandByte); writeDataPort((commandByte | request->commands[index].setBits) & ~request->commands[index].clearBits); request->commands[index].oldBits = commandByte; @@ -1240,7 +1257,21 @@ void ApplePS2Controller::processRequestQueue(IOInterruptEventSource *, int) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType) +size_t ApplePS2Controller::getPortFromStatus(UInt8 status) +{ + bool auxPort = status & kMouseData; + size_t port = auxPort ? kPS2AuxIdx : kPS2KbdIdx; + + if (_muxPresent && auxPort) { + port += (status >> PS2_STA_MUX_SHIFT) & PS2_STA_MUX_MASK; + } + + return port; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +UInt8 ApplePS2Controller::readDataPort(size_t expectedPort) { // // Blocks until keyboard or mouse data is available from the controller @@ -1256,7 +1287,7 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType) // UInt8 readByte; - UInt8 status; + UInt8 status = 0; UInt32 timeoutCounter = 20000; // (timeoutCounter * kDataDelay = 140 ms) while (1) @@ -1264,7 +1295,7 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType) #if DEBUGGER_SUPPORT int state; lockController(&state); // (lock out interrupt + access to queue) - if (deviceType == kDT_Keyboard && dequeueKeyboardData(&readByte)) + if (expectedPort == kPS2KbdIdx && dequeueKeyboardData(&readByte)) { unlockController(state); return readByte; @@ -1292,9 +1323,8 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType) #endif //DEBUGGER_SUPPORT if (!_suppressTimeout) - IOLog("%s: Timed out on %s input stream.\n", getName(), - (deviceType == kDT_Keyboard) ? "keyboard" : "mouse"); - return 0; + IOLog("%s: Timed out on input stream %ld.\n", getName(), expectedPort); + return 0; } // @@ -1319,22 +1349,15 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType) if (_suppressTimeout) // startup mode w/o interrupts return readByte; - if ( (status & kMouseData) ) - { - if (deviceType == kDT_Mouse) return readByte; - } - else - { - if (deviceType == kDT_Keyboard) return readByte; - } + size_t port = getPortFromStatus(status); + if (expectedPort == port) { return readByte; } // // The data we just received is for the other input stream, not the one // that was requested, so dispatch other device's interrupt handler. // - dispatchDriverInterrupt((deviceType==kDT_Keyboard)?kDT_Mouse:kDT_Keyboard, - readByte); + dispatchDriverInterrupt(port, readByte); } // while (forever) } @@ -1342,8 +1365,8 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType) #if OUT_OF_ORDER_DATA_CORRECTION_FEATURE -UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType, - UInt8 expectedByte) +UInt8 ApplePS2Controller::readDataPort(size_t expectedPort, + UInt8 expectedByte) { // // Blocks until keyboard or mouse data is available from the controller @@ -1376,9 +1399,10 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType, UInt8 firstByte = 0; bool firstByteHeld = false; + size_t port = kPS2KbdIdx; UInt8 readByte; bool requestedStream; - UInt8 status; + UInt8 status = 0; UInt32 timeoutCounter = 10000; // (timeoutCounter * kDataDelay = 70 ms) while (1) @@ -1386,7 +1410,7 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType, #if DEBUGGER_SUPPORT int state; lockController(&state); // (lock out interrupt + access to queue) - if (deviceType == kDT_Keyboard && dequeueKeyboardData(&readByte)) + if (expectedPort == kPS2KbdIdx && dequeueKeyboardData(&readByte)) { requestedStream = true; goto skipForwardToY; @@ -1417,8 +1441,7 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType, if (firstByteHeld) return firstByte; - IOLog("%s: Timed out on %s input stream.\n", getName(), - (deviceType == kDT_Keyboard) ? "keyboard" : "mouse"); + IOLog("%s: Timed out on input stream %ld.\n", getName(), expectedPort); return 0; } @@ -1437,15 +1460,9 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType, readByte = inb(kDataPort); requestedStream = false; + port = getPortFromStatus(status); - if ( (status & kMouseData) ) - { - if (deviceType == kDT_Mouse) requestedStream = true; - } - else - { - if (deviceType == kDT_Keyboard) requestedStream = true; - } + if (expectedPort == port) { requestedStream = true; } #if DEBUGGER_SUPPORT skipForwardToY: @@ -1472,7 +1489,7 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType, // if (!_ignoreOutOfOrder) - dispatchDriverInterrupt(deviceType, firstByte); + dispatchDriverInterrupt(expectedPort, firstByte); return readByte; } } @@ -1496,7 +1513,7 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType, // if (!_ignoreOutOfOrder) - dispatchDriverInterrupt(deviceType, readByte); + dispatchDriverInterrupt(expectedPort, readByte); return firstByte; } } @@ -1509,7 +1526,7 @@ UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType, // if (!_ignoreOutOfOrder) - dispatchDriverInterrupt(deviceType == kDT_Keyboard ? kDT_Mouse : kDT_Keyboard, readByte); + dispatchDriverInterrupt(port, readByte); } } // while (forever) } @@ -1644,7 +1661,8 @@ bool ApplePS2Controller::doEscape(UInt8 scancode) while (inb(kCommandPort) & kInputBusy) IODelay(kDataDelay); IODelay(kDataDelay); - outb(kCommandPort, kCP_EnableMouseClock); + if(!_kbdOnly) + outb(kCommandPort, kCP_EnableMouseClock); releaseModifiers = true; } @@ -1786,7 +1804,7 @@ IOReturn ApplePS2Controller::setPowerStateAction( OSObject * target, void * arg0, void * arg1, void * arg2, void * arg3 ) { - ApplePS2Controller * me = (ApplePS2Controller *) target; + ApplePS2Controller * me = (ApplePS2Controller *) target; #ifdef __LP64__ UInt32 powerState = (UInt32)(UInt64)arg0; @@ -1794,9 +1812,9 @@ IOReturn ApplePS2Controller::setPowerStateAction( OSObject * target, UInt32 powerState = (UInt32) arg0; #endif - me->setPowerStateGated( powerState ); + me->setPowerStateGated( powerState ); - return kIOReturnSuccess; + return kIOReturnSuccess; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1821,8 +1839,8 @@ void ApplePS2Controller::setPowerStateGated( UInt32 powerState ) // synchronous requests thanks to the recursive lock. // First Mouse, then Keyboard. - dispatchDriverPowerControl( kPS2C_DisableDevice, kDT_Mouse ); - dispatchDriverPowerControl( kPS2C_DisableDevice, kDT_Keyboard ); + dispatchDriverPowerControl( kPS2C_DisableDevice, kPS2AuxIdx ); + dispatchDriverPowerControl( kPS2C_DisableDevice, kPS2KbdIdx ); // 3. Freeze the request queue and drop all data received over // the PS/2 port. @@ -1863,18 +1881,37 @@ void ApplePS2Controller::setPowerStateGated( UInt32 powerState ) { resetController(); } - + #endif // FULL_INIT_AFTER_WAKE - + if (_muxPresent) { + setMuxMode(true); + } + +#if FULL_INIT_AFTER_WAKE + if (_resetControllerFlag & RESET_CONTROLLER_ON_WAKEUP) + { + resetDevices(); + flushDataPort(); + } +#endif // FULL_INIT_AFTER_WAKE + // // Transition from Sleep state to Working state in 4 stages. // // 1. Enable the PS/2 port -- but just the clocks + + if (_muxPresent) + { + enableMuxPorts(); + } DEBUG_LOG("%s: setCommandByte for wake 1\n", getName()); - setCommandByte(0, kCB_DisableKeyboardClock | kCB_DisableMouseClock | kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ); + if (!_kbdOnly) + setCommandByte(0, kCB_DisableKeyboardClock | kCB_DisableMouseClock | kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ); + else + setCommandByte(0, kCB_DisableKeyboardClock | kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ); // 2. Unblock the request queue and wake up all driver threads // that were blocked by submitRequest(). @@ -1887,19 +1924,22 @@ void ApplePS2Controller::setPowerStateGated( UInt32 powerState ) if (!_mouseWakeFirst) { - dispatchDriverPowerControl( kPS2C_EnableDevice, kDT_Keyboard ); - dispatchDriverPowerControl( kPS2C_EnableDevice, kDT_Mouse ); + dispatchDriverPowerControl( kPS2C_EnableDevice, kPS2KbdIdx ); + dispatchDriverPowerControl( kPS2C_EnableDevice, kPS2AuxIdx ); } else { - dispatchDriverPowerControl( kPS2C_EnableDevice, kDT_Mouse ); - dispatchDriverPowerControl( kPS2C_EnableDevice, kDT_Keyboard ); + dispatchDriverPowerControl( kPS2C_EnableDevice, kPS2AuxIdx ); + dispatchDriverPowerControl( kPS2C_EnableDevice, kPS2KbdIdx ); } // 4. Now safe to enable the IRQs... DEBUG_LOG("%s: setCommandByte for wake 2\n", getName()); - setCommandByte(kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ | kCB_SystemFlag, 0); + if (!_kbdOnly) + setCommandByte(kCB_EnableKeyboardIRQ | kCB_EnableMouseIRQ | kCB_SystemFlag, 0); + else + setCommandByte(kCB_EnableKeyboardIRQ | kCB_SystemFlag, 0); --_ignoreInterrupts; break; @@ -1921,77 +1961,48 @@ void ApplePS2Controller::setPowerStateGated( UInt32 powerState ) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Controller::dispatchDriverPowerControl( UInt32 whatToDo, PS2DeviceType deviceType ) +void ApplePS2Controller::dispatchDriverPowerControl( UInt32 whatToDo, size_t port ) { - if (kDT_Mouse == deviceType && _powerControlInstalledMouse) - (*_powerControlActionMouse)(_powerControlTargetMouse, whatToDo); + // Should be called with kPS2Aux or kPS2Kbd. + // "port" set to kPS2Aux will run the power action for all mux ports + assert (port < kPS2AuxMaxIdx); + + if (port == kPS2KbdIdx) + { + _devices[kPS2KbdIdx]->powerAction(whatToDo); + return; + } - if (kDT_Keyboard == deviceType && _powerControlInstalledKeyboard) - (*_powerControlActionKeyboard)(_powerControlTargetKeyboard, whatToDo); + for (size_t i = kPS2AuxIdx; i < _nubsCount; i++) { + _devices[i]->powerAction(whatToDo); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Controller::installPowerControlAction( - PS2DeviceType deviceType, - OSObject * target, - PS2PowerControlAction action ) +void ApplePS2Controller::notificationHandlerPublishGated(IOService * newService, IONotifier * notifier) { - if ( deviceType == kDT_Keyboard && _powerControlInstalledKeyboard == false ) - { - target->retain(); - _powerControlTargetKeyboard = target; - _powerControlActionKeyboard = action; - _powerControlInstalledKeyboard = true; - } - else if ( deviceType == kDT_Mouse && _powerControlInstalledMouse == false ) - { - target->retain(); - _powerControlTargetMouse = target; - _powerControlActionMouse = action; - _powerControlInstalledMouse = true; - } + IOLog("%s: Notification consumer published: %s\n", getName(), newService->getName()); + _notificationServices->setObject(newService); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Controller::uninstallPowerControlAction( PS2DeviceType deviceType ) +bool ApplePS2Controller::notificationHandlerPublish(void * refCon, IOService * newService, IONotifier * notifier) { - if ( deviceType == kDT_Keyboard && _powerControlInstalledKeyboard == true ) - { - _powerControlInstalledKeyboard = false; - _powerControlActionKeyboard = NULL; - _powerControlTargetKeyboard->release(); - _powerControlTargetKeyboard = 0; - } - else if ( deviceType == kDT_Mouse && _powerControlInstalledMouse == true ) - { - _powerControlInstalledMouse = false; - _powerControlActionMouse = NULL; - _powerControlTargetMouse->release(); - _powerControlTargetMouse = 0; - } + assert(_cmdGate != nullptr); + _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Controller::notificationHandlerPublishGated), newService, notifier); + return true; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Controller::notificationHandlerGated(IOService * newService, IONotifier * notifier) +void ApplePS2Controller::notificationHandlerTerminateGated(IOService * newService, IONotifier * notifier) { - if (notifier == _publishNotify) { - IOLog("%s: Notification consumer published: %s\n", getName(), newService->getName()); - _notificationServices->setObject(newService); - } - - if (notifier == _terminateNotify) { - IOLog("%s: Notification consumer terminated: %s\n", getName(), newService->getName()); - _notificationServices->removeObject(newService); - } + IOLog("%s: Notification consumer terminated: %s\n", getName(), newService->getName()); + _notificationServices->removeObject(newService); } -bool ApplePS2Controller::notificationHandler(void * refCon, IOService * newService, IONotifier * notifier) +bool ApplePS2Controller::notificationHandlerTerminate(void * refCon, IOService * newService, IONotifier * notifier) { - assert(_cmdGate != nullptr); - _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Controller::notificationHandlerGated), newService, notifier); + assert(_cmdGate != nullptr); + _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Controller::notificationHandlerTerminateGated), newService, notifier); return true; } @@ -2015,7 +2026,7 @@ void ApplePS2Controller::dispatchMessageGated(int* message, void* data) switch (pInfo->adbKeyCode) { - // Do not trigger on modifier key presses (for example multi-click select) + // Do not trigger on modifier key presses (for example multi-click select) case 0x38: // left shift case 0x3c: // right shift case 0x3b: // left control @@ -2027,8 +2038,9 @@ void ApplePS2Controller::dispatchMessageGated(int* message, void* data) case 0x3f: // osx fn (function) break; default: - int dispatchMessage = kPS2M_notifyKeyTime; - dispatchMessageGated(&dispatchMessage, &(pInfo->time)); + + int dispatchMsg = kPS2M_notifyKeyTime; + dispatchMessageGated(&dispatchMsg, &(pInfo->time)); } } } @@ -2119,7 +2131,7 @@ static OSString* getPlatformOverride(IORegistryEntry* reg, const char* sz) return NULL; } -static OSString* getPlatformManufacturer(IORegistryEntry* reg) +static LIBKERN_RETURNS_RETAINED OSString* getPlatformManufacturer(IORegistryEntry* reg) { // allow override in PS2K ACPI device OSString* id = getPlatformOverride(reg, "RM,oem-id"); @@ -2141,7 +2153,7 @@ static OSString* getPlatformManufacturer(IORegistryEntry* reg) return OSString::withCStringNoCopy(oemID); } -static OSString* getPlatformProduct(IORegistryEntry* reg) +static LIBKERN_RETURNS_RETAINED OSString* getPlatformProduct(IORegistryEntry* reg) { // allow override in PS2K ACPI device OSString* id = getPlatformOverride(reg, "RM,oem-table-id"); @@ -2416,3 +2428,13 @@ OSDictionary* ApplePS2Controller::makeConfigurationNode(OSDictionary* list, cons return result; } + +IOReturn ApplePS2Controller::startSMBusCompanion(OSDictionary *companionData, UInt8 smbusAddr) { + IOReturn ret = callPlatformFunction(_smbusCompanion, + false, + static_cast(this), + static_cast(companionData), + reinterpret_cast(smbusAddr), + nullptr); + return ret; +} diff --git a/VoodooPS2Controller/VoodooPS2Controller.h b/VoodooPS2Controller/VoodooPS2Controller.h index b64aa066..793e200d 100644 --- a/VoodooPS2Controller/VoodooPS2Controller.h +++ b/VoodooPS2Controller/VoodooPS2Controller.h @@ -25,7 +25,7 @@ #include #include -#include "LegacyIOService.h" +#include #include #include "ApplePS2Device.h" @@ -146,6 +146,16 @@ class ApplePS2MouseDevice; #define kWatchdogTimerInterval 100 +// Enable Mux commands +// Constants are from Linux +// https://github.com/torvalds/linux/blob/c2d7ed9d680fd14aa5486518bd0d0fa5963c6403/drivers/input/serio/i8042.c#L685-L693 + +#define kDP_MuxCmd 0xF0 +#define kDP_EnableMuxCmd1 0x56 +#define kDP_GetMuxVersion 0xA4 +#define kDP_DisableMuxCmd1 0xF6 +#define kDP_DisableMuxCmd2 0xA5 + #if DEBUGGER_SUPPORT // Definitions for our internal keyboard queue (holds keys processed by the // interrupt-time mini-monitor-key-sequence detection code). @@ -170,8 +180,8 @@ struct KeyboardQueueElement #endif // ps2rst flags -#define RESET_CONTROLLER_ON_BOOT 1 -#define RESET_CONTROLLER_ON_WAKEUP 2 +#define RESET_CONTROLLER_ON_BOOT 1 +#define RESET_CONTROLLER_ON_WAKEUP 2 class IOACPIPlatformDevice; @@ -182,6 +192,24 @@ enum { kPS2PowerStateCount }; +// i8042 Mux indexes +#define PS2_MUX_PORTS 4 + +#define PS2_STA_MUX_MASK 0x03 +#define PS2_STA_MUX_SHIFT 0x06 + +// Normally, the i8042 controller has 2 ports. With the mux active, +// there are 5 ports. 1 Keyboard port and 4 mux ports. All the muxed ports +// share the same IRQ. When the controller is in a multiplexed mode, 3 +// additional ports are added. + +enum { + kPS2KbdIdx = 0, + kPS2AuxIdx = 1, + kPS2AuxMaxIdx = 2, + kPS2MuxMaxIdx = PS2_MUX_PORTS + 1 +}; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ApplePS2Controller Class Declaration // @@ -191,9 +219,12 @@ class EXPORT ApplePS2Controller : public IOService typedef IOService super; OSDeclareDefaultStructors(ApplePS2Controller); -public: // interrupt-time variables and functions - IOInterruptEventSource * _interruptSourceKeyboard {nullptr}; +public: + // interrupt-time variables and functions +#if HANDLE_INTERRUPT_DATA_LATER IOInterruptEventSource * _interruptSourceMouse {nullptr}; + IOInterruptEventSource * _interruptSourceKeyboard {nullptr}; +#endif IOInterruptEventSource * _interruptSourceQueue {nullptr}; #if DEBUGGER_SUPPORT @@ -213,27 +244,13 @@ class EXPORT ApplePS2Controller : public IOService IOLock* _requestQueueLock {nullptr}; IOLock* _cmdbyteLock {nullptr}; - OSObject * _interruptTargetKeyboard {nullptr}; - OSObject * _interruptTargetMouse {nullptr}; - PS2InterruptAction _interruptActionKeyboard {nullptr}; - PS2InterruptAction _interruptActionMouse {nullptr}; - PS2PacketAction _packetActionKeyboard {nullptr}; - PS2PacketAction _packetActionMouse {nullptr}; bool _interruptInstalledKeyboard {false}; - bool _interruptInstalledMouse {false}; - - OSObject * _powerControlTargetKeyboard {nullptr}; - OSObject * _powerControlTargetMouse {nullptr}; - PS2PowerControlAction _powerControlActionKeyboard {nullptr}; - PS2PowerControlAction _powerControlActionMouse {nullptr}; - bool _powerControlInstalledKeyboard {false}; - bool _powerControlInstalledMouse {false}; + int _interruptInstalledMouse {0}; int _ignoreInterrupts {0}; int _ignoreOutOfOrder {0}; - ApplePS2MouseDevice * _mouseDevice {nullptr}; // mouse nub - ApplePS2KeyboardDevice * _keyboardDevice {nullptr}; // keyboard nub + ApplePS2Device * _devices [kPS2MuxMaxIdx] {nullptr}; IONotifier* _publishNotify {nullptr}; IONotifier* _terminateNotify {nullptr}; @@ -255,52 +272,56 @@ class EXPORT ApplePS2Controller : public IOService UInt32 _currentPowerState {kPS2PowerStateNormal}; bool _hardwareOffline {false}; bool _suppressTimeout {false}; -#ifdef NEWIRQ - bool _newIRQLayout {false}; -#endif int _wakedelay {10}; bool _mouseWakeFirst {false}; + bool _muxPresent {false}; + size_t _nubsCount {0}; IOCommandGate* _cmdGate {nullptr}; #if WATCHDOG_TIMER IOTimerEventSource* _watchdogTimer {nullptr}; #endif OSDictionary* _rmcfCache {nullptr}; const OSSymbol* _deliverNotification {nullptr}; + const OSSymbol* _smbusCompanion {nullptr}; int _resetControllerFlag {RESET_CONTROLLER_ON_BOOT | RESET_CONTROLLER_ON_WAKEUP}; + bool _kbdOnly {0}; - virtual PS2InterruptResult _dispatchDriverInterrupt(PS2DeviceType deviceType, UInt8 data); - virtual void dispatchDriverInterrupt(PS2DeviceType deviceType, UInt8 data); + virtual PS2InterruptResult _dispatchDriverInterrupt(size_t port, UInt8 data); + virtual void dispatchDriverInterrupt(size_t port, UInt8 data); #if HANDLE_INTERRUPT_DATA_LATER virtual void interruptOccurred(IOInterruptEventSource *, int); -#else - void packetReadyMouse(IOInterruptEventSource*, int); - void packetReadyKeyboard(IOInterruptEventSource*, int); #endif - void handleInterrupt(PS2DeviceType deviceType); + void handleInterrupt(bool watchdog = false); #if WATCHDOG_TIMER void onWatchdogTimer(); #endif virtual void processRequest(PS2Request * request); virtual void processRequestQueue(IOInterruptEventSource *, int); - virtual UInt8 readDataPort(PS2DeviceType deviceType); +#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE + virtual UInt8 readDataPort(size_t port, UInt8 expectedByte); +#endif + + virtual UInt8 readDataPort(size_t port); virtual void writeCommandPort(UInt8 byte); virtual void writeDataPort(UInt8 byte); void resetController(void); + bool setMuxMode(bool); + void flushDataPort(void); + void resetDevices(void); static void interruptHandlerMouse(OSObject*, void* refCon, IOService*, int); static void interruptHandlerKeyboard(OSObject*, void* refCon, IOService*, int); - void notificationHandlerGated(IOService * newService, IONotifier * notifier); - bool notificationHandler(void * refCon, IOService * newService, IONotifier * notifier); + void notificationHandlerPublishGated(IOService * newService, IONotifier * notifier); + bool notificationHandlerPublish(void * refCon, IOService * newService, IONotifier * notifier); + + void notificationHandlerTerminateGated(IOService * newService, IONotifier * notifier); + bool notificationHandlerTerminate(void * refCon, IOService * newService, IONotifier * notifier); void dispatchMessageGated(int* message, void* data); -#if OUT_OF_ORDER_DATA_CORRECTION_FEATURE - virtual UInt8 readDataPort(PS2DeviceType deviceType, UInt8 expectedByte); -#endif - static void setPowerStateCallout(thread_call_param_t param0, thread_call_param_t param1); @@ -310,10 +331,12 @@ class EXPORT ApplePS2Controller : public IOService virtual void setPowerStateGated(UInt32 newPowerState); - virtual void dispatchDriverPowerControl(UInt32 whatToDo, PS2DeviceType deviceType); + virtual void dispatchDriverPowerControl(UInt32 whatToDo, size_t port); void free(void) override; IOReturn setPropertiesGated(OSObject* props); void submitRequestAndBlockGated(PS2Request* request); + + size_t getPortFromStatus(UInt8 status); public: bool init(OSDictionary * properties) override; @@ -323,11 +346,9 @@ class EXPORT ApplePS2Controller : public IOService IOWorkLoop * getWorkLoop() const override; - virtual void installInterruptAction(PS2DeviceType deviceType, - OSObject * target, - PS2InterruptAction interruptAction, - PS2PacketAction packetAction); - virtual void uninstallInterruptAction(PS2DeviceType deviceType); + void enableMuxPorts(); + virtual void installInterruptAction(size_t port); + virtual void uninstallInterruptAction(size_t port); virtual PS2Request* allocateRequest(int max = kMaxCommands); virtual void freeRequest(PS2Request * request); @@ -338,12 +359,6 @@ class EXPORT ApplePS2Controller : public IOService IOReturn setPowerState(unsigned long powerStateOrdinal, IOService * policyMaker) override; - - virtual void installPowerControlAction(PS2DeviceType deviceType, - OSObject * target, - PS2PowerControlAction action); - - virtual void uninstallPowerControlAction(PS2DeviceType deviceType); virtual void dispatchMessage(int message, void* data); @@ -357,6 +372,8 @@ class EXPORT ApplePS2Controller : public IOService OSDictionary* getConfigurationOverride(IOACPIPlatformDevice* acpi, const char* method); OSObject* translateArray(OSArray* array); OSObject* translateEntry(OSObject* obj); + + IOReturn startSMBusCompanion(OSDictionary *companionData, UInt8 smbusAddr); }; #endif /* _APPLEPS2CONTROLLER_H */ diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard-Breakless-Info.plist b/VoodooPS2Keyboard/VoodooPS2Keyboard-Breakless-Info.plist index 3c32f874..9e6aaba2 100644 --- a/VoodooPS2Keyboard/VoodooPS2Keyboard-Breakless-Info.plist +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard-Breakless-Info.plist @@ -43,7 +43,7 @@ Swap capslock and left control Swap command and option - + Use ISO layout keyboard alt_handler_id diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist b/VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist index 767b08ae..7e224756 100644 --- a/VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard-Info.plist @@ -278,7 +278,7 @@ Swap capslock and left control Swap command and option - + Use ISO layout keyboard alt_handler_id diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard-RemapFN-Info.plist b/VoodooPS2Keyboard/VoodooPS2Keyboard-RemapFN-Info.plist index 401a44b9..eb4a48d1 100644 --- a/VoodooPS2Keyboard/VoodooPS2Keyboard-RemapFN-Info.plist +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard-RemapFN-Info.plist @@ -43,7 +43,7 @@ Swap capslock and left control Swap command and option - + Use ISO layout keyboard alt_handler_id diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp b/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp index 972907c7..fe47247e 100644 --- a/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard.cpp @@ -26,16 +26,13 @@ #define DEBUG_LITE #endif -#include "LegacyIOService.h" +#include -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winconsistent-missing-override" #include #include #include #include #include -#pragma clang diagnostic pop #include "ApplePS2ToADBMap.h" #include "VoodooPS2Controller.h" @@ -52,6 +49,9 @@ #define kHIDF12EjectDelay "HIDF12EjectDelay" #define kFunctionKeysStandard "Function Keys Standard" #define kFunctionKeysSpecial "Function Keys Special" +#define kRemapPrntScr "RemapPrntScr" +#define kNumLockSupport "NumLockSupport" +#define kNumLockOnAtBoot "NumLockOnAtBoot" #define kSwapCapsLockLeftControl "Swap capslock and left control" #define kSwapCommandOption "Swap command and option" #define kMakeApplicationKeyRightWindows "Make Application key into right windows" @@ -166,6 +166,9 @@ bool ApplePS2Keyboard::init(OSDictionary * dict) _ledState = 0; _lastdata = 0; + _remapPrntScr = false; + _numLockSupport = false; + _numLockOnAtBoot = false; _swapcommandoption = false; _sleepEjectTimer = 0; _cmdGate = 0; @@ -176,9 +179,8 @@ bool ApplePS2Keyboard::init(OSDictionary * dict) _keysSpecial = 0; _f12ejectdelay = 250; // default is 250 ms - // initialize ACPI support for keyboard backlight/screen brightness + // initialize ACPI support for keyboard backlight _provider = 0; - _brightnessLevels = 0; _backlightLevels = 0; _logscancodes = 0; @@ -383,64 +385,11 @@ bool ApplePS2Keyboard::start(IOService * provider) //REVIEW: should really look at the parent chain for IOACPIPlatformDevice instead. _provider = (IOACPIPlatformDevice*)IORegistryEntry::fromPath("IOService:/AppleACPIPlatformExpert/PS2K"); - // - // get brightness levels for ACPI based brightness keys - // - - OSObject* result = 0; - if (_provider) do - { - // check for brightness methods - if (kIOReturnSuccess != _provider->validateObject("KBCL") || kIOReturnSuccess != _provider->validateObject("KBCM") || kIOReturnSuccess != _provider->validateObject("KBQC")) - { - break; - } - // methods are there, so now try to collect brightness levels - if (kIOReturnSuccess != _provider->evaluateObject("KBCL", &result)) - { - DEBUG_LOG("ps2br: KBCL returned error\n"); - break; - } - OSArray* array = OSDynamicCast(OSArray, result); - if (!array) - { - DEBUG_LOG("ps2br: KBCL returned non-array package\n"); - break; - } - int count = array->getCount(); - if (count < 4) - { - DEBUG_LOG("ps2br: KBCL returned invalid package\n"); - break; - } - _brightnessCount = count; - _brightnessLevels = new int[_brightnessCount]; - if (!_brightnessLevels) - { - DEBUG_LOG("ps2br: _brightnessLevels new int[] failed\n"); - break; - } - for (int i = 0; i < _brightnessCount; i++) - { - OSNumber* num = OSDynamicCast(OSNumber, array->getObject(i)); - int brightness = num ? num->unsigned32BitValue() : 0; - _brightnessLevels[i] = brightness; - } -#ifdef DEBUG_VERBOSE - DEBUG_LOG("ps2br: Brightness levels: { "); - for (int i = 0; i < _brightnessCount; i++) - DEBUG_LOG("%d, ", _brightnessLevels[i]); - DEBUG_LOG("}\n"); -#endif - break; - } while (false); - - OSSafeReleaseNULL(result); - // // get keyboard backlight levels for ACPI based backlight keys // + OSObject* result = 0; if (_provider) do { // check for brightness methods @@ -503,6 +452,13 @@ bool ApplePS2Keyboard::start(IOService * provider) // initKeyboard(); + + // + // Set NumLock State to On (if specified). + // + + if (_numLockOnAtBoot) + setNumLock(true); pWorkLoop->addEventSource(_sleepEjectTimer); pWorkLoop->addEventSource(_cmdGate); @@ -795,6 +751,24 @@ void ApplePS2Keyboard::setParamPropertiesGated(OSDictionary * dict) _brightnessHack = true; } + if ((xml = OSDynamicCast(OSBoolean, dict->getObject(kRemapPrntScr)))) + { + _remapPrntScr = xml->getValue(); + setProperty(kRemapPrntScr, _remapPrntScr); + } + + if ((xml = OSDynamicCast(OSBoolean, dict->getObject(kNumLockSupport)))) + { + _numLockSupport = xml->getValue(); + setProperty(kNumLockSupport, _numLockSupport); + } + + if ((xml = OSDynamicCast(OSBoolean, dict->getObject(kNumLockOnAtBoot)))) + { + _numLockOnAtBoot = xml->getValue(); + setProperty(kNumLockOnAtBoot, _numLockOnAtBoot); + } + // these two options are mutually exclusive // kMakeApplicationKeyAppleFN is ignored if kMakeApplicationKeyRightWindows is set bool temp = false; @@ -953,18 +927,9 @@ void ApplePS2Keyboard::stop(IOService * provider) // Release ACPI provider for PS2K ACPI device // OSSafeReleaseNULL(_provider); - - // - // Release data related to screen brightness - // - if (_brightnessLevels) - { - delete[] _brightnessLevels; - _brightnessLevels = 0; - } - + // - // Release data related to screen brightness + // Release data related to keyboard backlight // if (_backlightLevels) { @@ -1008,7 +973,6 @@ IOReturn ApplePS2Keyboard::message(UInt32 type, IOService* provider, void* argum else IOLog("ApplePS2Keyboard::message: type=%x, provider=%p", type, provider); #endif - switch (type) { case kIOACPIMessageDeviceNotification: @@ -1050,8 +1014,41 @@ IOReturn ApplePS2Keyboard::message(UInt32 type, IOService* provider, void* argum } } break; - } + case kPS2K_getKeyboardStatus: + { + if (argument) { + bool* pResult = (bool*)argument; + *pResult = !_disableInput; + } + break; + } + + case kPS2K_setKeyboardStatus: + { + if (argument) { + bool enable = *((bool*)argument); + if (enable == _disableInput) + { + _disableInput = !enable; + } + } + break; + } + + case kPS2K_notifyKeystroke: + { + if (argument) { + PS2KeyInfo *keystroke = (PS2KeyInfo*)argument; + if (!keystroke->eatKey) { + // the key is consumed + keystroke->eatKey = true; + dispatchKeyboardEventX(keystroke->adbKeyCode, keystroke->goingDown, keystroke->time); + } + } + break; + } + } return kIOReturnSuccess; } @@ -1320,63 +1317,6 @@ void ApplePS2Keyboard::dispatchInvertBuffer() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// -// Note: attempted brightness through ACPI methods, but it didn't work. -// -// I think because Probook 4530s does some funny in things in its -// ACPI brightness methods. -// -// Just keeping it here in case someone wants to try with theirs. - -void ApplePS2Keyboard::modifyScreenBrightness(int adbKeyCode, bool goingDown) -{ - assert(_provider); - assert(_brightnessLevels); - - // get current brightness level - UInt32 result; - if (kIOReturnSuccess != _provider->evaluateInteger("KBQC", &result)) - { - DEBUG_LOG("ps2br: KBQC returned error\n"); - return; - } - int current = result; -#ifdef DEBUG_VERBOSE - if (goingDown) - DEBUG_LOG("ps2br: Current brightness: %d\n", current); -#endif - // calculate new brightness level, find current in table >= entry in table - // note first two entries in table are ac-power/battery - int index = 2; - while (index < _brightnessCount) - { - if (_brightnessLevels[index] >= current) - break; - ++index; - } - // move to next or previous - index += (adbKeyCode == 0x90 ? +1 : -1); - if (index >= _brightnessCount) - index = _brightnessCount - 1; - if (index < 2) - index = 2; -#ifdef DEBUG_VERBOSE - if (goingDown) - DEBUG_LOG("ps2br: setting brightness %d\n", _brightnessLevels[index]); -#endif - OSNumber* num = OSNumber::withNumber(_brightnessLevels[index], 32); - if (!num) - { - DEBUG_LOG("ps2br: OSNumber::withNumber failed\n"); - return; - } - if (goingDown && kIOReturnSuccess != _provider->evaluateObject("KBCM", NULL, (OSObject**)&num, 1)) - { - DEBUG_LOG("ps2br: KBCM returned error\n"); - } - num->release(); -} - // // Note: trying for ACPI backlight control for ASUS notebooks // @@ -1552,6 +1492,16 @@ bool ApplePS2Keyboard::dispatchKeyboardEventWithPacket(const UInt8* packet) // handle special cases switch (keyCode) { + case 0x45: // NumLock + if (_numLockSupport && (scanCode == 0xc5)) // NumLock -> Up + { + setNumLock(!numLock()); + return true; + } + else if (_numLockSupport && (scanCode == 0x45)) // NumLock -> Down + return false; + break; + case 0x4e: // Numpad+ case 0x4a: // Numpad- if (_backlightLevels && checkModifierState(kMaskLeftControl|kMaskLeftAlt)) @@ -1566,12 +1516,12 @@ bool ApplePS2Keyboard::dispatchKeyboardEventWithPacket(const UInt8* packet) // Fn+F2 generates e0 ab and so does Fn+F3 (we will null those out in ps2 map) static unsigned keys[] = { 0x2a, 0x1d }; // if Option key is down don't pull up on the Shift keys - int start = checkModifierState(kMaskLeftWindows) ? 1 : 0; - for (int i = start; i < countof(keys); i++) + int state = checkModifierState(kMaskLeftWindows) ? 1 : 0; + for (int i = state; i < countof(keys); i++) if (KBV_IS_KEYDOWN(keys[i])) dispatchKeyboardEventX(_PS2ToADBMap[keys[i]], false, now_abs); dispatchKeyboardEventX(keyCode == 0x4e ? 0x90 : 0x91, goingDown, now_abs); - for (int i = start; i < countof(keys); i++) + for (int i = state; i < countof(keys); i++) if (KBV_IS_KEYDOWN(keys[i])) dispatchKeyboardEventX(_PS2ToADBMap[keys[i]], true, now_abs); keyCode = 0; @@ -1614,27 +1564,120 @@ bool ApplePS2Keyboard::dispatchKeyboardEventWithPacket(const UInt8* packet) break; //REVIEW: this is getting a bit ugly + case 0x0054: // SysRq (PrntScr when combined with Alt modifier -left or right-) + if (!_remapPrntScr) + break; case 0x0128: // alternate that cannot fnkeys toggle (discrete trackpad toggle) + { + + // PrntScr is handled specially by some keyboard devices. + // See: 5.19 on https://www.win.tue.nl/~aeb/linux/kbd/scancodes-5.html#mtek +#ifdef DEBUG + UInt16 debug_originalModifiers = _PS2modifierState; +#endif + // Force Alt (Left) key to be down when receiving this keycode, dont rely on KB firmware + _PS2modifierState &= ~kMaskRightAlt; + _PS2modifierState |= kMaskLeftAlt; + keyCode = 0x0137; // Rewrite keycode + +#ifdef DEBUG + IOLog("VoodooPS2Keyboard: special PrntScr: modifiersBefore=%#.4X modifiersAfter=%#.4X\n", debug_originalModifiers, _PS2modifierState); +#endif + + // Fall to the original PrntScr handling case + } case 0x0137: // prt sc/sys rq { + if (!_remapPrntScr) + break; + + /* Supported Voodoo PrntScr Key combinations: + PrntScr Enable/Disable touchpad + Windows+PrntScr Enable/Disable touchpad+keyboard + Ctrl+Alt+PrntScr Reset and enable touchpad + Shift+PrntScr Send SysRq scancode to the kernel + + Notes: + - Alt+Windows combo seems to be masked out by some keyboard devices and dont produce any ScanCode. + Dont rely on it. + */ + unsigned origKeyCode = keyCode; - keyCode = 0; + keyCode = 0; // Handle all these keycode variants internally + +#ifdef DEBUG + bool debug_control = checkModifierStateAny(kMaskLeftControl|kMaskRightControl); + bool debug_alt = checkModifierStateAny(kMaskLeftAlt|kMaskRightAlt); + bool debug_shift = checkModifierStateAny(kMaskLeftShift|kMaskRightShift); + bool debug_windows = checkModifierStateAny(kMaskLeftWindows|kMaskRightWindows); + + IOLog("VoodooPS2Keyboard: PrtScr:: goingDown=%s control=%s alt=%s shift=%s windows=%s modifiers=%d\n", + goingDown ? "Yes" : "No", + debug_control ? "Yes" : "No", + debug_alt ? "Yes" : "No", + debug_shift ? "Yes" : "No", + debug_windows ? "Yes" : "No", + _PS2modifierState); +#endif + if (!goingDown) break; - if (!checkModifierState(kMaskLeftControl)) + + if (checkModifierStateAny(kMaskLeftControl|kMaskRightControl)) { - // get current enabled status, and toggle it - bool enabled; - _device->dispatchMessage(kPS2M_getDisableTouchpad, &enabled); - enabled = !enabled; - _device->dispatchMessage(kPS2M_setDisableTouchpad, &enabled); - // Disable keyboard input along with the touchpad using Windows(Option)+prtsc, useful for 2-in-1 applications. - if (checkModifierState(kMaskLeftWindows)) + // Shift is ignored from this point onwards + if (checkModifierStateAny(kMaskLeftAlt|kMaskRightAlt)) { - _disableInput = !_disableInput; + // Ctrl+Alt+PrntScr + IOLog("VoodooPS2Keyboard: Sending RESET signal to touchpad."); // Dont wrap into a DEBUG compilation condition since this should be a workaroung to be used on faulty states only + int val = 1; + _device->dispatchMessage(kPS2M_resetTouchpad, &val); // Reset touchpad } + } + else + { + if (checkModifierState(kMaskLeftShift) || checkModifierState(kMaskRightShift)) + { + // Shift+PrntScr, no other modifiers present +#ifdef DEBUG + IOLog("VoodooPS2Keyboard: Sending SysRq virtual scancode 0x58"); +#endif + dispatchKeyboardEventX(0x58, true, now_abs); // Send SysRq to the kernel + dispatchKeyboardEventX(0x58, false, now_abs); + } + else + { + if (checkModifierStateAny(kMaskRightShift|kMaskLeftShift|kMaskRightAlt|kMaskLeftAlt)) + break; // Eat combinations where Ctrl is Up and Alt or Shift are Down (!Ctrl+[Alt|Shift]) + + bool enabled; + if (checkModifierStateAny(kMaskLeftWindows|kMaskRightWindows)) + { + // Windows+PrntScr + // Disable keyboard input along with the touchpad using Windows(Option)+prtsc, useful for 2-in-1 applications. +#ifdef DEBUG + IOLog("VoodooPS2Keyboard: Toggling keyboard+Touchpad enabled state."); +#endif + enabled = _disableInput; + _disableInput = !_disableInput; + _device->dispatchMessage(kPS2M_setDisableTouchpad, &enabled); // Sync Keyboard and Touchpad enabled states + } + else + { + // No other modifiers pressed down +#ifdef DEBUG + IOLog("VoodooPS2Keyboard: Toggling Touchpad enabled state."); +#endif + // Touchpad enable/disable: get current enabled status, and toggle it + _device->dispatchMessage(kPS2M_getDisableTouchpad, &enabled); + enabled = !enabled; + _device->dispatchMessage(kPS2M_setDisableTouchpad, &enabled); + } + } + break; } + if (origKeyCode != 0x0137) break; // do not fall through for 0x0128 // fall through @@ -1664,10 +1707,14 @@ bool ApplePS2Keyboard::dispatchKeyboardEventWithPacket(const UInt8* packet) } } break; +#ifdef DEBUG + default: + IOLog("VoodooPS2Keyboard: Unhandled keycode: %#.4X\n", keyCode); +#endif } // If keyboard input is disabled drop the key code.. - if (_disableInput) + if (_disableInput && goingDown) keyCode=0; #ifdef DEBUG @@ -1699,14 +1746,6 @@ bool ApplePS2Keyboard::dispatchKeyboardEventWithPacket(const UInt8* packet) // special cases switch (adbKeyCode) { - case 0x90: - case 0x91: - if (_brightnessLevels) - { - modifyScreenBrightness(adbKeyCode, goingDown); - adbKeyCode = DEADKEY; - } - break; case 0x92: // eject if (0 == _PS2modifierState) { @@ -1884,7 +1923,7 @@ void ApplePS2Keyboard::setKeyboardEnable(bool enable) // (keyboard enable/disable command) TPS2Request<2> request; request.commands[0].command = kPS2C_WriteDataPort; - request.commands[0].inOrOut = enable ? kDP_Enable : kDP_SetDefaultsAndDisable; + request.commands[0].inOrOut = enable ? kDP_Enable : kDP_SetDefaults; request.commands[1].command = kPS2C_ReadDataPortAndCompare; request.commands[1].inOrOut = kSC_Acknowledge; request.commandsCount = 2; diff --git a/VoodooPS2Keyboard/VoodooPS2Keyboard.h b/VoodooPS2Keyboard/VoodooPS2Keyboard.h index d74b51ce..797120f9 100644 --- a/VoodooPS2Keyboard/VoodooPS2Keyboard.h +++ b/VoodooPS2Keyboard/VoodooPS2Keyboard.h @@ -25,12 +25,9 @@ #include #include "../VoodooPS2Controller/ApplePS2KeyboardDevice.h" -#include "LegacyIOHIKeyboard.h" +#include -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winconsistent-missing-override" #include -#pragma clang diagnostic pop #include @@ -48,13 +45,13 @@ (KBV_BITS_PER_UNIT-1))/KBV_BITS_PER_UNIT) #define KBV_KEYDOWN(n) \ - (_keyBitVector)[((n)>>KBV_BITS_SHIFT)] |= (1 << ((n) & KBV_BITS_MASK)) + (_keyBitVector)[((n)>>KBV_BITS_SHIFT)] |= (1U << ((n) & KBV_BITS_MASK)) #define KBV_KEYUP(n) \ - (_keyBitVector)[((n)>>KBV_BITS_SHIFT)] &= ~(1 << ((n) & KBV_BITS_MASK)) + (_keyBitVector)[((n)>>KBV_BITS_SHIFT)] &= ~(1U << ((n) & KBV_BITS_MASK)) #define KBV_IS_KEYDOWN(n) \ - (((_keyBitVector)[((n)>>KBV_BITS_SHIFT)] & (1 << ((n) & KBV_BITS_MASK))) != 0) + (((_keyBitVector)[((n)>>KBV_BITS_SHIFT)] & (1U << ((n) & KBV_BITS_MASK))) != 0) #define KBV_NUM_SCANCODES 256 @@ -101,17 +98,16 @@ class EXPORT ApplePS2Keyboard : public IOHIKeyboard int _logscancodes; UInt32 _f12ejectdelay; enum { kTimerSleep, kTimerEject } _timerFunc; + bool _remapPrntScr; + bool _numLockSupport; + bool _numLockOnAtBoot; // dealing with sleep key delay IOTimerEventSource* _sleepEjectTimer; UInt32 _maxsleeppresstime; - // ACPI support for screen brightness - IOACPIPlatformDevice * _provider; - int * _brightnessLevels; - int _brightnessCount; - // ACPI support for keyboard backlight + IOACPIPlatformDevice * _provider; int * _backlightLevels; int _backlightCount; @@ -142,6 +138,8 @@ class EXPORT ApplePS2Keyboard : public IOHIKeyboard void modifyScreenBrightness(int adbKeyCode, bool goingDown); inline bool checkModifierState(UInt16 mask) { return mask == (_PS2modifierState & mask); } + inline bool checkModifierStateAny(UInt16 mask) + { return (_PS2modifierState & mask); } void loadCustomPS2Map(OSArray* pArray); void loadBreaklessPS2(OSDictionary* dict, const char* name); diff --git a/VoodooPS2Mouse/VoodooPS2Mouse-Info.plist b/VoodooPS2Mouse/VoodooPS2Mouse-Info.plist index 6de86ef2..1efb2d9f 100644 --- a/VoodooPS2Mouse/VoodooPS2Mouse-Info.plist +++ b/VoodooPS2Mouse/VoodooPS2Mouse-Info.plist @@ -26,10 +26,6 @@ CFBundleIdentifier as.acidanthera.voodoo.driver.PS2Mouse - HIDPointerAccelerationType - HIDTrackpadAcceleration - HIDScrollAccelerationType - HIDTrackpadScrollAcceleration IOClass ApplePS2Mouse IOProviderClass @@ -38,37 +34,12 @@ Default - ActLikeTrackpad - ButtonCount 3 - Darwin 16+ - - ApplePreferenceCapability - - ApplePreferenceIdentifier - com.apple.AppleMultitouchTrackpad - MT Built-in - - MTHIDDevice - - SupportsGestureScrolling - - TrackpadEmbedded - - TrackpadFourFingerGestures - - TrackpadSecondaryClickCorners - - TrackpadThreeFingerDrag - - DefaultResolution 240 DisableDevice - DisableLEDUpdating - FakeMiddleButton ForceDefaultResolution @@ -81,78 +52,16 @@ 0 MouseYInverter 1 - ProcessBluetoothMouseStopsTrackpad - - ProcessUSBMouseStopsTrackpad - - QuietTimeAfterTyping - 500000000 ResolutionMode 3 ScrollResolution 5 ScrollYInverter 1 - TrackpadScroll - WakeDelay 1000 - HPQOEM - - 1411 - ProBook - 1619 - ProBook - 161C - ProBook - 164F - ProBook - 167C - ProBook - 167E - ProBook - 1680 - ProBook - 179B - ProBook - 179C - ProBook - 17A9 - ProBook - 17F0 - ProBook - 17F3 - ProBook - 17F6 - ProBook - 1942 - ProBook - 1949 - ProBook - 198F - ProBook - ProBook - - ActLikeTrackpad - - DisableDevice - - - ProBook-102 - ProBook - ProBook-87 - ProBook - - ProductID - 547 - RM,deliverNotifications - - USBMouseStopsTrackpad - 0 - VendorID - 1452 OSBundleLibraries diff --git a/VoodooPS2Mouse/VoodooPS2Mouse.cpp b/VoodooPS2Mouse/VoodooPS2Mouse.cpp index 0647c666..2bbebf02 100644 --- a/VoodooPS2Mouse/VoodooPS2Mouse.cpp +++ b/VoodooPS2Mouse/VoodooPS2Mouse.cpp @@ -20,16 +20,13 @@ * @APPLE_LICENSE_HEADER_END@ */ -#include "LegacyIOService.h" +#include -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winconsistent-missing-override" #include #include #include #include #include -#pragma clang diagnostic pop #include "VoodooPS2Controller.h" #include "VoodooPS2Mouse.h" @@ -77,22 +74,11 @@ bool ApplePS2Mouse::init(OSDictionary * dict) _type = kMouseTypeStandard; _buttonCount = 3; _mouseInfoBytes = (UInt32)-1; - ignoreall = false; - ledpresent = false; resmode = -1; forcesetres = false; scrollres = 10; - actliketrackpad = false; - keytime = 0; - maxaftertyping = 500000000; - buttonmask = ~0; - scroll = true; - noled = false; wakedelay = 1000; - usb_mouse_stops_trackpad = true; _cmdGate = 0; - _processusbmouse = true; - _processbluetoothmouse = true; // state for middle button _buttonTimer = 0; @@ -126,22 +112,12 @@ void ApplePS2Mouse::setParamPropertiesGated(OSDictionary * config) const struct {const char *name; int *var;} boolvars[]={ {"ForceDefaultResolution", &forceres}, {"ForceSetResolution", &forcesetres}, - {"ActLikeTrackpad", &actliketrackpad}, - {"DisableLEDUpdating", &noled}, {"FakeMiddleButton", &_fakemiddlebutton}, - {"ProcessUSBMouseStopsTrackpad", &_processusbmouse}, - {"ProcessBluetoothMouseStopsTrackpad", &_processbluetoothmouse}, }; const struct {const char* name; bool* var;} lowbitvars[]={ - {"TrackpadScroll", &scroll}, - {"OutsidezoneNoAction When Typing", &outzone_wt}, - {"PalmNoAction Permanent", &palm}, - {"PalmNoAction When Typing", &palm_wt}, - {"USBMouseStopsTrackpad", &usb_mouse_stops_trackpad}, }; const struct {const char* name; uint64_t* var; } int64vars[]={ {"MiddleClickTime", &_maxmiddleclicktime}, - {"QuietTimeAfterTyping", &maxaftertyping}, }; @@ -176,12 +152,6 @@ void ApplePS2Mouse::setParamPropertiesGated(OSDictionary * config) *int32vars[i].var = num->unsigned32BitValue(); setProperty(int32vars[i].name, *int32vars[i].var, 32); } - - // disable trackpad when USB mouse is plugged in and this functionality is requested - if (attachedHIDPointerDevices && attachedHIDPointerDevices->getCount() > 0) { - ignoreall = usb_mouse_stops_trackpad; - updateTouchpadLED(); - } // convert to IOFixed format... defres <<= 16; @@ -215,42 +185,6 @@ IOReturn ApplePS2Mouse::setProperties(OSObject *props) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2Mouse::injectVersionDependentProperties(OSDictionary *config) -{ - // inject properties specific to the version of Darwin that is runnning... - char buf[32]; - 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)))) - break; - // check for "Darwin major.x" - snprintf(buf, sizeof(buf), "Darwin %d.x", version_major); - 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+")))) - break; - } while (0); - - if (dict) - { - // found version specific properties above, inject... - 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)) - setProperty(key, value); - } - iter->release(); - } - } -} - ApplePS2Mouse* ApplePS2Mouse::probe(IOService * provider, SInt32 * score) { DEBUG_LOG("ApplePS2Mouse::probe entered...\n"); @@ -287,46 +221,31 @@ ApplePS2Mouse* ApplePS2Mouse::probe(IOService * provider, SInt32 * score) // load settings setParamPropertiesGated(config); - if (actliketrackpad) - injectVersionDependentProperties(config); OSSafeReleaseNULL(config); } - // remove some properties so system doesn't think it is a trackpad - // this should cause "Product" = "Mouse" in ioreg. - if (!actliketrackpad) - { - removeProperty("VendorID"); - removeProperty("ProductID"); - removeProperty("HIDPointerAccelerationType"); - removeProperty("HIDScrollAccelerationType"); - removeProperty("TrackpadScroll"); - } - // // Check to see if acknowledges are being received for commands to the mouse. // // (get information command) - TPS2Request<6> request; - request.commands[0].command = kPS2C_WriteCommandPort; - request.commands[0].inOrOut = kCP_TransmitToMouse; - request.commands[1].command = kPS2C_WriteDataPort; - request.commands[1].inOrOut = kDP_GetMouseInformation; - request.commands[2].command = kPS2C_ReadDataPortAndCompare; - request.commands[2].inOrOut = kSC_Acknowledge; + TPS2Request<5> request; + request.commands[0].command = kPS2C_WriteDataPort; + request.commands[0].inOrOut = kDP_GetMouseInformation; + request.commands[1].command = kPS2C_ReadDataPortAndCompare; + request.commands[1].inOrOut = kSC_Acknowledge; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; request.commands[3].command = kPS2C_ReadDataPort; request.commands[3].inOrOut = 0; request.commands[4].command = kPS2C_ReadDataPort; request.commands[4].inOrOut = 0; - request.commands[5].command = kPS2C_ReadDataPort; - request.commands[5].inOrOut = 0; - request.commandsCount = 6; + request.commandsCount = 5; assert(request.commandsCount <= countof(request.commands)); device->submitRequestAndBlock(&request); DEBUG_LOG("ApplePS2Mouse::probe leaving.\n"); - return 6 == request.commandsCount ? this : 0; + return 5 == request.commandsCount ? this : 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -375,9 +294,6 @@ bool ApplePS2Mouse::start(IOService * provider) resetMouse(); pWorkLoop->addEventSource(_cmdGate); - - attachedHIDPointerDevices = OSSet::withCapacity(1); - registerHIDPointerNotifications(); // // Setup button timer event source @@ -410,11 +326,8 @@ bool ApplePS2Mouse::start(IOService * provider) // Request message registration for keyboard to trackpad communication // - if (actliketrackpad) - { - //setProperty(kDeliverNotifications, true); - } - + setProperty(kDeliverNotifications, true); + return true; } @@ -429,9 +342,6 @@ void ApplePS2Mouse::stop(IOService * provider) // assert(_device == provider); - - unregisterHIDPointerNotifications(); - OSSafeReleaseNULL(attachedHIDPointerDevices); // // Disable the mouse itself, so that it may stop reporting mouse events. @@ -498,48 +408,24 @@ void ApplePS2Mouse::resetMouse() // ... it is just going to time out... and then later show up in the // input stream unexpectedly. - TPS2Request<8> request; - request.commands[0].command = kPS2C_WriteCommandPort; - request.commands[0].inOrOut = kCP_TransmitToMouse; + TPS2Request<6> request; + request.commands[0].command = kPS2C_WriteDataPort; + request.commands[0].inOrOut = kDP_SetDefaults; request.commands[1].command = kPS2C_WriteDataPort; - request.commands[1].inOrOut = kDP_SetDefaults; - request.commands[2].command = kPS2C_WriteCommandPort; - request.commands[2].inOrOut = kCP_TransmitToMouse; - request.commands[3].command = kPS2C_WriteDataPort; - request.commands[3].inOrOut = kDP_GetMouseInformation; - request.commands[4].command = kPS2C_ReadDataPortAndCompare; - request.commands[4].inOrOut = kSC_Acknowledge; + request.commands[1].inOrOut = kDP_GetMouseInformation; + request.commands[2].command = kPS2C_ReadDataPortAndCompare; + request.commands[2].inOrOut = kSC_Acknowledge; + request.commands[3].command = kPS2C_ReadDataPort; + request.commands[3].inOrOut = 0; + request.commands[4].command = kPS2C_ReadDataPort; + request.commands[4].inOrOut = 0; request.commands[5].command = kPS2C_ReadDataPort; request.commands[5].inOrOut = 0; - request.commands[6].command = kPS2C_ReadDataPort; - request.commands[6].inOrOut = 0; - request.commands[7].command = kPS2C_ReadDataPort; - request.commands[7].inOrOut = 0; - request.commandsCount = 8; + request.commandsCount = 6; assert(request.commandsCount <= countof(request.commands)); _device->submitRequestAndBlock(&request); - if (8 != request.commandsCount) + if (6 != request.commandsCount) DEBUG_LOG("%s: reset mouse sequence failed: %d\n", getName(), request.commandsCount); - - // Now deal with Synaptics specifics (ActLikeTrackpad trick)... - ledpresent = false; - do if (actliketrackpad && !noled) - { - // do Synaptics specific, but only if it is Synaptics device - UInt8 buf3[3]; - if (!getTouchPadData(0x0, buf3) || (0x46 != buf3[1] && 0x47 != buf3[1])) - break; - // it is Synaptics, now test for LED capability... - if (!getTouchPadData(0x2, buf3) || !(buf3[0] & 0x80)) - break; - int nExtendedQueries = (buf3[0] & 0x70) >> 4; - // check LED capability if query is supported - if (nExtendedQueries >= 1 && getTouchPadData(0x9, buf3)) - { - ledpresent = (buf3[0] >> 6) & 1; - DEBUG_LOG("%s: ledpresent=%d\n", getName(), ledpresent); - } - } while (false); // // Obtain our mouse's resolution and sampling rate. @@ -588,10 +474,7 @@ void ApplePS2Mouse::resetMouse() // be present to enable acceleration for Z-axis movement. // setProperty(kIOHIDScrollResolutionKey, (scrollres << 16), 32); - if (!actliketrackpad) - setProperty(kIOHIDScrollAccelerationTypeKey, kIOHIDMouseAccelerationType); - else - setProperty(kIOHIDScrollAccelerationTypeKey, kIOHIDTrackpadAccelerationType); + setProperty(kIOHIDScrollAccelerationTypeKey, kIOHIDMouseAccelerationType); } else { @@ -600,10 +483,8 @@ void ApplePS2Mouse::resetMouse() removeProperty(kIOHIDScrollResolutionKey); removeProperty(kIOHIDScrollAccelerationTypeKey); } - if (!actliketrackpad) - setProperty(kIOHIDPointerAccelerationTypeKey, kIOHIDMouseAccelerationType); - else - setProperty(kIOHIDPointerAccelerationTypeKey, kIOHIDTrackpadAccelerationType); + + setProperty(kIOHIDPointerAccelerationTypeKey, kIOHIDMouseAccelerationType); // simulate three buttons with only two buttons if enabled @@ -739,7 +620,7 @@ void ApplePS2Mouse::onButtonTimer(void) UInt32 ApplePS2Mouse::middleButton(UInt32 buttons, uint64_t now_abs, MBComingFrom from) { - if (!_fakemiddlebutton || _buttonCount <= 2 || (ignoreall && fromMouse == from)) + if (!_fakemiddlebutton || _buttonCount <= 2) return buttons; // cancel timer if we see input before timeout has fired, but after expired @@ -908,37 +789,24 @@ void ApplePS2Mouse::dispatchRelativePointerEventWithPacket(UInt8 * packet, // PS2 mice is -8 to +7, thus the upper four bits are just a sign // bit. If we just sign extend the lower four bits, the scroll // calculation works for normal scrollwheel mice and five button mice. - if (!actliketrackpad || scroll) - dz = (SInt16)(((SInt8)(packet[3] << 4)) >> 4); + dz = (SInt16)(((SInt8)(packet[3] << 4)) >> 4); } buttons = middleButton(buttons, now_abs, fromMouse); lastbuttons = buttons; - // ignore button 1 and 2 (could be simulated by trackpad) if just after typing - if (palm_wt || outzone_wt) - { - if (now_ns-keytime <= maxaftertyping) - buttonmask = ~(buttons & 0x3); - else - buttonmask = ~0; - buttons &= buttonmask; - } - - if (!ignoreall) - dispatchRelativePointerEventX(dx, mouseyinverter*dy, buttons, now_abs); + dispatchRelativePointerEventX(dx, mouseyinverter*dy, buttons, now_abs); - if ( dz && (!(palm_wt || outzone_wt) || now_ns-keytime > maxaftertyping)) + if (dz) { // // The Z counter is negative on an upwards scroll (away from the user), // and positive when scrolling downwards. Invert this before passing to // HID/CG. // - if (!ignoreall) - dispatchScrollWheelEventX(-scrollyinverter*dz, 0, 0, now_abs); + dispatchScrollWheelEventX(-scrollyinverter*dz, 0, 0, now_abs); } - + #ifdef DEBUG_VERBOSE IOLog("ps2m: dx=%d, dy=%d, dz=%d, buttons=%d\n", dx, dy, dz, buttons); #endif @@ -958,14 +826,12 @@ void ApplePS2Mouse::setMouseEnable(bool enable) // // (mouse enable/disable command) - TPS2Request<3> request; - request.commands[0].command = kPS2C_WriteCommandPort; - request.commands[0].inOrOut = kCP_TransmitToMouse; - request.commands[1].command = kPS2C_WriteDataPort; - request.commands[1].inOrOut = enable ? kDP_Enable : kDP_SetDefaultsAndDisable; - request.commands[2].command = kPS2C_ReadDataPortAndCompare; - request.commands[2].inOrOut = kSC_Acknowledge; - request.commandsCount = 3; + TPS2Request<2> request; + request.commands[0].command = kPS2C_WriteDataPort; + request.commands[0].inOrOut = enable ? kDP_Enable : kDP_SetDefaultsAndDisable; + request.commands[1].command = kPS2C_ReadDataPortAndCompare; + request.commands[1].inOrOut = kSC_Acknowledge; + request.commandsCount = 2; assert(request.commandsCount <= countof(request.commands)); _device->submitRequestAndBlock(&request); } @@ -984,20 +850,16 @@ void ApplePS2Mouse::setMouseSampleRate(UInt8 sampleRate) // // (set mouse sample rate command) - TPS2Request<6> request; - request.commands[0].command = kPS2C_WriteCommandPort; - request.commands[0].inOrOut = kCP_TransmitToMouse; - request.commands[1].command = kPS2C_WriteDataPort; - request.commands[1].inOrOut = kDP_SetMouseSampleRate; - request.commands[2].command = kPS2C_ReadDataPortAndCompare; - request.commands[2].inOrOut = kSC_Acknowledge; - request.commands[3].command = kPS2C_WriteCommandPort; - request.commands[3].inOrOut = kCP_TransmitToMouse; - request.commands[4].command = kPS2C_WriteDataPort; - request.commands[4].inOrOut = sampleRate; - request.commands[5].command = kPS2C_ReadDataPortAndCompare; - request.commands[5].inOrOut = kSC_Acknowledge; - request.commandsCount = 6; + TPS2Request<4> request; + request.commands[0].command = kPS2C_WriteDataPort; + request.commands[0].inOrOut = kDP_SetMouseSampleRate; + request.commands[1].command = kPS2C_ReadDataPortAndCompare; + request.commands[1].inOrOut = kSC_Acknowledge; + request.commands[2].command = kPS2C_WriteDataPort; + request.commands[2].inOrOut = sampleRate; + request.commands[3].command = kPS2C_ReadDataPortAndCompare; + request.commands[3].inOrOut = kSC_Acknowledge; + request.commandsCount = 4; assert(request.commandsCount <= countof(request.commands)); _device->submitRequestAndBlock(&request); } @@ -1021,20 +883,16 @@ void ApplePS2Mouse::setMouseResolution(UInt8 resolution) DEBUG_LOG("%s::setMouseResolution(0x%x)\n", getName(), resolution); // (set mouse resolution command) - TPS2Request<6> request; - request.commands[0].command = kPS2C_WriteCommandPort; - request.commands[0].inOrOut = kCP_TransmitToMouse; - request.commands[1].command = kPS2C_WriteDataPort; - request.commands[1].inOrOut = kDP_SetMouseResolution; - request.commands[2].command = kPS2C_ReadDataPortAndCompare; - request.commands[2].inOrOut = kSC_Acknowledge; - request.commands[3].command = kPS2C_WriteCommandPort; - request.commands[3].inOrOut = kCP_TransmitToMouse; - request.commands[4].command = kPS2C_WriteDataPort; - request.commands[4].inOrOut = resolution; - request.commands[5].command = kPS2C_ReadDataPortAndCompare; - request.commands[5].inOrOut = kSC_Acknowledge; - request.commandsCount = 6; + TPS2Request<4> request; + request.commands[0].command = kPS2C_WriteDataPort; + request.commands[0].inOrOut = kDP_SetMouseResolution; + request.commands[1].command = kPS2C_ReadDataPortAndCompare; + request.commands[1].inOrOut = kSC_Acknowledge; + request.commands[2].command = kPS2C_WriteDataPort; + request.commands[2].inOrOut = resolution; + request.commands[3].command = kPS2C_ReadDataPortAndCompare; + request.commands[3].inOrOut = kSC_Acknowledge; + request.commandsCount = 4; assert(request.commandsCount <= countof(request.commands)); _device->submitRequestAndBlock(&request); } @@ -1139,28 +997,26 @@ UInt32 ApplePS2Mouse::getMouseInformation() UInt32 returnValue = (UInt32)(-1); // (get information command) - TPS2Request<6> request; - request.commands[0].command = kPS2C_WriteCommandPort; - request.commands[0].inOrOut = kCP_TransmitToMouse; - request.commands[1].command = kPS2C_WriteDataPort; - request.commands[1].inOrOut = kDP_GetMouseInformation; - request.commands[2].command = kPS2C_ReadDataPortAndCompare; - request.commands[2].inOrOut = kSC_Acknowledge; + TPS2Request<5> request; + request.commands[0].command = kPS2C_WriteDataPort; + request.commands[0].inOrOut = kDP_GetMouseInformation; + request.commands[1].command = kPS2C_ReadDataPortAndCompare; + request.commands[1].inOrOut = kSC_Acknowledge; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; request.commands[3].command = kPS2C_ReadDataPort; request.commands[3].inOrOut = 0; request.commands[4].command = kPS2C_ReadDataPort; request.commands[4].inOrOut = 0; - request.commands[5].command = kPS2C_ReadDataPort; - request.commands[5].inOrOut = 0; - request.commandsCount = 6; + request.commandsCount = 5; assert(request.commandsCount <= countof(request.commands)); _device->submitRequestAndBlock(&request); - if (request.commandsCount == 6) // success? + if (request.commandsCount == 5) // success? { - returnValue = ((UInt32)request.commands[3].inOrOut << 16) | - ((UInt32)request.commands[4].inOrOut << 8 ) | - ((UInt32)request.commands[5].inOrOut); + returnValue = ((UInt32)request.commands[2].inOrOut << 16) | + ((UInt32)request.commands[3].inOrOut << 8 ) | + ((UInt32)request.commands[4].inOrOut); } DEBUG_LOG("%s::getMouseInformation() returns 0x%x\n", getName(), returnValue); @@ -1184,21 +1040,19 @@ UInt8 ApplePS2Mouse::getMouseID() UInt8 returnValue = (UInt8)(-1); // (get information command) - TPS2Request<4> request; - request.commands[0].command = kPS2C_WriteCommandPort; - request.commands[0].inOrOut = kCP_TransmitToMouse; - request.commands[1].command = kPS2C_WriteDataPort; - request.commands[1].inOrOut = kDP_GetId; - request.commands[2].command = kPS2C_ReadDataPortAndCompare; - request.commands[2].inOrOut = kSC_Acknowledge; - request.commands[3].command = kPS2C_ReadDataPort; - request.commands[3].inOrOut = 0; - request.commandsCount = 4; + TPS2Request<3> request; + request.commands[0].command = kPS2C_WriteDataPort; + request.commands[0].inOrOut = kDP_GetId; + request.commands[1].command = kPS2C_ReadDataPortAndCompare; + request.commands[1].inOrOut = kSC_Acknowledge; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; + request.commandsCount = 3; assert(request.commandsCount <= countof(request.commands)); _device->submitRequestAndBlock(&request); - if (request.commandsCount == 4) // success? - returnValue = request.commands[3].inOrOut; + if (request.commandsCount == 3) // success? + returnValue = request.commands[2].inOrOut; DEBUG_LOG("%s::getMouseID returns 0x%x\n", getName(), returnValue); return returnValue; @@ -1223,360 +1077,6 @@ void ApplePS2Mouse::setDevicePowerState( UInt32 whatToDo ) // Enable mouse and restore state. resetMouse(); - - // update touchpad LED after sleep - updateTouchpadLED(); - break; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -IOReturn ApplePS2Mouse::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 is true when trackpad has been disabled - if (enable == ignoreall) - { - // save state, and update LED - ignoreall = !enable; - updateTouchpadLED(); - } break; - } - - 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; - switch (pInfo->adbKeyCode) - { - // don't store key time for modifier keys going down - case 0x38: // left shift - case 0x3c: // right shift - case 0x3b: // left control - case 0x3e: // right control - case 0x3a: // left alt (command) - case 0x3d: // right alt - case 0x37: // left windows (option) - case 0x36: // right windows - if (pInfo->goingDown) - break; - default: - keytime = pInfo->time; - } - break; - } - } - - return kIOReturnSuccess; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// -// This code is specific to Synaptics Touchpads that have an LED indicator, but -// it does no harm to Synaptics units that don't have an LED. -// -// Generally it is used to indicate that the touchpad has been made inactive. -// -// In the case of this package, we can disable the touchpad with both keyboard -// and the touchpad itself. -// -// Linux sources were very useful in figuring this out... -// This patch to support HP Probook Synaptics LED in Linux was where found the -// information: -// https://github.com/mmonaco/PKGBUILDs/blob/master/synaptics-led/synled.patch -// -// To quote from the email: -// -// From: Takashi Iwai -// Date: Sun, 16 Sep 2012 14:19:41 -0600 -// Subject: [PATCH] input: Add LED support to Synaptics device -// -// The new Synaptics devices have an LED on the top-left corner. -// This patch adds a new LED class device to control it. It's created -// dynamically upon synaptics device probing. -// -// The LED is controlled via the command 0x0a with parameters 0x88 or 0x10. -// This seems only on/off control although other value might be accepted. -// -// The detection of the LED isn't clear yet. It should have been the new -// capability bits that indicate the presence, but on real machines, it -// doesn't fit. So, for the time being, the driver checks the product id -// in the ext capability bits and assumes that LED exists on the known -// devices. -// -// Signed-off-by: Takashi Iwai -// - -//REVIEW: this code copied from VoodooPS2SynapticsTouchPad.cpp -// would be nice to figure out how to share this code between the two kexts - -void ApplePS2Mouse::updateTouchpadLED() -{ - if (ledpresent && !noled) - setTouchpadLED(ignoreall ? 0x88 : 0x10); -} - -bool ApplePS2Mouse::setTouchpadLED(UInt8 touchLED) -{ - TPS2Request<12> request; - - // send NOP before special command sequence - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[0].inOrOut = kDP_SetMouseScaling1To1; - - // 4 set resolution commands, each encode 2 data bits of LED level - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[1].inOrOut = kDP_SetMouseResolution; - request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[2].inOrOut = (touchLED >> 6) & 0x3; - - request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[3].inOrOut = kDP_SetMouseResolution; - request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[4].inOrOut = (touchLED >> 4) & 0x3; - - request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[5].inOrOut = kDP_SetMouseResolution; - request.commands[6].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[6].inOrOut = (touchLED >> 2) & 0x3; - - request.commands[7].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[7].inOrOut = kDP_SetMouseResolution; - request.commands[8].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[8].inOrOut = (touchLED >> 0) & 0x3; - - // Set sample rate 10 (10 is command for setting LED) - request.commands[9].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[9].inOrOut = kDP_SetMouseSampleRate; - request.commands[10].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[10].inOrOut = 10; // 0x0A command for setting LED - - // finally send NOP command to end the special sequence - request.commands[11].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[11].inOrOut = kDP_SetMouseScaling1To1; - request.commandsCount = 12; - assert(request.commandsCount <= countof(request.commands)); - _device->submitRequestAndBlock(&request); - - return 12 == request.commandsCount; -} - -bool ApplePS2Mouse::getTouchPadData(UInt8 dataSelector, UInt8 buf3[]) -{ - TPS2Request<14> request; - - // Disable stream mode before the command sequence. - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[0].inOrOut = kDP_SetDefaultsAndDisable; - - // 4 set resolution commands, each encode 2 data bits. - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[1].inOrOut = kDP_SetMouseResolution; - request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[2].inOrOut = (dataSelector >> 6) & 0x3; - - request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[3].inOrOut = kDP_SetMouseResolution; - request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[4].inOrOut = (dataSelector >> 4) & 0x3; - - request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[5].inOrOut = kDP_SetMouseResolution; - request.commands[6].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[6].inOrOut = (dataSelector >> 2) & 0x3; - - request.commands[7].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[7].inOrOut = kDP_SetMouseResolution; - request.commands[8].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[8].inOrOut = (dataSelector >> 0) & 0x3; - - // Read response bytes. - request.commands[9].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[9].inOrOut = kDP_GetMouseInformation; - request.commands[10].command = kPS2C_ReadDataPort; - request.commands[10].inOrOut = 0; - request.commands[11].command = kPS2C_ReadDataPort; - request.commands[11].inOrOut = 0; - request.commands[12].command = kPS2C_ReadDataPort; - request.commands[12].inOrOut = 0; - request.commands[13].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[13].inOrOut = kDP_SetDefaultsAndDisable; - request.commandsCount = 14; - assert(request.commandsCount <= countof(request.commands)); - _device->submitRequestAndBlock(&request); - if (14 != request.commandsCount) - return false; - - // store results - buf3[0] = request.commands[10].inOrOut; - buf3[1] = request.commands[11].inOrOut; - buf3[2] = request.commands[12].inOrOut; - - return true; -} - -void ApplePS2Mouse::registerHIDPointerNotifications() -{ - IOServiceMatchingNotificationHandler notificationHandler = OSMemberFunctionCast(IOServiceMatchingNotificationHandler, this, &ApplePS2Mouse::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"); - - 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"); - 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); - OSSafeReleaseNULL(matchingDictionary); } } - -void ApplePS2Mouse::unregisterHIDPointerNotifications() -{ - // Free device matching notifiers - if (usb_hid_publish_notify) { - usb_hid_publish_notify->remove(); - OSSafeReleaseNULL(usb_hid_publish_notify); - } - - if (usb_hid_terminate_notify) { - usb_hid_terminate_notify->remove(); - OSSafeReleaseNULL(usb_hid_terminate_notify); - } - - if (bluetooth_hid_publish_notify) { - bluetooth_hid_publish_notify->remove(); - OSSafeReleaseNULL(bluetooth_hid_publish_notify); - } - - if (bluetooth_hid_terminate_notify) { - bluetooth_hid_terminate_notify->remove(); - OSSafeReleaseNULL(bluetooth_hid_terminate_notify); - } - - attachedHIDPointerDevices->flushCollection(); -} - -void ApplePS2Mouse::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")); - - if (propDeviceClass != NULL) { - - long classOfDevice = propDeviceClass->unsigned32BitValue(); - - long deviceClassMajor = (classOfDevice & 0x1F00) >> 8; - long deviceClassMinor = (classOfDevice & 0xFF) >> 2; - - if (deviceClassMajor == kBluetoothDeviceClassMajorPeripheral) { // Bluetooth peripheral devices - - long deviceClassMinor1 = (deviceClassMinor) & 0x30; - long deviceClassMinor2 = (deviceClassMinor) & 0x0F; - - if (deviceClassMinor1 == kBluetoothDeviceClassMinorPeripheral1Pointing || // Seperate pointing device - deviceClassMinor1 == kBluetoothDeviceClassMinorPeripheral1Combo) // Combo bluetooth keyboard/touchpad - { - if (deviceClassMinor2 == kBluetoothDeviceClassMinorPeripheral2Unclassified || // Mouse - 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()); - } - } - } - } - } - - 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 - ignoreall = false; - } - } -} - -bool ApplePS2Mouse::notificationHIDAttachedHandler(void * refCon, - IOService * newService, - IONotifier * notifier) -{ - if (_cmdGate) { // defensive - _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2Mouse::notificationHIDAttachedHandlerGated), newService, notifier); - } - - return true; -} - diff --git a/VoodooPS2Mouse/VoodooPS2Mouse.h b/VoodooPS2Mouse/VoodooPS2Mouse.h index ef685462..a1ff789e 100644 --- a/VoodooPS2Mouse/VoodooPS2Mouse.h +++ b/VoodooPS2Mouse/VoodooPS2Mouse.h @@ -24,13 +24,10 @@ #define _APPLEPS2MOUSE_H #include "../VoodooPS2Controller/ApplePS2MouseDevice.h" -#include "LegacyIOHIPointing.h" +#include -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winconsistent-missing-override" #include #include -#pragma clang diagnostic pop // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Local Declarations @@ -77,28 +74,7 @@ class EXPORT ApplePS2Mouse : public IOHIPointing int forcesetres; int32_t resmode; int32_t scrollres; - int actliketrackpad; - uint64_t keytime; - uint64_t maxaftertyping; - UInt32 buttonmask; - bool outzone_wt, palm, palm_wt; - bool scroll; - bool ignoreall; - bool ledpresent; - int noled; int wakedelay; - bool usb_mouse_stops_trackpad; - - int _processusbmouse; - int _processbluetoothmouse; - - OSSet* attachedHIDPointerDevices; - - IONotifier* usb_hid_publish_notify; // Notification when an USB mouse HID device is connected - IONotifier* usb_hid_terminate_notify; // Notification when an USB mouse HID device is disconnected - - IONotifier* bluetooth_hid_publish_notify; // Notification when a bluetooth HID device is connected - IONotifier* bluetooth_hid_terminate_notify; // Notification when a bluetooth HID device is disconnected // for middle button simulation enum mbuttonstate @@ -132,19 +108,9 @@ class EXPORT ApplePS2Mouse : public IOHIPointing virtual void initMouse(); virtual void resetMouse(); virtual void setDevicePowerState(UInt32 whatToDo); - - void updateTouchpadLED(); - bool setTouchpadLED(UInt8 touchLED); - bool getTouchPadData(UInt8 dataSelector, UInt8 buf3[]); - void setParamPropertiesGated(OSDictionary * dict); - void injectVersionDependentProperties(OSDictionary* dict); - - void registerHIDPointerNotifications(); - void unregisterHIDPointerNotifications(); + void setParamPropertiesGated(OSDictionary * dict); - void notificationHIDAttachedHandlerGated(IOService * newService, IONotifier * notifier); - bool notificationHIDAttachedHandler(void * refCon, IOService * newService, IONotifier * notifier); protected: IOItemCount buttonCount() override; IOFixed resolution() override; @@ -171,8 +137,6 @@ class EXPORT ApplePS2Mouse : public IOHIPointing IOReturn setParamProperties(OSDictionary * dict) override; IOReturn setProperties (OSObject *props) override; - - IOReturn message(UInt32 type, IOService* provider, void* argument) override; }; #endif /* _APPLEPS2MOUSE_H */ diff --git a/VoodooPS2Trackpad/VoodooPS2ALPSGlidePoint.cpp b/VoodooPS2Trackpad/VoodooPS2ALPSGlidePoint.cpp index 0358e74c..85ae570a 100644 --- a/VoodooPS2Trackpad/VoodooPS2ALPSGlidePoint.cpp +++ b/VoodooPS2Trackpad/VoodooPS2ALPSGlidePoint.cpp @@ -2,13 +2,13 @@ * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.2 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. - * + * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -16,76 +16,237 @@ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ -#include "LegacyIOService.h" +#include +#include "VoodooPS2ALPSGlidePoint.h" #include #include +#include +#include +#include +#include +#include #include "VoodooPS2Controller.h" -#include "VoodooPS2ALPSGlidePoint.h" +#include "VoodooInputMultitouch/VoodooInputTransducer.h" +#include "VoodooInputMultitouch/VoodooInputMessages.h" + +#undef NULL +#define NULL 0 enum { - // - // - kTapEnabled = 0x01 + kTapEnabled = 0x01 +}; + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#define BIT(x) (1 << (x)) + + +/* ============================================================================================== */ +/* ===============================||\\ alps.c Definitions //||=================================== */ +/* ============================================================================================== */ + +/* + * Definitions for ALPS version 3 and 4 command mode protocol + */ +#define ALPS_CMD_NIBBLE_10 0x01f2 + +#define ALPS_REG_BASE_RUSHMORE 0xc2c0 +#define ALPS_REG_BASE_V7 0xc2c0 +#define ALPS_REG_BASE_PINNACLE 0x0000 + +static const struct alps_nibble_commands alps_v3_nibble_commands[] = { + { kDP_MouseSetPoll, 0x00 }, /* 0 no send/recv */ + { kDP_SetDefaults, 0x00 }, /* 1 no send/recv */ + { kDP_SetMouseScaling2To1, 0x00 }, /* 2 no send/recv */ + { kDP_SetMouseSampleRate | 0x1000, 0x0a }, /* 3 send=1 recv=0 */ + { kDP_SetMouseSampleRate | 0x1000, 0x14 }, /* 4 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x28 }, /* 5 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x3c }, /* 6 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x50 }, /* 7 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x64 }, /* 8 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0xc8 }, /* 9 ..*/ + { kDP_CommandNibble10 | 0x0100, 0x00 }, /* a send=0 recv=1 */ + { kDP_SetMouseResolution | 0x1000, 0x00 }, /* b send=1 recv=0 */ + { kDP_SetMouseResolution | 0x1000, 0x01 }, /* c ..*/ + { kDP_SetMouseResolution | 0x1000, 0x02 }, /* d ..*/ + { kDP_SetMouseResolution | 0x1000, 0x03 }, /* e ..*/ + { kDP_SetMouseScaling1To1, 0x00 }, /* f no send/recv */ +}; + +static const struct alps_nibble_commands alps_v4_nibble_commands[] = { + { kDP_Enable, 0x00 }, /* 0 no send/recv */ + { kDP_SetDefaults, 0x00 }, /* 1 no send/recv */ + { kDP_SetMouseScaling2To1, 0x00 }, /* 2 no send/recv */ + { kDP_SetMouseSampleRate | 0x1000, 0x0a }, /* 3 send=1 recv=0 */ + { kDP_SetMouseSampleRate | 0x1000, 0x14 }, /* 4 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x28 }, /* 5 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x3c }, /* 6 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x50 }, /* 7 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0x64 }, /* 8 ..*/ + { kDP_SetMouseSampleRate | 0x1000, 0xc8 }, /* 9 ..*/ + { kDP_CommandNibble10 | 0x0100, 0x00 }, /* a send=0 recv=1 */ + { kDP_SetMouseResolution | 0x1000, 0x00 }, /* b send=1 recv=0 */ + { kDP_SetMouseResolution | 0x1000, 0x01 }, /* c ..*/ + { kDP_SetMouseResolution | 0x1000, 0x02 }, /* d ..*/ + { kDP_SetMouseResolution | 0x1000, 0x03 }, /* e ..*/ + { kDP_SetMouseScaling1To1, 0x00 }, /* f no send/recv */ +}; + +static const struct alps_nibble_commands alps_v6_nibble_commands[] = { + { kDP_Enable, 0x00 }, /* 0 */ + { kDP_SetMouseSampleRate, 0x0a }, /* 1 */ + { kDP_SetMouseSampleRate, 0x14 }, /* 2 */ + { kDP_SetMouseSampleRate, 0x28 }, /* 3 */ + { kDP_SetMouseSampleRate, 0x3c }, /* 4 */ + { kDP_SetMouseSampleRate, 0x50 }, /* 5 */ + { kDP_SetMouseSampleRate, 0x64 }, /* 6 */ + { kDP_SetMouseSampleRate, 0xc8 }, /* 7 */ + { kDP_GetId, 0x00 }, /* 8 */ + { kDP_GetMouseInformation, 0x00 }, /* 9 */ + { kDP_SetMouseResolution, 0x00 }, /* a */ + { kDP_SetMouseResolution, 0x01 }, /* b */ + { kDP_SetMouseResolution, 0x02 }, /* c */ + { kDP_SetMouseResolution, 0x03 }, /* d */ + { kDP_SetMouseScaling2To1, 0x00 }, /* e */ + { kDP_SetMouseScaling1To1, 0x00 }, /* f */ }; -// ============================================================================= -// ApplePS2ALPSGlidePoint Class Implementation -// -OSDefineMetaClassAndStructors(ApplePS2ALPSGlidePoint, IOHIPointing); +#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ +#define ALPS_PASS 0x04 /* device has a pass-through port */ + +#define ALPS_WHEEL 0x08 /* hardware wheel present */ +#define ALPS_FW_BK_1 0x10 /* front & back buttons present */ +#define ALPS_FW_BK_2 0x20 /* front & back buttons present */ +#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ +#define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with 6-byte ALPS packet */ +#define ALPS_STICK_BITS 0x100 /* separate stick button bits */ +#define ALPS_BUTTONPAD 0x200 /* device is a clickpad */ +#define ALPS_DUALPOINT_WITH_PRESSURE 0x400 /* device can report trackpoint pressure */ + + +static const struct alps_model_info alps_model_data[] = { + /* + * XXX This entry is suspicious. First byte has zero lower nibble, + * which is what a normal mouse would report. Also, the value 0x0e + * isn't valid per PS/2 spec. + */ + { { 0x20, 0x02, 0x0e }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, + + { { 0x22, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, + { { 0x22, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D600 */ + { { 0x32, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */ + { { 0x33, 0x02, 0x0a }, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } }, /* UMAX-530T */ + { { 0x52, 0x01, 0x14 }, { ALPS_PROTO_V2, 0xff, 0xff, + ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } }, /* Toshiba Tecra A11-11L */ + { { 0x53, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x53, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x60, 0x03, 0xc8 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, /* HP ze1115 */ + { { 0x62, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xcf, 0xcf, + ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } }, /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ + { { 0x63, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x63, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x63, 0x02, 0x28 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Fujitsu Siemens S6010 */ + { { 0x63, 0x02, 0x3c }, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } }, /* Toshiba Satellite S2400-103 */ + { { 0x63, 0x02, 0x50 }, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } }, /* NEC Versa L320 */ + { { 0x63, 0x02, 0x64 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x63, 0x03, 0xc8 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Dell Latitude D800 */ + { { 0x73, 0x00, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } }, /* ThinkPad R61 8918-5QG */ + { { 0x73, 0x00, 0x14 }, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } }, /* Dell XT2 */ + { { 0x73, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } }, + { { 0x73, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } }, /* Ahtec Laptop */ + { { 0x73, 0x02, 0x50 }, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } }, /* Dell Vostro 1400 */ +}; -UInt32 ApplePS2ALPSGlidePoint::deviceType() -{ return NX_EVS_DEVICE_TYPE_MOUSE; }; +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -UInt32 ApplePS2ALPSGlidePoint::interfaceID() -{ return NX_EVS_DEVICE_INTERFACE_BUS_ACE; }; +// ============================================================================= +// ApplePS2ALPSGlidePoint Class Implementation //////////////////////////////// +// ============================================================================= -IOItemCount ApplePS2ALPSGlidePoint::buttonCount() { return 2; }; -IOFixed ApplePS2ALPSGlidePoint::resolution() { return _resolution; }; -bool IsItALPS(ALPSStatus_t *E6,ALPSStatus_t *E7); +#define super IOService +OSDefineMetaClassAndStructors(ApplePS2ALPSGlidePoint, IOService); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool ApplePS2ALPSGlidePoint::init(OSDictionary * dict) -{ +bool ApplePS2ALPSGlidePoint::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; + DEBUG_LOG("%s: Version %s starting on OS X Darwin %d.%d.\n", getName() , kmod_info.version, version_major, version_minor); + + setProperty("Revision", 24, 32); - // initialize state... - _device = 0; - _interruptHandlerInstalled = false; - _packetByteCount = 0; - _resolution = (100) << 16; // (100 dpi, 4 counts/mm) - _touchPadModeByte = kTapEnabled; - _scrolling = SCROLL_NONE; - _zscrollpos = 0; - return true; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ApplePS2ALPSGlidePoint::injectVersionDependentProperties(OSDictionary *config) { + // inject properties specific to the version of Darwin that is runnning... + char buf[32]; + 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)))) + break; + // check for "Darwin major.x" + snprintf(buf, sizeof(buf), "Darwin %d.x", version_major); + 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+")))) + break; + } while (0); + + if (dict) + { + // found version specific properties above, inject... + 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)) + setProperty(key, value); + } + iter->release(); + } + } +} + +ApplePS2ALPSGlidePoint *ApplePS2ALPSGlidePoint::probe(IOService *provider, SInt32 *score) { + DEBUG_LOG("%s: probe entered...\n", getName()); + + // + // 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). + // -ApplePS2ALPSGlidePoint* ApplePS2ALPSGlidePoint::probe( IOService * provider, SInt32 * score ) -{ - DEBUG_LOG("ApplePS2ALPSGlidePoint::probe entered...\n"); - if (!super::probe(provider, score)) return 0; + _device = (ApplePS2MouseDevice *) provider; + // find config specific to Platform Profile OSDictionary* list = OSDynamicCast(OSDictionary, getProperty(kPlatformProfile)); - ApplePS2Device* device = (ApplePS2Device*)provider; - OSDictionary* config = device->getController()->makeConfigurationNode(list, "ALPS GlidePoint"); + OSDictionary* config = _device->getController()->makeConfigurationNode(list, "ALPS GlidePoint"); if (config) { // if DisableDevice is Yes, then do not load at all... @@ -93,106 +254,83 @@ ApplePS2ALPSGlidePoint* ApplePS2ALPSGlidePoint::probe( IOService * provider, SIn if (disable && disable->isTrue()) { config->release(); + _device = 0; return 0; } #ifdef DEBUG // save configuration for later/diagnostics... setProperty(kMergedConfiguration, config); #endif + // load settings specific to Platform Profile + setParamPropertiesGated(config); + injectVersionDependentProperties(config); + OSSafeReleaseNULL(config); } - OSSafeReleaseNULL(config); - - ALPSStatus_t E6,E7; - // - // 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). - // - bool success = false; + _device->lock(); + resetMouse(); + + bool success; + if (identify() != 0) { + success = false; + } else { + success = true; + IOLog("%s: TouchPad driver started...\n", getName()); + } + _device->unlock(); - _device = (ApplePS2MouseDevice *) provider; + _device = 0; - getModel(&E6, &E7); + return success ? this : 0; +} - DEBUG_LOG("E7: { 0x%02x, 0x%02x, 0x%02x } E6: { 0x%02x, 0x%02x, 0x%02x }", - E7.byte0, E7.byte1, E7.byte2, E6.byte0, E6.byte1, E6.byte2); +bool ApplePS2ALPSGlidePoint::resetMouse() { + TPS2Request<3> request; + + // Reset mouse + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = kDP_Reset; + request.commands[1].command = kPS2C_ReadDataPort; + request.commands[1].inOrOut = 0; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; + request.commandsCount = 3; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); - success = IsItALPS(&E6,&E7); - DEBUG_LOG("ALPS Device? %s\n", (success ? "yes" : "no")); + // Verify the result + if (request.commands[1].inOrOut != kSC_Reset && request.commands[2].inOrOut != kSC_ID) { + IOLog("%s: Failed to reset mouse, return values did not match. [0x%02x, 0x%02x]\n", getName(), request.commands[1].inOrOut, request.commands[2].inOrOut); + return false; + } + return true; +} - // override - //success = true; - _touchPadVersion = (E7.byte2 & 0x0f) << 8 | E7.byte0; - - _device = 0; +bool ApplePS2ALPSGlidePoint::handleOpen(IOService *forClient, IOOptionBits options, void *arg) { + if (forClient && forClient->getProperty(VOODOO_INPUT_IDENTIFIER)) { + voodooInputInstance = forClient; + voodooInputInstance->retain(); - DEBUG_LOG("ApplePS2ALPSGlidePoint::probe leaving.\n"); - - return (success) ? this : 0; + return true; + } + return false; } -bool IsItALPS(ALPSStatus_t *E6,ALPSStatus_t *E7) -{ - bool success = false; - short i; - - UInt8 byte0, byte1, byte2; - byte0 = E7->byte0; - byte1 = E7->byte1; - byte2 = E7->byte2; - - #define NUM_SINGLES 10 - static int singles[NUM_SINGLES * 3] ={ - 0x33,0x2,0x0a, - 0x53,0x2,0x0a, - 0x53,0x2,0x14, - 0x63,0x2,0xa, - 0x63,0x2,0x14, - 0x73,0x2,0x0a, // 3622947 - 0x63,0x2,0x28, - 0x63,0x2,0x3c, - 0x63,0x2,0x50, - 0x63,0x2,0x64}; - #define NUM_DUALS 3 - static int duals[NUM_DUALS * 3]={ - 0x20,0x2,0xe, - 0x22,0x2,0xa, - 0x22,0x2,0x14}; - - for (i = 0; i < NUM_SINGLES; i++) - { - if ((byte0 == singles[i * 3]) && (byte1 == singles[i * 3 + 1]) && - (byte2 == singles[i * 3 + 2])) - { - success = true; - break; - } - } - - if (!success) - { - for(i = 0;i < NUM_DUALS;i++) - { - if ((byte0 == duals[i * 3]) && (byte1 == duals[i * 3 + 1]) && - (byte2 == duals[i * 3 + 2])) - { - success = true; - break; - } - } - } - return success; +bool ApplePS2ALPSGlidePoint::handleIsOpen(const IOService *forClient) const { + if (forClient == nullptr) { + return voodooInputInstance != nullptr; + } else { + return voodooInputInstance == forClient; + } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool ApplePS2ALPSGlidePoint::start( IOService * provider ) -{ - UInt64 enabledProperty; +void ApplePS2ALPSGlidePoint::handleClose(IOService *forClient, IOOptionBits options) { + if (forClient == voodooInputInstance) { + OSSafeReleaseNULL(voodooInputInstance); + } +} +bool ApplePS2ALPSGlidePoint::start( IOService * provider ) { // // The driver has been instructed to start. This is called after a // successful probe and match. @@ -209,83 +347,73 @@ bool ApplePS2ALPSGlidePoint::start( IOService * provider ) _device->retain(); // - // Announce hardware properties. + // Setup workloop with command gate for thread synchronization... // + IOWorkLoop* pWorkLoop = getWorkLoop(); + _cmdGate = IOCommandGate::commandGate(this); + if (!pWorkLoop || !_cmdGate) + { + _device->release(); + _device = nullptr; + return false; + } - IOLog("ApplePS2Trackpad: ALPS GlidePoint v%d.%d\n", - (UInt8)(_touchPadVersion >> 8), (UInt8)(_touchPadVersion)); + pWorkLoop->addEventSource(_cmdGate); // - // Advertise some supported features (tapping, edge scrolling). + // Lock the controller during initialization // - enabledProperty = 1; - - setProperty("Clicking", enabledProperty, - sizeof(enabledProperty) * 8); - setProperty("TrackpadScroll", enabledProperty, - sizeof(enabledProperty) * 8); - setProperty("TrackpadHorizScroll", enabledProperty, - sizeof(enabledProperty) * 8); - - // - // 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. - // + _device->lock(); - setProperty(kIOHIDPointerAccelerationTypeKey, kIOHIDTrackpadAccelerationType); + attachedHIDPointerDevices = OSSet::withCapacity(1); + registerHIDPointerNotifications(); // - // Lock the controller during initialization - // - - _device->lock(); - - // Enable tapping - setTapEnable( true ); - - // Enable Absolute Mode - setAbsoluteMode(); - - // - // Finally, we enable the trackpad itself, so that it may start reporting - // asynchronous events. - // - - setTouchPadEnable(true); - - // - // Enable the mouse clock (should already be so) and the mouse IRQ line. + // Perform any implementation specific device initialization // + if (!deviceSpecificInit()) { + _device->unlock(); + _device->release(); + return false; + } // // Install our driver's interrupt handler, for asynchronous data delivery. // - + _device->installInterruptAction(this, OSMemberFunctionCast(PS2InterruptAction, this, &ApplePS2ALPSGlidePoint::interruptOccurred), OSMemberFunctionCast(PS2PacketAction, this, &ApplePS2ALPSGlidePoint::packetReady)); _interruptHandlerInstalled = true; - + // now safe to allow other threads _device->unlock(); - + + // + // Install our power control handler. + // + + _device->installPowerControlAction( this, + OSMemberFunctionCast(PS2PowerControlAction, this, &ApplePS2ALPSGlidePoint::setDevicePowerState) ); + _powerControlHandlerInstalled = true; + + // + // Request message registration for keyboard to trackpad communication // - // Install our power control handler. - // - _device->installPowerControlAction( this, OSMemberFunctionCast(PS2PowerControlAction,this, - &ApplePS2ALPSGlidePoint::setDevicePowerState) ); - _powerControlHandlerInstalled = true; + //setProperty(kDeliverNotifications, true); + + registerService(); return true; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2ALPSGlidePoint::stop( IOService * provider ) -{ +void ApplePS2ALPSGlidePoint::stop(IOService *provider) { + + DEBUG_LOG("%s: stop called\n", getName()); + // // The driver has been instructed to stop. Note that we must break all // connections to other service objects now (ie. no registered actions, @@ -294,529 +422,3383 @@ void ApplePS2ALPSGlidePoint::stop( IOService * provider ) assert(_device == provider); + unregisterHIDPointerNotifications(); + OSSafeReleaseNULL(attachedHIDPointerDevices); + + ignoreall = false; + // // Disable the mouse itself, so that it may stop reporting mouse events. // setTouchPadEnable(false); + // free up timer for scroll momentum + IOWorkLoop* pWorkLoop = getWorkLoop(); + if (pWorkLoop) + { + if (_cmdGate) + { + pWorkLoop->removeEventSource(_cmdGate); + _cmdGate->release(); + _cmdGate = 0; + } + } + // // Uninstall the interrupt handler. // - if ( _interruptHandlerInstalled ) _device->uninstallInterruptAction(); - _interruptHandlerInstalled = false; + if (_interruptHandlerInstalled) + { + _device->uninstallInterruptAction(); + _interruptHandlerInstalled = false; + } // // Uninstall the power control handler. // - if ( _powerControlHandlerInstalled ) _device->uninstallPowerControlAction(); - _powerControlHandlerInstalled = false; + if (_powerControlHandlerInstalled) + { + _device->uninstallPowerControlAction(); + _powerControlHandlerInstalled = false; + } // // Release the pointer to the provider object. // - + OSSafeReleaseNULL(_device); - - super::stop(provider); -} -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + super::stop(provider); +} -PS2InterruptResult ApplePS2ALPSGlidePoint::interruptOccurred(UInt8 data) -{ +PS2InterruptResult ApplePS2ALPSGlidePoint::interruptOccurred(UInt8 data) { // // This will be invoked automatically from our device when asynchronous // events need to be delivered. Process the trackpad data. Do NOT issue // any BLOCKING commands to our device in this context. // - // Ignore all bytes until we see the start of a packet, otherwise the - // packets may get out of sequence and things will get very confusing. - // - - if (0 == _packetByteCount && (data & 0xc8) != 0x08 && (data & 0xf8) != 0xf8) - { - DEBUG_LOG("%s: Unexpected byte0 data (%02x) from PS/2 controller\n", getName(), data); - return kPS2IR_packetBuffering; + + UInt8 *packet = _ringBuffer.head(); + + /* Save first packet */ + if (0 == _packetByteCount) { + packet[0] = data; } - if (_packetByteCount >= 1 && data == 0x80) - { - DEBUG_LOG("%s: Unexpected byte%d data (%02x) from PS/2 controller\n", getName(), _packetByteCount, data); - _packetByteCount = 0; + + /* Reset PSMOUSE_BAD_DATA flag */ + priv.PSMOUSE_BAD_DATA = false; + + /* + * Check if we are dealing with a bare PS/2 packet, presumably from + * a device connected to the external PS/2 port. Because bare PS/2 + * protocol does not have enough constant bits to self-synchronize + * properly we only do this if the device is fully synchronized. + * Can not distinguish V8's first byte from PS/2 packet's + */ + if (priv.proto_version != ALPS_PROTO_V8 && + (packet[0] & 0xc8) == 0x08) { + if (_packetByteCount == 3) { + DEBUG_LOG("%s: Dealing with bare PS/2 packet\n", getName()); + //dispatchRelativePointerEventWithPacket(packet, kPacketLengthSmall); //Dr Hurt: allow this? + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; + } + packet[_packetByteCount++] = data; return kPS2IR_packetBuffering; } - UInt8* packet = _ringBuffer.head(); - packet[_packetByteCount++] = data; - if (kPacketLengthLarge == _packetByteCount || - (kPacketLengthSmall == _packetByteCount && (packet[0] & 0xc8) == 0x08)) - { - // complete 6 or 3-byte packet received... - _ringBuffer.advanceHead(kPacketLengthMax); - _packetByteCount = 0; + /* Check for PS/2 packet stuffed in the middle of ALPS packet. */ + if ((priv.flags & ALPS_PS2_INTERLEAVED) && + _packetByteCount >= 4 && (packet[3] & 0x0f) == 0x0f) { + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); return kPS2IR_packetReady; } - return kPS2IR_packetBuffering; -} -void ApplePS2ALPSGlidePoint::packetReady() -{ - // empty the ring buffer, dispatching each packet... - while (_ringBuffer.count() >= kPacketLengthMax) - { - UInt8* packet = _ringBuffer.tail(); - // now we have complete packet, either 6-byte or 3-byte - if ((packet[0] & 0xf8) == 0xf8) - dispatchAbsolutePointerEventWithPacket(packet, kPacketLengthLarge); - else - dispatchRelativePointerEventWithPacket(packet, kPacketLengthSmall); - _ringBuffer.advanceTail(kPacketLengthMax); + /* alps_is_valid_first_byte */ + if ((packet[0] & priv.mask0) != priv.byte0) { + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2ALPSGlidePoint::dispatchAbsolutePointerEventWithPacket(UInt8* packet, UInt32 packetSize) -{ - UInt32 buttons = 0; - int left = 0, right = 0, middle = 0; - int xdiff, ydiff, scroll; - uint64_t now_abs; - bool wasNotScrolling, willScroll; - - int x = (packet[1] & 0x7f) | ((packet[2] & 0x78) << (7-3)); - int y = (packet[4] & 0x7f) | ((packet[3] & 0x70) << (7-4)); - int z = packet[5]; // touch pression - - clock_get_uptime(&now_abs); - - left |= (packet[2]) & 1; - left |= (packet[3]) & 1; - right |= (packet[3] >> 1) & 1; - if (packet[0] != 0xff) - { - left |= (packet[0]) & 1; - right |= (packet[0] >> 1) & 1; - middle |= (packet[0] >> 2) & 1; - middle |= (packet[3] >> 2) & 1; + /* Bytes 2 - pktsize should have 0 in the highest bit */ + if (priv.proto_version < ALPS_PROTO_V5 && + _packetByteCount >= 2 && _packetByteCount <= priv.pktsize && + (packet[_packetByteCount - 1] & 0x80)) { + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; } - buttons |= left ? 0x01 : 0; - buttons |= right ? 0x02 : 0; - buttons |= middle ? 0x04 : 0; - - /*DEBUG_LOG("Absolute packet: x: %d, y: %d, xpos: %d, ypos: %d, buttons: %x, " - "z: %d, zpos: %d\n", x, y, (int)_xpos, (int)_ypos, (int)buttons, - (int)z, (int)_zpos);*/ - - wasNotScrolling = _scrolling == SCROLL_NONE; - scroll = insideScrollArea(x, y); - - willScroll = ((scroll & SCROLL_VERT) && _edgevscroll) || - ((scroll & SCROLL_HORIZ) && _edgehscroll); + /* alps_is_valid_package_v7 */ + if (priv.proto_version == ALPS_PROTO_V7 && + (((_packetByteCount == 3) && ((packet[2] & 0x40) != 0x40)) || + ((_packetByteCount == 4) && ((packet[3] & 0x48) != 0x48)) || + ((_packetByteCount == 6) && ((packet[5] & 0x40) != 0x0)))) { + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; + } - // Make sure we are still relative - if (z == 0 || (_zpos >= 1 && z != 0 && !willScroll)) - { - _xpos = x; - _ypos = y; + /* alps_is_valid_package_ss4_v2 */ + if (priv.proto_version == ALPS_PROTO_V8 && + ((_packetByteCount == 4 && ((packet[3] & 0x08) != 0x08)) || + (_packetByteCount == 6 && ((packet[5] & 0x10) != 0x0)))) { + priv.PSMOUSE_BAD_DATA = true; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; } - - // Are we scrolling? - if (willScroll) + + packet[_packetByteCount++] = data; + if (_packetByteCount == priv.pktsize) { - if (_zscrollpos <= 0 || wasNotScrolling) - { - _xscrollpos = x; - _yscrollpos = y; - } - - xdiff = x - _xscrollpos; - ydiff = y - _yscrollpos; - - ydiff = (scroll == SCROLL_VERT) ? -((int)((double)ydiff * _edgeaccellvalue)) : 0; - xdiff = (scroll == SCROLL_HORIZ) ? -((int)((double)xdiff * _edgeaccellvalue)) : 0; - - // Those "if" should provide angle tapping (simulate click on up/down - // buttons of a scrollbar), but i have to investigate more on the values, - // since currently they don't work... - if (ydiff == 0 && scroll == SCROLL_HORIZ) - ydiff = ((x >= 950 ? 25 : (x <= 100 ? -25 : 0)) / max(_edgeaccellvalue, 1)); - - if (xdiff == 0 && scroll == SCROLL_VERT) - xdiff = ((y >= 950 ? 25 : (y <= 100 ? -25 : 0)) / max(_edgeaccellvalue, 1)); - - dispatchScrollWheelEventX(ydiff, xdiff, 0, now_abs); - _zscrollpos = z; - return; + _ringBuffer.advanceHead(priv.pktsize); + return kPS2IR_packetReady; } - - _zpos = z == 0 ? _zpos + 1 : 0; - _scrolling = SCROLL_NONE; - - xdiff = x - _xpos; - ydiff = y - _ypos; - - _xpos = x; - _ypos = y; - - //DEBUG_LOG("Sending event: %d,%d,%d\n",xdiff,ydiff,(int)buttons); - dispatchRelativePointerEventX(xdiff, ydiff, buttons, now_abs); + return kPS2IR_packetBuffering; } -int ApplePS2ALPSGlidePoint::insideScrollArea(int x, int y) -{ - int scroll = 0; - if (x > 900) scroll |= SCROLL_VERT; - if (y > 650) scroll |= SCROLL_HORIZ; - - if (x > 900 && y > 650) - { - if (_scrolling == SCROLL_VERT) - scroll = SCROLL_VERT; - else - scroll = SCROLL_HORIZ; +void ApplePS2ALPSGlidePoint::packetReady() { + // empty the ring buffer, dispatching each packet... + while (_ringBuffer.count() >= priv.pktsize) { + UInt8 *packet = _ringBuffer.tail(); + if (priv.PSMOUSE_BAD_DATA == false) { + if (!ignoreall) + (this->*process_packet)(packet); + } else { + IOLog("%s: an invalid or bare packet has been dropped...\n", getName()); + /* Might need to perform a full HW reset here if we keep receiving bad packets (consecutively) */ + } + _packetByteCount = 0; + _ringBuffer.advanceTail(priv.pktsize); } - - _scrolling = scroll; - return scroll; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2ALPSGlidePoint:: - dispatchRelativePointerEventWithPacket( UInt8 * packet, - UInt32 packetSize ) -{ - // - // Process the three byte relative format packet that was retreived from the - // trackpad. The format of the bytes is as follows: - // - // 7 6 5 4 3 2 1 0 - // ----------------------- - // YO XO YS XS 1 M R L - // X7 X6 X5 X4 X3 X3 X1 X0 (X delta) - // Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 (Y delta) - // - - UInt32 buttons = 0; - SInt32 dx, dy; +bool ApplePS2ALPSGlidePoint::deviceSpecificInit() { - if ( (packet[0] & 0x1) ) buttons |= 0x1; // left button (bit 0 in packet) - if ( (packet[0] & 0x2) ) buttons |= 0x2; // right button (bit 1 in packet) - if ( (packet[0] & 0x4) ) buttons |= 0x4; // middle button (bit 2 in packet) - - dx = packet[1]; - if(packet[0] & 0x10) - dx = dx -256; - - dy = packet[2]; - if(packet[0] & 0x20) - dy = dy - 256; - - uint64_t now_abs; - clock_get_uptime(&now_abs); - dispatchRelativePointerEventX(dx, dy, buttons, now_abs); -} + // Setup expected packet size + priv.pktsize = priv.proto_version == ALPS_PROTO_V4 ? 8 : 6; -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + if (!(this->*hw_init)()) { + goto init_fail; + } -void ApplePS2ALPSGlidePoint::setTapEnable( bool enable ) -{ - // - // Instructs the trackpad to honor or ignore tapping - // - - ALPSStatus_t Status; - getStatus(&Status); - if (Status.byte0 & 0x04) - { - DEBUG_LOG("Tapping can only be toggled.\n"); - enable = false; - } - - int cmd = enable ? kDP_SetMouseSampleRate : kDP_SetMouseResolution; - int arg = enable ? 0x0A : 0x00; - - TPS2Request<10> request; - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[0].inOrOut = kDP_GetMouseInformation; //sync.. - request.commands[1].command = kPS2C_ReadDataPort; - request.commands[1].inOrOut = 0; - request.commands[2].command = kPS2C_ReadDataPort; - request.commands[2].inOrOut = 0; - request.commands[3].command = kPS2C_ReadDataPort; - request.commands[3].inOrOut = 0; - request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[4].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[5].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[6].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[6].inOrOut = cmd; - request.commands[7].command = kPS2C_WriteCommandPort; - request.commands[7].inOrOut = kCP_TransmitToMouse; - request.commands[8].command = kPS2C_WriteDataPort; - request.commands[8].inOrOut = arg; - request.commands[9].command = kPS2C_ReadDataPortAndCompare; - request.commands[9].inOrOut = kSC_Acknowledge; - request.commandsCount = 10; - assert(request.commandsCount <= countof(request.commands)); - _device->submitRequestAndBlock(&request); + return true; - getStatus(&Status); +init_fail: + IOLog("%s: Hardware initialization failed. TouchPad probably won't work\n", getName()); + resetMouse(); + return false; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/* ============================================================================================== */ +/* ==============================||\\ alps.c Implementation //||================================= */ +/* ============================================================================================== */ -void ApplePS2ALPSGlidePoint::setTouchPadEnable( bool enable ) -{ - // - // Instructs the trackpad to start or stop the reporting of data packets. - // It is safe to issue this request from the interrupt/completion context. - // - // (mouse enable/disable command) - TPS2Request<5> request; - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[0].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[1].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[2].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[3].inOrOut = kDP_SetDefaultsAndDisable; +void ApplePS2ALPSGlidePoint::alps_process_packet_v1_v2(UInt8 *packet) { - // (mouse or pad enable/disable command) - request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[4].inOrOut = enable ? kDP_Enable : kDP_SetDefaultsAndDisable; - request.commandsCount = 5; - assert(request.commandsCount <= countof(request.commands)); - _device->submitRequestAndBlock(&request); -} + // Check if input is disabled via ApplePS2Keyboard request + if (ignoreall) + return; -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + int x, y, z, fin, ges, left, right, middle, buttons = 0; + // Unused code + // int back = 0, forward = 0, fingers = 0; + + if (priv.proto_version == ALPS_PROTO_V1) { + left = packet[2] & 0x10; + right = packet[2] & 0x08; + middle = 0; + x = packet[1] | ((packet[0] & 0x07) << 7); + y = packet[4] | ((packet[3] & 0x07) << 7); + z = packet[5]; + } else { + left = packet[3] & 1; + right = packet[3] & 2; + middle = packet[3] & 4; + x = packet[1] | ((packet[2] & 0x78) << (7 - 3)); + y = packet[4] | ((packet[3] & 0x70) << (7 - 4)); + z = packet[5]; + } -IOReturn ApplePS2ALPSGlidePoint::setParamProperties( OSDictionary * dict ) -{ - OSNumber * clicking = OSDynamicCast( OSNumber, dict->getObject("Clicking") ); - OSNumber * dragging = OSDynamicCast( OSNumber, dict->getObject("Dragging") ); - OSNumber * draglock = OSDynamicCast( OSNumber, dict->getObject("DragLock") ); - OSNumber * hscroll = OSDynamicCast( OSNumber, dict->getObject("TrackpadHorizScroll") ); - OSNumber * vscroll = OSDynamicCast( OSNumber, dict->getObject("TrackpadScroll") ); - OSNumber * eaccell = OSDynamicCast( OSNumber, dict->getObject("HIDTrackpadScrollAcceleration") ); - - OSCollectionIterator* iter = OSCollectionIterator::withCollection( dict ); - OSObject* obj; - - iter->reset(); - while ((obj = iter->getNextObject()) != NULL) - { - OSString* str = OSDynamicCast( OSString, obj ); - OSNumber* val = OSDynamicCast( OSNumber, dict->getObject( str ) ); - - if (val) - DEBUG_LOG("%s: Dictionary Object: %s Value: %d\n", getName(), - str->getCStringNoCopy(), val->unsigned32BitValue()); - else - DEBUG_LOG("%s: Dictionary Object: %s Value: ??\n", getName(), - str->getCStringNoCopy()); - } - OSSafeReleaseNULL(iter); - if ( clicking ) - { - UInt8 newModeByteValue = clicking->unsigned32BitValue() & 0x1 ? - kTapEnabled : - 0; - - if (_touchPadModeByte != newModeByteValue) - { - _touchPadModeByte = newModeByteValue; - setTapEnable(_touchPadModeByte); - setProperty("Clicking", clicking); - setAbsoluteMode(); //restart the mouse... + // macOS does not support forward and back buttons + /* + if (priv.flags & ALPS_FW_BK_1) { + back = packet[0] & 0x10; + forward = packet[2] & 4; + } + + if (priv.flags & ALPS_FW_BK_2) { + back = packet[3] & 4; + forward = packet[2] & 4; + if ((middle = forward && back)) { + forward = back = 0; } } + */ - if (dragging) - { - _dragging = dragging->unsigned32BitValue() & 0x1 ? true : false; - setProperty("Dragging", dragging); - } + ges = packet[2] & 1; + fin = packet[2] & 2; - if (draglock) - { - _draglock = draglock->unsigned32BitValue() & 0x1 ? true : false; - setProperty("DragLock", draglock); - } + if ((priv.flags & ALPS_DUALPOINT) && z == 127) { + int dx, dy; + dx = x > 383 ? (x - 768) : x; + dy = -(y > 255 ? (y - 512) : y); - if (hscroll) - { - _edgehscroll = hscroll->unsigned32BitValue() & 0x1 ? true : false; - setProperty("TrackpadHorizScroll", hscroll); + voodooTrackpoint(kIOMessageVoodooTrackpointRelativePointer, dx, dy, buttons); + return; } - if (vscroll) - { - _edgevscroll = vscroll->unsigned32BitValue() & 0x1 ? true : false; - setProperty("TrackpadScroll", vscroll); - } - if (eaccell) - { - _edgeaccell = eaccell->unsigned32BitValue(); - _edgeaccellvalue = (((double)(_edgeaccell / 1966.08)) / 75.0); - _edgeaccellvalue = _edgeaccellvalue == 0 ? 0.01 : _edgeaccellvalue; - setProperty("HIDTrackpadScrollAcceleration", eaccell); + /* Some models have separate stick button bits */ + if (priv.flags & ALPS_STICK_BITS) { + left |= packet[0] & 1; + right |= packet[0] & 2; + middle |= packet[0] & 4; } - return super::setParamProperties(dict); -} + /* To make button reporting compatible with rest of driver */ + buttons |= left ? 0x01 : 0; + buttons |= right ? 0x02 : 0; + buttons |= middle ? 0x04 : 0; -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + /* Convert hardware tap to a reasonable Z value */ + if (ges && !fin) { + z = 40; + } -void ApplePS2ALPSGlidePoint::setDevicePowerState( UInt32 whatToDo ) -{ + // REVIEW: Check if this is correct + /* + * A "tap and drag" operation is reported by the hardware as a transition + * from (!fin && ges) to (fin && ges). This should be translated to the + * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually. + */ + // if (ges && fin && !priv.prev_fin) { + // z = 0; + // fingers = 0; + // voodooTrackpoint(kIOMessageVoodooTrackpointRelativePointer, x, y, buttons); + // } + + priv.prev_fin = fin; + + // fingers = z > 30 ? 1 : 0; + + if (z > 30) + voodooTrackpoint(kIOMessageVoodooTrackpointRelativePointer, x, y, buttons); + + if (priv.flags & ALPS_WHEEL) { + int scrollAmount = ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07); + if (scrollAmount) + voodooTrackpoint(kIOMessageVoodooTrackpointScrollWheel, 0, -scrollAmount, buttons); + } +} + +static void alps_get_bitmap_points(unsigned int map, + struct alps_bitmap_point *low, + struct alps_bitmap_point *high, + int *fingers) +{ + struct alps_bitmap_point *point; + int i, bit, prev_bit = 0; + + point = low; + for (i = 0; map != 0; i++, map >>= 1) { + bit = map & 1; + if (bit) { + if (!prev_bit) { + point->start_bit = i; + point->num_bits = 0; + (*fingers)++; + } + point->num_bits++; + } else { + if (prev_bit) + point = high; + } + prev_bit = bit; + } +} + +/* + * Process bitmap data from semi-mt protocols. Returns the number of + * fingers detected. A return value of 0 means at least one of the + * bitmaps was empty. + * + * The bitmaps don't have enough data to track fingers, so this function + * only generates points representing a bounding box of all contacts. + * These points are returned in fields->mt when the return value + * is greater than 0. + */ +int ApplePS2ALPSGlidePoint::alps_process_bitmap(struct alps_data *priv, + struct alps_fields *fields) +{ + + int i, fingers_x = 0, fingers_y = 0, fingers, closest; + struct alps_bitmap_point x_low = {0,}, x_high = {0,}; + struct alps_bitmap_point y_low = {0,}, y_high = {0,}; + struct input_mt_pos corner[4]; + + + if (!fields->x_map || !fields->y_map) { + return 0; + } + + alps_get_bitmap_points(fields->x_map, &x_low, &x_high, &fingers_x); + alps_get_bitmap_points(fields->y_map, &y_low, &y_high, &fingers_y); + + /* + * Fingers can overlap, so we use the maximum count of fingers + * on either axis as the finger count. + */ + fingers = max(fingers_x, fingers_y); + + /* + * If an axis reports only a single contact, we have overlapping or + * adjacent fingers. Divide the single contact between the two points. + */ + if (fingers_x == 1) { + i = (x_low.num_bits - 1) / 2; + x_low.num_bits = x_low.num_bits - i; + x_high.start_bit = x_low.start_bit + i; + x_high.num_bits = max(i, 1); + } + + if (fingers_y == 1) { + i = (y_low.num_bits - 1) / 2; + y_low.num_bits = y_low.num_bits - i; + y_high.start_bit = y_low.start_bit + i; + y_high.num_bits = max(i, 1); + } + + /* top-left corner */ + corner[0].x = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[0].y = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* top-right corner */ + corner[1].x = (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[1].y = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* bottom-right corner */ + corner[2].x = (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[2].y = (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* bottom-left corner */ + corner[3].x = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[3].y = (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* x-bitmap order is reversed on v5 touchpads */ + if (priv->proto_version == ALPS_PROTO_V5) { + for (i = 0; i < 4; i++) + corner[i].x = priv->x_max - corner[i].x; + } + + /* y-bitmap order is reversed on v3 and v4 touchpads */ + if (priv->proto_version == ALPS_PROTO_V3 || priv->proto_version == ALPS_PROTO_V4) { + for (i = 0; i < 4; i++) + corner[i].y = priv->y_max - corner[i].y; + } + + /* + * We only select a corner for the second touch once per 2 finger + * touch sequence to avoid the chosen corner (and thus the coordinates) + * jumping around when the first touch is in the middle. + */ + if (priv->second_touch == -1) { + /* Find corner closest to our st coordinates */ + closest = 0x7fffffff; + for (i = 0; i < 4; i++) { + int dx = fields->st.x - corner[i].x; + int dy = fields->st.y - corner[i].y; + int distance = dx * dx + dy * dy; + + if (distance < closest) { + priv->second_touch = i; + closest = distance; + } + } + /* And select the opposite corner to use for the 2nd touch */ + priv->second_touch = (priv->second_touch + 2) % 4; + } + + fields->mt[0] = fields->st; + fields->mt[1] = corner[priv->second_touch]; + +#if DEBUG + IOLog("%s: BITMAP\n", getName()); + + unsigned int ymap = fields->y_map; + + for (int i = 0; ymap != 0; i++, ymap >>= 1) { + unsigned int xmap = fields->x_map; + char bitLog[160]; + + for (int j = 0; xmap != 0; j++, xmap >>= 1) { + strlcat(bitLog, (ymap & 1 && xmap & 1) ? "1 " : "0 ", sizeof(bitLog) + 1); + } + + IOLog("%s: %s\n", getName(), bitLog); + } + + IOLog("%s: Process Bitmap, Corner=%d, Fingers=%d, x1=%d, x2=%d, y1=%d, y2=%d xmap=%d ymap=%d\n", getName(), priv->second_touch, fingers, fields->mt[0].x, fields->mt[1].x, fields->mt[0].y, fields->mt[1].y, fields->x_map, fields->y_map); +#endif // DEBUG + return fingers; +} + +void ApplePS2ALPSGlidePoint::alps_process_trackstick_packet_v3(UInt8 *packet) { + int x, y, left, right, middle; + // Unused code + // int z; + UInt32 buttons = 0, raw_buttons = 0; + + /* It should be a DualPoint when received trackstick packet */ + if (!(priv.flags & ALPS_DUALPOINT)) { + DEBUG_LOG("%s: Rejected trackstick packet from non DualPoint device\n", getName()); + return; + } + + /* Sanity check packet */ + if (!(packet[0] & 0x40)) { + DEBUG_LOG("%s: Bad trackstick packet, disregarding...\n", getName()); + return; + } + + /* There is a special packet that seems to indicate the end + * of a stream of trackstick data. Filter these out + */ + if (packet[1] == 0x7f && packet[2] == 0x7f && packet[4] == 0x7f) { + return; + } + + x = (SInt8) (((packet[0] & 0x20) << 2) | (packet[1] & 0x7f)); + y = (SInt8) (((packet[0] & 0x10) << 3) | (packet[2] & 0x7f)); + // Unused code + // z = (packet[4] & 0x7f); + + /* + * The x and y values tend to be quite large, and when used + * alone the trackstick is difficult to use. Scale them down + * to compensate. + */ + x /= 8; + y /= 8; + + /* To get proper movement direction */ + y = -y; + + /* + * Most ALPS models report the trackstick buttons in the touchpad + * packets, but a few report them here. No reliable way has been + * found to differentiate between the models upfront, so we enable + * the quirk in response to seeing a button press in the trackstick + * packet. + */ + left = packet[3] & 0x01; + right = packet[3] & 0x02; + middle = packet[3] & 0x04; + + if (!(priv.quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) && + (left || middle || right)) { + priv.quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS; + } + + if (priv.quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) { + raw_buttons |= left ? 0x01 : 0; + raw_buttons |= right ? 0x02 : 0; + raw_buttons |= middle ? 0x04 : 0; + } + + /* Button status can appear in normal packet */ + if (0 == raw_buttons) { + buttons = lastbuttons; + } else { + buttons = raw_buttons; + lastbuttons = buttons; + } + + /* If middle button is pressed, switch to scroll mode. Else, move pointer normally */ + if (0 == (buttons & 0x04)) { + voodooTrackpoint(kIOMessageVoodooTrackpointRelativePointer, x, y, buttons); + } else { + voodooTrackpoint(kIOMessageVoodooTrackpointScrollWheel, x, y, buttons); + } +} + +bool ApplePS2ALPSGlidePoint::alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p) { + f->left = !!(p[3] & 0x01); + f->right = !!(p[3] & 0x02); + f->middle = !!(p[3] & 0x04); + + f->ts_left = !!(p[3] & 0x10); + f->ts_right = !!(p[3] & 0x20); + f->ts_middle = !!(p[3] & 0x40); + return true; +} + +bool ApplePS2ALPSGlidePoint::alps_decode_pinnacle(struct alps_fields *f, UInt8 *p) { + f->first_mp = !!(p[4] & 0x40); + f->is_mp = !!(p[0] & 0x40); + + if (f->is_mp) { + f->fingers = (p[5] & 0x3) + 1; + f->x_map = ((p[4] & 0x7e) << 8) | + ((p[1] & 0x7f) << 2) | + ((p[0] & 0x30) >> 4); + f->y_map = ((p[3] & 0x70) << 4) | + ((p[2] & 0x7f) << 1) | + (p[4] & 0x01); + } else { + f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + ((p[0] & 0x30) >> 4); + f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->pressure = p[5] & 0x7f; + + alps_decode_buttons_v3(f, p); + } + return true; +} + +bool ApplePS2ALPSGlidePoint::alps_decode_rushmore(struct alps_fields *f, UInt8 *p) { + f->first_mp = !!(p[4] & 0x40); + f->is_mp = !!(p[5] & 0x40); + + if (f->is_mp) { + f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1; + f->x_map = ((p[5] & 0x10) << 11) | + ((p[4] & 0x7e) << 8) | + ((p[1] & 0x7f) << 2) | + ((p[0] & 0x30) >> 4); + f->y_map = ((p[5] & 0x20) << 6) | + ((p[3] & 0x70) << 4) | + ((p[2] & 0x7f) << 1) | + (p[4] & 0x01); + } else { + f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + ((p[0] & 0x30) >> 4); + f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->pressure = p[5] & 0x7f; + + alps_decode_buttons_v3(f, p); + } + return true; +} + +bool ApplePS2ALPSGlidePoint::alps_decode_dolphin(struct alps_fields *f, UInt8 *p) { + uint64_t palm_data = 0; + + f->first_mp = !!(p[0] & 0x02); + f->is_mp = !!(p[0] & 0x20); + + if (!f->is_mp) { + f->st.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7)); + f->st.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3)); + f->pressure = (p[0] & 4) ? 0 : p[5] & 0x7f; + alps_decode_buttons_v3(f, p); + } else { + f->fingers = ((p[0] & 0x6) >> 1 | + (p[0] & 0x10) >> 2); + + palm_data = (p[1] & 0x7f) | + ((p[2] & 0x7f) << 7) | + ((p[4] & 0x7f) << 14) | + ((p[5] & 0x7f) << 21) | + ((p[3] & 0x07) << 28) | + (((uint64_t)p[3] & 0x70) << 27) | + (((uint64_t)p[0] & 0x01) << 34); + + /* Y-profile is stored in P(0) to p(n-1), n = y_bits; */ + f->y_map = palm_data & (BIT(priv.y_bits) - 1); + + /* X-profile is stored in p(n) to p(n+m-1), m = x_bits; */ + f->x_map = (palm_data >> priv.y_bits) & (BIT(priv.x_bits) - 1); + } + return true; +} + +void ApplePS2ALPSGlidePoint::alps_process_touchpad_packet_v3_v5(UInt8 *packet) { + int fingers = 0; + struct alps_fields f; + + // Check if input is disabled via ApplePS2Keyboard request + if (ignoreall) + return; + + memset(&f, 0, sizeof(f)); + + (this->*decode_fields)(&f, packet); + /* + * There's no single feature of touchpad position and bitmap packets + * that can be used to distinguish between them. We rely on the fact + * that a bitmap packet should always follow a position packet with + * bit 6 of packet[4] set. + */ + if (priv.multi_packet) { + /* + * Sometimes a position packet will indicate a multi-packet + * sequence, but then what follows is another position + * packet. Check for this, and when it happens process the + * position packet as usual. + */ + if (f.is_mp) { + fingers = f.fingers; + /* + * Bitmap processing uses position packet's coordinate + * data, so we need to do decode it first. + */ + (this->*decode_fields)(&f, priv.multi_data); + if (alps_process_bitmap(&priv, &f) == 0) { + fingers = 0; /* Use st data */ + } + } else { + priv.multi_packet = 0; + } + } + + /* + * Bit 6 of byte 0 is not usually set in position packets. The only + * times it seems to be set is in situations where the data is + * suspect anyway, e.g. a palm resting flat on the touchpad. Given + * this combined with the fact that this bit is useful for filtering + * out misidentified bitmap packets, we reject anything with this + * bit set. + */ + if (f.is_mp) { + return; + } + + if (!priv.multi_packet && (f.first_mp)) { + priv.multi_packet = 1; + memcpy(priv.multi_data, packet, sizeof(priv.multi_data)); + return; + } + + priv.multi_packet = 0; + +#if 0 + /* + * Sometimes the hardware sends a single packet with z = 0 + * in the middle of a stream. Real releases generate packets + * with x, y, and z all zero, so these seem to be flukes. + * Ignore them. + */ + if (f.st.x && f.st.y && !f.pressure) { + //return; //Dr Hurt: This causes jitter + } +#endif + + /* Use st data when we don't have mt data */ + if (fingers < 2) { + f.mt[0].x = f.st.x; + f.mt[0].y = f.st.y; + fingers = f.pressure > 0 ? 1 : 0; + priv.second_touch = -1; + } + + /* Reverse y co-ordinates to have 0 at bottom for gestures to work */ + f.mt[0].y = priv.y_max - f.mt[0].y; + f.mt[1].y = priv.y_max - f.mt[1].y; + + /* Ignore 1 finger events after 2 finger scroll to prevent jitter */ + // if (last_fingers == 2 && fingers == 1 && scrolldebounce) + // fingers = 2; + + prepareVoodooInput(f, fingers); + + alps_buttons(f); +} + +void ApplePS2ALPSGlidePoint::alps_process_packet_v3(UInt8 *packet) { + /* + * v3 protocol packets come in three types, two representing + * touchpad data and one representing trackstick data. + * Trackstick packets seem to be distinguished by always + * having 0x3f in the last byte. This value has never been + * observed in the last byte of either of the other types + * of packets. + */ + if (packet[5] == 0x3f) { + alps_process_trackstick_packet_v3(packet); + return; + } + + alps_process_touchpad_packet_v3_v5(packet); +} + +void ApplePS2ALPSGlidePoint::alps_process_packet_v6(UInt8 *packet) { + // Check if input is disabled via ApplePS2Keyboard request + if (ignoreall) + return; + + int x, y, z; + int buttons = 0; + + /* + * We can use Byte5 to distinguish if the packet is from Touchpad + * or Trackpoint. + * Touchpad: 0 - 0x7E + * Trackpoint: 0x7F + */ + if (packet[5] == 0x7F) { + /* It should be a DualPoint when received Trackpoint packet */ + if (!(priv.flags & ALPS_DUALPOINT)) { + DEBUG_LOG("%s: Rejected trackstick packet from non DualPoint device\n", getName()); + return; + } + + /* Trackpoint packet */ + x = packet[1] | ((packet[3] & 0x20) << 2); + y = packet[2] | ((packet[3] & 0x40) << 1); + z = packet[4]; + + left = packet[3] & 0x01; + right = packet[3] & 0x02; + middle = packet[3] & 0x04; + + buttons |= left ? 0x01 : 0; + buttons |= right ? 0x02 : 0; + buttons |= middle ? 0x04 : 0; + + /* To prevent the cursor jump when finger lifted */ + // z is unused code + if (x == 0x7F && y == 0x7F && z == 0x7F) + x = y = 0; + + // Y is inverted + y = -y; + + /* Divide 4 since trackpoint's speed is too fast */ + voodooTrackpoint(kIOMessageVoodooTrackpointRelativePointer, x/4, y/4, buttons); + return; + } + + /* Touchpad packet */ + struct alps_fields f; + + f.mt[0].x = packet[1] | ((packet[3] & 0x78) << 4); + f.mt[0].y = packet[2] | ((packet[4] & 0x78) << 4); + z = packet[5]; + f.pressure = z; + f.left = packet[3] & 0x01; + f.right = packet[3] & 0x02; + + f.fingers = z > 30 ? 1 : 0; + + buttons |= f.left ? 0x01 : 0; + buttons |= f.right ? 0x02 : 0; + + voodooTrackpoint(kIOMessageVoodooTrackpointRelativePointer, f.mt[0].x, f.mt[0].y, buttons); +} + +void ApplePS2ALPSGlidePoint::alps_process_packet_v4(UInt8 *packet) { + // Check if input is disabled via ApplePS2Keyboard request + if (ignoreall) + return; + + SInt32 offset; + // UInt32 buttons = 0; + struct alps_fields f; + + f.fingers = 0; + + /* + * v4 has a 6-byte encoding for bitmap data, but this data is + * broken up between 3 normal packets. Use priv.multi_packet to + * track our position in the bitmap packet. + */ + if (packet[6] & 0x40) { + /* sync, reset position */ + priv.multi_packet = 0; + } + + if (priv.multi_packet > 2) { + return; + } + + offset = 2 * priv.multi_packet; + priv.multi_data[offset] = packet[6]; + priv.multi_data[offset + 1] = packet[7]; + + f.left = !!(packet[4] & 0x01); + f.right = !!(packet[4] & 0x02); + + f.st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | + ((packet[0] & 0x30) >> 4); + f.st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); + f.pressure = packet[5] & 0x7f; + + if (++priv.multi_packet > 2) { + priv.multi_packet = 0; + + f.x_map = ((priv.multi_data[2] & 0x1f) << 10) | + ((priv.multi_data[3] & 0x60) << 3) | + ((priv.multi_data[0] & 0x3f) << 2) | + ((priv.multi_data[1] & 0x60) >> 5); + f.y_map = ((priv.multi_data[5] & 0x01) << 10) | + ((priv.multi_data[3] & 0x1f) << 5) | + (priv.multi_data[1] & 0x1f); + + f.fingers = alps_process_bitmap(&priv, &f); + + } + + /* Use st data when we don't have mt data */ + if (f.fingers < 2) { + f.mt[0].x = f.st.x; + f.mt[0].y = f.st.y; + f.fingers = f.pressure > 0 ? 1 : 0; + priv.second_touch = -1; + } + + prepareVoodooInput(f, f.fingers); + + alps_buttons(f); +} + +unsigned char ApplePS2ALPSGlidePoint::alps_get_packet_id_v7(UInt8 *byte) { + unsigned char packet_id; + + if (byte[4] & 0x40) + packet_id = V7_PACKET_ID_TWO; + else if (byte[4] & 0x01) + packet_id = V7_PACKET_ID_MULTI; + else if ((byte[0] & 0x10) && !(byte[4] & 0x43)) + packet_id = V7_PACKET_ID_NEW; + else if (byte[1] == 0x00 && byte[4] == 0x00) + packet_id = V7_PACKET_ID_IDLE; + else + packet_id = V7_PACKET_ID_UNKNOWN; + + return packet_id; +} + +void ApplePS2ALPSGlidePoint::alps_get_finger_coordinate_v7(struct input_mt_pos *mt, + UInt8 *pkt, + UInt8 pkt_id) +{ + mt[0].x = ((pkt[2] & 0x80) << 4); + mt[0].x |= ((pkt[2] & 0x3F) << 5); + mt[0].x |= ((pkt[3] & 0x30) >> 1); + mt[0].x |= (pkt[3] & 0x07); + mt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07); + + mt[1].x = ((pkt[3] & 0x80) << 4); + mt[1].x |= ((pkt[4] & 0x80) << 3); + mt[1].x |= ((pkt[4] & 0x3F) << 4); + mt[1].y = ((pkt[5] & 0x80) << 3); + mt[1].y |= ((pkt[5] & 0x3F) << 4); + + switch (pkt_id) { + case V7_PACKET_ID_TWO: + mt[1].x &= ~0x000F; + mt[1].y |= 0x000F; + /* Detect false-positive touches where x & y report max value */ + if (mt[1].y == 0x7ff && mt[1].x == 0xff0) + mt[1].x = 0; /* y gets set to 0 at the end of this function */ + break; + + case V7_PACKET_ID_MULTI: + mt[1].x &= ~0x003F; + mt[1].y &= ~0x0020; + mt[1].y |= ((pkt[4] & 0x02) << 4); + mt[1].y |= 0x001F; + break; + + case V7_PACKET_ID_NEW: + mt[1].x &= ~0x003F; + mt[1].x |= (pkt[0] & 0x20); + mt[1].y |= 0x000F; + break; + } + + mt[0].y = 0x7FF - mt[0].y; + mt[1].y = 0x7FF - mt[1].y; +} + +int ApplePS2ALPSGlidePoint::alps_get_mt_count(struct input_mt_pos *mt) { + int i, fingers = 0; + + for (i = 0; i < MAX_TOUCHES; i++) { + if (mt[i].x != 0 || mt[i].y != 0) + fingers++; + } + + return fingers; +} + +bool ApplePS2ALPSGlidePoint::alps_decode_packet_v7(struct alps_fields *f, UInt8 *p){ + //IOLog("%s: Decode V7 touchpad Packet... 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", getName(), p[0], p[1], p[2], p[3], p[4], p[5]); + + unsigned char pkt_id; + + pkt_id = alps_get_packet_id_v7(p); + if (pkt_id == V7_PACKET_ID_IDLE) { + DEBUG_LOG("%s: V7_PACKET_ID_IDLE\n", getName()); + return true; + } + if (pkt_id == V7_PACKET_ID_UNKNOWN) { + DEBUG_LOG("%s: V7_PACKET_ID_UNKNOWN\n", getName()); + return false; + } + + /* + * NEW packets are send to indicate a discontinuity in the finger + * coordinate reporting. Specifically a finger may have moved from + * slot 0 to 1 or vice versa. INPUT_MT_TRACK takes care of this for + * us. + * + * NEW packets have 3 problems: + * 1) They do not contain middle / right button info (on non clickpads) + * this can be worked around by preserving the old button state + * 2) They do not contain an accurate fingercount, and they are + * typically send when the number of fingers changes. We cannot use + * the old finger count as that may mismatch with the amount of + * touch coordinates we've available in the NEW packet + * 3) Their x data for the second touch is inaccurate leading to + * a possible jump of the x coordinate by 16 units when the first + * non NEW packet comes in + * Since problems 2 & 3 cannot be worked around, just ignore them. + */ + if (pkt_id == V7_PACKET_ID_NEW) { + DEBUG_LOG("%s: V7_PACKET_ID_NEW\n", getName()); + return false; + } + + alps_get_finger_coordinate_v7(f->mt, p, pkt_id); + + if (pkt_id == V7_PACKET_ID_TWO) { + DEBUG_LOG("%s: V7_PACKET_ID_TWO\n", getName()); + f->fingers = alps_get_mt_count(f->mt); + } + else { /* pkt_id == V7_PACKET_ID_MULTI */ + DEBUG_LOG("%s: V7_PACKET_ID_MULTI\n", getName()); + f->fingers = 3 + (p[5] & 0x03); + } + + f->left = (p[0] & 0x80) >> 7; + if (priv.flags & ALPS_BUTTONPAD) { + if (p[0] & 0x20) + f->fingers++; + if (p[0] & 0x10) + f->fingers++; + } else { + f->right = (p[0] & 0x20) >> 5; + f->middle = (p[0] & 0x10) >> 4; + } + + /* Sometimes a single touch is reported in mt[1] rather then mt[0] */ + if (f->fingers == 1 && f->mt[0].x == 0 && f->mt[0].y == 0) { + f->mt[0].x = f->mt[1].x; + f->mt[0].y = f->mt[1].y; + f->mt[1].x = 0; + f->mt[1].y = 0; + } + return true; +} + +void ApplePS2ALPSGlidePoint::alps_process_trackstick_packet_v7(UInt8 *packet) { + int x, y, left, right, middle; + // Disable unused code + // int z; + int buttons = 0; + + /* It should be a DualPoint when received trackstick packet */ + if (!(priv.flags & ALPS_DUALPOINT)) { + IOLog("%s: Rejected trackstick packet from non DualPoint device\n", getName()); + return; + } + + x = (SInt8) ((packet[2] & 0xbf) | ((packet[3] & 0x10) << 2)); + y = (SInt8) ((packet[3] & 0x07) | (packet[4] & 0xb8) | ((packet[3] & 0x20) << 1)); +#if 0 + z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1); +#endif + + // Y is inverted + y = -y; + + left = (packet[1] & 0x01); + right = (packet[1] & 0x02) >> 1; + middle = (packet[1] & 0x04) >> 2; + + buttons |= left ? 0x01 : 0; + buttons |= right ? 0x02 : 0; + buttons |= middle ? 0x04 : 0; + + lastTrackStickButtons = buttons; + buttons |= lastTouchpadButtons; + + /* If middle button is pressed, switch to scroll mode. Else, move pointer normally */ + if (0 == (buttons & 0x04)) { + voodooTrackpoint(kIOMessageVoodooTrackpointRelativePointer, x, y, buttons); + } else { + voodooTrackpoint(kIOMessageVoodooTrackpointScrollWheel, x, y, buttons); + } +} + +void ApplePS2ALPSGlidePoint::alps_process_touchpad_packet_v7(UInt8 *packet){ + struct alps_fields f; + + // Check if input is disabled via ApplePS2Keyboard request + if (ignoreall) + return; + + memset(&f, 0, sizeof(alps_fields)); + + if (!((this->*decode_fields)(&f, packet))) + return; + + /* Reverse y co-ordinates to have 0 at bottom for gestures to work */ + f.mt[0].y = priv.y_max - f.mt[0].y; + f.mt[1].y = priv.y_max - f.mt[1].y; + + if (_forceTouchMode != 0) + f.pressure = z_finger; + + prepareVoodooInput(f, f.fingers); + + alps_buttons(f); +} + +void ApplePS2ALPSGlidePoint::alps_process_packet_v7(UInt8 *packet){ + if (packet[0] == 0x48 && (packet[4] & 0x47) == 0x06) + alps_process_trackstick_packet_v7(packet); + else + alps_process_touchpad_packet_v7(packet); +} + +unsigned char ApplePS2ALPSGlidePoint::alps_get_pkt_id_ss4_v2(UInt8 *byte) { + unsigned char pkt_id = SS4_PACKET_ID_IDLE; + + switch (byte[3] & 0x30) { + case 0x00: + if (SS4_IS_IDLE_V2(byte)) { + pkt_id = SS4_PACKET_ID_IDLE; + } else { + pkt_id = SS4_PACKET_ID_ONE; + } + break; + case 0x10: + /* two-finger finger positions */ + pkt_id = SS4_PACKET_ID_TWO; + break; + case 0x20: + /* stick pointer */ + pkt_id = SS4_PACKET_ID_STICK; + break; + case 0x30: + /* third and fourth finger positions */ + pkt_id = SS4_PACKET_ID_MULTI; + break; + } + + return pkt_id; +} + +bool ApplePS2ALPSGlidePoint::alps_decode_ss4_v2(struct alps_fields *f, UInt8 *p){ + + //struct alps_data *priv; + unsigned char pkt_id; + unsigned int no_data_x, no_data_y; + + pkt_id = alps_get_pkt_id_ss4_v2(p); + + /* Current packet is 1Finger coordinate packet */ + switch (pkt_id) { + case SS4_PACKET_ID_ONE: + DEBUG_LOG("%s: SS4_PACKET_ID_ONE\n", getName()); + f->mt[0].x = SS4_1F_X_V2(p); + f->mt[0].y = SS4_1F_Y_V2(p); + DEBUG_LOG("%s: Coordinates for SS4_PACKET_ID_ONE: %dx%d\n", getName(), f->mt[0].x, f->mt[0].y); + f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f; + /* + * When a button is held the device will give us events + * with x, y, and pressure of 0. This causes annoying jumps + * if a touch is released while the button is held. + * Handle this by claiming zero contacts. + */ + f->fingers = f->pressure > 0 ? 1 : 0; + f->first_mp = 0; + f->is_mp = 0; + break; + + case SS4_PACKET_ID_TWO: + DEBUG_LOG("%s: SS4_PACKET_ID_TWO\n", getName()); + if (priv.flags & ALPS_BUTTONPAD) { + if (IS_SS4PLUS_DEV(priv.dev_id)) { + f->mt[0].x = SS4_PLUS_BTL_MF_X_V2(p, 0); + f->mt[1].x = SS4_PLUS_BTL_MF_X_V2(p, 1); + } else { + f->mt[0].x = SS4_BTL_MF_X_V2(p, 0); + f->mt[1].x = SS4_BTL_MF_X_V2(p, 1); + } + f->mt[0].y = SS4_BTL_MF_Y_V2(p, 0); + f->mt[1].y = SS4_BTL_MF_Y_V2(p, 1); + } else { + if (IS_SS4PLUS_DEV(priv.dev_id)) { + f->mt[0].x = SS4_PLUS_STD_MF_X_V2(p, 0); + f->mt[1].x = SS4_PLUS_STD_MF_X_V2(p, 1); + } else { + f->mt[0].x = SS4_STD_MF_X_V2(p, 0); + f->mt[1].x = SS4_STD_MF_X_V2(p, 1); + } + f->mt[0].y = SS4_STD_MF_Y_V2(p, 0); + f->mt[1].y = SS4_STD_MF_Y_V2(p, 1); + } + DEBUG_LOG("%s: Coordinates for SS4_PACKET_ID_TWO: [0]:%dx%d [1]:%dx%d\n", getName(), f->mt[0].x, f->mt[0].y, f->mt[1].x, f->mt[1].y); + f->pressure = SS4_MF_Z_V2(p, 0) ? 0x30 : 0; + + if (SS4_IS_MF_CONTINUE(p)) { + f->first_mp = 1; + } else { + f->fingers = 2; + f->first_mp = 0; + } + f->is_mp = 0; + + break; + + case SS4_PACKET_ID_MULTI: + DEBUG_LOG("%s: SS4_PACKET_ID_MULTI\n", getName()); + if (priv.flags & ALPS_BUTTONPAD) { + if (IS_SS4PLUS_DEV(priv.dev_id)) { + f->mt[2].x = SS4_PLUS_BTL_MF_X_V2(p, 0); + f->mt[3].x = SS4_PLUS_BTL_MF_X_V2(p, 1); + no_data_x = SS4_PLUS_MFPACKET_NO_AX_BL; + } else { + f->mt[2].x = SS4_BTL_MF_X_V2(p, 0); + f->mt[3].x = SS4_BTL_MF_X_V2(p, 1); + no_data_x = SS4_MFPACKET_NO_AX_BL; + } + no_data_y = SS4_MFPACKET_NO_AY_BL; + + f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0); + f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1); + } else { + if (IS_SS4PLUS_DEV(priv.dev_id)) { + f->mt[2].x = SS4_PLUS_STD_MF_X_V2(p, 0); + f->mt[3].x = SS4_PLUS_STD_MF_X_V2(p, 1); + no_data_x = SS4_PLUS_MFPACKET_NO_AX; + } else { + f->mt[2].x = SS4_STD_MF_X_V2(p, 0); + f->mt[3].x = SS4_STD_MF_X_V2(p, 1); + no_data_x = SS4_MFPACKET_NO_AX; + } + no_data_y = SS4_MFPACKET_NO_AY; + + f->mt[2].y = SS4_STD_MF_Y_V2(p, 0); + f->mt[3].y = SS4_STD_MF_Y_V2(p, 1); + } + DEBUG_LOG("%s: Coordinates for SS4_PACKET_ID_MULTI: [2]:%dx%d [3]:%dx%d\n", getName(), f->mt[2].x, f->mt[2].y, f->mt[3].x, f->mt[3].y); + + f->first_mp = 0; + f->is_mp = 1; + + if (SS4_IS_5F_DETECTED(p)) { + f->fingers = 5; + } else if (f->mt[3].x == no_data_x && + f->mt[3].y == no_data_y) { + f->mt[3].x = 0; + f->mt[3].y = 0; + f->fingers = 3; + } else { + f->fingers = 4; + } + break; + + case SS4_PACKET_ID_STICK: + DEBUG_LOG("%s: SS4_PACKET_ID_STICK\n", getName()); + /* + * x, y, and pressure are decoded in + * alps_process_packet_ss4_v2() + */ + f->first_mp = 0; + f->is_mp = 0; + break; + + case SS4_PACKET_ID_IDLE: + default: + memset(f, 0, sizeof(struct alps_fields)); + break; + } + + /* handle buttons */ + if (pkt_id == SS4_PACKET_ID_STICK) { + f->ts_left = !!(SS4_BTN_V2(p) & 0x01); + f->ts_right = !!(SS4_BTN_V2(p) & 0x02); + f->ts_middle = !!(SS4_BTN_V2(p) & 0x04); + } else { + f->left = !!(SS4_BTN_V2(p) & 0x01); + if (!(priv.flags & ALPS_BUTTONPAD)) { + f->right = !!(SS4_BTN_V2(p) & 0x02); + f->middle = !!(SS4_BTN_V2(p) & 0x04); + } + } + return true; +} + +void ApplePS2ALPSGlidePoint::alps_process_packet_ss4_v2(UInt8 *packet) { + // Check if input is disabled via ApplePS2Keyboard request + if (ignoreall) + return; + + int buttons = 0; + struct alps_fields f; + int x, y, pressure; + + memset(&f, 0, sizeof(struct alps_fields)); + (this->*decode_fields)(&f, packet); + if (priv.multi_packet) { + /* + * Sometimes the first packet will indicate a multi-packet + * sequence, but sometimes the next multi-packet would not + * come. Check for this, and when it happens process the + * position packet as usual. + */ + if (f.is_mp) { + /* Now process the 1st packet */ + (this->*decode_fields)(&f, priv.multi_data); + } else { + priv.multi_packet = 0; + } + } + + /* + * "f.is_mp" would always be '0' after merging the 1st and 2nd packet. + * When it is set, it means 2nd packet comes without 1st packet come. + */ + if (f.is_mp) { + return; + } + + /* Save the first packet */ + if (!priv.multi_packet && f.first_mp) { + priv.multi_packet = 1; + memcpy(priv.multi_data, packet, sizeof(priv.multi_data)); + return; + } + + priv.multi_packet = 0; + + /* Report trackstick */ + if (alps_get_pkt_id_ss4_v2(packet) == SS4_PACKET_ID_STICK) { + if (!(priv.flags & ALPS_DUALPOINT)) { + IOLog("%s: Rejected trackstick packet from non DualPoint device\n", getName()); + return; + } + + x = (SInt8) (((packet[0] & 1) << 7) | (packet[1] & 0x7f)); + y = (SInt8) (((packet[3] & 1) << 7) | (packet[2] & 0x7f)); +#if DEBUG + pressure = (packet[4] & 0x7f); +#endif + + buttons |= f.ts_left ? 0x01 : 0; + buttons |= f.ts_right ? 0x02 : 0; + buttons |= f.ts_middle ? 0x04 : 0; + + if ((abs(x) >= 0x7f) || (abs(y) >= 0x7f)) { + return; + } + + // Y is inverted + y = -y; + + // Divide by 3 since trackpoint's speed is too fast + x /= 3; + y /= 3; + + DEBUG_LOG("%s: Trackstick report: X=%d, Y=%d, Z=%d\n", getName(), x, y, pressure); + /* If middle button is pressed, switch to scroll mode. Else, move pointer normally */ + if (0 == (buttons & 0x04)) { + voodooTrackpoint(kIOMessageVoodooTrackpointRelativePointer, x, y, buttons); + } else { + voodooTrackpoint(kIOMessageVoodooTrackpointScrollWheel, x, y, buttons); + } + return; + } + + /* Reverse y co-ordinates to have 0 at bottom for gestures to work */ + f.mt[0].y = priv.y_max - f.mt[0].y; + f.mt[1].y = priv.y_max - f.mt[1].y; + + prepareVoodooInput(f, f.fingers); + + alps_buttons(f); +} + +bool ApplePS2ALPSGlidePoint::alps_command_mode_send_nibble(int nibble) { + SInt32 command; + // The largest amount of requests we will have is 2 right now + // 1 for the initial command, and 1 for sending data OR 1 for receiving data + // If the nibble commands at the top change then this will need to change as + // well. For now we will just validate that the request will not overload + // this object. + TPS2Request<2> request; + int cmdCount = 0, send = 0, receive = 0, i; + + if (nibble > 0xf) { + IOLog("%s::alps_command_mode_send_nibble ERROR: nibble value is greater than 0xf, command may fail\n", getName()); + } + + request.commands[cmdCount].command = kPS2C_SendCommandAndCompareAck; + command = priv.nibble_commands[nibble].command; + request.commands[cmdCount++].inOrOut = command & 0xff; + + send = (command >> 12 & 0xf); + receive = (command >> 8 & 0xf); + + if ((send > 1) || ((send + receive + 1) > 2)) { + return false; + } + + if (send > 0) { + request.commands[cmdCount].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmdCount++].inOrOut = priv.nibble_commands[nibble].data; + } + + for (i = 0; i < receive; i++) { + request.commands[cmdCount].command = kPS2C_ReadDataPort; + request.commands[cmdCount++].inOrOut = 0; + } + + request.commandsCount = cmdCount; + assert(request.commandsCount <= countof(request.commands)); + + _device->submitRequestAndBlock(&request); + + return request.commandsCount == cmdCount; +} + +bool ApplePS2ALPSGlidePoint::alps_command_mode_set_addr(int addr) { + + TPS2Request<1> request; + int i, nibble; + + // DEBUG_LOG("%s: command mode set addr with addr command: 0x%02x\n", getName(), priv.addr_command); + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = priv.addr_command; + request.commandsCount = 1; + _device->submitRequestAndBlock(&request); + + if (request.commandsCount != 1) { + return false; + } + + for (i = 12; i >= 0; i -= 4) { + nibble = (addr >> i) & 0xf; + if (!alps_command_mode_send_nibble(nibble)) { + return false; + } + } + + return true; +} + +int ApplePS2ALPSGlidePoint::alps_command_mode_read_reg(int addr) { + TPS2Request<4> request; + ALPSStatus_t status; + + if (!alps_command_mode_set_addr(addr)) { + DEBUG_LOG("%s: Failed to set addr to read register\n", getName()); + return -1; + } + + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = kDP_GetMouseInformation; //sync.. + request.commands[1].command = kPS2C_ReadDataPort; + request.commands[1].inOrOut = 0; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; + request.commands[3].command = kPS2C_ReadDataPort; + request.commands[3].inOrOut = 0; + request.commandsCount = 4; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + if (request.commandsCount != 4) { + return -1; + } + + status.bytes[0] = request.commands[1].inOrOut; + status.bytes[1] = request.commands[2].inOrOut; + status.bytes[2] = request.commands[3].inOrOut; + + // IOLog("%s: read reg result: { 0x%02x, 0x%02x, 0x%02x }\n", getName(), status.bytes[0], status.bytes[1], status.bytes[2]); + + /* The address being read is returned in the first 2 bytes + * of the result. Check that the address matches the expected + * address. + */ + if (addr != ((status.bytes[0] << 8) | status.bytes[1])) { + DEBUG_LOG("%s: ERROR: read wrong registry value, expected: %x\n", getName(), addr); + return -1; + } + + return status.bytes[2]; +} + +bool ApplePS2ALPSGlidePoint::alps_command_mode_write_reg(int addr, UInt8 value) { + + if (!alps_command_mode_set_addr(addr)) { + return false; + } + + return alps_command_mode_write_reg(value); +} + +bool ApplePS2ALPSGlidePoint::alps_command_mode_write_reg(UInt8 value) { + if (!alps_command_mode_send_nibble((value >> 4) & 0xf)) { + return false; + } + if (!alps_command_mode_send_nibble(value & 0xf)) { + return false; + } + + return true; +} + +bool ApplePS2ALPSGlidePoint::alps_rpt_cmd(SInt32 init_command, SInt32 init_arg, SInt32 repeated_command, ALPSStatus_t *report) { + TPS2Request<9> request; + int byte0, cmd; + cmd = 0; + + if (init_command) { + request.commands[cmd].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_SetMouseResolution; + request.commands[cmd].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmd++].inOrOut = init_arg; + } + + + // 3X run command + request.commands[cmd].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmd++].inOrOut = repeated_command; + request.commands[cmd].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmd++].inOrOut = repeated_command; + request.commands[cmd].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmd++].inOrOut = repeated_command; + + // Get info/result + request.commands[cmd].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_GetMouseInformation; + byte0 = cmd; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commandsCount = cmd; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + report->bytes[0] = request.commands[byte0].inOrOut; + report->bytes[1] = request.commands[byte0+1].inOrOut; + report->bytes[2] = request.commands[byte0+2].inOrOut; + + DEBUG_LOG("%s: %02x report: [0x%02x 0x%02x 0x%02x]\n", + getName(), + repeated_command, + report->bytes[0], + report->bytes[1], + report->bytes[2]); + + return request.commandsCount == cmd; +} + +bool ApplePS2ALPSGlidePoint::alps_enter_command_mode() { + DEBUG_LOG("%s: enter command mode\n", getName()); + TPS2Request<4> request; + ALPSStatus_t status; + + if (!alps_rpt_cmd(NULL, NULL, kDP_MouseResetWrap, &status)) { + IOLog("%s: Failed to enter command mode!\n", getName()); + return false; + } + return true; +} + +bool ApplePS2ALPSGlidePoint::alps_exit_command_mode() { + DEBUG_LOG("%s: exit command mode\n", getName()); + TPS2Request<1> request; + + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = kDP_SetMouseStreamMode; + request.commandsCount = 1; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + return true; +} + +/* + * For DualPoint devices select the device that should respond to + * subsequent commands. It looks like glidepad is behind stickpointer, + * I'd thought it would be other way around... + */ +bool ApplePS2ALPSGlidePoint::alps_passthrough_mode_v2(bool enable) { + int cmd = enable ? kDP_SetMouseScaling2To1 : kDP_SetMouseScaling1To1; + TPS2Request<4> request; + + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = cmd; + request.commands[1].command = kPS2C_SendCommandAndCompareAck; + request.commands[1].inOrOut = cmd; + request.commands[2].command = kPS2C_SendCommandAndCompareAck; + request.commands[2].inOrOut = cmd; + request.commands[3].command = kPS2C_SendCommandAndCompareAck; + request.commands[3].inOrOut = kDP_SetDefaultsAndDisable; + request.commandsCount = 4; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + return request.commandsCount == 4; +} + +bool ApplePS2ALPSGlidePoint::alps_absolute_mode_v1_v2() { + + /* Try ALPS magic knock - 4 disable before enable */ + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_Enable); + + /* + * Switch mouse to poll (remote) mode so motion data will not + * get in our way + */ + ps2_command_short(kDP_MouseSetPoll); + + return true; +} + +int ApplePS2ALPSGlidePoint::alps_monitor_mode_send_word(int word) { + int i, nibble; + + /* + * b0-b11 are valid bits, send sequence is inverse. + * e.g. when word = 0x0123, nibble send sequence is 3, 2, 1 + */ + for (i = 0; i <= 8; i += 4) { + nibble = (word >> i) & 0xf; + alps_command_mode_send_nibble(nibble); + } + + return 0; +} + +int ApplePS2ALPSGlidePoint::alps_monitor_mode_write_reg(int addr, int value) { + ps2_command_short(kDP_Enable); + alps_monitor_mode_send_word(0x0A0); // 0x0A0 is the command to write the word + alps_monitor_mode_send_word(addr); + alps_monitor_mode_send_word(value); + ps2_command_short(kDP_SetDefaultsAndDisable); + + return 0; +} + +int ApplePS2ALPSGlidePoint::alps_monitor_mode(bool enable) { + TPS2Request<4> request; + int cmd = 0; + + if (enable) { + /* EC E9 F5 F5 E7 E6 E7 E9 to enter monitor mode */ + ps2_command_short(kDP_MouseResetWrap); + request.commands[cmd].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_GetMouseInformation; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commandsCount = cmd; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_SetDefaultsAndDisable); + ps2_command_short(kDP_SetMouseScaling2To1); + ps2_command_short(kDP_SetMouseScaling1To1); + ps2_command_short(kDP_SetMouseScaling2To1); + + /* Get Info */ + request.commands[cmd].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_GetMouseInformation; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commandsCount = cmd; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + } else { + /* EC to exit monitor mode */ + ps2_command_short(kDP_MouseResetWrap); + } + + return 0; +} + +void ApplePS2ALPSGlidePoint::alps_absolute_mode_v6() { + // enter monitor mode, to write the register / + alps_monitor_mode(true); + alps_monitor_mode_write_reg(0x000, 0x181); + alps_monitor_mode(false); +} + +bool ApplePS2ALPSGlidePoint::alps_get_status(ALPSStatus_t *status) { + /* Get status: 0xF5 0xF5 0xF5 0xE9 */ + return alps_rpt_cmd(NULL, NULL, kDP_SetDefaultsAndDisable, status); +} + +/* + * Turn touchpad tapping on or off. The sequences are: + * 0xE9 0xF5 0xF5 0xF3 0x0A to enable, + * 0xE9 0xF5 0xF5 0xE8 0x00 to disable. + * My guess that 0xE9 (GetInfo) is here as a sync point. + * For models that also have stickpointer (DualPoints) its tapping + * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but + * we don't fiddle with it. + */ +bool ApplePS2ALPSGlidePoint::alps_tap_mode(bool enable) { + int cmd = enable ? kDP_SetMouseSampleRate : kDP_SetMouseResolution; + UInt8 tapArg = enable ? 0x0A : 0x00; + TPS2Request<8> request; + ALPSStatus_t result; + + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = kDP_GetMouseInformation; + request.commands[1].command = kPS2C_ReadDataPort; + request.commands[1].inOrOut = 0; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; + request.commands[3].command = kPS2C_ReadDataPort; + request.commands[3].inOrOut = 0; + request.commands[4].command = kPS2C_SendCommandAndCompareAck; + request.commands[4].inOrOut = kDP_SetDefaultsAndDisable; + request.commands[5].command = kPS2C_SendCommandAndCompareAck; + request.commands[5].inOrOut = kDP_SetDefaultsAndDisable; + request.commands[6].command = kPS2C_SendCommandAndCompareAck; + request.commands[6].inOrOut = cmd; + request.commands[7].command = kPS2C_SendCommandAndCompareAck; + request.commands[7].inOrOut = tapArg; + request.commandsCount = 8; + _device->submitRequestAndBlock(&request); + + if (request.commandsCount != 8) { + DEBUG_LOG("%s: Enabling tap mode failed before getStatus call, command count=%d\n", getName(), request.commandsCount); + return false; + } + + return alps_get_status(&result); +} + + +bool ApplePS2ALPSGlidePoint::alps_hw_init_v1_v2() { + TPS2Request<1> request; + + if (priv.flags & ALPS_PASS) { + if (!alps_passthrough_mode_v2(true)) { + return false; + } + } + + if (!alps_tap_mode(true)) { + IOLog("%s: Failed to enable hardware tapping\n", getName()); + return false; + } + + if (!alps_absolute_mode_v1_v2()) { + IOLog("%s: Failed to enable absolute mode\n", getName()); + return false; + } + + if (priv.flags & ALPS_PASS) { + if (!alps_passthrough_mode_v2(false)) { + return false; + } + } + + /* ALPS needs stream mode, otherwise it won't report any data */ + ps2_command_short(kDP_SetMouseStreamMode); + + return true; +} + +bool ApplePS2ALPSGlidePoint::alps_hw_init_v6() { + /* Enter passthrough mode to let trackpoint enter 6byte raw mode */ + alps_passthrough_mode_v2(true); + + /* alps_trackstick_enter_extended_mode_v3_v6 */ + ps2_command_short(kDP_SetMouseScaling1To1); + ps2_command_short(kDP_SetMouseScaling1To1); + ps2_command_short(kDP_SetMouseScaling1To1); + ps2_command(0xC8, kDP_SetMouseSampleRate); + ps2_command(0x14, kDP_SetMouseSampleRate); + + alps_passthrough_mode_v2(false); + + alps_absolute_mode_v6(); + + return true; +} + +/* + * Enable or disable passthrough mode to the trackstick. + */ +bool ApplePS2ALPSGlidePoint::alps_passthrough_mode_v3(int regBase, bool enable) { + int regVal; + bool ret = false; + + DEBUG_LOG("%s: passthrough mode enable=%d\n", getName(), enable); + + if (!alps_enter_command_mode()) { + IOLog("%s: Failed to enter command mode while enabling passthrough mode\n", getName()); + return false; + } + + regVal = alps_command_mode_read_reg(regBase + 0x0008); + if (regVal == -1) { + IOLog("%s: Failed to read register while setting up passthrough mode\n", getName()); + goto error; + } + + if (enable) { + regVal |= 0x01; + } else { + regVal &= ~0x01; + } + + ret = alps_command_mode_write_reg(regVal); + +error: + if (!alps_exit_command_mode()) { + IOLog("%s: failed to exit command mode while enabling passthrough mode v3\n", getName()); + return false; + } + + return ret; +} + +/* Must be in command mode when calling this function */ +bool ApplePS2ALPSGlidePoint::alps_absolute_mode_v3() { + + int regVal; + + regVal = alps_command_mode_read_reg(0x0004); + if (regVal == -1) { + return false; + } + + regVal |= 0x06; + if (!alps_command_mode_write_reg(regVal)) { + return false; + } + + return true; +} + +IOReturn ApplePS2ALPSGlidePoint::alps_probe_trackstick_v3_v7(int regBase) { + int ret = kIOReturnIOError, regVal; + + if (!alps_enter_command_mode()) { + goto error; + } + + regVal = alps_command_mode_read_reg(regBase + 0x08); + + if (regVal == -1) { + goto error; + } + + /* bit 7: trackstick is present */ + ret = regVal & 0x80 ? 0 : kIOReturnNoDevice; + +error: + alps_exit_command_mode(); + return ret; +} + +IOReturn ApplePS2ALPSGlidePoint::alps_setup_trackstick_v3(int regBase) { + IOReturn ret = 0; + ALPSStatus_t report; + TPS2Request<3> request; + + /* + * We need to configure trackstick to report data for touchpad in + * extended format. And also we need to tell touchpad to expect data + * from trackstick in extended format. Without this configuration + * trackstick packets sent from touchpad are in basic format which is + * different from what we expect. + */ + if (!alps_passthrough_mode_v3(regBase, true)) { + return kIOReturnIOError; + } + + /* + * E7 report for the trackstick + * + * There have been reports of failures to seem to trace back + * to the above trackstick check failing. When these occur + * this E7 report fails, so when that happens we continue + * with the assumption that there isn't a trackstick after + * all. + */ + if (!alps_rpt_cmd(NULL, NULL, kDP_SetMouseScaling2To1, &report)) { + IOLog("%s: Failed to initialize trackstick (E7 report failed)\n", getName()); + ret = kIOReturnNoDevice; + } else { + /* + * Not sure what this does, but it is absolutely + * essential. Without it, the touchpad does not + * work at all and the trackstick just emits normal + * PS/2 packets. + */ + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = kDP_SetMouseScaling1To1; + request.commands[1].command = kPS2C_SendCommandAndCompareAck; + request.commands[1].inOrOut = kDP_SetMouseScaling1To1; + request.commands[2].command = kPS2C_SendCommandAndCompareAck; + request.commands[2].inOrOut = kDP_SetMouseScaling1To1; + request.commandsCount = 3; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + if (request.commandsCount != 3) { + IOLog("%s: error sending magic E6 scaling sequence\n", getName()); + ret = kIOReturnIOError; + goto error; + } + if (!(alps_command_mode_send_nibble(0x9) && alps_command_mode_send_nibble(0x4))) { + IOLog("%s: Error sending magic E6 nibble sequence\n", getName()); + ret = kIOReturnIOError; + goto error; + } + DEBUG_LOG("%s: Sent magic E6 sequence\n", getName()); + + /* + * This ensures the trackstick packets are in the format + * supported by this driver. If bit 1 isn't set the packet + * format is different. + */ + if (!(alps_enter_command_mode() && + alps_command_mode_write_reg(regBase + 0x0008, 0x82) && + alps_exit_command_mode())) { + ret = -kIOReturnIOError; + //goto error; + } + } +error: + if (!alps_passthrough_mode_v3(regBase, false)) { + ret = kIOReturnIOError; + } + + return ret; +} + +bool ApplePS2ALPSGlidePoint::alps_hw_init_v3() { + int regVal; + + if ((priv.flags & ALPS_DUALPOINT) && + alps_setup_trackstick_v3(ALPS_REG_BASE_PINNACLE) == kIOReturnIOError) + goto error; + + if (!(alps_enter_command_mode() && + alps_absolute_mode_v3())) { + IOLog("%s: Failed to enter absolute mode\n", getName()); + goto error; + } + + regVal = alps_command_mode_read_reg(0x0006); + if (regVal == -1) + goto error; + if (!alps_command_mode_write_reg(regVal | 0x01)) + goto error; + + regVal = alps_command_mode_read_reg(0x0007); + if (regVal == -1) + goto error; + if (!alps_command_mode_write_reg(regVal | 0x01)) + goto error; + + if (alps_command_mode_read_reg(0x0144) == -1) + goto error; + if (!alps_command_mode_write_reg(0x04)) + goto error; + + if (alps_command_mode_read_reg(0x0159) == -1) + goto error; + if (!alps_command_mode_write_reg(0x03)) + goto error; + + if (alps_command_mode_read_reg(0x0163) == -1) + goto error; + if (!alps_command_mode_write_reg(0x0163, 0x03)) + goto error; + + if (alps_command_mode_read_reg(0x0162) == -1) + goto error; + if (!alps_command_mode_write_reg(0x0162, 0x04)) + goto error; + + alps_exit_command_mode(); + + /* Set rate and enable data reporting */ + /* param is set here to 0x28, in Linux code it is 0x64 + ref: https://github.com/torvalds/linux/blob/3593030761630e09200072a4bd06468892c27be3/drivers/input/mouse/alps.c#L2269 + */ + ps2_command(0x28, kDP_SetMouseSampleRate); + ps2_command_short(kDP_Enable); + + return true; + +error: + /* + * Leaving the touchpad in command mode will essentially render + * it unusable until the machine reboots, so exit it here just + * to be safe + */ + alps_exit_command_mode(); + return false; +} + +bool ApplePS2ALPSGlidePoint::alps_get_v3_v7_resolution(int reg_pitch) { + int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys; + + reg = alps_command_mode_read_reg(reg_pitch); + if (reg < 0) + return reg; + + x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */ + x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */ + + y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */ + y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */ + + reg = alps_command_mode_read_reg(reg_pitch + 1); + if (reg < 0) + return reg; + + x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */ + x_electrode = 17 + x_electrode; + + y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */ + y_electrode = 13 + y_electrode; + + x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */ + y_phys = y_pitch * (y_electrode - 1); /* In 0.1 mm units */ + + priv.x_res = priv.x_max * 10 / x_phys; /* units / mm */ + priv.y_res = priv.y_max * 10 / y_phys; /* units / mm */ + + /*IOLog("pitch %dx%d num-electrodes %dx%d physical size %dx%d mm res %dx%d\n", + x_pitch, y_pitch, x_electrode, y_electrode, + x_phys / 10, y_phys / 10, priv.x_res, priv.y_res);*/ + + return true; +} + +bool ApplePS2ALPSGlidePoint::alps_hw_init_rushmore_v3() { + int regVal; + + if (priv.flags & ALPS_DUALPOINT) { + regVal = alps_setup_trackstick_v3(ALPS_REG_BASE_RUSHMORE); + if (regVal == kIOReturnIOError) { + goto error; + } + } + + if (!alps_enter_command_mode() || + alps_command_mode_read_reg(0xc2d9) == -1 || + !alps_command_mode_write_reg(0xc2cb, 0x00)) { + goto error; + } + + if (!alps_get_v3_v7_resolution(0xc2da)) + goto error; + + regVal = alps_command_mode_read_reg(0xc2c6); + if (regVal == -1) + goto error; + if (!alps_command_mode_write_reg(regVal & 0xfd)) + goto error; + + if (!alps_command_mode_write_reg(0xc2c9, 0x64)) + goto error; + + /* enter absolute mode */ + regVal = alps_command_mode_read_reg(0xc2c4); + if (regVal == -1) + goto error; + if (!alps_command_mode_write_reg(regVal | 0x02)) + goto error; + + alps_exit_command_mode(); + + /* Enable data reporting */ + ps2_command_short(kDP_Enable); + + return true; + +error: + alps_exit_command_mode(); + return false; +} + +/* Must be in command mode when calling this function */ +bool ApplePS2ALPSGlidePoint::alps_absolute_mode_v4() { + int regVal; + + regVal = alps_command_mode_read_reg(0x0004); + if (regVal == -1) { + return false; + } + + regVal |= 0x02; + if (!alps_command_mode_write_reg(regVal)) { + return false; + } + + return true; +} + +bool ApplePS2ALPSGlidePoint::alps_hw_init_v4() { + + if (!alps_enter_command_mode()) + goto error; + + if (!alps_absolute_mode_v4()) { + IOLog("%s: Failed to enter absolute mode\n", getName()); + goto error; + } + + if (!alps_command_mode_write_reg(0x0007, 0x8c)) + goto error; + + if (!alps_command_mode_write_reg(0x0149, 0x03)) + goto error; + + if (!alps_command_mode_write_reg(0x0160, 0x03)) + goto error; + + if (!alps_command_mode_write_reg(0x017f, 0x15)) + goto error; + + if (!alps_command_mode_write_reg(0x0151, 0x01)) + goto error; + + if (!alps_command_mode_write_reg(0x0168, 0x03)) + goto error; + + if (!alps_command_mode_write_reg(0x014a, 0x03)) + goto error; + + if (!alps_command_mode_write_reg(0x0161, 0x03)) + goto error; + + alps_exit_command_mode(); + + /* + * This sequence changes the output from a 9-byte to an + * 8-byte format. All the same data seems to be present, + * just in a more compact format. + */ + ps2_command(0xc8, kDP_SetMouseSampleRate); + ps2_command(0x64, kDP_SetMouseSampleRate); + ps2_command(0x50, kDP_SetMouseSampleRate); + ps2_command_short(kDP_GetId); + + /* Set rate and enable data reporting */ + ps2_command(0x64, kDP_SetMouseSampleRate); + ps2_command_short(kDP_Enable); + return true; + +error: + /* + * Leaving the touchpad in command mode will essentially render + * it unusable until the machine reboots, so exit it here just + * to be safe + */ + alps_exit_command_mode(); + return false; +} + +void ApplePS2ALPSGlidePoint::alps_get_otp_values_ss4_v2(unsigned char index, unsigned char otp[]) { + TPS2Request<4> request; + + switch (index) { + case 0: + ps2_command_short(kDP_SetMouseStreamMode); + ps2_command_short(kDP_SetMouseStreamMode); + + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = kDP_GetMouseInformation; + request.commands[1].command = kPS2C_ReadDataPort; + request.commands[1].inOrOut = 0; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; + request.commands[3].command = kPS2C_ReadDataPort; + request.commands[3].inOrOut = 0; + request.commandsCount = 4; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + otp[0] = request.commands[1].inOrOut; + otp[1] = request.commands[2].inOrOut; + otp[2] = request.commands[3].inOrOut; + + break; + + case 1: + ps2_command_short(kDP_MouseSetPoll); + ps2_command_short(kDP_MouseSetPoll); + + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = kDP_GetMouseInformation; + request.commands[1].command = kPS2C_ReadDataPort; + request.commands[1].inOrOut = 0; + request.commands[2].command = kPS2C_ReadDataPort; + request.commands[2].inOrOut = 0; + request.commands[3].command = kPS2C_ReadDataPort; + request.commands[3].inOrOut = 0; + request.commandsCount = 4; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + otp[0] = request.commands[1].inOrOut; + otp[1] = request.commands[2].inOrOut; + otp[2] = request.commands[3].inOrOut; + + break; + } +} + +void ApplePS2ALPSGlidePoint::alps_update_device_area_ss4_v2(unsigned char otp[][4], struct alps_data *priv) { + int num_x_electrode; + int num_y_electrode; + int x_pitch, y_pitch, x_phys, y_phys; + + DEBUG_LOG("%s: Accessing 'Update Device Area'\n", getName()); + + if (IS_SS4PLUS_DEV(priv->dev_id)) { + DEBUG_LOG("%s: Device is SS4_PLUS\n", getName()); + num_x_electrode = SS4PLUS_NUMSENSOR_XOFFSET + (otp[0][2] & 0x0F); + num_y_electrode = SS4PLUS_NUMSENSOR_YOFFSET + ((otp[0][2] >> 4) & 0x0F); + + priv->x_max = (num_x_electrode - 1) * SS4PLUS_COUNT_PER_ELECTRODE; + priv->y_max = (num_y_electrode - 1) * SS4PLUS_COUNT_PER_ELECTRODE; + + x_pitch = (otp[0][1] & 0x0F) + SS4PLUS_MIN_PITCH_MM; + y_pitch = ((otp[0][1] >> 4) & 0x0F) + SS4PLUS_MIN_PITCH_MM; + + } else { + DEBUG_LOG("%s: Device is SS4\n", getName()); + num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0] & 0x0F); + num_y_electrode = SS4_NUMSENSOR_YOFFSET + ((otp[1][0] >> 4) & 0x0F); + + priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE; + priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE; + + x_pitch = ((otp[1][2] >> 2) & 0x07) + SS4_MIN_PITCH_MM; + y_pitch = ((otp[1][2] >> 5) & 0x07) + SS4_MIN_PITCH_MM; + } + + x_phys = x_pitch * (num_x_electrode - 1); /* In 0.1 mm units */ + y_phys = y_pitch * (num_y_electrode - 1); /* In 0.1 mm units */ + + DEBUG_LOG("%s: Your dimensions are: %dx%d\n", getName(), x_phys, y_phys); + + priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */ + priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */ +} + +void ApplePS2ALPSGlidePoint::alps_update_btn_info_ss4_v2(unsigned char otp[][4], struct alps_data *priv) { + unsigned char is_btnless; + + if (IS_SS4PLUS_DEV(priv->dev_id)) + is_btnless = (otp[1][0] >> 1) & 0x01; + else + is_btnless = (otp[1][1] >> 3) & 0x01; + + if (is_btnless) + priv->flags |= ALPS_BUTTONPAD; + + is_btnless ? setProperty("Clickpad", kOSBooleanTrue) : setProperty("Clickpad", kOSBooleanFalse); +} + +void ApplePS2ALPSGlidePoint::alps_update_dual_info_ss4_v2(unsigned char otp[][4], struct alps_data *priv) { + bool is_dual = false; + int reg_val = 0; + + if (IS_SS4PLUS_DEV(priv->dev_id)) { + is_dual = (otp[0][0] >> 4) & 0x01; + + if (!is_dual) { + /* For support TrackStick of Thinkpad L/E series */ + if (alps_exit_command_mode()) + if (alps_enter_command_mode()) + reg_val = alps_command_mode_read_reg(0xD7); + alps_exit_command_mode(); + ps2_command_short(kDP_Enable); + + if (reg_val == 0x0C || reg_val == 0x1D) + is_dual = true; + } + } + + if (is_dual) + priv->flags |= ALPS_DUALPOINT | + ALPS_DUALPOINT_WITH_PRESSURE; + + is_dual ? setProperty("Trackpoint", kOSBooleanTrue) : setProperty("Trackpoint", kOSBooleanFalse); +} + +void ApplePS2ALPSGlidePoint::alps_set_defaults_ss4_v2(struct alps_data *priv) { + unsigned char otp[2][4]; + + memset(otp, 0, sizeof(otp)); + + alps_get_otp_values_ss4_v2(1, &otp[1][0]); + alps_get_otp_values_ss4_v2(0, &otp[0][0]); + + alps_update_device_area_ss4_v2(otp, priv); + + alps_update_btn_info_ss4_v2(otp, priv); + + alps_update_dual_info_ss4_v2(otp, priv); +} + +int ApplePS2ALPSGlidePoint::alps_dolphin_get_device_area(struct alps_data *priv) { + int num_x_electrode, num_y_electrode; + TPS2Request<4> request; + int cmd = 0; + ALPSStatus_t status; + + alps_enter_command_mode(); + + ps2_command_short(kDP_MouseResetWrap); + ps2_command_short(kDP_MouseSetPoll); + ps2_command_short(kDP_MouseSetPoll); + ps2_command(0x0a, kDP_SetMouseSampleRate); + ps2_command(0x0a, kDP_SetMouseSampleRate); + + request.commands[cmd].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmd++].inOrOut = kDP_GetMouseInformation; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commands[cmd].command = kPS2C_ReadDataPort; + request.commands[cmd++].inOrOut = 0; + request.commandsCount = cmd; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + /* results */ + status.bytes[0] = request.commands[1].inOrOut; + status.bytes[1] = request.commands[2].inOrOut; + status.bytes[2] = request.commands[3].inOrOut; + + /* + * Dolphin's sensor line number is not fixed. It can be calculated + * by adding the device's register value with DOLPHIN_PROFILE_X/YOFFSET. + * Further more, we can get device's x_max and y_max by multiplying + * sensor line number with DOLPHIN_COUNT_PER_ELECTRODE. + * + * e.g. When we get register's sensor_x = 11 & sensor_y = 8, + * real sensor line number X = 11 + 8 = 19, and + * real sensor line number Y = 8 + 1 = 9. + * So, x_max = (19 - 1) * 64 = 1152, and + * y_max = (9 - 1) * 64 = 512. + */ + num_x_electrode = DOLPHIN_PROFILE_XOFFSET + (status.bytes[2] & 0x0F); + num_y_electrode = DOLPHIN_PROFILE_YOFFSET + ((status.bytes[2] >> 4) & 0x0F); + priv->x_bits = num_x_electrode; + priv->y_bits = num_y_electrode; + priv->x_max = (num_x_electrode - 1) * DOLPHIN_COUNT_PER_ELECTRODE; + priv->y_max = (num_y_electrode - 1) * DOLPHIN_COUNT_PER_ELECTRODE; + + alps_exit_command_mode(); + + return 0; +} + +bool ApplePS2ALPSGlidePoint::alps_hw_init_dolphin_v1() { + + ps2_command_short(kDP_SetMouseStreamMode); + ps2_command(0x64, kDP_SetMouseSampleRate); + ps2_command(0x28, kDP_SetMouseSampleRate); + ps2_command_short(kDP_Enable); + + return true; +} + +bool ApplePS2ALPSGlidePoint::alps_hw_init_v7(){ + int reg_val; + + if (!alps_enter_command_mode()) + goto error; + + if (alps_command_mode_read_reg(0xc2d9) == -1) + goto error; + + if (!alps_get_v3_v7_resolution(0xc397)) + goto error; + + if (!alps_command_mode_write_reg(0xc2c9, 0x64)) + goto error; + + reg_val = alps_command_mode_read_reg(0xc2c4); + if (reg_val == -1) + goto error; + + if (!alps_command_mode_write_reg(reg_val | 0x02)) + goto error; + + alps_exit_command_mode(); + + ps2_command(0x28, kDP_SetMouseSampleRate); + ps2_command_short(kDP_Enable); + + return true; + +error: + alps_exit_command_mode(); + return false; +} + +bool ApplePS2ALPSGlidePoint::alps_hw_init_ss4_v2() { + /* enter absolute mode */ + ps2_command_short(kDP_SetMouseStreamMode); + ps2_command_short(kDP_SetMouseStreamMode); + ps2_command(0x64, kDP_SetMouseSampleRate); + ps2_command(0x28, kDP_SetMouseSampleRate); + + /* T.B.D. Decread noise packet number, delete in the future */ + alps_exit_command_mode(); + alps_enter_command_mode(); + alps_command_mode_write_reg(0x001D, 0x20); + alps_exit_command_mode(); + + /* final init */ + ps2_command_short(kDP_Enable); + + return true; + +} + +void ApplePS2ALPSGlidePoint::set_protocol() { + setProperty("ALPS Version", priv.proto_version, 32); + + priv.byte0 = 0x8f; + priv.mask0 = 0x8f; + priv.flags = ALPS_DUALPOINT; + + priv.x_max = 2000; + priv.y_max = 1400; + priv.x_bits = 15; + priv.y_bits = 11; + + switch (priv.proto_version) { + case ALPS_PROTO_V1: + case ALPS_PROTO_V2: + hw_init = &ApplePS2ALPSGlidePoint::alps_hw_init_v1_v2; + process_packet = &ApplePS2ALPSGlidePoint::alps_process_packet_v1_v2; + //set_abs_params = alps_set_abs_params_st; + priv.x_max = 1023; + priv.y_max = 767; + break; + + case ALPS_PROTO_V3: + hw_init = &ApplePS2ALPSGlidePoint::alps_hw_init_v3; + process_packet = &ApplePS2ALPSGlidePoint::alps_process_packet_v3; + //set_abs_params = alps_set_abs_params_semi_mt; + decode_fields = &ApplePS2ALPSGlidePoint::alps_decode_pinnacle; + priv.nibble_commands = alps_v3_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + + if (alps_probe_trackstick_v3_v7(ALPS_REG_BASE_PINNACLE)) { + priv.flags &= ~ALPS_DUALPOINT; + } else { + IOLog("%s: TrackStick detected...\n", getName()); + } + + break; + + case ALPS_PROTO_V3_RUSHMORE: + hw_init = &ApplePS2ALPSGlidePoint::alps_hw_init_rushmore_v3; + process_packet = &ApplePS2ALPSGlidePoint::alps_process_packet_v3; + //set_abs_params = alps_set_abs_params_semi_mt; + decode_fields = &ApplePS2ALPSGlidePoint::alps_decode_rushmore; + priv.nibble_commands = alps_v3_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + // This causes jumps in scrolling + //priv.x_bits = 16; + //priv.y_bits = 12; + + // Pinnacle dimensions + priv.x_max = 2047; + priv.y_max = 1535; + + if (alps_probe_trackstick_v3_v7(ALPS_REG_BASE_RUSHMORE)) { + priv.flags &= ~ALPS_DUALPOINT; + } else { + IOLog("%s: TrackStick detected...\n", getName()); + } + + break; + + case ALPS_PROTO_V4: + hw_init = &ApplePS2ALPSGlidePoint::alps_hw_init_v4; + process_packet = &ApplePS2ALPSGlidePoint::alps_process_packet_v4; + //set_abs_params = alps_set_abs_params_semi_mt; + priv.nibble_commands = alps_v4_nibble_commands; + priv.addr_command = kDP_SetDefaultsAndDisable; + break; + + case ALPS_PROTO_V5: + hw_init = &ApplePS2ALPSGlidePoint::alps_hw_init_dolphin_v1; + process_packet = &ApplePS2ALPSGlidePoint::alps_process_touchpad_packet_v3_v5; + decode_fields = &ApplePS2ALPSGlidePoint::alps_decode_dolphin; + //set_abs_params = alps_set_abs_params_semi_mt; + priv.nibble_commands = alps_v3_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + priv.byte0 = 0xc8; + priv.mask0 = 0xc8; + priv.flags = 0; + //priv.x_max = 1360; + //priv.y_max = 660; + priv.x_bits = 23; + priv.y_bits = 12; + + alps_dolphin_get_device_area(&priv); + break; + + case ALPS_PROTO_V6: + hw_init = &ApplePS2ALPSGlidePoint::alps_hw_init_v6; + process_packet = &ApplePS2ALPSGlidePoint::alps_process_packet_v6; + //set_abs_params = alps_set_abs_params_st; + priv.nibble_commands = alps_v6_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + priv.byte0 = 0xc8; + priv.mask0 = 0xc8; + priv.flags = 0; + priv.x_max = 2047; + priv.y_max = 1535; + break; + + case ALPS_PROTO_V7: + hw_init = &ApplePS2ALPSGlidePoint::alps_hw_init_v7; + process_packet = &ApplePS2ALPSGlidePoint::alps_process_packet_v7; + decode_fields = &ApplePS2ALPSGlidePoint::alps_decode_packet_v7; + //set_abs_params = alps_set_abs_params_v7; + priv.nibble_commands = alps_v3_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + priv.byte0 = 0x48; + priv.mask0 = 0x48; + + priv.x_max = 0xfff; + priv.y_max = 0x7ff; + + if (priv.fw_ver[1] != 0xba){ + priv.flags |= ALPS_BUTTONPAD; + IOLog("%s: ButtonPad Detected...\n", getName()); + } + + if (alps_probe_trackstick_v3_v7(ALPS_REG_BASE_V7)){ + priv.flags &= ~ALPS_DUALPOINT; + } else { + IOLog("%s: TrackStick detected...\n", getName()); + } + + break; + + case ALPS_PROTO_V8: + hw_init = &ApplePS2ALPSGlidePoint::alps_hw_init_ss4_v2; + process_packet = &ApplePS2ALPSGlidePoint::alps_process_packet_ss4_v2; + decode_fields = &ApplePS2ALPSGlidePoint::alps_decode_ss4_v2; + //set_abs_params = alps_set_abs_params_ss4_v2; + priv.nibble_commands = alps_v3_nibble_commands; + priv.addr_command = kDP_MouseResetWrap; + priv.byte0 = 0x18; + priv.mask0 = 0x18; + priv.flags = 0; + + alps_set_defaults_ss4_v2(&priv); + break; + } + + if (priv.proto_version != ALPS_PROTO_V1 || + priv.proto_version != ALPS_PROTO_V2 || + priv.proto_version != ALPS_PROTO_V6) + set_resolution(); + + setProperty(VOODOO_INPUT_TRANSFORM_KEY, 0ull, 32); + setProperty("VoodooInputSupported", kOSBooleanTrue); +} + +bool ApplePS2ALPSGlidePoint::matchTable(ALPSStatus_t *e7, ALPSStatus_t *ec) { + const struct alps_model_info *model; + int i; + + IOLog("%s: Touchpad with Signature { %d, %d, %d }\n", getName(), e7->bytes[0], e7->bytes[1], e7->bytes[2]); + + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { + model = &alps_model_data[i]; + + if (!memcmp(e7->bytes, model->signature, sizeof(model->signature))) { + + priv.proto_version = model->protocol_info.version; + + // log model version: + if (priv.proto_version == ALPS_PROTO_V1) { + IOLog("%s: Found an ALPS V1 TouchPad\n", getName()); + } else if (priv.proto_version == ALPS_PROTO_V2) { + IOLog("%s: Found an ALPS V2 TouchPad\n", getName()); + } else if (priv.proto_version == ALPS_PROTO_V3_RUSHMORE) { + IOLog("%s: Found an ALPS V3 Rushmore TouchPad\n", getName()); + } else if (priv.proto_version == ALPS_PROTO_V4) { + IOLog("%s: Found an ALPS V4 TouchPad\n", getName()); + }else if (priv.proto_version == ALPS_PROTO_V6) { + IOLog("%s: Found an ALPS V6 TouchPad\n", getName()); + } + + set_protocol(); + + priv.flags = model->protocol_info.flags; + priv.byte0 = model->protocol_info.byte0; + priv.mask0 = model->protocol_info.mask0; + + return true; + } + } + + return false; +} + +IOReturn ApplePS2ALPSGlidePoint::identify() { + ALPSStatus_t e6, e7, ec; + + /* + * First try "E6 report". + * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. + * The bits 0-2 of the first byte will be 1s if some buttons are + * pressed. + */ + + if (!alps_rpt_cmd(kDP_SetMouseResolution, NULL, kDP_SetMouseScaling1To1, &e6)) { + IOLog("%s: identify: not an ALPS device. Error getting E6 report\n", getName()); + //return kIOReturnIOError; + } + + if ((e6.bytes[0] & 0xf8) != 0 || e6.bytes[1] != 0 || (e6.bytes[2] != 10 && e6.bytes[2] != 100)) { + IOLog("%s: identify: not an ALPS device. Invalid E6 report\n", getName()); + //return kIOReturnInvalid; + } + + /* + * Now get the "E7" and "EC" reports. These will uniquely identify + * most ALPS touchpads. + */ + if (!(alps_rpt_cmd(kDP_SetMouseResolution, NULL, kDP_SetMouseScaling2To1, &e7) && + alps_rpt_cmd(kDP_SetMouseResolution, NULL, kDP_MouseResetWrap, &ec) && + alps_exit_command_mode())) { + IOLog("%s: identify: not an ALPS device. Error getting E7/EC report\n", getName()); + return kIOReturnIOError; + } + + if (matchTable(&e7, &ec)) { + return 0; + + } else if (e7.bytes[0] == 0x73 && e7.bytes[1] == 0x02 && e7.bytes[2] == 0x64 && + ec.bytes[2] == 0x8a) { + priv.proto_version = ALPS_PROTO_V4; + IOLog("%s: Found a V4 TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", getName(), e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + } else if (e7.bytes[0] == 0x73 && e7.bytes[1] == 0x03 && e7.bytes[2] == 0x50 && + ec.bytes[0] == 0x73 && (ec.bytes[1] == 0x01 || ec.bytes[1] == 0x02)) { + priv.proto_version = ALPS_PROTO_V5; + IOLog("%s: Found a V5 Dolphin TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", getName(), e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + } else if (ec.bytes[0] == 0x88 && + ((ec.bytes[1] & 0xf0) == 0xb0 || (ec.bytes[1] & 0xf0) == 0xc0)) { + priv.proto_version = ALPS_PROTO_V7; + IOLog("%s: Found a V7 TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", getName(), e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + } else if (ec.bytes[0] == 0x88 && ec.bytes[1] == 0x08) { + priv.proto_version = ALPS_PROTO_V3_RUSHMORE; + IOLog("%s: Found a V3 Rushmore TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", getName(), e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + } else if (ec.bytes[0] == 0x88 && ec.bytes[1] == 0x07 && + ec.bytes[2] >= 0x90 && ec.bytes[2] <= 0x9d) { + priv.proto_version = ALPS_PROTO_V3; + IOLog("%s: Found a V3 Pinnacle TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", getName(), e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + } else if (e7.bytes[0] == 0x73 && e7.bytes[1] == 0x03 && + (e7.bytes[2] == 0x14 || e7.bytes[2] == 0x28)) { + priv.proto_version = ALPS_PROTO_V8; + IOLog("%s: Found a V8 TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", getName(), e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + } else if (e7.bytes[0] == 0x73 && e7.bytes[1] == 0x03 && e7.bytes[2] == 0xc8) { + priv.proto_version = ALPS_PROTO_V9; + IOLog("%s: Found a unsupported V9 TouchPad with ID: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x\n", getName(), e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + + } else { + IOLog("%s: Touchpad didn't match any known IDs: E7=0x%02x 0x%02x 0x%02x, EC=0x%02x 0x%02x 0x%02x ... driver will now exit\n", + getName(), e7.bytes[0], e7.bytes[1], e7.bytes[2], ec.bytes[0], ec.bytes[1], ec.bytes[2]); + return kIOReturnInvalid; + } + + /* Save Device ID and Firmware version */ + memcpy(priv.dev_id, e7.bytes, 3); + memcpy(priv.fw_ver, ec.bytes, 3); + set_protocol(); + return 0; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2ALPSGlidePoint::setTouchPadEnable(bool enable) { + DEBUG_LOG("%s: setTouchpadEnable enter\n", getName()); + // + // Instructs the trackpad to start or stop the reporting of data packets. + // It is safe to issue this request from the interrupt/completion context. + // + + if (enable) { + initTouchPad(); + } else { + // to disable just reset the mouse + resetMouse(); + } +} + +void ApplePS2ALPSGlidePoint::ps2_command(unsigned char value, UInt8 command) { + TPS2Request<2> request; + int cmdCount = 0; + + request.commands[cmdCount].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmdCount++].inOrOut = command; + request.commands[cmdCount].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmdCount++].inOrOut = value; + request.commandsCount = cmdCount; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + //return request.commandsCount = cmdCount; +} + +void ApplePS2ALPSGlidePoint::ps2_command_short(UInt8 command) { + TPS2Request<1> request; + int cmdCount = 0; + + request.commands[cmdCount].command = kPS2C_SendCommandAndCompareAck; + request.commands[cmdCount++].inOrOut = command; + request.commandsCount = cmdCount; + assert(request.commandsCount <= countof(request.commands)); + _device->submitRequestAndBlock(&request); + + //return request.commandsCount = cmdCount; +} + +int ApplePS2ALPSGlidePoint::abs(int x) { + return (x < 0 ? -(x) : x); +} + +/* ============================================================================================== */ +/* ===========================||\\PROCESS AND DISPATCH TO macOS//||============================== */ +/* ============================================================================================== */ + + +void ApplePS2ALPSGlidePoint::set_resolution() { + physical_max_x = priv.x_max * 4; // this number was determined experimentally + physical_max_y = priv.y_max * 4.5; // this number was determined experimentally + + logical_max_x = priv.x_max; + logical_max_y = priv.y_max; + + setProperty("X Max", priv.x_max, 32); + setProperty("Y Max", priv.y_max, 32); + + if (priv.proto_version == ALPS_PROTO_V7) { + margin_size_x = 3 * xupmm; + + if (maxXOverride != -1) + logical_max_x = maxXOverride; + } + + setProperty(VOODOO_INPUT_LOGICAL_MAX_X_KEY, logical_max_x - margin_size_x, 32); + setProperty(VOODOO_INPUT_LOGICAL_MAX_Y_KEY, logical_max_y, 32); + + setProperty(VOODOO_INPUT_PHYSICAL_MAX_X_KEY, physical_max_x, 32); + setProperty(VOODOO_INPUT_PHYSICAL_MAX_Y_KEY, physical_max_y, 32); + + DEBUG_LOG("%s: logical_max %dx%d physical_max %dx%d upmm %dx%d\n", + getName(), logical_max_x, logical_max_y, + physical_max_x, physical_max_y, + xupmm, yupmm); +} + +void ApplePS2ALPSGlidePoint::voodooTrackpoint(UInt32 type, SInt8 x, SInt8 y, int buttons) { + AbsoluteTime timestamp; + clock_get_uptime(×tamp); + + switch (type) { + case kIOMessageVoodooTrackpointRelativePointer: + RelativePointerEvent rpevent; + rpevent.dx = x; + rpevent.dy = y; + rpevent.buttons = buttons; + rpevent.timestamp = timestamp; + super::messageClient(kIOMessageVoodooTrackpointRelativePointer, voodooInputInstance, &rpevent, sizeof(rpevent)); + break; + case kIOMessageVoodooTrackpointScrollWheel: + ScrollWheelEvent swevent; + swevent.deltaAxis1 = -y; + swevent.deltaAxis2 = -x; + swevent.deltaAxis3 = 0; + swevent.timestamp = timestamp; + super::messageClient(kIOMessageVoodooTrackpointScrollWheel, voodooInputInstance, &swevent, sizeof(swevent)); + break; + } +} + +void ApplePS2ALPSGlidePoint::alps_buttons(struct alps_fields &f) { + bool prev_left = left; + bool prev_right = right; + bool prev_middle = middle; + bool prev_left_ts = left_ts; + bool last_clicked = clicked; + + left = f.left; + right = f.right | f.ts_right; + middle = f.middle | f.ts_middle; + left_ts = f.ts_left; + + AbsoluteTime timestamp; + clock_get_uptime(×tamp); + RelativePointerEvent event; + event.dx = 0; + event.dy = 0; + event.timestamp = timestamp; + + // Physical left button (for non-Clickpads) + // Only used if trackpad is not a clickpad + if (!(priv.flags & ALPS_BUTTONPAD)) { + if (left && !prev_left) { + event.buttons = 0x01; + clicked = true; + } else if (prev_left && !left) { + event.buttons = 0x00; + clicked = false; + } + } + // Physical right button + if (right && !prev_right) { + event.buttons = 0x02; + clicked = true; + } else if (prev_right && !right) { + event.buttons = 0x00; + clicked = false; + } + // Physical middle button + if (middle && !prev_middle) { + event.buttons = 0x04; + clicked = true; + } else if (prev_middle && !middle) { + event.buttons = 0x00; + clicked = false; + } + // Physical left button (Trackstick) + if (left_ts && !prev_left_ts) { + event.buttons = 0x01; + clicked = true; + } else if (prev_left_ts && !left_ts) { + event.buttons = 0x00; + clicked = false; + } + + if (last_clicked != clicked) + super::messageClient(kIOMessageVoodooTrackpointRelativePointer, voodooInputInstance, &event, sizeof(event)); +} + +void ApplePS2ALPSGlidePoint::prepareVoodooInput(struct alps_fields &f, int fingers) { + for (int i = 0; i < MAX_TOUCHES; i++) // free up all virtual fingers + virtualFingerStates[i].touch = false; + + DEBUG_LOG("%s: Amount of finger(s): %d\n", getName(), fingers); + + // limit to 4 fingers + for (int i = 0; i < min(4, fingers); i++) { + // V8 touchpads do not need workarounds for 3+ fingers + if (priv.proto_version == ALPS_PROTO_V8) { + virtualFingerStates[i].x = f.mt[i].x; + virtualFingerStates[i].y = f.mt[i].y; + } else { + if (i == 0 || i == 1) { + virtualFingerStates[i].x = f.mt[i].x; + virtualFingerStates[i].y = f.mt[i].y; + } else if (i == 2) { + virtualFingerStates[i].x = f.mt[0].x + 5; + virtualFingerStates[i].y = f.mt[0].y + 5; + } else if (i == 3) { + virtualFingerStates[i].x = f.mt[1].x - 5; + virtualFingerStates[i].y = f.mt[1].y + 5; + } + } + + virtualFingerStates[i].pressure = f.pressure; + virtualFingerStates[i].touch = true; + + // Only use this if trackpad is a clickpad + virtualFingerStates[0].button = priv.flags & ALPS_BUTTONPAD ? f.left : 0; + + if (virtualFingerStates[i].x > X_MAX_POSITIVE) + virtualFingerStates[i].x -= 1 << ABS_POS_BITS; + else if (virtualFingerStates[i].x == X_MAX_POSITIVE) + virtualFingerStates[i].x = XMAX; + + if (virtualFingerStates[i].y > Y_MAX_POSITIVE) + virtualFingerStates[i].y -= 1 << ABS_POS_BITS; + else if (virtualFingerStates[i].y == Y_MAX_POSITIVE) + virtualFingerStates[i].y = YMAX; + } + + DEBUG_LOG("%s: virtualFingerStates[0] report: x: %d, y: %d, z: %d\n", getName(), virtualFingerStates[0].x, virtualFingerStates[0].y, virtualFingerStates[0].pressure); + + clampedFingerCount = fingers; + + if (clampedFingerCount > MAX_TOUCHES) + clampedFingerCount = MAX_TOUCHES; + + if (fingers > 0) + reportVoodooInput = true; + + if (reportVoodooInput) { + if (fingers == 0 && lastFingerCount == 0) + reportVoodooInput = false; + sendTouchData(); + } +} + +void ApplePS2ALPSGlidePoint::sendTouchData() { + AbsoluteTime timestamp; + clock_get_uptime(×tamp); + 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; + + if (lastFingerCount != clampedFingerCount) { + lastFingerCount = clampedFingerCount; + return; // Skip while fingers are placed on the touchpad or removed + } + + static_assert(VOODOO_INPUT_MAX_TRANSDUCERS >= MAX_TOUCHES, "VoodooPS2ALPSGlidePoint: Trackpad supports too many fingers\n"); + + int transducers_count = 0; + for (int i = 0; i < MAX_TOUCHES; i++) { + const auto &state = virtualFingerStates[i]; + if (!state.touch) { + continue; + } + + auto &transducer = inputEvent.transducers[transducers_count]; + + transducer.previousCoordinates = transducer.currentCoordinates; + + transducer.currentCoordinates.x = state.x; + transducer.currentCoordinates.y = logical_max_y + 1 - state.y; + + transducer.timestamp = timestamp; + + transducer.isValid = true; + transducer.isTransducerActive = true; + + transducer.secondaryId = i; + transducer.fingerType = (MT2FingerType) (kMT2FingerTypeIndexFinger + (i % 4)); + transducer.type = FINGER; + + switch (_forceTouchMode) { + case FORCE_TOUCH_BUTTON: // Physical button is translated into force touch instead of click + transducer.supportsPressure = true; + transducer.isPhysicalButtonDown = false; + transducer.currentCoordinates.pressure = state.button ? 255 : 0; + break; + + case FORCE_TOUCH_THRESHOLD: // Force touch is touch with pressure over threshold + transducer.supportsPressure = true; + transducer.isPhysicalButtonDown = state.button; + transducer.currentCoordinates.pressure = state.pressure > _forceTouchPressureThreshold ? 255 : 0; + break; + + case FORCE_TOUCH_VALUE: // Pressure is passed to system as is + transducer.supportsPressure = true; + transducer.isPhysicalButtonDown = state.button; + transducer.currentCoordinates.pressure = state.pressure; + break; + + case FORCE_TOUCH_CUSTOM: // Pressure is passed, but with locking + transducer.supportsPressure = true; + transducer.isPhysicalButtonDown = state.button; + + if (clampedFingerCount != 1) { + transducer.currentCoordinates.pressure = state.pressure > _forceTouchPressureThreshold ? 255 : 0; + break; + } + + double value; + if (state.pressure >= _forceTouchCustomDownThreshold) { + value = 1.0; + } else if (state.pressure <= _forceTouchCustomUpThreshold) { + value = 0.0; + } else { + double base = ((double) (state.pressure - _forceTouchCustomUpThreshold)) / ((double) (_forceTouchCustomDownThreshold - _forceTouchCustomUpThreshold)); + value = 1; + for (int i = 0; i < _forceTouchCustomPower; ++i) { + value *= base; + } + } + + transducer.currentCoordinates.pressure = (int) (value * 255); + break; + + case FORCE_TOUCH_DISABLED: + default: + transducer.supportsPressure = false; + transducer.isPhysicalButtonDown = state.button; + transducer.currentCoordinates.pressure = 0; + break; + } + + 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 < transducers_count; i++) { + if (inputEvent.transducers[i].currentCoordinates.y > maxY) { + maxY = inputEvent.transducers[i].currentCoordinates.y; + newThumbIndex = i; + } + if (inputEvent.transducers[i].fingerType == kMT2FingerTypeThumb) { + currentThumbIndex = i; + } + } + inputEvent.transducers[currentThumbIndex].fingerType = inputEvent.transducers[newThumbIndex].fingerType; + inputEvent.transducers[newThumbIndex].fingerType = kMT2FingerTypeThumb; + } + + inputEvent.contact_count = transducers_count; + inputEvent.timestamp = timestamp; + + if (voodooInputInstance) { + super::messageClient(kIOMessageVoodooInputMessage, voodooInputInstance, &inputEvent, sizeof(VoodooInputEvent)); + } + + lastFingerCount = clampedFingerCount; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2ALPSGlidePoint::initTouchPad() { + // + // Clear packet buffer pointer to avoid issues caused by + // stale packet fragments. + // + + _packetByteCount = 0; + _ringBuffer.reset(); + + // clear state of control key cache + _modifierdown = 0; + + // initialize the touchpad + deviceSpecificInit(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2ALPSGlidePoint::setParamPropertiesGated(OSDictionary * config) { + if (NULL == config) + return; + + const struct {const char *name; int *var;} int32vars[]={ + {"FingerZ", &z_finger}, + {"WakeDelay", &wakedelay}, + {"UnitsPerMMX", &xupmm}, + {"UnitsPerMMY", &yupmm}, + {"MinLogicalXOverride", &minXOverride}, + {"MinLogicalYOverride", &minYOverride}, + {"MaxLogicalXOverride", &maxXOverride}, + {"MaxLogicalYOverride", &maxYOverride}, + {"ForceTouchMode", (int*)&_forceTouchMode}, // 0 - disable, 1 - left button, 2 - pressure threshold, 3 - pass pressure value + {"ForceTouchPressureThreshold", &_forceTouchPressureThreshold}, // used in mode 2 + {"ForceTouchCustomDownThreshold", &_forceTouchCustomDownThreshold}, // used in mode 4 + {"ForceTouchCustomUpThreshold", &_forceTouchCustomUpThreshold}, // used in mode 4 + {"ForceTouchCustomPower", &_forceTouchCustomPower}, // used in mode 4 + }; + + const struct {const char *name; int *var;} boolvars[]={ + {"ProcessUSBMouseStopsTrackpad", &_processusbmouse}, + {"ProcessBluetoothMouseStopsTrackpad", &_processbluetoothmouse}, + }; + + const struct {const char* name; bool* var;} lowbitvars[]={ + {"USBMouseStopsTrackpad", &usb_mouse_stops_trackpad}, + }; + const struct {const char* name; uint64_t* var; } int64vars[]={ + {"QuietTimeAfterTyping", &maxaftertyping}, + }; + + OSBoolean *bl; + OSNumber *num; + // 64-bit config items + 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(); + 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; + 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)))) + { + *lowbitvars[i].var = bl->isTrue(); + setProperty(lowbitvars[i].name, *lowbitvars[i].var ? kOSBooleanTrue : kOSBooleanFalse); + } + } + + // disable trackpad when USB mouse is plugged in and this functionality is requested + if (attachedHIDPointerDevices && attachedHIDPointerDevices->getCount() > 0) { + ignoreall = usb_mouse_stops_trackpad; + } + + if (_forceTouchMode == FORCE_TOUCH_BUTTON) { + int val[16]; + if (PE_parse_boot_argn("rp0", val, sizeof(val)) || + PE_parse_boot_argn("rp", val, sizeof(val)) || + PE_parse_boot_argn("container-dmg", val, sizeof(val)) || + PE_parse_boot_argn("root-dmg", val, sizeof(val)) || + PE_parse_boot_argn("auth-root-dmg", val, sizeof(val))) + _forceTouchMode = FORCE_TOUCH_DISABLED; + } +} + +IOReturn ApplePS2ALPSGlidePoint::setProperties(OSObject *props) { + OSDictionary *dict = OSDynamicCast(OSDictionary, props); + if (dict && _cmdGate) + { + // syncronize through workloop... + _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2ALPSGlidePoint::setParamPropertiesGated), dict); + } + + return super::setProperties(props); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +void ApplePS2ALPSGlidePoint::setDevicePowerState( UInt32 whatToDo ) { switch ( whatToDo ) { case kPS2C_DisableDevice: - // - // Disable touchpad. + // Disable touchpad (synchronous). // setTouchPadEnable( false ); break; case kPS2C_EnableDevice: - - setTapEnable( _touchPadModeByte ); - // - // Finally, we enable the trackpad itself, so that it may - // start reporting asynchronous events. + // Must not issue any commands before the device has + // completed its power-on self-test and calibration. // - setAbsoluteMode(); - - _ringBuffer.reset(); - _packetByteCount = 0; - - setTouchPadEnable( true ); + + IOSleep(wakedelay); + + // MARK: Find another way to fix trackpad breaking on V8 after sleep + // This workaround is very messy and unstable. + // A proper fix is needed. + // Reset and re-initialize touchpad + _device->lock(); + resetMouse(); + identify(); + initTouchPad(); + _device->unlock(); break; - } + } } -void ApplePS2ALPSGlidePoint::getStatus(ALPSStatus_t *status) -{ - // (read command byte) - TPS2Request<7> request; - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[0].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[1].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[2].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[3].inOrOut = kDP_GetMouseInformation; - request.commands[4].command = kPS2C_ReadDataPort; - request.commands[4].inOrOut = 0; - request.commands[5].command = kPS2C_ReadDataPort; - request.commands[5].inOrOut = 0; - request.commands[6].command = kPS2C_ReadDataPort; - request.commands[6].inOrOut = 0; - request.commandsCount = 7; - assert(request.commandsCount <= countof(request.commands)); - _device->submitRequestAndBlock(&request); - - status->byte0 = request.commands[4].inOrOut; - status->byte1 = request.commands[5].inOrOut; - status->byte2 = request.commands[6].inOrOut; - - DEBUG_LOG("getStatus(): [%02x %02x %02x]\n", status->byte0, status->byte1, status->byte2); +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +IOReturn ApplePS2ALPSGlidePoint::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 is true when trackpad has been disabled + if (enable == ignoreall) + { + // save state, and update LED + ignoreall = !enable; + } + break; + } + + case kPS2M_resetTouchpad: + { + int *reqCode = (int *)argument; + DEBUG_LOG("%s: kPS2M_resetTouchpad reqCode: %d\n", getName() , *reqCode); + if (*reqCode == 1) { + ignoreall = false; + _device->lock(); + resetMouse(); + IOSleep(wakedelay); + identify(); + initTouchPad(); + _device->unlock(); + } + break; + } + + 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; + static const int masks[] = + { + 0x10, // 0x36 + 0x100000, // 0x37 + 0, // 0x38 + 0, // 0x39 + 0x080000, // 0x3a + 0x040000, // 0x3b + 0, // 0x3c + 0x08, // 0x3d + 0x04, // 0x3e + 0x200000, // 0x3f + }; + + switch (pInfo->adbKeyCode) + { + // don't store key time for modifier keys going down + // track modifiers for scrollzoom feature... + // (note: it turns out we didn't need to do this, but leaving this code in for now in case it is useful) + case 0x38: // left shift + case 0x3c: // right shift + case 0x3b: // left control + case 0x3e: // right control + case 0x3a: // left windows (option) + case 0x3d: // right windows + case 0x37: // left alt (command) + case 0x36: // right alt + case 0x3f: // osx fn (function) + if (pInfo->goingDown) + { + _modifierdown |= masks[pInfo->adbKeyCode-0x36]; + break; + } + _modifierdown &= ~masks[pInfo->adbKeyCode-0x36]; + keytime = pInfo->time; + break; + + default: + keytime = pInfo->time; + } + break; + } + } + + return kIOReturnSuccess; } -void ApplePS2ALPSGlidePoint::getModel(ALPSStatus_t *E6,ALPSStatus_t *E7) -{ - // "E6 report" - TPS2Request<9> request; - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[0].inOrOut = kDP_SetMouseResolution; - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[1].inOrOut = 0; - - // 3X set mouse scaling 1 to 1 - request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[2].inOrOut = kDP_SetMouseScaling1To1; - request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[3].inOrOut = kDP_SetMouseScaling1To1; - request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[4].inOrOut = kDP_SetMouseScaling1To1; - request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[5].inOrOut = kDP_GetMouseInformation; - request.commands[6].command = kPS2C_ReadDataPort; - request.commands[6].inOrOut = 0; - request.commands[7].command = kPS2C_ReadDataPort; - request.commands[7].inOrOut = 0; - request.commands[8].command = kPS2C_ReadDataPort; - request.commands[8].inOrOut = 0; - request.commandsCount = 9; - assert(request.commandsCount <= countof(request.commands)); - _device->submitRequestAndBlock(&request); - - // result is "E6 report" - E6->byte0 = request.commands[6].inOrOut; - E6->byte1 = request.commands[7].inOrOut; - E6->byte2 = request.commands[8].inOrOut; - - // Now fetch "E7 report" - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[0].inOrOut = kDP_SetMouseResolution; - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[1].inOrOut = 0; - - // 3X set mouse scaling 2 to 1 - request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[2].inOrOut = kDP_SetMouseScaling2To1; - request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[3].inOrOut = kDP_SetMouseScaling2To1; - request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[4].inOrOut = kDP_SetMouseScaling2To1; - request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[5].inOrOut = kDP_GetMouseInformation; - request.commands[6].command = kPS2C_ReadDataPort; - request.commands[6].inOrOut = 0; - request.commands[7].command = kPS2C_ReadDataPort; - request.commands[7].inOrOut = 0; - request.commands[8].command = kPS2C_ReadDataPort; - request.commands[8].inOrOut = 0; - request.commandsCount = 9; - assert(request.commandsCount <= countof(request.commands)); - _device->submitRequestAndBlock(&request); +void ApplePS2ALPSGlidePoint::registerHIDPointerNotifications() { + IOServiceMatchingNotificationHandler notificationHandler = OSMemberFunctionCast(IOServiceMatchingNotificationHandler, this, &ApplePS2ALPSGlidePoint::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"); + + 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); + } - // result is "E7 report" - E7->byte0 = request.commands[6].inOrOut; - E7->byte1 = request.commands[7].inOrOut; - E7->byte2 = request.commands[8].inOrOut; + // Determine if we should listen for bluetooth mouse attach events as per configuration + if (_processbluetoothmouse) { + // Bluetooth HID devices + 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); + OSSafeReleaseNULL(matchingDictionary); + } } -void ApplePS2ALPSGlidePoint::setAbsoluteMode() -{ - // (read command byte) - TPS2Request<6> request; - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[0].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[1].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[2].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[3].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[4].inOrOut = kDP_Enable; - request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; - request.commands[5].inOrOut = 0xF0; //Set poll ??! - request.commandsCount = 6; - assert(request.commandsCount <= countof(request.commands)); - _device->submitRequestAndBlock(&request); +void ApplePS2ALPSGlidePoint::unregisterHIDPointerNotifications() { + // Free device matching notifiers + // remove() releases them + if (usb_hid_publish_notify) + usb_hid_publish_notify->remove(); + + if (usb_hid_terminate_notify) + usb_hid_terminate_notify->remove(); + + if (bluetooth_hid_publish_notify) + bluetooth_hid_publish_notify->remove(); + + if (bluetooth_hid_terminate_notify) + bluetooth_hid_terminate_notify->remove(); + + attachedHIDPointerDevices->flushCollection(); +} + +void ApplePS2ALPSGlidePoint::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")); + + if (propDeviceClass != NULL) { + + long classOfDevice = propDeviceClass->unsigned32BitValue(); + + long deviceClassMajor = (classOfDevice & 0x1F00) >> 8; + long deviceClassMinor = (classOfDevice & 0xFF) >> 2; + + if (deviceClassMajor == kBluetoothDeviceClassMajorPeripheral) { // Bluetooth peripheral devices + + long deviceClassMinor1 = (deviceClassMinor) & 0x30; + long deviceClassMinor2 = (deviceClassMinor) & 0x0F; + + if (deviceClassMinor1 == kBluetoothDeviceClassMinorPeripheral1Pointing || // Seperate pointing device + deviceClassMinor1 == kBluetoothDeviceClassMinorPeripheral1Combo) // Combo bluetooth keyboard/touchpad + { + if (deviceClassMinor2 == kBluetoothDeviceClassMinorPeripheral2Unclassified || // Mouse + 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()); + } + } + } + } + } + + 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 + ignoreall = false; + } + } +} + +bool ApplePS2ALPSGlidePoint::notificationHIDAttachedHandler(void * refCon, IOService * newService, IONotifier * notifier) { + if (_cmdGate) { // defensive + _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2ALPSGlidePoint::notificationHIDAttachedHandlerGated), newService, notifier); + } + + return true; } diff --git a/VoodooPS2Trackpad/VoodooPS2ALPSGlidePoint.h b/VoodooPS2Trackpad/VoodooPS2ALPSGlidePoint.h index 44c830c0..5f5804bf 100644 --- a/VoodooPS2Trackpad/VoodooPS2ALPSGlidePoint.h +++ b/VoodooPS2Trackpad/VoodooPS2ALPSGlidePoint.h @@ -2,13 +2,13 @@ * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ - * + * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.2 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. - * + * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -16,104 +16,527 @@ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. - * + * * @APPLE_LICENSE_HEADER_END@ */ -#ifndef _APPLEPS2SYNAPTICSTOUCHPAD_H -#define _APPLEPS2SYNAPTICSTOUCHPAD_H +#ifndef _APPLEPS2ALPSTOUCHPAD_H +#define _APPLEPS2ALPSTOUCHPAD_H + +#include "ApplePS2MouseDevice.h" +#include +#include +#include "VoodooInputMultitouch/VoodooInputEvent.h" +#include "VoodooPS2TrackpadCommon.h" + +#define ALPS_PROTO_V1 0x100 +#define ALPS_PROTO_V2 0x200 +#define ALPS_PROTO_V3 0x300 +#define ALPS_PROTO_V3_RUSHMORE 0x310 +#define ALPS_PROTO_V4 0x400 +#define ALPS_PROTO_V5 0x500 +#define ALPS_PROTO_V6 0x600 +#define ALPS_PROTO_V7 0x700 /* t3btl t4s */ +#define ALPS_PROTO_V8 0x800 /* SS4btl SS4s */ +#define ALPS_PROTO_V9 0x900 /* ss3btl */ + +#define MAX_TOUCHES 5 + +#define DOLPHIN_COUNT_PER_ELECTRODE 64 +#define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */ +#define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */ + +struct alps_virtual_finger_state { + UInt32 x; + UInt32 y; + uint8_t pressure; + bool touch; + bool button; + MT2FingerType fingerType; +}; + +/* + * enum SS4_PACKET_ID - defines the packet type for V8 + * SS4_PACKET_ID_IDLE: There's no finger and no button activity. + * SS4_PACKET_ID_ONE: There's one finger on touchpad + * or there's button activities. + * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad + * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad + * SS4_PACKET_ID_STICK: A stick pointer packet + */ +enum SS4_PACKET_ID { + SS4_PACKET_ID_IDLE = 0, + SS4_PACKET_ID_ONE, + SS4_PACKET_ID_TWO, + SS4_PACKET_ID_MULTI, + SS4_PACKET_ID_STICK, +}; + +#define SS4_COUNT_PER_ELECTRODE 256 +#define SS4_NUMSENSOR_XOFFSET 7 +#define SS4_NUMSENSOR_YOFFSET 7 +#define SS4_MIN_PITCH_MM 50 + +#define SS4_MASK_NORMAL_BUTTONS 0x07 + +#define SS4PLUS_COUNT_PER_ELECTRODE 128 +#define SS4PLUS_NUMSENSOR_XOFFSET 16 +#define SS4PLUS_NUMSENSOR_YOFFSET 5 +#define SS4PLUS_MIN_PITCH_MM 37 + +#define IS_SS4PLUS_DEV(_b) (((_b[0]) == 0x73) && \ + ((_b[1]) == 0x03) && \ + ((_b[2]) == 0x28) \ + ) + +#define SS4_IS_IDLE_V2(_b) (((_b[0]) == 0x18) && \ + ((_b[1]) == 0x10) && \ + ((_b[2]) == 0x00) && \ + ((_b[3] & 0x88) == 0x08) && \ + ((_b[4]) == 0x10) && \ + ((_b[5]) == 0x00) \ + ) + +#define SS4_1F_X_V2(_b) (((_b[0]) & 0x0007) | \ + ((_b[1] << 3) & 0x0078) | \ + ((_b[1] << 2) & 0x0380) | \ + ((_b[2] << 5) & 0x1C00) \ + ) + +#define SS4_1F_Y_V2(_b) (((_b[2]) & 0x000F) | \ + ((_b[3] >> 2) & 0x0030) | \ + ((_b[4] << 6) & 0x03C0) | \ + ((_b[4] << 5) & 0x0C00) \ + ) + +#define SS4_1F_Z_V2(_b) (((_b[5]) & 0x0F) | \ + ((_b[5] >> 1) & 0x70) | \ + ((_b[4]) & 0x80) \ + ) + +#define SS4_1F_LFB_V2(_b) (((_b[2] >> 4) & 0x01) == 0x01) + +#define SS4_MF_LF_V2(_b, _i) ((_b[1 + (_i) * 3] & 0x0004) == 0x0004) + +#define SS4_BTN_V2(_b) ((_b[0] >> 5) & SS4_MASK_NORMAL_BUTTONS) + +#define SS4_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 5) & 0x00E0) | \ + ((_b[1 + _i * 3] << 5) & 0x1F00) \ + ) + +#define SS4_PLUS_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 4) & 0x0070) | \ + ((_b[1 + (_i) * 3] << 4) & 0x0F80) \ + ) + +#define SS4_STD_MF_Y_V2(_b, _i) (((_b[1 + (_i) * 3] << 3) & 0x0010) | \ + ((_b[2 + (_i) * 3] << 5) & 0x01E0) | \ + ((_b[2 + (_i) * 3] << 4) & 0x0E00) \ + ) + +#define SS4_BTL_MF_X_V2(_b, _i) (SS4_STD_MF_X_V2(_b, _i) | \ + ((_b[0 + (_i) * 3] >> 3) & 0x0010) \ + ) + +#define SS4_PLUS_BTL_MF_X_V2(_b, _i) (SS4_PLUS_STD_MF_X_V2(_b, _i) | \ + ((_b[0 + (_i) * 3] >> 4) & 0x0008) \ + ) + +#define SS4_BTL_MF_Y_V2(_b, _i) (SS4_STD_MF_Y_V2(_b, _i) | \ + ((_b[0 + (_i) * 3] >> 3) & 0x0008) \ + ) + +#define SS4_MF_Z_V2(_b, _i) (((_b[1 + (_i) * 3]) & 0x0001) | \ + ((_b[1 + (_i) * 3] >> 1) & 0x0002) \ + ) + +#define SS4_IS_MF_CONTINUE(_b) ((_b[2] & 0x10) == 0x10) +#define SS4_IS_5F_DETECTED(_b) ((_b[2] & 0x10) == 0x10) + +#define SS4_MFPACKET_NO_AX 8160 /* X-Coordinate value */ +#define SS4_MFPACKET_NO_AY 4080 /* Y-Coordinate value */ +#define SS4_MFPACKET_NO_AX_BL 8176 /* Buttonless X-Coord value */ +#define SS4_MFPACKET_NO_AY_BL 4088 /* Buttonless Y-Coord value */ +#define SS4_PLUS_MFPACKET_NO_AX 4080 /* SS4 PLUS, X */ +#define SS4_PLUS_MFPACKET_NO_AX_BL 4088 /* Buttonless SS4 PLUS, X */ + +/* + * enum V7_PACKET_ID - defines the packet type for V7 + * V7_PACKET_ID_IDLE: There's no finger and no button activity. + * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad + * or there's button activities. + * V7_PACKET_ID_MULTI: There are at least three non-resting fingers. + * V7_PACKET_ID_NEW: The finger position in slot is not continues from + * previous packet. + */ +enum V7_PACKET_ID { + V7_PACKET_ID_IDLE, + V7_PACKET_ID_TWO, + V7_PACKET_ID_MULTI, + V7_PACKET_ID_NEW, + V7_PACKET_ID_UNKNOWN, +}; + +/** + * struct alps_protocol_info - information about protocol used by a device + * @version: Indicates V1/V2/V3/... + * @byte0: Helps figure out whether a position report packet matches the + * known format for this model. The first byte of the report, ANDed with + * mask0, should match byte0. + * @mask0: The mask used to check the first byte of the report. + * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + */ +struct alps_protocol_info { + UInt16 version; + UInt8 byte0, mask0; + unsigned int flags; +}; + +/** + * struct alps_model_info - touchpad ID table + * @signature: E7 response string to match. + * @protocol_info: information about protocol used by the device. + * + * Many (but not all) ALPS touchpads can be identified by looking at the + * values returned in the "E7 report" and/or the "EC report." This table + * lists a number of such touchpads. + */ +struct alps_model_info { + UInt8 signature[3]; + struct alps_protocol_info protocol_info; +}; + +/** + * struct alps_nibble_commands - encodings for register accesses + * @command: PS/2 command used for the nibble + * @data: Data supplied as an argument to the PS/2 command, if applicable + * + * The ALPS protocol uses magic sequences to transmit binary data to the + * touchpad, as it is generally not OK to send arbitrary bytes out the + * PS/2 port. Each of the sequences in this table sends one nibble of the + * register address or (write) data. Different versions of the ALPS protocol + * use slightly different encodings. + */ +struct alps_nibble_commands { + SInt32 command; + UInt8 data; +}; + +struct alps_bitmap_point { + int start_bit; + int num_bits; +}; + +struct input_mt_pos { + UInt32 x; + UInt32 y; +}; + +/** + * struct alps_fields - decoded version of the report packet + * @x_map: Bitmap of active X positions for MT. + * @y_map: Bitmap of active Y positions for MT. + * @fingers: Number of fingers for MT. + * @pressure: Pressure. + * @st: position for ST. + * @mt: position for MT. + * @first_mp: Packet is the first of a multi-packet report. + * @is_mp: Packet is part of a multi-packet report. + * @left: Left touchpad button is active. + * @right: Right touchpad button is active. + * @middle: Middle touchpad button is active. + * @ts_left: Left trackstick button is active. + * @ts_right: Right trackstick button is active. + * @ts_middle: Middle trackstick button is active. + */ +struct alps_fields { + unsigned int x_map; + unsigned int y_map; + unsigned int fingers; + + int pressure; + struct input_mt_pos st; + struct input_mt_pos mt[MAX_TOUCHES]; + + unsigned int first_mp:1; + unsigned int is_mp:1; + + unsigned int left:1; + unsigned int right:1; + unsigned int middle:1; + + unsigned int ts_left:1; + unsigned int ts_right:1; + unsigned int ts_middle:1; +}; + +class ApplePS2ALPSGlidePoint; + +/** + * struct alps_data - private data structure for the ALPS driver + * @nibble_commands: Command mapping used for touchpad register accesses. + * @addr_command: Command used to tell the touchpad that a register address + * follows. + * @proto_version: Indicates V1/V2/V3/... + * @byte0: Helps figure out whether a position report packet matches the + * known format for this model. The first byte of the report, ANDed with + * mask0, should match byte0. + * @mask0: The mask used to check the first byte of the report. + * @fw_ver: cached copy of firmware version (EC report) + * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + * @x_max: Largest possible X position value. + * @y_max: Largest possible Y position value. + * @x_bits: Number of X bits in the MT bitmap. + * @y_bits: Number of Y bits in the MT bitmap. + * @prev_fin: Finger bit from previous packet. + * @multi_packet: Multi-packet data in progress. + * @multi_data: Saved multi-packet data. + * @f: Decoded packet data fields. + * @quirks: Bitmap of ALPS_QUIRK_*. + */ +struct alps_data { + /* these are autodetected when the device is identified */ + const struct alps_nibble_commands *nibble_commands; + SInt32 addr_command; + UInt16 proto_version; + UInt8 byte0, mask0; + UInt8 dev_id[3]; + UInt8 fw_ver[3]; + int flags; + SInt32 x_max; + SInt32 y_max; + SInt32 x_bits; + SInt32 y_bits; + unsigned int x_res; + unsigned int y_res; -#include "../VoodooPS2Controller/ApplePS2MouseDevice.h" -#include "LegacyIOHIPointing.h" + SInt32 prev_fin; + SInt32 multi_packet; + int second_touch; + UInt8 multi_data[6]; + struct alps_fields f; + UInt8 quirks; + bool PSMOUSE_BAD_DATA; + int pktsize = 6; +}; + +// Pulled out of alps_data, now saved as vars on class +// makes invoking a little easier +typedef bool (ApplePS2ALPSGlidePoint::*hw_init)(); +typedef bool (ApplePS2ALPSGlidePoint::*decode_fields)(struct alps_fields *f, UInt8 *p); +typedef void (ApplePS2ALPSGlidePoint::*process_packet)(UInt8 *packet); +//typedef void (ALPS::*set_abs_params)(); + +#define ALPS_QUIRK_TRACKSTICK_BUTTONS 1 /* trakcstick buttons in trackstick packet */ + +// // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ApplePS2ALPSGlidePoint Class Declaration // -typedef struct ALPSStatus -{ - UInt8 byte0; - UInt8 byte1; - UInt8 byte2; +typedef struct ALPSStatus { + UInt8 bytes[3]; } ALPSStatus_t; -#define SCROLL_NONE 0 -#define SCROLL_HORIZ 1 -#define SCROLL_VERT 2 +#define XMIN 0 +#define XMAX 6143 +#define YMIN 0 +#define YMAX 6143 +#define XMIN_NOMINAL 1472 +#define XMAX_NOMINAL 5472 +#define YMIN_NOMINAL 1408 +#define YMAX_NOMINAL 4448 -#define kPacketLengthSmall 3 -#define kPacketLengthLarge 6 -#define kPacketLengthMax 6 +#define ABS_POS_BITS 13 +#define X_MAX_POSITIVE 8176 +#define Y_MAX_POSITIVE 8176 -class EXPORT ApplePS2ALPSGlidePoint : public IOHIPointing -{ - typedef IOHIPointing super; - OSDeclareDefaultStructors( ApplePS2ALPSGlidePoint ); +#define kPacketLength 6 +#define kDP_CommandNibble10 0xf2 -private: - ApplePS2MouseDevice * _device {nullptr}; - bool _interruptHandlerInstalled {false}; - bool _powerControlHandlerInstalled {false}; - RingBuffer _ringBuffer; - UInt32 _packetByteCount {0}; - IOFixed _resolution; - UInt16 _touchPadVersion; - UInt8 _touchPadModeByte; - - bool _dragging; - bool _edgehscroll; - bool _edgevscroll; - UInt32 _edgeaccell; - double _edgeaccellvalue; - bool _draglock; +// predeclure stuff +struct alps_data; + +class EXPORT ApplePS2ALPSGlidePoint : public IOService { + OSDeclareDefaultStructors( ApplePS2ALPSGlidePoint ); private: - SInt32 _xpos, _xscrollpos; - SInt32 _ypos, _yscrollpos; - SInt32 _zpos, _zscrollpos; - short _scrolling; - -protected: - virtual void dispatchRelativePointerEventWithPacket( UInt8 * packet, - UInt32 packetSize ); - virtual void dispatchAbsolutePointerEventWithPacket(UInt8 *packet,UInt32 packetSize); - virtual void getModel(ALPSStatus_t *e6,ALPSStatus_t *e7); - virtual void setAbsoluteMode(); - virtual void getStatus(ALPSStatus_t *status); - virtual int insideScrollArea(int x,int y); - - virtual void setTapEnable( bool enable ); - virtual void setTouchPadEnable( bool enable ); -#if _NO_TOUCHPAD_ENABLE_ - virtual UInt32 getTouchPadData( UInt8 dataSelector ); - virtual bool setTouchPadModeByte( UInt8 modeByteValue, - bool enableStreamMode = false ); -#endif - virtual PS2InterruptResult interruptOccurred(UInt8 data); - virtual void packetReady(); - virtual void setDevicePowerState(UInt32 whatToDo); - - inline void dispatchRelativePointerEventX(int dx, int dy, UInt32 buttonState, uint64_t now) - { dispatchRelativePointerEvent(dx, dy, buttonState, *(AbsoluteTime*)&now); } - inline void dispatchScrollWheelEventX(short deltaAxis1, short deltaAxis2, short deltaAxis3, uint64_t now) - { dispatchScrollWheelEvent(deltaAxis1, deltaAxis2, deltaAxis3, *(AbsoluteTime*)&now); } - -protected: - IOItemCount buttonCount() override; - IOFixed resolution() override; + IOService *voodooInputInstance {nullptr}; + ApplePS2MouseDevice * _device {nullptr}; + bool _interruptHandlerInstalled {false}; + bool _powerControlHandlerInstalled {false}; + RingBuffer _ringBuffer {}; + UInt32 _packetByteCount {0}; + + IOCommandGate* _cmdGate {nullptr}; + + VoodooInputEvent inputEvent {}; + + // buttons and scroll wheel + unsigned int clicked:1; + unsigned int left:1; + unsigned int right:1; + unsigned int middle:1; + + unsigned int left_ts:1; + + // VoodooInput + int margin_size_x {0}; + + uint32_t logical_max_x {0}; + uint32_t logical_max_y {0}; + + uint32_t physical_max_x {0}; + uint32_t physical_max_y {0}; + + alps_virtual_finger_state virtualFingerStates[MAX_TOUCHES] {}; + + int clampedFingerCount {0}; + int lastFingerCount; + bool reportVoodooInput; + + int minXOverride {-1}, minYOverride {-1}, maxXOverride {-1}, maxYOverride {-1}; + + ForceTouchMode _forceTouchMode {FORCE_TOUCH_DISABLED}; + int _forceTouchPressureThreshold {100}; + + int _forceTouchCustomDownThreshold {90}; + int _forceTouchCustomUpThreshold {20}; + int _forceTouchCustomPower {8}; + + // normal state + UInt32 lastbuttons {0}; + UInt32 lastTrackStickButtons, lastTouchpadButtons; + uint64_t keytime {0}; + bool ignoreall {false}; + int z_finger {45}; + uint64_t maxaftertyping {100000000}; + int wakedelay {1000}; + // HID Notification + 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 + + int _modifierdown {0}; // state of left+right control keys + + // for scaling x/y values + int xupmm {50}, yupmm {50}; // 50 is just arbitrary, but same + + alps_data priv; + hw_init hw_init; + decode_fields decode_fields; + process_packet process_packet; + // set_abs_params set_abs_params; + + void injectVersionDependentProperties(OSDictionary* dict); + bool resetMouse(); + bool handleOpen(IOService *forClient, IOOptionBits options, void *arg) override; + void handleClose(IOService *forClient, IOOptionBits options) override; + bool handleIsOpen(const IOService *forClient) const override; + PS2InterruptResult interruptOccurred(UInt8 data); + void packetReady(); + virtual bool deviceSpecificInit(); + + void alps_process_packet_v1_v2(UInt8 *packet); + int alps_process_bitmap(struct alps_data *priv, struct alps_fields *f); + void alps_process_trackstick_packet_v3(UInt8 * packet); + bool alps_decode_buttons_v3(struct alps_fields *f, UInt8 *p); + bool alps_decode_pinnacle(struct alps_fields *f, UInt8 *p); + bool alps_decode_rushmore(struct alps_fields *f, UInt8 *p); + bool alps_decode_dolphin(struct alps_fields *f, UInt8 *p); + void alps_process_touchpad_packet_v3_v5(UInt8 * packet); + void alps_process_packet_v3(UInt8 *packet); + void alps_process_packet_v6(UInt8 *packet); + void alps_process_packet_v4(UInt8 *packet); + unsigned char alps_get_packet_id_v7(UInt8 *byte); + void alps_get_finger_coordinate_v7(struct input_mt_pos *mt, UInt8 *pkt, UInt8 pkt_id); + int alps_get_mt_count(struct input_mt_pos *mt); + bool alps_decode_packet_v7(struct alps_fields *f, UInt8 *p); + void alps_process_trackstick_packet_v7(UInt8 *packet); + void alps_process_touchpad_packet_v7(UInt8 *packet); + void alps_process_packet_v7(UInt8 *packet); + unsigned char alps_get_pkt_id_ss4_v2(UInt8 *byte); + bool alps_decode_ss4_v2(struct alps_fields *f, UInt8 *p); + void alps_process_packet_ss4_v2(UInt8 *packet); + bool alps_command_mode_send_nibble(int value); + bool alps_command_mode_set_addr(int addr); + int alps_command_mode_read_reg(int addr); + bool alps_command_mode_write_reg(int addr, UInt8 value); + bool alps_command_mode_write_reg(UInt8 value); + bool alps_rpt_cmd(SInt32 init_command, SInt32 init_arg, SInt32 repeated_command, ALPSStatus_t *report); + bool alps_enter_command_mode(); + bool alps_exit_command_mode(); + bool alps_passthrough_mode_v2(bool enable); + bool alps_absolute_mode_v1_v2(); + int alps_monitor_mode_send_word(int word); + int alps_monitor_mode_write_reg(int addr, int value); + int alps_monitor_mode(bool enable); + void alps_absolute_mode_v6(); + bool alps_get_status(ALPSStatus_t *status); + bool alps_tap_mode(bool enable); + bool alps_hw_init_v1_v2(); + bool alps_hw_init_v6(); + bool alps_passthrough_mode_v3(int regBase, bool enable); + bool alps_absolute_mode_v3(); + IOReturn alps_probe_trackstick_v3_v7(int regBase); + IOReturn alps_setup_trackstick_v3(int regBase); + bool alps_hw_init_v3(); + bool alps_get_v3_v7_resolution(int reg_pitch); + bool alps_hw_init_rushmore_v3(); + bool alps_absolute_mode_v4(); + bool alps_hw_init_v4(); + void alps_get_otp_values_ss4_v2(unsigned char index, unsigned char otp[]); + void alps_update_device_area_ss4_v2(unsigned char otp[][4], struct alps_data *priv); + void alps_update_btn_info_ss4_v2(unsigned char otp[][4], struct alps_data *priv); + void alps_update_dual_info_ss4_v2(unsigned char otp[][4], struct alps_data *priv); + void alps_set_defaults_ss4_v2(struct alps_data *priv); + int alps_dolphin_get_device_area(struct alps_data *priv); + bool alps_hw_init_dolphin_v1(); + bool alps_hw_init_v7(); + bool alps_hw_init_ss4_v2(); + void set_protocol(); + bool matchTable(ALPSStatus_t *e7, ALPSStatus_t *ec); + IOReturn identify(); + void setTouchPadEnable(bool enable); + void ps2_command(unsigned char value, UInt8 command); + void ps2_command_short(UInt8 command); + int abs(int x); + void set_resolution(); + void voodooTrackpoint(UInt32 type, SInt8 x, SInt8 y, int buttons); + void alps_buttons(struct alps_fields &f); + + void prepareVoodooInput(struct alps_fields &f, int fingers); + void sendTouchData(); + + virtual void initTouchPad(); + virtual void setParamPropertiesGated(OSDictionary* dict); + virtual void setDevicePowerState(UInt32 whatToDo); + + void registerHIDPointerNotifications(); + void unregisterHIDPointerNotifications(); + + void notificationHIDAttachedHandlerGated(IOService * newService, IONotifier * notifier); + bool notificationHIDAttachedHandler(void * refCon, IOService * newService, IONotifier * notifier); public: - bool init( OSDictionary * properties ) override; - ApplePS2ALPSGlidePoint * 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; + bool init(OSDictionary * dict) override; + ApplePS2ALPSGlidePoint * probe(IOService *provider, SInt32 *score) override; + + bool start(IOService *provider) override; + void stop(IOService *provider) override; + + IOReturn setProperties(OSObject *props) override; + + IOReturn message(UInt32 type, IOService* provider, void* argument) override; }; -#endif /* _APPLEPS2SYNAPTICSTOUCHPAD_H */ +#endif /* _APPLEPS2ALPSTOUCHPAD_H */ diff --git a/VoodooPS2Trackpad/VoodooPS2Elan.cpp b/VoodooPS2Trackpad/VoodooPS2Elan.cpp new file mode 100644 index 00000000..b0e60200 --- /dev/null +++ b/VoodooPS2Trackpad/VoodooPS2Elan.cpp @@ -0,0 +1,2245 @@ +/* +* Elan PS2 touchpad integration +* +* Mostly contains code ported from Linux +* https://github.com/torvalds/linux/blob/master/drivers/input/mouse/elantech.c +* +* Created by Bartosz Korczyński (@bandysc), Hiep Bao Le (@hieplpvip) +* Special thanks to Kishor Prins (@kprinssu), EMlyDinEsHMG and whole VoodooInput team +*/ + +// generally one cannot IOLog from interrupt context, it eventually leads to kernel panic +// but it is useful sometimes +#if 0 +#define INTERRUPT_LOG(args...) do { IOLog(args); } while (0) +#else +#define INTERRUPT_LOG(args...) do { } while (0) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "VoodooPS2Controller.h" +#include "VoodooPS2Elan.h" +#include "VoodooInputMultitouch/VoodooInputTransducer.h" +#include "VoodooInputMultitouch/VoodooInputMessages.h" + +// ============================================================================= +// ApplePS2Elan Class Implementation +// + +OSDefineMetaClassAndStructors(ApplePS2Elan, IOService); + +bool ApplePS2Elan::init(OSDictionary *dict) { + // Initialize this object's minimal state. This is invoked right after this + // object is instantiated. + + if (!super::init(dict)) { + return false; + } + + // announce version + 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); + + return true; +} + +void ApplePS2Elan::injectVersionDependentProperties(OSDictionary *config) { + // inject properties specific to the version of Darwin that is runnning... + char buf[32]; + 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)))) { + break; + } + + // check for "Darwin major.x" + snprintf(buf, sizeof(buf), "Darwin %d.x", version_major); + 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+")))) { + break; + } + } while (0); + + if (dict) { + // found version specific properties above, inject... + 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)) { + setProperty(key, value); + } + } + iter->release(); + } + } +} + +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)) { + return 0; + } + + _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) { + // if DisableDevice is Yes, then do not load at all... + OSBoolean *disable = OSDynamicCast(OSBoolean, config->getObject(kDisableDevice)); + if (disable && disable->isTrue()) { + config->release(); + _device = 0; + return 0; + } +#ifdef DEBUG + // save configuration for later/diagnostics... + setProperty(kMergedConfiguration, config); +#endif + + // load settings specific to Platform Profile + setParamPropertiesGated(config); + injectVersionDependentProperties(config); + OSSafeReleaseNULL(config); + } + + resetMouse(); + + DEBUG_LOG("VoodooPS2Elan: send magic knock to the device.\n"); + // send magic knock to the device + if (elantechDetect()) { + DEBUG_LOG("VoodooPS2Elan: elan touchpad not detected\n"); + return NULL; + } + + resetMouse(); + + if (elantechQueryInfo()) { + DEBUG_LOG("VoodooPS2Elan: query info failed\n"); + return NULL; + } + + DEBUG_LOG("VoodooPS2Elan: capabilities: %x %x %x\n", info.capabilities[0], info.capabilities[1], info.capabilities[2]); + DEBUG_LOG("VoodooPS2Elan: samples: %x %x %x\n", info.capabilities[0], info.capabilities[1], info.capabilities[2]); + DEBUG_LOG("VoodooPS2Elan: hw_version: %x\n", info.hw_version); + DEBUG_LOG("VoodooPS2Elan: fw_version: %x\n", info.fw_version); + DEBUG_LOG("VoodooPS2Elan: x_min: %d\n", info.x_min); + DEBUG_LOG("VoodooPS2Elan: y_min: %d\n", info.y_min); + DEBUG_LOG("VoodooPS2Elan: x_max: %d\n", info.x_max); + DEBUG_LOG("VoodooPS2Elan: y_max: %d\n", info.y_max); + DEBUG_LOG("VoodooPS2Elan: x_res: %d\n", info.x_res); + DEBUG_LOG("VoodooPS2Elan: y_res: %d\n", info.y_res); + DEBUG_LOG("VoodooPS2Elan: x_traces: %d\n", info.x_traces); + DEBUG_LOG("VoodooPS2Elan: y_traces: %d\n", info.y_traces); + DEBUG_LOG("VoodooPS2Elan: width: %d\n", info.width); + DEBUG_LOG("VoodooPS2Elan: bus: %d\n", info.bus); + DEBUG_LOG("VoodooPS2Elan: paritycheck: %d\n", info.paritycheck); + DEBUG_LOG("VoodooPS2Elan: jumpy_cursor: %d\n", info.jumpy_cursor); + DEBUG_LOG("VoodooPS2Elan: reports_pressure: %d\n", info.reports_pressure); + DEBUG_LOG("VoodooPS2Elan: crc_enabled: %d\n", info.crc_enabled); + DEBUG_LOG("VoodooPS2Elan: set_hw_resolution: %d\n", info.set_hw_resolution); + DEBUG_LOG("VoodooPS2Elan: has_trackpoint: %d\n", info.has_trackpoint); + DEBUG_LOG("VoodooPS2Elan: has_middle_button: %d\n", info.has_middle_button); + + DEBUG_LOG("VoodooPS2Elan: elan touchpad detected. Probing finished.\n"); + + _device = nullptr; + + return this; +} + +bool ApplePS2Elan::handleOpen(IOService *forClient, IOOptionBits options, void *arg) { + if (forClient && forClient->getProperty(VOODOO_INPUT_IDENTIFIER)) { + voodooInputInstance = forClient; + voodooInputInstance->retain(); + + return true; + } + + return false; +} + +bool ApplePS2Elan::handleIsOpen(const IOService *forClient) const { + if (forClient == nullptr) { + return voodooInputInstance != nullptr; + } else { + return voodooInputInstance == forClient; + } +} + +void ApplePS2Elan::handleClose(IOService *forClient, IOOptionBits options) { + if (forClient == voodooInputInstance) { + OSSafeReleaseNULL(voodooInputInstance); + } +} + +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)) { + return false; + } + + // Maintain a pointer to and retain the provider object. + _device = (ApplePS2MouseDevice *)provider; + _device->retain(); + + // 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); + +#ifdef DEBUG + if (info.bus == ETP_BUS_PS2_ONLY) { + setProperty("Bus", "ETP_BUS_PS2_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) { + setProperty("Bus", "ETP_BUS_SMB_HST_NTFY_ONLY"); + } 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) { + 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)) { + 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) { + setProperty("SMBus NOTE", "It looks like your touchpad does not support SMBus protocol."); + } +#endif + + // Setup workloop with command gate for thread syncronization... + IOWorkLoop *pWorkLoop = getWorkLoop(); + _cmdGate = IOCommandGate::commandGate(this); + 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(PS2PacketAction, this, &ApplePS2Elan::packetReady)); + _interruptHandlerInstalled = true; + + // 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; + + // Request message registration for keyboard to trackpad communication + //setProperty(kDeliverNotifications, true); + + return true; +} + +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 + // 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 touchpad + setTouchPadEnable(false); + + // Release command gate + IOWorkLoop *pWorkLoop = getWorkLoop(); + if (pWorkLoop) { + if (_cmdGate) { + pWorkLoop->removeEventSource(_cmdGate); + OSSafeReleaseNULL(_cmdGate); + } + } + + // Uninstall the interrupt handler + if (_interruptHandlerInstalled) { + _device->uninstallInterruptAction(); + _interruptHandlerInstalled = false; + } + + // Uninstall the power control handler + if (_powerControlHandlerInstalled) { + _device->uninstallPowerControlAction(); + _powerControlHandlerInstalled = false; + } + + // Release the pointer to the provider object. + OSSafeReleaseNULL(_device); + + super::stop(provider); +} + +void ApplePS2Elan::setParamPropertiesGated(OSDictionary *config) { + if (NULL == config) { + return; + } + + const struct {const char *name; int *var;} int32vars[] = { + {"WakeDelay", &wakedelay}, + {"TrackpointDeadzone", &_trackpointDeadzone}, + {"TrackpointMultiplierX", &_trackpointMultiplierX}, + {"TrackpointMultiplierY", &_trackpointMultiplierY}, + {"TrackpointDividerX", &_trackpointDividerX}, + {"TrackpointDividerY", &_trackpointDividerY}, + {"TrackpointScrollMultiplierX", &_trackpointScrollMultiplierX}, + {"TrackpointScrollMultiplierY", &_trackpointScrollMultiplierY}, + {"TrackpointScrollDividerY", &_trackpointScrollDividerX}, + {"TrackpointScrollDividerY", &_trackpointScrollDividerY}, + {"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}, + }; + + OSBoolean *bl; + OSNumber *num; + + // highrate? + if ((bl = OSDynamicCast(OSBoolean, config->getObject("UseHighRate")))) { + setProperty("UseHighRate", bl->isTrue()); + } + + // 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)))) { + *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(); + setProperty(boolvars[i].name, *boolvars[i].var ? kOSBooleanTrue : kOSBooleanFalse); + } + } + + // 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; + setProperty(lowbitvars[i].name, *lowbitvars[i].var ? 1 : 0, 32); + } 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); + } + } + + // disable trackpad when USB mouse is plugged in and this functionality is requested + if (attachedHIDPointerDevices && attachedHIDPointerDevices->getCount() > 0) { + ignoreall = usb_mouse_stops_trackpad; + } + + setTrackpointProperties(); +} + +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); +} + +void ApplePS2Elan::setTrackpointProperties() +{ + // Trackpoint information for VoodooInput + OSDictionary *trackpoint = OSDictionary::withCapacity(10); + if (trackpoint == nullptr) + return; + + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_DEADZONE, _trackpointDeadzone); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_BTN_CNT, 3); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_MOUSE_MULT_X, _trackpointMultiplierX); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_MOUSE_MULT_Y, _trackpointMultiplierY); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_MOUSE_DIV_X, _trackpointDividerX); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_MOUSE_DIV_Y, _trackpointDividerY); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_SCROLL_MULT_X, _trackpointScrollMultiplierX); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_SCROLL_MULT_Y, _trackpointScrollMultiplierY); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_SCROLL_DIV_X, _trackpointScrollDividerX); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_SCROLL_DIV_Y, _trackpointScrollDividerY); + + setProperty(VOODOO_TRACKPOINT_KEY, trackpoint); + OSSafeReleaseNULL(trackpoint); +} + +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; + DEBUG_LOG("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; + } + + case kPS2M_notifyKeyTime: + { + // just remember last time key pressed... this can be used in + // interrupt handler to detect unintended input while typing + keytime = *((uint64_t*)argument); + break; + } + } + + return kIOReturnSuccess; +} + +void ApplePS2Elan::setDevicePowerState(UInt32 whatToDo) { + switch (whatToDo) { + case kPS2C_DisableDevice: + // 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 + IOSleep(wakedelay); + + // Clear packet buffer pointer to avoid issues caused by stale packet fragments + _packetByteCount = 0; + _ringBuffer.reset(); + + // Reset and enable the touchpad + resetMouse(); + elantechSetupPS2(); + setTouchPadEnable(true); + break; + } +} + +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"); + + 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"); + 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); + OSSafeReleaseNULL(matchingDictionary); + } +} + +void ApplePS2Elan::unregisterHIDPointerNotifications() { + // Free device matching notifiers + // remove() releases them + + if (usb_hid_publish_notify) { + usb_hid_publish_notify->remove(); + } + + if (usb_hid_terminate_notify) { + usb_hid_terminate_notify->remove(); + } + + if (bluetooth_hid_publish_notify) { + bluetooth_hid_publish_notify->remove(); + } + + if (bluetooth_hid_terminate_notify) { + bluetooth_hid_terminate_notify->remove(); + } + + attachedHIDPointerDevices->flushCollection(); +} + +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")); + + if (propDeviceClass != NULL) { + UInt32 classOfDevice = propDeviceClass->unsigned32BitValue(); + + UInt32 deviceClassMajor = (classOfDevice & 0x1F00) >> 8; + UInt32 deviceClassMinor = (classOfDevice & 0xFF) >> 2; + + if (deviceClassMajor == kBluetoothDeviceClassMajorPeripheral) { // Bluetooth peripheral devices + UInt32 deviceClassMinor1 = (deviceClassMinor) & 0x30; + UInt32 deviceClassMinor2 = (deviceClassMinor) & 0x0F; + + if (deviceClassMinor1 == kBluetoothDeviceClassMinorPeripheral1Pointing || // Seperate pointing device + deviceClassMinor1 == kBluetoothDeviceClassMinorPeripheral1Combo) // Combo bluetooth keyboard/touchpad + { + if (deviceClassMinor2 == kBluetoothDeviceClassMinorPeripheral2Unclassified || // Mouse + 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()); + } + } + } + } + } + + 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 + ignoreall = false; + } + } +} + +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 + +template +int ApplePS2Elan::ps2_command(UInt8 *params, unsigned int command) { + TPS2Request<1 + I> request; + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = command; + 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++) { + params[i] = request.commands[i + 1].inOrOut; + } + + return request.commandsCount != 1 + I; +} + +/* + * A retrying version of ps2_command + */ +template +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) { + break; + } + tries--; + DEBUG_LOG("VoodooPS2Elan: retrying ps2 command 0x%02x (%d).\n", command, tries); + IOSleep(ETP_PS2_COMMAND_DELAY); + } while (tries > 0); + + if (rc) { + DEBUG_LOG("VoodooPS2Elan: ps2 command 0x%02x failed.\n", command); + } + + return rc; +} + +/* + * ps2_sliced_command() sends an extended PS/2 command to the mouse + * using sliced syntax, understood by advanced devices, such as Logitech + * or Synaptics touchpads. The command is encoded as: + * 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 j = 0; + + TPS2Request<> request; + request.commands[j].command = kPS2C_SendCommandAndCompareAck; + request.commands[j++].inOrOut = kDP_SetMouseScaling1To1; + + for (int i = 6; i >= 0; i -= 2) { + UInt8 d = (command >> i) & 3; + request.commands[j].command = kPS2C_SendCommandAndCompareAck; + request.commands[j++].inOrOut = kDP_SetMouseResolution; + + request.commands[j].command = kPS2C_SendCommandAndCompareAck; + request.commands[j++].inOrOut = d; + } + + request.commandsCount = j; + _device->submitRequestAndBlock(&request); + + return request.commandsCount != j; +} + +/* + * 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)) { + DEBUG_LOG("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) { + if (ps2_command<0>(NULL, ETP_PS2_CUSTOM_COMMAND) || + ps2_command<0>(NULL, c) || + ps2_command(param, kDP_GetMouseInformation)) { + DEBUG_LOG("VoodooPS2Elan: query 0x%02x failed.\n", c); + return -1; + } + + 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); + } +} + +bool ApplePS2Elan::elantech_is_signature_valid(const unsigned char *param) { + static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10 }; + + if (param[0] == 0) { + return false; + } + + 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) { + return true; + } + + for (int i = 0; i < sizeof(rates) / sizeof(*rates); i++) { + if (param[2] == rates[i]) { + return false; + } + } + + return true; +} + +/* + * (value from firmware) * 10 + 790 = dpi + * we also have to convert dpi to dots/mm (*10/254 to avoid floating point) + */ +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) { + unsigned char param[3]; + + 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); + *bus = param[2]; + + return 0; +} + +/* + * Use magic knock to detect Elantech touchpad + */ +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<3>(param, kDP_GetMouseInformation)) { + 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)) { + DEBUG_LOG("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. + if (synaptics_send_cmd<3>(ETP_FW_VERSION_QUERY, param)) { + DEBUG_LOG("VoodooPS2Elan: failed to query firmware version.\n"); + return -1; + } + + DEBUG_LOG("VoodooPS2Elan: Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", param[0], param[1], param[2]); + + if (!elantech_is_signature_valid(param)) { + DEBUG_LOG("VoodooPS2Elan: Probably not a real Elantech touchpad. Aborting.\n"); + return -1; + } + + return 0; +} + +int ApplePS2Elan::elantechQueryInfo() { + unsigned char param[3]; + unsigned char traces; + + // Do the version query again so we can store the result + if (synaptics_send_cmd<3>(ETP_FW_VERSION_QUERY, param)) { + DEBUG_LOG("VoodooPS2Elan: failed to query firmware version.\n"); + return -1; + } + + info.fw_version = (param[0] << 16) | (param[1] << 8) | param[2]; + + if (elantechSetProperties()) { + DEBUG_LOG("VoodooPS2Elan: unknown hardware version, aborting...\n"); + return -1; + } + + DEBUG_LOG("VoodooPS2Elan assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n", + info.hw_version, param[0], param[1], param[2]); + + if (send_cmd<3>(ETP_CAPABILITIES_QUERY, info.capabilities)) { + DEBUG_LOG("VoodooPS2Elan: failed to query capabilities.\n"); + return -1; + } + + DEBUG_LOG("VoodooPS2Elan: Elan capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", + info.capabilities[0], info.capabilities[1], + info.capabilities[2]); + + if (info.hw_version != 1) { + if (send_cmd<3>(ETP_SAMPLE_QUERY, info.samples)) { + DEBUG_LOG("VoodooPS2Elan: failed to query sample data\n"); + return -1; + } + DEBUG_LOG("VoodooPS2Elan: Elan sample query result %02x, %02x, %02x\n", + info.samples[0], + info.samples[1], + info.samples[2]); + } + + 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. + DEBUG_LOG("VoodooPS2Elan: absolute mode broken, forcing standard PS/2 protocol\n"); + return -1; + } + + // 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)) { + DEBUG_LOG("VoodooPS2Elan: failed to query resolution data.\n"); + } + } + + // query range information + switch (info.hw_version) { + 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; + + case 2: + if (info.fw_version == 0x020800 || + info.fw_version == 0x020b00 || + info.fw_version == 0x020030) { + info.x_min = ETP_XMIN_V2; + info.y_min = ETP_YMIN_V2; + info.x_max = ETP_XMAX_V2; + info.y_max = ETP_YMAX_V2; + } else { + if (send_cmd<3>(ETP_FW_ID_QUERY, param)) { + return -1; + } + + 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; + } + + 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; + + 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; + + 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; + } + + info.width = info.x_max / (traces - 1); + + // column number of traces + info.x_traces = traces; + + // row number of traces + traces = info.capabilities[2]; + if ((traces >= 2) && (traces <= info.y_max)) { + info.y_traces = traces; + } + + break; + } + + // check if device has buttonpad + info.is_buttonpad = (info.fw_version & 0x001000) != 0; + + // check for the middle button + info.has_middle_button = ETP_NEW_IC_SMBUS_HOST_NOTIFY(info.fw_version) && !info.is_buttonpad; + + 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; + + // Set packet length (4 for v1, 6 for v2 and newer) + _packetLength = (info.hw_version == 1) ? 4 : 6; + + return 0; +} + +/* + * 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 - info.x_min + 1) * 100 / info.x_res, 32); + setProperty(VOODOO_INPUT_PHYSICAL_MAX_Y_KEY, (info.y_max - info.y_min + 1) * 100 / info.y_res, 32); + + setProperty(VOODOO_INPUT_TRANSFORM_KEY, 0ull, 32); + setProperty("VoodooInputSupported", kOSBooleanTrue); + registerService(); + + return 0; +} + +/* + * Put the touchpad into absolute mode + */ +int ApplePS2Elan::elantechSetAbsoluteMode() { + unsigned char val; + int tries = ETP_READ_BACK_TRIES; + int rc = 0; + + switch (info.hw_version) { + case 1: + etd.reg_10 = 0x16; + etd.reg_11 = 0x8f; + if (elantechWriteReg(0x10, etd.reg_10) || + elantechWriteReg(0x11, etd.reg_11)) { + rc = -1; + } + break; + + case 2: + // Windows driver values + etd.reg_10 = 0x54; + 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)) { + rc = -1; + } + break; + + case 3: + if (info.set_hw_resolution) { + etd.reg_10 = 0x0b; + } else { + etd.reg_10 = 0x01; + } + + if (elantechWriteReg(0x10, etd.reg_10)) { + rc = -1; + } + + break; + + case 4: + etd.reg_07 = 0x01; + if (elantechWriteReg(0x07, etd.reg_07)) { + rc = -1; + } + + 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. + do { + rc = elantechReadReg(0x10, &val); + if (rc == 0) { + break; + } + tries--; + DEBUG_LOG("VoodooPS2Elan: retrying read (%d).\n", tries); + IOSleep(ETP_READ_BACK_DELAY); + } while (tries > 0); + + if (rc) { + DEBUG_LOG("VoodooPS2Elan: failed to read back register 0x10.\n"); + } else if (info.hw_version == 1 && !(val & ETP_R10_ABSOLUTE_MODE)) { + DEBUG_LOG("VoodooPS2Elan: touchpad refuses to switch to absolute mode.\n"); + rc = -1; + } + } + +skip_readback_reg_10: + if (rc) { + DEBUG_LOG("VoodooPS2Elan: failed to initialise registers.\n"); + } + + return rc; +} + +/* + * Initialize the touchpad + */ +int ApplePS2Elan::elantechSetupPS2() { + etd.parity[0] = 1; + for (int i = 1; i < 256; i++) + etd.parity[i] = etd.parity[i & (i - 1)] ^ 1; + + if (elantechSetAbsoluteMode()) { + DEBUG_LOG("VoodooPS2: failed to put touchpad into absolute mode.\n"); + return -1; + } + + /* + if (info.fw_version == 0x381f17) { + etd.original_set_rate = psmouse->set_rate; + psmouse->set_rate = elantech_set_rate_restore_reg_07; + } + */ + + if (elantechSetInputParams()) { + DEBUG_LOG("VoodooPS2: failed to query touchpad range.\n"); + return -1; + } + + // set resolution and dpi + TPS2Request<> request; + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = kDP_SetDefaultsAndDisable; // 0xF5, Disable data reporting + request.commands[1].command = kPS2C_SendCommandAndCompareAck; + request.commands[1].inOrOut = kDP_SetMouseSampleRate; // 0xF3 + request.commands[2].command = kPS2C_SendCommandAndCompareAck; + request.commands[2].inOrOut = _mouseSampleRate; // 200 dpi + request.commands[3].command = kPS2C_SendCommandAndCompareAck; + request.commands[3].inOrOut = kDP_SetMouseResolution; // 0xE8 + request.commands[4].command = kPS2C_SendCommandAndCompareAck; + request.commands[4].inOrOut = _mouseResolution; // 0x03 = 8 counts/mm + request.commands[5].command = kPS2C_SendCommandAndCompareAck; + request.commands[5].inOrOut = kDP_SetMouseScaling1To1; // 0xE6 + request.commands[6].command = kPS2C_SendCommandAndCompareAck; + request.commands[6].inOrOut = kDP_Enable; // 0xF4, Enable Data Reporting + request.commandsCount = 7; + _device->submitRequestAndBlock(&request); + + return 0; +} + +/* + * 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] = {0, 0, 0}; + 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) { + DEBUG_LOG("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) { + DEBUG_LOG("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::elantechRescale(unsigned int x, unsigned int y) { + bool needs_update = false; + + if (x > info.x_max) { + info.x_max = x; + needs_update = true; + } + + if (y > info.y_max) { + info.y_max = y; + needs_update = true; + } + + if (needs_update) { + 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 - info.x_min + 1) * 100 / info.x_res, 32); + setProperty(VOODOO_INPUT_PHYSICAL_MAX_Y_KEY, (info.y_max - info.y_min + 1) * 100 / info.y_res, 32); + + if (voodooInputInstance) { + VoodooInputDimensions dims = { + static_cast(info.x_min), static_cast(info.x_max), + static_cast(info.y_min), static_cast(info.y_max) + }; + + super::messageClient(kIOMessageVoodooInputUpdateDimensionsMessage, voodooInputInstance, &dims, sizeof(dims)); + } + } +} + +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; + + // 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]); + + // pressure: (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); + // finger 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); + 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; + } + } + + 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 = 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 (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::elantechReportAbsoluteV3(int packetType) { + unsigned char *packet = _ringBuffer.tail(); + unsigned int fingers = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0; + + // byte 0: n1 n0 . . . . R L + fingers = (packet[0] & 0xc0) >> 6; + + INTERRUPT_LOG("report abs v3 type %d finger %u x %d y %d btn %d (%02x %02x %02x %02x %02x %02x)\n", packetType, fingers, + ((packet[1] & 0x0f) << 8) | packet[2], + (((packet[4] & 0x0f) << 8) | packet[5]), + packet[0] & 0x03, packet[0], packet[1], packet[2], packet[3], packet[4], packet[5]); + + 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 = (((packet[4] & 0x0f) << 8) | packet[5]); + elantechRescale(x1, y1); + y1 = info.y_max - y1; + 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; + } + + // packet_type == PACKET_V3_TAIL + x1 = etd.mt[0].x; + y1 = etd.mt[0].y; + x2 = ((packet[1] & 0x0f) << 8) | packet[2]; + y2 = (((packet[4] & 0x0f) << 8) | packet[5]); + elantechRescale(x2, y2); + y2 = info.y_max - y2; + break; + } + + // pressure: (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); + // finger width: ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); + + 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; + } + } + + 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 = 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 (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::elantechReportAbsoluteV4(int packetType) { + 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; + } +} + +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; + } + + 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]; + + 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; + + trackpointReport.timestamp = timestamp; + trackpointReport.buttons = trackpointLeftButton | trackpointMiddleButton | trackpointRightButton; + trackpointReport.dx = dx; + trackpointReport.dy = dy; + super::messageClient(kIOMessageVoodooTrackpointMessage, voodooInputInstance, + &trackpointReport, sizeof(trackpointReport)); +} + +void ApplePS2Elan::processPacketStatusV4() { + unsigned char *packet = _ringBuffer.tail(); + unsigned fingers; + leftButton = packet[0] & 0x1; + rightButton = packet[0] & 0x2; + + // notify finger state change + fingers = packet[1] & 0x1f; + int count = 0; + for (int i = 0; i < ETP_MAX_FINGERS; i++) { + if ((fingers & (1 << i)) == 0) { + // 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 { + virtualFinger[i].touch = true; + INTERRUPT_LOG("VoodooPS2Elan: %d finger has been touched the touchpad\n", i); + count++; + } + } + + heldFingers = count; + + headPacketsCount = 0; + + // 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; + } + + int x = ((packet[1] & 0x0f) << 8) | packet[2]; + int y = info.y_max - (((packet[4] & 0x0f) << 8) | packet[5]); + + 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); + + virtualFinger[id].button = (packet[0] & 0x3); + virtualFinger[id].prev = virtualFinger[id].now; + virtualFinger[id].pressure = pres; + virtualFinger[id].width = traces; + + virtualFinger[id].now.x = x; + virtualFinger[id].now.y = y; + + if (headPacketsCount == heldFingers) { + headPacketsCount = 0; + sendTouchData(); + } +} + +void ApplePS2Elan::processPacketMotionV4() { + unsigned char *packet = _ringBuffer.tail(); + int weight, delta_x1 = 0, delta_y1 = 0, delta_x2 = 0, delta_y2 = 0; + int id, sid; + + 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. + delta_x1 = (signed char)packet[1]; + delta_y1 = (signed char)packet[2]; + delta_x2 = (signed char)packet[4]; + delta_y2 = (signed char)packet[5]; + + 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] & 0x3); + virtualFinger[sid].prev = virtualFinger[sid].now; + virtualFinger[sid].now.x += delta_x2 * weight; + virtualFinger[sid].now.y -= delta_y2 * weight; + } + + sendTouchData(); +} + +MT2FingerType ApplePS2Elan::GetBestFingerType(int i) { + switch (i) { + case 0: return kMT2FingerTypeIndexFinger; + case 1: return kMT2FingerTypeMiddleFinger; + case 2: return kMT2FingerTypeRingFinger; + case 3: return kMT2FingerTypeThumb; + case 4: return kMT2FingerTypeLittleFinger; + + default: + break; + } + return kMT2FingerTypeIndexFinger; +} + +void ApplePS2Elan::sendTouchData() { + AbsoluteTime timestamp; + clock_get_uptime(×tamp); + 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; + } + + 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 + 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 < transducers_count; i++) { + if (inputEvent.transducers[i].currentCoordinates.y > maxY) { + maxY = inputEvent.transducers[i].currentCoordinates.y; + newThumbIndex = i; + } + if (inputEvent.transducers[i].fingerType == kMT2FingerTypeThumb) { + currentThumbIndex = i; + } + } + inputEvent.transducers[currentThumbIndex].fingerType = inputEvent.transducers[newThumbIndex].fingerType; + inputEvent.transducers[newThumbIndex].fingerType = kMT2FingerTypeThumb; + } + + 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 = transducers_count; + inputEvent.timestamp = timestamp; + + if (voodooInputInstance) { + super::messageClient(kIOMessageVoodooInputMessage, voodooInputInstance, &inputEvent, sizeof(VoodooInputEvent)); + } + + if (!info.is_buttonpad) { + if (transducers_count == 0) { + trackpointReport.timestamp = timestamp; + trackpointReport.buttons = leftButton | rightButton; + trackpointReport.dx = trackpointReport.dy = 0; + super::messageClient(kIOMessageVoodooTrackpointMessage, voodooInputInstance, + &trackpointReport, sizeof(trackpointReport)); + } else { + UInt32 buttons = 0; + bool send = false; + if (lastLeftButton != leftButton) { + buttons |= leftButton; + send = true; + } + if (lastRightButton != rightButton) { + buttons |= rightButton; + send = true; + } + if (send) { + trackpointReport.timestamp = timestamp; + trackpointReport.buttons = buttons; + trackpointReport.dx = trackpointReport.dy = 0; + super::messageClient(kIOMessageVoodooTrackpointMessage, voodooInputInstance, + &trackpointReport, sizeof(trackpointReport)); + } + } + + lastLeftButton = leftButton; + lastRightButton = rightButton; + } +} + +PS2InterruptResult ApplePS2Elan::interruptOccurred(UInt8 data) { + UInt8 *packet = _ringBuffer.head(); + packet[_packetByteCount++] = data; + + if (_packetByteCount == _packetLength) { + _ringBuffer.advanceHead(_packetLength); + _packetByteCount = 0; + return kPS2IR_packetReady; + } + + return kPS2IR_packetBuffering; +} + +void ApplePS2Elan::packetReady() { + INTERRUPT_LOG("VoodooPS2Elan: packet ready occurred\n"); + // empty the ring buffer, dispatching each packet... + 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: + 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 + 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: invalid packet received\n"); + break; + + case PACKET_TRACKPOINT: + 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(_packetLength); + } +} + +void ApplePS2Elan::resetMouse() { + UInt8 params[2]; + ps2_command<2>(params, kDP_Reset); + + if (params[0] != 0xaa && params[1] != 0x00) { + DEBUG_LOG("VoodooPS2Elan: failed resetting.\n"); + } +} + +void ApplePS2Elan::setTouchPadEnable(bool enable) { + ps2_command<0>(NULL, enable ? kDP_Enable : kDP_SetDefaultsAndDisable); +} diff --git a/VoodooPS2Trackpad/VoodooPS2Elan.h b/VoodooPS2Trackpad/VoodooPS2Elan.h new file mode 100644 index 00000000..e3230d4d --- /dev/null +++ b/VoodooPS2Trackpad/VoodooPS2Elan.h @@ -0,0 +1,356 @@ +/* + * Elan PS2 touchpad integration + * + * Mostly contains code ported from Linux + * https://github.com/torvalds/linux/blob/master/drivers/input/mouse/elantech.c + * + * Created by Bartosz Korczyński (@bandysc), Hiep Bao Le (@hieplpvip) + * Special thanks to Kishor Prins (@kprinssu), EMlyDinEsHMG and whole VoodooInput team + */ + +#ifndef _APPLEPS2ELAN_H +#define _APPLEPS2ELAN_H + +#include "../VoodooPS2Controller/ApplePS2MouseDevice.h" +#include +#include +#include + +#include "VoodooInputMultitouch/VoodooInputEvent.h" +#include "VoodooPS2TrackpadCommon.h" + +struct elan_virtual_finger_state { + TouchCoordinates prev; + TouchCoordinates now; + uint8_t pressure; + uint8_t width; + bool touch; + bool button; + MT2FingerType fingerType; +}; + +#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_CAPABILITIES_QUERY 0x02 +#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_READWRITE 0x00 + +/* + * Hardware version 2 custom PS/2 command value + */ +#define ETP_PS2_CUSTOM_COMMAND 0xf8 + +/* + * Times to retry a ps2_command and millisecond delay between tries + */ +#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 + +/* + * Register bitmasks for hardware version 1 + */ +#define ETP_R10_ABSOLUTE_MODE 0x04 +#define ETP_R11_4_BYTE_MODE 0x02 + +/* + * Capability bitmasks + */ +#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_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 + +// Preasure min-max +#define ETP_PMIN_V2 0 +#define ETP_PMAX_V2 255 + +// Width min-max +#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 + +/* + * track up to 5 fingers for v4 hardware + */ +#define ETP_MAX_FINGERS 5 + +/* + * weight value for v4 hardware + */ +#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_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 + +/* + * New ICs are either using SMBus Host Notify or just plain PS2. + * + * ETP_FW_VERSION_QUERY is: + * Byte 1: + * - bit 0..3: IC BODY + * Byte 2: + * - bit 4: HiddenButton + * - bit 5: PS2_SMBUS_NOTIFY + * - bit 6: PS2CRCCheck + */ +#define ETP_NEW_IC_SMBUS_HOST_NOTIFY(fw_version) \ + ((((fw_version) & 0x0f2000) == 0x0f2000) && \ + ((fw_version) & 0x0000ff) > 0) + +/* + * The base position for one finger, v4 hardware + */ +struct finger_pos { + unsigned int x; + unsigned int y; +}; + +struct elantech_device_info { + unsigned char capabilities[3]; + unsigned char samples[3]; + unsigned char debug; + unsigned char hw_version; + unsigned int fw_version; + unsigned int x_min; + unsigned int y_min; + unsigned int x_max; + unsigned int y_max; + unsigned int x_res; + unsigned int y_res; + unsigned int x_traces; + unsigned int y_traces; + unsigned int width; + unsigned int bus; + bool paritycheck; + bool jumpy_cursor; + bool reports_pressure; + bool crc_enabled; + bool set_hw_resolution; + bool is_buttonpad; + bool has_trackpoint; + bool has_middle_button; +}; + +struct elantech_data { + unsigned char reg_07; + unsigned char reg_10; + unsigned char reg_11; + unsigned char reg_20; + unsigned char reg_21; + unsigned char reg_22; + unsigned char reg_23; + unsigned char reg_24; + unsigned char reg_25; + unsigned char reg_26; + unsigned int single_finger_reports; + struct finger_pos mt[ETP_MAX_FINGERS]; + unsigned char parity[256]; +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// ApplePS2Elan Class Declaration +// + +class EXPORT ApplePS2Elan : public IOService { + typedef IOService super; + OSDeclareDefaultStructors(ApplePS2Elan); + +private: + IOService* voodooInputInstance {nullptr}; + ApplePS2MouseDevice* _device {nullptr}; + bool _interruptHandlerInstalled {false}; + bool _powerControlHandlerInstalled {false}; + UInt32 _packetByteCount {0}; + UInt32 _packetLength {0}; + RingBuffer _ringBuffer {}; + + IOCommandGate* _cmdGate {nullptr}; + + VoodooInputEvent inputEvent {}; + TrackpointReport trackpointReport {}; + + // when trackpad has physical button + UInt32 leftButton = 0; + UInt32 rightButton = 0; + UInt32 lastLeftButton = 0; + UInt32 lastRightButton = 0; + + const float sin30deg = 0.5f; + const float cos30deg = 0.86602540378f; + UInt32 lastFingers = 0; + + int heldFingers = 0; + int headPacketsCount = 0; + elan_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 wakedelay {1000}; + int _trackpointDeadzone {1}; + int _trackpointMultiplierX {120}; + int _trackpointMultiplierY {120}; + int _trackpointDividerX {120}; + int _trackpointDividerY {120}; + int _trackpointScrollMultiplierX {120}; + int _trackpointScrollMultiplierY {120}; + int _trackpointScrollDividerX {120}; + int _trackpointScrollDividerY {120}; + + int _mouseResolution {0x3}; + int _mouseSampleRate {200}; + + bool _set_hw_resolution {false}; + + bool ignoreall {false}; + bool usb_mouse_stops_trackpad {true}; + + 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; + bool handleIsOpen(const IOService *forClient) const override; + + void setParamPropertiesGated(OSDictionary *dict); + void injectVersionDependentProperties(OSDictionary *dict); + void setTrackpointProperties(); + + void registerHIDPointerNotifications(); + void unregisterHIDPointerNotifications(); + + void notificationHIDAttachedHandlerGated(IOService *newService, IONotifier *notifier); + bool notificationHIDAttachedHandler(void *refCon, IOService *newService, IONotifier *notifier); + + elantech_data etd {}; + elantech_device_info info {}; + int elantechDetect(); + int elantechQueryInfo(); + int elantechSetProperties(); + int elantechSetAbsoluteMode(); + int elantechSetInputParams(); + 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(); + int elantechPacketCheckV4(); + void elantechRescale(unsigned int x, unsigned int y); + void elantechReportAbsoluteV1(); + void elantechReportAbsoluteV2(); + void elantechReportAbsoluteV3(int packetType); + void elantechReportAbsoluteV4(int packetType); + void elantechReportTrackpoint(); + void processPacketStatusV4(); + void processPacketHeadV4(); + void processPacketMotionV4(); + void sendTouchData(); + void resetMouse(); + void setTouchPadEnable(bool enable); + + static MT2FingerType GetBestFingerType(int i); + + template + int ps2_command(UInt8* params, unsigned int command); + template + int elantech_ps2_command(unsigned char *param, int command); + int ps2_sliced_command(UInt8 command); + template + int synaptics_send_cmd(unsigned char c, unsigned char *param); + template + int elantech_send_cmd(unsigned char c, unsigned char *param); + template + int send_cmd(unsigned char c, unsigned char *param); + + 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; + + IOReturn setProperties(OSObject *props) override; + + IOReturn message(UInt32 type, IOService* provider, void* argument) override; +}; + +#endif /* _ApplePS2Elan_H */ diff --git a/VoodooPS2Trackpad/VoodooPS2SMBusDevice.cpp b/VoodooPS2Trackpad/VoodooPS2SMBusDevice.cpp new file mode 100644 index 00000000..7b938230 --- /dev/null +++ b/VoodooPS2Trackpad/VoodooPS2SMBusDevice.cpp @@ -0,0 +1,87 @@ +// +// VoodooPS2SMBusDevice.cpp +// VoodooPS2Trackpad +// +// Created by Avery Black on 9/13/24. +// Copyright © 2024 Acidanthera. All rights reserved. +// + +#include "VoodooPS2SMBusDevice.h" + +// ============================================================================= +// ApplePS2SmbusDevice Class Implementation +// + +OSDefineMetaClassAndStructors(ApplePS2SmbusDevice, IOService); + +ApplePS2SmbusDevice *ApplePS2SmbusDevice::withReset(bool resetNeeded, OSDictionary *data, uint8_t addr) { + ApplePS2SmbusDevice *dev = OSTypeAlloc(ApplePS2SmbusDevice); + + if (dev == nullptr) { + return nullptr; + } + + if (!dev->init()) { + OSSafeReleaseNULL(dev); + return nullptr; + } + + dev->_resetNeeded = resetNeeded; + dev->_data = data; + dev->_data->retain(); + dev->_addr = addr; + return dev; +} + +bool ApplePS2SmbusDevice::start(IOService *provider) { + if (!super::start(provider)) + return false; + + _nub = OSDynamicCast(ApplePS2MouseDevice, provider); + if (_nub == nullptr) + return false; + + if (_resetNeeded) + resetDevice(); + + if (_nub->startSMBusCompanion(_data, _addr) != kIOReturnSuccess) { + return false; + } + + _nub->installPowerControlAction(this, + OSMemberFunctionCast(PS2PowerControlAction, this, &ApplePS2SmbusDevice::powerAction)); + return true; +} + +void ApplePS2SmbusDevice::stop(IOService *provider) { + _nub->uninstallPowerControlAction(); + resetDevice(); + super::stop(provider); +} + +void ApplePS2SmbusDevice::free() { + OSSafeReleaseNULL(_data); + super::free(); +} + +void ApplePS2SmbusDevice::powerAction(uint32_t ordinal) { + if (ordinal == kPS2C_EnableDevice && _resetNeeded) { + (void) resetDevice(); + } +} + +IOReturn ApplePS2SmbusDevice::resetDevice() { + TPS2Request<> request; + request.commands[0].command = kPS2C_SendCommandAndCompareAck; + request.commands[0].inOrOut = kDP_SetDefaultsAndDisable; // F5 + request.commandsCount = 1; + _nub->submitRequestAndBlock(&request); + + if (request.commandsCount == 1) { + DEBUG_LOG("VoodooPS2Trackpad: sending $FF failed: %d\n", request.commandsCount); + return kIOReturnError; + } + + return kIOReturnSuccess; +} + diff --git a/VoodooPS2Trackpad/VoodooPS2SMBusDevice.h b/VoodooPS2Trackpad/VoodooPS2SMBusDevice.h new file mode 100644 index 00000000..de3bfd75 --- /dev/null +++ b/VoodooPS2Trackpad/VoodooPS2SMBusDevice.h @@ -0,0 +1,46 @@ +// +// VoodooPS2SMBusDevice.hpp +// VoodooPS2Trackpad +// +// Created by Avery Black on 9/13/24. +// Copyright © 2024 Acidanthera. All rights reserved. +// + +#ifndef VoodooPS2SMBusDevice_hpp +#define VoodooPS2SMBusDevice_hpp + + +#include "../VoodooPS2Controller/ApplePS2MouseDevice.h" +#include + +#include "VoodooPS2TrackpadCommon.h" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// ApplePS2SmbusDevice Class Declaration +// Synaptics and Elans devices still need resets over PS2. This acts as a +// PS/2 stub driver that attaches in lieu of the full touchpad driver to reset +// on wakeups and sleep. This also prevents other devices attaching and using +// the otherwise unused PS/2 interface +// + +class EXPORT ApplePS2SmbusDevice : public IOService { + typedef IOService super; + OSDeclareDefaultStructors(ApplePS2SmbusDevice); +public: + static ApplePS2SmbusDevice *withReset(bool resetNeeded, OSDictionary *data, uint8_t addr); + + bool start(IOService *provider) override; + void stop(IOService *provider) override; + void free() override; + +private: + ApplePS2MouseDevice *_nub {nullptr}; + bool _resetNeeded {false}; + OSDictionary *_data {nullptr}; + uint8_t _addr{0}; + + IOReturn resetDevice(); + void powerAction(uint32_t ordinal); +}; + +#endif /* VoodooPS2SMBusDevice_hpp */ diff --git a/VoodooPS2Trackpad/VoodooPS2SentelicFSP.cpp b/VoodooPS2Trackpad/VoodooPS2SentelicFSP.cpp index 4d1f05d9..84e9eeaf 100644 --- a/VoodooPS2Trackpad/VoodooPS2SentelicFSP.cpp +++ b/VoodooPS2Trackpad/VoodooPS2SentelicFSP.cpp @@ -20,7 +20,7 @@ * @APPLE_LICENSE_HEADER_END@ */ -#include "LegacyIOService.h" +#include #include #include @@ -91,19 +91,17 @@ bool ApplePS2SentelicFSP::init(OSDictionary* dict) int fsp_ps2_command(ApplePS2MouseDevice * device, PS2Request * request, int cmd) { - request->commands[0].command = kPS2C_WriteCommandPort; - request->commands[0].inOrOut = kCP_TransmitToMouse; - request->commands[1].command = kPS2C_WriteDataPort; - request->commands[1].inOrOut = cmd; - request->commands[2].command = kPS2C_ReadDataPort; - request->commands[2].inOrOut = 0; + request->commands[0].command = kPS2C_WriteDataPort; + request->commands[0].inOrOut = cmd; + request->commands[1].command = kPS2C_ReadDataPort; + request->commands[1].inOrOut = 0; - request->commandsCount = 3; + request->commandsCount = 2; device->submitRequestAndBlock(request); //IOLog("ApplePS2Trackpad: Sentelic FSP: fsp_ps2_command(cmd = %0x) => %0x\n", cmd, request->commands[2].inOrOut); - return (request->commandsCount == 3) ? request->commands[2].inOrOut : -1; + return (request->commandsCount == 2) ? request->commands[1].inOrOut : -1; } unsigned int fsp_reg_read(ApplePS2MouseDevice * device, PS2Request * request, int reg) @@ -127,7 +125,7 @@ unsigned int fsp_reg_read(ApplePS2MouseDevice * device, PS2Request * request, in fsp_ps2_command(device, request, register_select); fsp_ps2_command(device, request, register_value); - request->commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request->commands[0].command = kPS2C_SendCommandAndCompareAck; request->commands[0].inOrOut = kDP_GetMouseInformation; request->commands[1].command = kPS2C_ReadDataPort; request->commands[1].inOrOut = 0; @@ -211,22 +209,22 @@ void fsp_opctag_enable(ApplePS2MouseDevice * device, PS2Request * request, int e int fsp_intellimouse_mode(ApplePS2MouseDevice * device, PS2Request * request) { - request->commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request->commands[0].command = kPS2C_SendCommandAndCompareAck; request->commands[0].inOrOut = kDP_SetMouseSampleRate; - request->commands[1].command = kPS2C_SendMouseCommandAndCompareAck; + request->commands[1].command = kPS2C_SendCommandAndCompareAck; request->commands[1].inOrOut = 200; - request->commands[2].command = kPS2C_SendMouseCommandAndCompareAck; + request->commands[2].command = kPS2C_SendCommandAndCompareAck; request->commands[2].inOrOut = kDP_SetMouseSampleRate; - request->commands[3].command = kPS2C_SendMouseCommandAndCompareAck; + request->commands[3].command = kPS2C_SendCommandAndCompareAck; request->commands[3].inOrOut = 200; - request->commands[4].command = kPS2C_SendMouseCommandAndCompareAck; + request->commands[4].command = kPS2C_SendCommandAndCompareAck; request->commands[4].inOrOut = kDP_SetMouseSampleRate; - request->commands[5].command = kPS2C_SendMouseCommandAndCompareAck; + request->commands[5].command = kPS2C_SendCommandAndCompareAck; request->commands[5].inOrOut = 80; - request->commands[6].command = kPS2C_SendMouseCommandAndCompareAck; + request->commands[6].command = kPS2C_SendCommandAndCompareAck; request->commands[6].inOrOut = kDP_GetId; request->commands[7].command = kPS2C_ReadDataPort; request->commands[7].inOrOut = 0; @@ -519,7 +517,7 @@ void ApplePS2SentelicFSP::setTouchPadEnable( bool enable ) // (mouse enable/disable command) TPS2Request<> request; - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].command = kPS2C_SendCommandAndCompareAck; request.commands[0].inOrOut = enable ? kDP_Enable : kDP_SetDefaultsAndDisable; request.commandsCount = 1; assert(request.commandsCount <= countof(request.commands)); @@ -540,32 +538,32 @@ UInt32 ApplePS2SentelicFSP::getTouchPadData( UInt8 dataSelector ) TPS2Request<13> request; // Disable stream mode before the command sequence. - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].command = kPS2C_SendCommandAndCompareAck; request.commands[0].inOrOut = kDP_SetDefaultsAndDisable; // 4 set resolution commands, each encode 2 data bits. - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[1].command = kPS2C_SendCommandAndCompareAck; request.commands[1].inOrOut = kDP_SetMouseResolution; - request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[2].command = kPS2C_SendCommandAndCompareAck; request.commands[2].inOrOut = (dataSelector >> 6) & 0x3; - request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[3].command = kPS2C_SendCommandAndCompareAck; request.commands[3].inOrOut = kDP_SetMouseResolution; - request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[4].command = kPS2C_SendCommandAndCompareAck; request.commands[4].inOrOut = (dataSelector >> 4) & 0x3; - request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[5].command = kPS2C_SendCommandAndCompareAck; request.commands[5].inOrOut = kDP_SetMouseResolution; - request.commands[6].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[6].command = kPS2C_SendCommandAndCompareAck; request.commands[6].inOrOut = (dataSelector >> 2) & 0x3; - request.commands[7].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[7].command = kPS2C_SendCommandAndCompareAck; request.commands[7].inOrOut = kDP_SetMouseResolution; - request.commands[8].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[8].command = kPS2C_SendCommandAndCompareAck; request.commands[8].inOrOut = (dataSelector >> 0) & 0x3; // Read response bytes. - request.commands[9].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[9].command = kPS2C_SendCommandAndCompareAck; request.commands[9].inOrOut = kDP_GetMouseInformation; request.commands[10].command = kPS2C_ReadDataPort; request.commands[10].inOrOut = 0; @@ -663,38 +661,38 @@ bool ApplePS2SentelicFSP::setTouchPadModeByte(UInt8 modeByteValue, bool enableSt TPS2Request<12> request; // Disable stream mode before the command sequence. - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].command = kPS2C_SendCommandAndCompareAck; request.commands[0].inOrOut = kDP_SetDefaultsAndDisable; // 4 set resolution commands, each encode 2 data bits. - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[1].command = kPS2C_SendCommandAndCompareAck; request.commands[1].inOrOut = kDP_SetMouseResolution; - request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[2].command = kPS2C_SendCommandAndCompareAck; request.commands[2].inOrOut = (modeByteValue >> 6) & 0x3; - request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[3].command = kPS2C_SendCommandAndCompareAck; request.commands[3].inOrOut = kDP_SetMouseResolution; - request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[4].command = kPS2C_SendCommandAndCompareAck; request.commands[4].inOrOut = (modeByteValue >> 4) & 0x3; - request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[5].command = kPS2C_SendCommandAndCompareAck; request.commands[5].inOrOut = kDP_SetMouseResolution; - request.commands[6].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[6].command = kPS2C_SendCommandAndCompareAck; request.commands[6].inOrOut = (modeByteValue >> 2) & 0x3; - request.commands[7].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[7].command = kPS2C_SendCommandAndCompareAck; request.commands[7].inOrOut = kDP_SetMouseResolution; - request.commands[8].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[8].command = kPS2C_SendCommandAndCompareAck; request.commands[8].inOrOut = (modeByteValue >> 0) & 0x3; // Set sample rate 20 to set mode byte 2. Older pads have 4 mode // bytes (0,1,2,3), but only mode byte 2 remain in modern pads. - request.commands[9].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[9].command = kPS2C_SendCommandAndCompareAck; request.commands[9].inOrOut = kDP_SetMouseSampleRate; - request.commands[10].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[10].command = kPS2C_SendCommandAndCompareAck; request.commands[10].inOrOut = 20; - request.commands[11].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[11].command = kPS2C_SendCommandAndCompareAck; request.commands[11].inOrOut = enableStreamMode ? kDP_Enable : kDP_SetMouseScaling1To1; request.commandsCount = 12; assert(request.commandsCount <= countof(request.commands)); diff --git a/VoodooPS2Trackpad/VoodooPS2SentelicFSP.h b/VoodooPS2Trackpad/VoodooPS2SentelicFSP.h index 7c6a18eb..bcb56def 100644 --- a/VoodooPS2Trackpad/VoodooPS2SentelicFSP.h +++ b/VoodooPS2Trackpad/VoodooPS2SentelicFSP.h @@ -25,7 +25,7 @@ #include "../VoodooPS2Controller/ApplePS2MouseDevice.h" -#include "LegacyIOHIPointing.h" +#include #define kPacketLengthMax 4 #define kPacketLengthStandard 3 diff --git a/VoodooPS2Trackpad/VoodooPS2SynapticsTouchPad.cpp b/VoodooPS2Trackpad/VoodooPS2SynapticsTouchPad.cpp index 34647232..ee668b82 100644 --- a/VoodooPS2Trackpad/VoodooPS2SynapticsTouchPad.cpp +++ b/VoodooPS2Trackpad/VoodooPS2SynapticsTouchPad.cpp @@ -37,54 +37,24 @@ #define kTPDN "TPDN" // Trackpad Disable Notification -#include "LegacyIOService.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#include #include -#include #include #include #include #include #include -#pragma clang diagnostic pop +#include "VoodooPS2SMBusDevice.h" #include "VoodooPS2Controller.h" #include "VoodooPS2SynapticsTouchPad.h" #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 -}; - // ============================================================================= // ApplePS2SynapticsTouchPad Class Implementation // -OSDefineMetaClassAndStructors(ApplePS2SynapticsTouchPad, IOHIPointing); - -UInt32 ApplePS2SynapticsTouchPad::deviceType() -{ return NX_EVS_DEVICE_TYPE_MOUSE; }; - -UInt32 ApplePS2SynapticsTouchPad::interfaceID() -{ return NX_EVS_DEVICE_INTERFACE_BUS_ACE; }; - -IOItemCount ApplePS2SynapticsTouchPad::buttonCount() { return _buttonCount; }; -IOFixed ApplePS2SynapticsTouchPad::resolution() { return _resolution << 16; }; +OSDefineMetaClassAndStructors(ApplePS2SynapticsTouchPad, IOService); #define abs(x) ((x) < 0 ? -(x) : (x)) @@ -106,6 +76,10 @@ bool ApplePS2SynapticsTouchPad::init(OSDictionary * dict) memset(freeFingerTypes, true, kMT2FingerTypeCount); freeFingerTypes[kMT2FingerTypeUndefined] = false; + + _smbusCompanion = OSSymbol::withCString(kSmbusCompanion); + if (_smbusCompanion == NULL) + return false; // announce version extern kmod_info_t kmod_info; @@ -154,7 +128,7 @@ void ApplePS2SynapticsTouchPad::injectVersionDependentProperties(OSDictionary *c } } -ApplePS2SynapticsTouchPad* ApplePS2SynapticsTouchPad::probe(IOService * provider, SInt32 * score) +IOService* ApplePS2SynapticsTouchPad::probe(IOService * provider, SInt32 * score) { DEBUG_LOG("ApplePS2SynapticsTouchPad::probe entered...\n"); @@ -170,7 +144,6 @@ ApplePS2SynapticsTouchPad* ApplePS2SynapticsTouchPad::probe(IOService * provider return 0; _device = (ApplePS2MouseDevice*)provider; - bool forceSynaptics = false; // find config specific to Platform Profile OSDictionary* list = OSDynamicCast(OSDictionary, getProperty(kPlatformProfile)); @@ -185,83 +158,74 @@ ApplePS2SynapticsTouchPad* ApplePS2SynapticsTouchPad::probe(IOService * provider _device = 0; return 0; } - if (OSBoolean* force = OSDynamicCast(OSBoolean, config->getObject("ForceSynapticsDetect"))) - { - // "ForceSynapticsDetect" can be set to treat a trackpad as Synaptics which does not identify itself properly... - forceSynaptics = force->isTrue(); - } #ifdef DEBUG // save configuration for later/diagnostics... setProperty(kMergedConfiguration, config); #endif // load settings specific to Platform Profile - setParamPropertiesGated(config); + setPropertiesGated(config); injectVersionDependentProperties(config); OSSafeReleaseNULL(config); } - // for diagnostics... - UInt8 buf3[3]; - bool success = getTouchPadData(0x0, buf3); + bool success = getTouchPadData(SYNAPTICS_IDENTIFY_QUERY, reinterpret_cast(&_identity)); if (!success) { IOLog("VoodooPS2Trackpad: Identify TouchPad command failed\n"); + return 0; } - else + + INFO_LOG("VoodooPS2Trackpad: Identity = { 0x%x.%x, constant: %x }\n", + _identity.major_ver, _identity.minor_ver, _identity.synaptics_const); + + // 0x46 seemed to be returned on laptops with muxed PS/2 ports. Since muxing was added, + // I have not seen any devices return 0x46 ~ Avery + if (_identity.synaptics_const != 0x47) { - INFO_LOG("VoodooPS2Trackpad: Identify bytes = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); - if (0x46 != buf3[1] && 0x47 != buf3[1]) - { - IOLog("VoodooPS2Trackpad: Identify TouchPad command returned incorrect byte 2 (of 3): 0x%02x\n", buf3[1]); - } - _touchPadType = buf3[1]; + IOLog("VoodooPS2Trackpad: Identify TouchPad command returned incorrect byte 2 (of 3): 0x%02x\n", + _identity.synaptics_const); + return 0; } - if (success) + // Only support 4.x or later touchpads. + if (_identity.major_ver < 0x4) { - // some synaptics touchpads return 0x46 in byte2 and have a different numbering scheme - // this is all experimental for those touchpads - - // most synaptics touchpads return 0x47, and we only support v4.0 or better - // in the case of 0x46, we allow versions as low as v2.0 + IOLog("VoodooPS2Trackpad: TouchPad(0x47) v%d.%d is not supported\n", + _identity.major_ver, _identity.minor_ver); + return 0; + } + + // + // Query the touchpad for the capabilities we need to know. + // + queryCapabilities(); + + // + // Attempt to start SMBus Companion. If succesful, attach a stub PS/2 driver. + // + IOService *resources = getResourceService(); + if (_cont_caps.intertouch && resources && resources->getProperty(_smbusCompanion)) { + // Helpful information for SMBus drivers + OSDictionary *dictionary = OSDictionary::withCapacity(2); + dictionary->setObject("TrackstickButtons", _securepad.trackstick_btns ? + kOSBooleanTrue : kOSBooleanFalse); + dictionary->setObject("Clickpad", _cont_caps.one_btn_clickpad ? + kOSBooleanTrue : kOSBooleanFalse); + ApplePS2SmbusDevice *smbus = ApplePS2SmbusDevice::withReset(true, dictionary, 0x2C); - success = false; - _touchPadVersion = (buf3[2] & 0x0f) << 8 | buf3[0]; - if (0x47 == buf3[1]) - { - // for diagnostics... - if ( _touchPadVersion < 0x400) - { - IOLog("VoodooPS2Trackpad: TouchPad(0x47) v%d.%d is not supported\n", - (UInt8)(_touchPadVersion >> 8), (UInt8)(_touchPadVersion)); - } - // Only support 4.x or later touchpads. - success = _touchPadVersion >= 0x400; - } - if (0x46 == buf3[1]) - { - // for diagnostics... - if ( _touchPadVersion < 0x200) - { - IOLog("VoodooPS2Trackpad: TouchPad(0x46) v%d.%d is not supported\n", - (UInt8)(_touchPadVersion >> 8), (UInt8)(_touchPadVersion)); - } - // Only support 2.x or later touchpads. - success = _touchPadVersion >= 0x200; - } - if (forceSynaptics) - { - IOLog("VoodooPS2Trackpad: Forcing Synaptics detection due to ForceSynapticsDetect\n"); - success = true; - } + // gIOMatchCategoryKey is necessary to prevent multiple services attaching to the PS2 device + if (smbus) + smbus->setProperty(gIOMatchCategoryKey, getProperty(gIOMatchCategoryKey)); + OSSafeReleaseNULL(dictionary); + return smbus; } _device = 0; DEBUG_LOG("ApplePS2SynapticsTouchPad::probe leaving.\n"); - return success ? this : 0; + return this; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -270,21 +234,19 @@ void ApplePS2SynapticsTouchPad::doHardwareReset() { TPS2Request<> request; int i = 0; - request.commands[i].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[i].command = kPS2C_SendCommandAndCompareAck; request.commands[i++].inOrOut = kDP_SetDefaultsAndDisable; // F5 - request.commands[i].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[i].command = kPS2C_SendCommandAndCompareAck; request.commands[i++].inOrOut = kDP_SetDefaultsAndDisable; // F5 - request.commands[i].command = kPS2C_WriteCommandPort; - request.commands[i++].inOrOut = kCP_TransmitToMouse; request.commands[i].command = kPS2C_WriteDataPort; request.commands[i++].inOrOut = kDP_Reset; // FF request.commands[i].command = kPS2C_ReadDataPortAndCompare; request.commands[i++].inOrOut = kSC_Acknowledge; request.commands[i].command = kPS2C_SleepMS; request.commands[i++].inOrOut32 = wakedelay*2; - request.commands[i].command = kPS2C_ReadMouseDataPortAndCompare; + request.commands[i].command = kPS2C_ReadDataPortAndCompare; request.commands[i++].inOrOut = 0xAA; - request.commands[i].command = kPS2C_ReadMouseDataPortAndCompare; + request.commands[i].command = kPS2C_ReadDataPortAndCompare; request.commands[i++].inOrOut = 0x00; request.commandsCount = i; DEBUG_LOG("VoodooPS2Trackpad: sending kDP_Reset $FF\n"); @@ -298,139 +260,105 @@ void ApplePS2SynapticsTouchPad::doHardwareReset() void ApplePS2SynapticsTouchPad::queryCapabilities() { + synaptics_logic_min_max logic_size; + synaptics_model model_data; + UInt8 *buf; + + bzero(&logic_size, sizeof(synaptics_logic_min_max)); + bzero(&model_data, sizeof(synaptics_model)); + // get TouchPad general capabilities - UInt8 buf3[3]; - if (!getTouchPadData(0x2, buf3) || !(buf3[0] & 0x80)) - buf3[0] = buf3[2] = 0; - int nExtendedQueries = (buf3[0] & 0x70) >> 4; - INFO_LOG("VoodooPS2Trackpad: nExtendedQueries=%d\n", nExtendedQueries); - UInt8 supportsEW = buf3[2] & (1<<5); - INFO_LOG("VoodooPS2Trackpad: supports EW=%d\n", supportsEW != 0); - - // deal with pass through capability - if (!skippassthru) - { - UInt8 passthru2 = buf3[2] >> 7; - // see if guest device for pass through is present - UInt8 passthru1 = 0; - if (getTouchPadData(0x1, buf3)) - { - // first byte, bit 0 indicates guest present - passthru1 = buf3[0] & 0x01; + buf = reinterpret_cast(&_capabilities); + if (!getTouchPadData(SYNA_CAPABILITIES_QUERY, buf)) { + bzero(&_capabilities, sizeof(_capabilities)); + } + + INFO_LOG("VoodooPS2Trackpad: nExtendedQueries=%d\n", _capabilities.extended_queries); + INFO_LOG("VoodooPS2Trackpad: supports EW=%d\n", _capabilities.extended_w_supported); + + buf = reinterpret_cast(&model_data); + if (getTouchPadData(SYNA_MODEL_QUERY, buf)) { + UInt16 combined_version = (_identity.major_ver << 8) | _identity.minor_ver; + if (combined_version >= 0x705) { + setProperty("Board ID", model_data.model_number, 32); } - // trackpad must have both guest present and pass through capability - passthru = passthru1 & passthru2; + } + #ifdef SIMULATE_PASSTHRU - passthru = true; + _capabilities.passthrough = 1; #endif - INFO_LOG("VoodooPS2Trackpad: passthru1=%d, passthru2=%d, passthru=%d\n", passthru1, passthru2, passthru); - } - if (forcepassthru) - { - passthru = true; - INFO_LOG("VoodooPS2Trackpad: Forcing Passthru\n"); - } + INFO_LOG("VoodooPS2Trackpad: Passthrough=%d, Guest Present=%d\n", + _capabilities.passthrough, model_data.guest_present); - // deal with LED capability - if (0x46 == _touchPadType) - { - ledpresent = true; - INFO_LOG("VoodooPS2Trackpad: ledpresent=%d (forced for type 0x46)\n", ledpresent); - } - else if (nExtendedQueries >= 1 && getTouchPadData(0x9, buf3)) - { - ledpresent = (buf3[0] >> 6) & 1; - INFO_LOG("VoodooPS2Trackpad: ledpresent=%d\n", ledpresent); + // Get button data in case VoodooRMI needs it + if (model_data.more_extended_caps) { + buf = reinterpret_cast(&_securepad); + if (!getTouchPadData(SYNA_SECUREPAD_QUERY, buf)) { + bzero(&_securepad, sizeof(synaptics_securepad_id)); + } } - // get resolution data for scaling x -> y or y -> x depending - if (getTouchPadData(0x8, buf3) && (buf3[1] & 0x80) && buf3[0] && buf3[2]) - { - xupmm = buf3[0]; - yupmm = buf3[2]; + buf = reinterpret_cast(&_extended_id); + if (_capabilities.extended_queries >= 1 && getTouchPadData(SYNA_EXTENDED_ID_QUERY, buf)) { + INFO_LOG("VoodooPS2Trackpad: ledpresent=%d\n", _extended_id.has_leds); + + if (_extended_id.extended_btns > SYNAPTICS_MAX_EXT_BTNS) { + INFO_LOG("VoodooPS2Trackpad: Too many external buttons!\n"); + _extended_id.extended_btns = 0; + } } - // now gather some more information about the touchpad - if (getTouchPadData(0x1, buf3)) - { - INFO_LOG("VoodooPS2Trackpad: Mode/model($01) bytes = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); - } - if (getTouchPadData(0x2, buf3)) - { - INFO_LOG("VoodooPS2Trackpad: Capabilities($02) bytes = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); - } - if (getTouchPadData(0x3, buf3)) - { - INFO_LOG("VoodooPS2Trackpad: Model ID($03) bytes = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); - } - if (getTouchPadData(0x6, buf3)) - { - INFO_LOG("VoodooPS2Trackpad: SN Prefix($06) bytes = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); - } - if (getTouchPadData(0x7, buf3)) - { - INFO_LOG("VoodooPS2Trackpad: SN Suffix($07) bytes = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); - } - if (getTouchPadData(0x8, buf3)) - { - INFO_LOG("VoodooPS2Trackpad: Resolutions($08) bytes = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); - } - if (nExtendedQueries >= 1 && getTouchPadData(0x9, buf3)) - { - INFO_LOG("VoodooPS2Trackpad: Extended Model($09) bytes = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); + // get resolution data for scaling x -> y or y -> x depending + if (!getTouchPadData(SYNA_SCALE_QUERY, reinterpret_cast(&_scale)) || + _scale.xupmm == 0 || _scale.yupmm == 0) { + // "Typical" values from docs + _scale.xupmm = 85; + _scale.yupmm = 94; } - bool reportsMax = false; - bool reportsMin = false; - bool deluxeLeds = false; + bool reports_min = false; + bool reports_max = false; - if (nExtendedQueries >= 4 && getTouchPadData(0xc, buf3)) - { - setProperty("0xc Query", buf3, 3); - INFO_LOG("VoodooPS2Trackpad: Continued Capabilities($0C) bytes = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); + buf = reinterpret_cast(&_cont_caps); + if (_capabilities.extended_queries >= 4 && getTouchPadData(SYNA_CONT_CAPS_QUERY, buf)) { + INFO_LOG("VoodooPS2Trackpad: Continued Capabilities($0C) = { smbus_addr=0x%x }\n", _cont_caps.intertouch_addr); - clickpadtype = ((buf3[0] & 0x10) >> 4) | ((buf3[1] & 0x01) << 1); #ifdef SIMULATE_CLICKPAD - clickpadtype = 1; + _const_caps.one_btn_clickpad = 1; DEBUG_LOG("VoodooPS2Trackpad: clickpadtype=1 simulation set\n"); #endif - INFO_LOG("VoodooPS2Trackpad: clickpadtype=%d\n", clickpadtype); - _reportsv = (bool)(buf3[1] >> 3) & (1 << 3); - INFO_LOG("VoodooPS2Trackpad: _reportsv=%d\n", _reportsv); - - // automatically set extendedwmode if supported - if (supportsEW) - { - _extendedwmodeSupported = true; - INFO_LOG("VoodooPS2Trackpad: Trackpad supports extendedW mode\n"); + + reports_min = _cont_caps.reports_min; + reports_max = _cont_caps.reports_max; + INFO_LOG("VoodooPS2Trackpad: clickpadtype=%d\n", _cont_caps.one_btn_clickpad); + INFO_LOG("VoodooPS2Trackpad: _reportsv=%d\n", _cont_caps.reports_v); + + if (_cont_caps.intertouch) { + IOLog("VoodooPS2Trackpad: Trackpad supports Intertouch/SMBus operation\n"); + setProperty("Intertouch Support", kOSBooleanTrue); } - - reportsMax = (bool)(buf3[0] & (1 << 1)); - reportsMin = (bool)(buf3[1] & (1 << 5)); - deluxeLeds = (bool)(buf3[1] & (1 << 1)); } - if (reportsMax && getTouchPadData(0xd, buf3)) - { - logical_max_x = (buf3[0] << 5) | ((buf3[1] & 0x0f) << 1); - logical_max_y = (buf3[2] << 5) | ((buf3[1] & 0xf0) >> 3); - - INFO_LOG("VoodooPS2Trackpad: Maximum coords($0D) bytes = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); - } - if (deluxeLeds && getTouchPadData(0xe, buf3)) + + buf = reinterpret_cast(&logic_size); + if (reports_max && getTouchPadData(SYNA_LOGIC_MAX_QUERY, buf)) { - INFO_LOG("VoodooPS2Trackpad: Deluxe LED bytes($0E) = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); + logical_max_x = SYNA_LOGIC_X(logic_size); + logical_max_y = SYNA_LOGIC_Y(logic_size); + INFO_LOG("VoodooPS2Trackpad: Maximum coords($0D) = { 0x%x, 0x%x }\n", + logical_max_x, logical_max_y); } // 5 mm margins - margin_size_x = 5 * xupmm; - margin_size_y = 5 * yupmm; + margin_size_x = 5 * _scale.xupmm; + margin_size_y = 5 * _scale.yupmm; - if (reportsMin && getTouchPadData(0xf, buf3)) + if (reports_min && getTouchPadData(SYNA_LOGIC_MIN_QUERY, buf)) { - logical_min_x = (buf3[0] << 5) | ((buf3[1] & 0x0f) << 1); - logical_min_y = (buf3[2] << 5) | ((buf3[1] & 0xf0) >> 3); - DEBUG_LOG("VoodooPS2Trackpad: Minimum coords bytes($0F) = { 0x%x, 0x%x, 0x%x }\n", buf3[0], buf3[1], buf3[2]); + logical_min_x = SYNA_LOGIC_X(logic_size); + logical_min_y = SYNA_LOGIC_Y(logic_size); + DEBUG_LOG("VoodooPS2Trackpad: Minimum coords($0F) = { 0x%x, 0x%x }\n", + logical_min_x, logical_min_y); } else { logical_min_x = logical_max_x - 3 * margin_size_x; @@ -456,22 +384,23 @@ void ApplePS2SynapticsTouchPad::queryCapabilities() setProperty(VOODOO_INPUT_LOGICAL_MAX_Y_KEY, logical_max_y - logical_min_y, 32); // physical dimensions are specified in 0.01 mm units - physical_max_x = (logical_max_x + 1 - (reportsMin ? logical_min_x : 0)) * 100 / xupmm; - physical_max_y = (logical_max_y + 1 - (reportsMin ? logical_min_y : 0)) * 100 / yupmm; + physical_max_x = (logical_max_x + 1 - (reports_min ? logical_min_x : 0)) * 100 / _scale.xupmm; + physical_max_y = (logical_max_y + 1 - (reports_min ? logical_min_y : 0)) * 100 / _scale.yupmm; setProperty(VOODOO_INPUT_PHYSICAL_MAX_X_KEY, physical_max_x, 32); setProperty(VOODOO_INPUT_PHYSICAL_MAX_Y_KEY, physical_max_y, 32); - setProperty(kIOFBTransformKey, 0ull, 32); + setProperty(VOODOO_INPUT_TRANSFORM_KEY, 0ull, 32); + + setTrackpointProperties(); + setProperty("VoodooInputSupported", kOSBooleanTrue); - - registerService(); - + INFO_LOG("VoodooPS2Trackpad: logical %dx%d-%dx%d physical_max %dx%d upmm %dx%d", logical_min_x, logical_min_y, logical_max_x, logical_max_y, physical_max_x, physical_max_y, - xupmm, yupmm); + _scale.xupmm, _scale.yupmm); } bool ApplePS2SynapticsTouchPad::handleOpen(IOService *forClient, IOOptionBits options, void *arg) { @@ -482,12 +411,21 @@ bool ApplePS2SynapticsTouchPad::handleOpen(IOService *forClient, IOOptionBits op return true; } - return super::handleOpen(forClient, options, arg); + return false; +} + +bool ApplePS2SynapticsTouchPad::handleIsOpen(const IOService *forClient) const { + if (forClient == nullptr) { + return voodooInputInstance != nullptr; + } else { + return voodooInputInstance == forClient; + } } void ApplePS2SynapticsTouchPad::handleClose(IOService *forClient, IOOptionBits options) { - OSSafeReleaseNULL(voodooInputInstance); - super::handleClose(forClient, options); + if (forClient == voodooInputInstance) { + OSSafeReleaseNULL(voodooInputInstance); + } } bool ApplePS2SynapticsTouchPad::start( IOService * provider ) @@ -511,26 +449,12 @@ bool ApplePS2SynapticsTouchPad::start( IOService * provider ) // Announce hardware properties. // - IOLog("VoodooPS2Trackpad starting: Synaptics TouchPad reports type 0x%02x, version %d.%d\n", - _touchPadType, (UInt8)(_touchPadVersion >> 8), (UInt8)(_touchPadVersion)); + IOLog("VoodooPS2Trackpad starting: Synaptics TouchPad reports version %d.%d\n", + _identity.major_ver, _identity.minor_ver); char buf[128]; - snprintf(buf, sizeof(buf), "type 0x%02x, version %d.%d", _touchPadType, (UInt8)(_touchPadVersion >> 8), (UInt8)(_touchPadVersion)); + snprintf(buf, sizeof(buf), "type 0x%02x, version %d.%d", + 0x47, _identity.major_ver, _identity.minor_ver); setProperty("RM,TrackpadInfo", buf); - - // - // 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("HIDScrollResolutionX", _scrollresolution << 16, 32); - setProperty("HIDScrollResolutionY", _scrollresolution << 16, 32); // // Setup workloop with command gate for thread syncronization... @@ -543,6 +467,8 @@ bool ApplePS2SynapticsTouchPad::start( IOService * provider ) _device = nullptr; return false; } + + pWorkLoop->addEventSource(_cmdGate); // // Lock the controller during initialization @@ -560,36 +486,13 @@ bool ApplePS2SynapticsTouchPad::start( IOService * provider ) attachedHIDPointerDevices = OSSet::withCapacity(1); registerHIDPointerNotifications(); - - // - // Setup button timer event source - // - if (_buttonCount >= 3) - { - _buttonTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &ApplePS2SynapticsTouchPad::onButtonTimer)); - if (!_buttonTimer) - { - _device->unlock(); - _device->release(); - _device = nullptr; - return false; - } - pWorkLoop->addEventSource(_buttonTimer); - } - - pWorkLoop->addEventSource(_cmdGate); - - // - // Query the touchpad for the capabilities we need to know. - // - queryCapabilities(); // // Set the touchpad mode byte, which will also... // Enable the mouse clock (should already be so) and the mouse IRQ line. // Enable the touchpad itself. // - setTouchpadModeByte(); + enterAdvancedGestureMode(); // // Install our driver's interrupt handler, for asynchronous data delivery. @@ -629,7 +532,7 @@ bool ApplePS2SynapticsTouchPad::start( IOService * provider ) // Update LED -- it could have been disabled then computer was restarted // updateTouchpadLED(); - + registerService(); return true; } @@ -667,12 +570,6 @@ void ApplePS2SynapticsTouchPad::stop( IOService * provider ) IOWorkLoop* pWorkLoop = getWorkLoop(); if (pWorkLoop) { - if (_buttonTimer) - { - pWorkLoop->removeEventSource(_buttonTimer); - _buttonTimer->release(); - _buttonTimer = 0; - } if (_cmdGate) { pWorkLoop->removeEventSource(_cmdGate); @@ -712,6 +609,12 @@ void ApplePS2SynapticsTouchPad::stop( IOService * provider ) // OSSafeReleaseNULL(_provider); + // + // Release OSSymbols + // + + OSSafeReleaseNULL(_smbusCompanion); + super::stop(provider); } @@ -820,7 +723,7 @@ void ApplePS2SynapticsTouchPad::assignVirtualFinger(int physicalFinger) { return; } for (int j = 0; j < SYNAPTICS_MAX_FINGERS; j++) { - virtual_finger_state &vfj = virtualFingerStates[j]; + synaptics_virtual_finger_state &vfj = virtualFingerStates[j]; if (!vfj.touch) { fingerStates[physicalFinger].virtualFingerIndex = j; vfj.touch = true; @@ -832,7 +735,7 @@ void ApplePS2SynapticsTouchPad::assignVirtualFinger(int physicalFinger) { } } -void ApplePS2SynapticsTouchPad::assignFingerType(virtual_finger_state &vf) { +void ApplePS2SynapticsTouchPad::assignFingerType(synaptics_virtual_finger_state &vf) { vf.fingerType = kMT2FingerTypeUndefined; for (MT2FingerType i = kMT2FingerTypeIndexFinger; i < kMT2FingerTypeCount; i = (MT2FingerType)(i + 1)) if (freeFingerTypes[i]) { @@ -843,9 +746,161 @@ void ApplePS2SynapticsTouchPad::assignFingerType(virtual_finger_state &vf) { } -void ApplePS2SynapticsTouchPad::synaptics_parse_hw_state(const UInt8 buf[]) -{ +void ApplePS2SynapticsTouchPad::synaptics_parse_normal_packet(const UInt8 buf[], const int w) { + DEBUG_LOG("synaptics_parse_hw_state: =============NORMAL PACKET============="); + + int x = buf[4] | + ((buf[1] & 0x0f) << 8) | + ((buf[3] & 0x10) << 8); + + int y = buf[5] | + ((buf[1] & 0xf0) << 4) | + ((buf[3] & 0x20) << 7); + + // normal "packet" + if (w >= 4) { // One finger + fingerStates[0].x = x; + fingerStates[0].y = y; + fingerStates[0].z = buf[2]; // pressure + fingerStates[0].w = w; // width + } + else { // Multiple fingers, read virtual V field + fingerStates[0].x = x; + fingerStates[0].y = y; + fingerStates[0].z = buf[2] & 0xfe; // pressure + fingerStates[0].w = 8 + ((buf[2] & 1) << 2 | (buf[5] & 2) | (buf[4] & 2 >> 1)); + } + DEBUG_LOG("synaptics_parse_hw_state: finger 0 pressure %d width %d\n", fingerStates[0].z, fingerStates[0].w); + + if (fingerStates[0].x > X_MAX_POSITIVE) + fingerStates[0].x -= 1 << ABS_POS_BITS; + else if (fingerStates[0].x == X_MAX_POSITIVE) + fingerStates[0].x = XMAX; + + if (fingerStates[0].y > Y_MAX_POSITIVE) + fingerStates[0].y -= 1 << ABS_POS_BITS; + else if (fingerStates[0].y == Y_MAX_POSITIVE) + fingerStates[0].y = YMAX; + + // count the number of fingers + // my port of synaptics_image_sensor_process from synaptics.c from Linux Kernel + int fingerCount = 0; + if(fingerStates[0].z < z_finger) { + fingerCount = 0; + agmFingerCount = 0; + fingerStates[0].w = 0; + } + else if(w >= 4) { + fingerCount = 1; + agmFingerCount = 0; + } else if(w == 0) + fingerCount = MAX(2, MIN(agmFingerCount, SYNAPTICS_MAX_FINGERS)); + else if(w == 1) + fingerCount = MAX(3, MIN(agmFingerCount, SYNAPTICS_MAX_FINGERS)); + + clampedFingerCount = fingerCount; + + if (clampedFingerCount > SYNAPTICS_MAX_FINGERS) + clampedFingerCount = SYNAPTICS_MAX_FINGERS; + + if (renumberFingers()) + sendTouchData(); +} + +// advanced gesture packet (half-resolution packets) +// my port of synaptics_parse_agm from synaptics.c from Linux Kernel +void ApplePS2SynapticsTouchPad::synaptics_parse_agm_packet(const UInt8 buf[]) { + int agmPacketType = (buf[5] & 0x30) >> 4; + + switch(agmPacketType) { + case 1: + DEBUG_LOG("synaptics_parse_hw_state: ===========EXTENDED PACKET==========="); + fingerStates[1].x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1; + fingerStates[1].y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1; + fingerStates[1].z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1; + fingerStates[1].w = 8 + ((buf[5] & 1) << 2 | (buf[2] & 1) << 1 | (buf[1] & 1)); + + DEBUG_LOG("synaptics_parse_hw_state: finger 1 pressure %d width %d\n", fingerStates[1].z, fingerStates[1].w); + + if (fingerStates[1].x > X_MAX_POSITIVE) + fingerStates[1].x -= 1 << ABS_POS_BITS; + else if (fingerStates[1].x == X_MAX_POSITIVE) + fingerStates[1].x = XMAX; + + if (fingerStates[1].y > Y_MAX_POSITIVE) + fingerStates[1].y -= 1 << ABS_POS_BITS; + else if (fingerStates[1].y == Y_MAX_POSITIVE) + fingerStates[1].y = YMAX; + + break; + case 2: + DEBUG_LOG("synaptics_parse_hw_state: ===========FINGER COUNT PACKET==========="); + agmFingerCount = buf[1] & 0x0f; + DEBUG_LOG("synaptics_parse_hw_state: %d fingers, prim_idx: %d, sec_idx: %d\n", + agmFingerCount, buf[2], buf[4]); + break; + default: + break; + } +} +void ApplePS2SynapticsTouchPad::synaptics_parse_passthru(const UInt8 buf[], UInt32 buttons) { + AbsoluteTime timestamp; + clock_get_uptime(×tamp); + + UInt32 passbuttons = buf[1] & 0x7; // mask for just M R L + + // if there are buttons set in the last pass through packet, then be sure + // they are set in any trackpad dispatches. + // otherwise, you might see double clicks that aren't there + _lastPassthruButtons = passbuttons; + + SInt32 dx = ((buf[1] & 0x10) ? 0xffffff00 : 0 ) | buf[4]; + SInt32 dy = ((buf[1] & 0x20) ? 0xffffff00 : 0 ) | buf[5]; + buttons |= passbuttons; + +#ifdef DEBUG_VERBOSE + static int count = 0; + IOLog("ps2: passthru packet dx=%d, dy=%d, buttons=%d (%d)\n", dx, dy, buttons, count++); +#endif + + trackpointReport.timestamp = timestamp; + trackpointReport.dx = dx; + trackpointReport.dy = -dy; + trackpointReport.buttons = buttons; + + super::messageClient(kIOMessageVoodooTrackpointMessage, voodooInputInstance, + &trackpointReport, sizeof(TrackpointReport)); +} + +int ApplePS2SynapticsTouchPad::synaptics_parse_ext_btns(const UInt8 buf[], const int w) { + UInt8 extBtnsChanged = (buf[0] ^ buf[3]) & 0x2; + + // The extended buttons override bits from x/y position, so are only part of the + // packet if their state changes and the packet isn't an Extended W packet. + if (extBtnsChanged == 0 || w == SYNA_W_EXTENDED) { + return _lastExtendedButtons; + } + + UInt8 btnBits = (_extended_id.extended_btns + 1) / 2; + int extendedBtns = 0; + + // Extended buttons have a pattern of: + // Byte 4 X7 X6 X5 X4 B6 B4 B2 B0 + // Byte 5 Y7 Y6 Y5 Y4 B7 B5 B3 B1 + // This needs to be converted to one value with up to 8 buttons in it + for (int i = 0; i < btnBits; i++) { + UInt8 mask = 1 << i; + extendedBtns |= (buf[4] & mask) << i; + extendedBtns |= (buf[5] & mask) << (i + 1); + } + + extendedBtns &= (1 << _extended_id.extended_btns) - 1; + _lastExtendedButtons = extendedBtns; + return _lastExtendedButtons; +} + +void ApplePS2SynapticsTouchPad::synaptics_parse_hw_state(const UInt8 buf[]) { // Check if input is disabled via ApplePS2Keyboard request if (ignoreall) return; @@ -853,287 +908,57 @@ void ApplePS2SynapticsTouchPad::synaptics_parse_hw_state(const UInt8 buf[]) int w = (((buf[0] & 0x30) >> 2) | ((buf[0] & 0x04) >> 1) | ((buf[3] & 0x04) >> 2)); - - - int x = buf[4]|((buf[1]&0x0f)<<8)|((buf[3]&0x10)<<8); - int y = buf[5]|((buf[1]&0xf0)<<4)|((buf[3]&0x20)<<7); DEBUG_LOG("VoodooPS2 w: %d\n", w); + // ------ Buttons ------ - //I'm just reimplement RehabMan old code here, maybe sounds like a hacky solution but hey at least it works! - - UInt32 buttonsraw = buf[0] & 0x03; // mask for just R L - UInt32 buttons = buttonsraw; - - if (passthru && 3 == w) - passbuttons = buf[1] & 0x7; // mask for just M R L + UInt32 buttons = buf[0] & 0x3; // R L + UInt32 xorBtns = buf[0] ^ buf[3]; // Useful for extended buttons below - buttons |= passbuttons; - lastbuttons = buttons; + if (_cont_caps.one_btn_clickpad) { + // Clickpad reports it's button in the middle mouse button position + _clickpad_pressed = xorBtns & 0x1; + } - if (clickpadtype) - { - // ClickPad puts its "button" presses in a different location - // And for single button ClickPad we have to provide a way to simulate right clicks - int clickbuttons = buf[3] & 0x3; - - //Let's quickly do some extra logic to see if we are pressing any of the physical buttons for the trackpoint - if (isthinkpad) - { - - DEBUG_LOG("IS THINKPAD"); - // parse packets for buttons - TrackPoint Buttons may not be passthru - int bp = buf[3] & 0x3; // 1 on clickpad or 2 for the 2 real buttons - int lb = buf[4] & 0x3; // 1 for left real button - int rb = buf[5] & 0x3; // 1 for right real button - - if (bp == 2) - { - if( lb == 1 ) - { // left click - clickbuttons = 0x1; - } - else if ( rb == 1 ) - { // right click - clickbuttons = 0x2; - } - else if ( lb == 2 ) - { // middle click - clickbuttons = 0x4; - } - else - { - clickbuttons = 0x0; - } - thinkpadButtonState = clickbuttons; - buttons=clickbuttons; - setClickButtons(clickbuttons); - } - else - { - clickbuttons = bp; - } - } - - // always clear _clickbutton state, when ClickPad is not clicked - if (!clickbuttons) - setClickButtons(0); - - //Remember the button state on thinkpads.. this is required so we can handle the middle click vs middle scrolling appropriately. - if (isthinkpad) - { - if (thinkpadButtonState) - _clickbuttons = thinkpadButtonState; - } - buttons |= _clickbuttons; - lastbuttons = buttons; - + if (_capabilities.middle_btn && (xorBtns & 0x1)) { + buttons |= 0x4; } + if (_extended_id.extended_btns > 0) { + buttons |= synaptics_parse_ext_btns(buf, w); + } + DEBUG_LOG("VoodooPS2 Buttons %d\n", buttons); - // advanced gesture packet (half-resolution packets) - // my port of synaptics_parse_agm from synaptics.c from Linux Kernel - DEBUG_LOG("buttons %d", buttons); + // ------ Motion Data ------ - if(w == 2) { - int agmPacketType = (buf[5] & 0x30) >> 4; - - switch(agmPacketType) { - case 1: - DEBUG_LOG("synaptics_parse_hw_state: ===========EXTENDED PACKET==========="); - fingerStates[1].x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1; - fingerStates[1].y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1; - fingerStates[1].z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1; - fingerStates[1].w = 8 + ((buf[5] & 1) << 2 | (buf[2] & 1) << 1 | (buf[1] & 1)); - - DEBUG_LOG("synaptics_parse_hw_state: finger 1 pressure %d width %d\n", fingerStates[1].z, fingerStates[1].w); - - if (fingerStates[1].x > X_MAX_POSITIVE) - fingerStates[1].x -= 1 << ABS_POS_BITS; - else if (fingerStates[1].x == X_MAX_POSITIVE) - fingerStates[1].x = XMAX; - - if (fingerStates[1].y > Y_MAX_POSITIVE) - fingerStates[1].y -= 1 << ABS_POS_BITS; - else if (fingerStates[1].y == Y_MAX_POSITIVE) - fingerStates[1].y = YMAX; - - break; - case 2: - DEBUG_LOG("synaptics_parse_hw_state: ===========FINGER COUNT PACKET==========="); - agmFingerCount = buf[1] & 0x0f; - DEBUG_LOG("synaptics_parse_hw_state: %d fingers\n", agmFingerCount); - break; - default: - break; - } - } - else if (w == 3 && passthru) { - AbsoluteTime timestamp; - clock_get_uptime(×tamp); - - - - - UInt32 buttonsraw = buf[0] & 0x03; // mask for just R L - UInt32 buttons = buttonsraw; - - - UInt32 passbuttons = buf[1] & 0x7; // mask for just M R L - // if there are buttons set in the last pass through packet, then be sure - // they are set in any trackpad dispatches. - // otherwise, you might see double clicks that aren't there - buttons |= passbuttons; - lastbuttons = buttons; - - // New Lenovo clickpads do not have buttons, so LR in packet byte 1 is zero and thus - // passbuttons is 0. Instead we need to check the trackpad buttons in byte 0 and byte 3 - // However for clickpads that would miss right clicks, so use the last clickbuttons that - // were saved. - UInt32 combinedButtons = buttons | ((buf[0] & 0x3) | (buf[3] & 0x3)) | _clickbuttons | thinkpadButtonState; - - SInt32 dx = ((buf[1] & 0x10) ? 0xffffff00 : 0 ) | buf[4]; - SInt32 dy = ((buf[1] & 0x20) ? 0xffffff00 : 0 ) | buf[5]; - if (/*mousemiddlescroll && */((buf[1] & 0x4) || thinkpadButtonState == 4)) // only for physical middle button - { - if (dx != 0 || dy != 0) - thinkpadMiddleScrolled = true; - // middle button treats deltas for scrolling - SInt32 scrollx = 0, scrolly = 0; - if (abs(dx) > abs(dy)) - scrollx = dx;// * mousescrollmultiplierx; - else - scrolly = dy;// * mousescrollmultipliery; - - if (isthinkpad && thinkpadMiddleButtonPressed) - { - scrolly = scrolly * thinkpadNubScrollYMultiplier; - scrollx = scrollx * thinkpadNubScrollXMultiplier; - } - - dispatchScrollWheelEvent(scrolly, -scrollx, 0, timestamp); - dx = dy = 0; - } - dx *= mousemultiplierx; - dy *= mousemultipliery; - //If this is a thinkpad, we do extra logic here to see if we're doing a middle click - if (isthinkpad) - { - if (/*mousemiddlescroll && */combinedButtons == 4) - { - thinkpadMiddleButtonPressed = true; - } - else - { - if (thinkpadMiddleButtonPressed && !thinkpadMiddleScrolled) - dispatchRelativePointerEvent(dx, -dy, 4, timestamp); - dispatchRelativePointerEvent(dx, -dy, combinedButtons, timestamp); - thinkpadMiddleButtonPressed = false; - thinkpadMiddleScrolled = false; - } - } - else - { - dispatchRelativePointerEvent(dx, -dy, combinedButtons, timestamp); - } -#ifdef DEBUG_VERBOSE - static int count = 0; - IOLog("ps2: passthru packet dx=%d, dy=%d, buttons=%d (%d)\n", dx, dy, combinedButtons, count++); -#endif - return; - - } - else { - DEBUG_LOG("synaptics_parse_hw_state: =============NORMAL PACKET============="); - - // normal "packet" - if (w >= 4) { // One finger - fingerStates[0].x = x; - fingerStates[0].y = y; - fingerStates[0].z = buf[2]; // pressure - fingerStates[0].w = w; // width - } - else { // Multiple fingers, read virtual V field - fingerStates[0].x = x; - fingerStates[0].y = y; - fingerStates[0].z = buf[2] & 0xfe; // pressure - fingerStates[0].w = 8 + ((buf[2] & 1) << 2 | (buf[5] & 2) | (buf[4] & 2 >> 1)); - } - DEBUG_LOG("synaptics_parse_hw_state: finger 0 pressure %d width %d\n", fingerStates[0].z, fingerStates[0].w); - - - bool prev_right = right; - // That's wrong according to the docs! - left = (buf[0] ^ buf[3]) & 1; - right = (buf[0] ^ buf[3]) & 2; - - if (fingerStates[0].x > X_MAX_POSITIVE) - fingerStates[0].x -= 1 << ABS_POS_BITS; - else if (fingerStates[0].x == X_MAX_POSITIVE) - fingerStates[0].x = XMAX; - - if (fingerStates[0].y > Y_MAX_POSITIVE) - fingerStates[0].y -= 1 << ABS_POS_BITS; - else if (fingerStates[0].y == Y_MAX_POSITIVE) - fingerStates[0].y = YMAX; - - // count the number of fingers - // my port of synaptics_image_sensor_process from synaptics.c from Linux Kernel - int fingerCount = 0; - if(fingerStates[0].z < z_finger) { - fingerCount = 0; - agmFingerCount = 0; - fingerStates[0].w = 0; - } - else if(w >= 4) { - fingerCount = 1; - agmFingerCount = 0; - } else if(w == 0) - fingerCount = MAX(2, MIN(agmFingerCount, SYNAPTICS_MAX_FINGERS)); - else if(w == 1) - fingerCount = MAX(3, MIN(agmFingerCount, SYNAPTICS_MAX_FINGERS)); - - clampedFingerCount = fingerCount; - - if (clampedFingerCount > SYNAPTICS_MAX_FINGERS) - clampedFingerCount = SYNAPTICS_MAX_FINGERS; - - if (renumberFingers()) - sendTouchData(); - - - AbsoluteTime timestamp; - clock_get_uptime(×tamp); - - - if (isthinkpad) - { - if (buttons == 4) - { - thinkpadMiddleButtonPressed = true; - } - else - { - if (thinkpadMiddleButtonPressed && !thinkpadMiddleScrolled) - dispatchRelativePointerEvent(0, 0, 4, timestamp); - dispatchRelativePointerEvent(0, 0, buttons, timestamp); - thinkpadMiddleButtonPressed = false; - thinkpadMiddleScrolled = false; - } - }else{//Deactivated this thingy because I was sending a right click after I pressed the left physical button on my thinkpad - if (right && !prev_right){ - dispatchRelativePointerEvent(0, 0, 0x02, timestamp); - } - else if (prev_right && !(right)){ - dispatchRelativePointerEvent(0, 0, 0x00, timestamp); - } - } - - + switch (w) { + case SYNA_W_EXTENDED: + buttons |= _lastPassthruButtons; + synaptics_parse_agm_packet(buf); + break; + case SYNA_W_PASSTHRU: + synaptics_parse_passthru(buf, buttons); + return; + default: + buttons |= _lastPassthruButtons; + synaptics_parse_normal_packet(buf, w); + break; } + // ------ Report buttons ------ + + AbsoluteTime timestamp; + clock_get_uptime(×tamp); + + trackpointReport.timestamp = timestamp; + trackpointReport.dx = 0; + trackpointReport.dy = 0; + trackpointReport.buttons = buttons; + + super::messageClient(kIOMessageVoodooTrackpointMessage, voodooInputInstance, + &trackpointReport, sizeof(TrackpointReport)); } template @@ -1241,14 +1066,14 @@ bool ApplePS2SynapticsTouchPad::renumberFingers() { else if (clampedFingerCount == 3) { const auto &f0v = virtualFingerStates[f0.virtualFingerIndex]; const auto &f1v = virtualFingerStates[f1.virtualFingerIndex]; - auto &f2 = fingerStates[2]; - f2.x += ((f0.x - f0v.x_avg.newest()) + (f1.x - f1v.x_avg.newest())) / 2; - f2.y += ((f0.y - f0v.y_avg.newest()) + (f1.y - f1v.y_avg.newest())) / 2; - f2.z = (f0.z + f1.z) / 2; - f2.w = (f0.w + f1.w) / 2; - - clip_no_update_limits(f2.x, logical_min_x, logical_max_x, margin_size_x); - clip_no_update_limits(f2.y, logical_min_y, logical_max_y, margin_size_y); + auto &fs2 = fingerStates[2]; + fs2.x += ((f0.x - f0v.x_avg.newest()) + (f1.x - f1v.x_avg.newest())) / 2; + fs2.y += ((f0.y - f0v.y_avg.newest()) + (f1.y - f1v.y_avg.newest())) / 2; + fs2.z = (f0.z + f1.z) / 2; + fs2.w = (f0.w + f1.w) / 2; + + clip_no_update_limits(fs2.x, logical_min_x, logical_max_x, margin_size_x); + clip_no_update_limits(fs2.y, logical_min_y, logical_max_y, margin_size_y); } } else @@ -1524,12 +1349,12 @@ bool ApplePS2SynapticsTouchPad::renumberFingers() { IOLog("synaptics_parse_hw_state: ERROR: invalid physical finger %d", fi.virtualFingerIndex); continue; } - virtual_finger_state &fiv = virtualFingerStates[fi.virtualFingerIndex]; + synaptics_virtual_finger_state &fiv = virtualFingerStates[fi.virtualFingerIndex]; fiv.x_avg.filter(fi.x); fiv.y_avg.filter(fi.y); fiv.width = fi.w; fiv.pressure = fi.z; - fiv.button = left; + fiv.button = _clickpad_pressed; } // Thumb detection. Must happen after setting coordinates (filter) @@ -1556,7 +1381,8 @@ bool ApplePS2SynapticsTouchPad::renumberFingers() { } } - DEBUG_LOG("synaptics_parse_hw_state: lastFingerCount=%d clampedFingerCount=%d left=%d", lastFingerCount, clampedFingerCount, left); + DEBUG_LOG("synaptics_parse_hw_state: lastFingerCount=%d clampedFingerCount=%d clickpad=%d", + lastFingerCount, clampedFingerCount, _clickpad_pressed); return true; } @@ -1643,7 +1469,7 @@ void ApplePS2SynapticsTouchPad::sendTouchData() { } else { double base = ((double) (state.pressure - _forceTouchCustomUpThreshold)) / ((double) (_forceTouchCustomDownThreshold - _forceTouchCustomUpThreshold)); value = 1; - for (int i = 0; i < _forceTouchCustomPower; ++i) { + for (int j = 0; j < _forceTouchCustomPower; ++j) { value *= base; } } @@ -1702,138 +1528,6 @@ void ApplePS2SynapticsTouchPad::sendTouchData() { lastSentFingerCount = inputEvent.contact_count; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2SynapticsTouchPad::onButtonTimer(void) -{ - uint64_t now_abs; - clock_get_uptime(&now_abs); - - middleButton(lastbuttons, now_abs, fromTimer); -} - -UInt32 ApplePS2SynapticsTouchPad::middleButton(UInt32 buttons, uint64_t now_abs, MBComingFrom from) -{ - if (!_fakemiddlebutton || _buttonCount <= 2 || (ignoreall && fromTrackpad == from)) - return buttons; - - // cancel timer if we see input before timeout has fired, but after expired - bool timeout = false; - uint64_t now_ns; - absolutetime_to_nanoseconds(now_abs, &now_ns); - if (fromTimer == from || fromCancel == from || now_ns - _buttontime > _maxmiddleclicktime) - timeout = true; - - // - // A state machine to simulate middle buttons with two buttons pressed - // together. - // - switch (_mbuttonstate) - { - // no buttons down, waiting for something to happen - case STATE_NOBUTTONS: - if (fromCancel != from) - { - if (buttons & 0x4) - _mbuttonstate = STATE_NOOP; - else if (0x3 == buttons) - _mbuttonstate = STATE_MIDDLE; - else if (0x0 != buttons) - { - // only single button, so delay this for a bit - _pendingbuttons = buttons; - _buttontime = now_ns; - setTimerTimeout(_buttonTimer, _maxmiddleclicktime); - _mbuttonstate = STATE_WAIT4TWO; - } - } - break; - - // waiting for second button to come down or timeout - case STATE_WAIT4TWO: - if (!timeout && 0x3 == buttons) - { - _pendingbuttons = 0; - cancelTimer(_buttonTimer); - _mbuttonstate = STATE_MIDDLE; - } - else if (timeout || buttons != _pendingbuttons) - { - if (fromTimer == from || !(buttons & _pendingbuttons)) - dispatchRelativePointerEventX(0, 0, buttons|_pendingbuttons, now_abs); - _pendingbuttons = 0; - cancelTimer(_buttonTimer); - if (0x0 == buttons) - _mbuttonstate = STATE_NOBUTTONS; - else - _mbuttonstate = STATE_NOOP; - } - break; - - // both buttons down and delivering middle button - case STATE_MIDDLE: - if (0x0 == buttons) - _mbuttonstate = STATE_NOBUTTONS; - else if (0x3 != (buttons & 0x3)) - { - // only single button, so delay to see if we get to none - _pendingbuttons = buttons; - _buttontime = now_ns; - setTimerTimeout(_buttonTimer, _maxmiddleclicktime); - _mbuttonstate = STATE_WAIT4NONE; - } - break; - - // was middle button, but one button now up, waiting for second to go up - case STATE_WAIT4NONE: - if (!timeout && 0x0 == buttons) - { - _pendingbuttons = 0; - cancelTimer(_buttonTimer); - _mbuttonstate = STATE_NOBUTTONS; - } - else if (timeout || buttons != _pendingbuttons) - { - if (fromTimer == from) - dispatchRelativePointerEventX(0, 0, buttons|_pendingbuttons, now_abs); - _pendingbuttons = 0; - cancelTimer(_buttonTimer); - if (0x0 == buttons) - _mbuttonstate = STATE_NOBUTTONS; - else - _mbuttonstate = STATE_NOOP; - } - break; - - case STATE_NOOP: - if (0x0 == buttons) - _mbuttonstate = STATE_NOBUTTONS; - break; - } - - // modify buttons after new state set - switch (_mbuttonstate) - { - case STATE_MIDDLE: - buttons = 0x4; - break; - - case STATE_WAIT4NONE: - case STATE_WAIT4TWO: - buttons &= ~0x3; - break; - - case STATE_NOBUTTONS: - case STATE_NOOP: - break; - } - - // return modified buttons - return buttons; -} - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ApplePS2SynapticsTouchPad::setTouchPadEnable( bool enable ) @@ -1845,7 +1539,7 @@ void ApplePS2SynapticsTouchPad::setTouchPadEnable( bool enable ) // (mouse enable/disable command) TPS2Request<1> request; - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].command = kPS2C_SendCommandAndCompareAck; request.commands[0].inOrOut = enable ? kDP_Enable : kDP_SetDefaultsAndDisable; request.commandsCount = 1; assert(request.commandsCount <= countof(request.commands)); @@ -1857,9 +1551,9 @@ void ApplePS2SynapticsTouchPad::setTouchPadEnable( bool enable ) bool ApplePS2SynapticsTouchPad::getTouchPadStatus( UInt8 buf3[] ) { TPS2Request<6> request; - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].command = kPS2C_SendCommandAndCompareAck; request.commands[0].inOrOut = kDP_SetDefaultsAndDisable; - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[1].command = kPS2C_SendCommandAndCompareAck; request.commands[1].inOrOut = kDP_GetMouseInformation; request.commands[2].command = kPS2C_ReadDataPort; request.commands[2].inOrOut = 0; @@ -1867,7 +1561,7 @@ bool ApplePS2SynapticsTouchPad::getTouchPadStatus( UInt8 buf3[] ) request.commands[3].inOrOut = 0; request.commands[4].command = kPS2C_ReadDataPort; request.commands[4].inOrOut = 0; - request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[5].command = kPS2C_SendCommandAndCompareAck; request.commands[5].inOrOut = kDP_SetDefaultsAndDisable; request.commandsCount = 6; assert(request.commandsCount <= countof(request.commands)); @@ -1888,32 +1582,32 @@ bool ApplePS2SynapticsTouchPad::getTouchPadData(UInt8 dataSelector, UInt8 buf3[] TPS2Request<14> request; // Disable stream mode before the command sequence. - request.commands[0].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[0].command = kPS2C_SendCommandAndCompareAck; request.commands[0].inOrOut = kDP_SetDefaultsAndDisable; // 4 set resolution commands, each encode 2 data bits. - request.commands[1].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[1].command = kPS2C_SendCommandAndCompareAck; request.commands[1].inOrOut = kDP_SetMouseResolution; - request.commands[2].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[2].command = kPS2C_SendCommandAndCompareAck; request.commands[2].inOrOut = (dataSelector >> 6) & 0x3; - request.commands[3].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[3].command = kPS2C_SendCommandAndCompareAck; request.commands[3].inOrOut = kDP_SetMouseResolution; - request.commands[4].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[4].command = kPS2C_SendCommandAndCompareAck; request.commands[4].inOrOut = (dataSelector >> 4) & 0x3; - request.commands[5].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[5].command = kPS2C_SendCommandAndCompareAck; request.commands[5].inOrOut = kDP_SetMouseResolution; - request.commands[6].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[6].command = kPS2C_SendCommandAndCompareAck; request.commands[6].inOrOut = (dataSelector >> 2) & 0x3; - request.commands[7].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[7].command = kPS2C_SendCommandAndCompareAck; request.commands[7].inOrOut = kDP_SetMouseResolution; - request.commands[8].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[8].command = kPS2C_SendCommandAndCompareAck; request.commands[8].inOrOut = (dataSelector >> 0) & 0x3; // Read response bytes. - request.commands[9].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[9].command = kPS2C_SendCommandAndCompareAck; request.commands[9].inOrOut = kDP_GetMouseInformation; request.commands[10].command = kPS2C_ReadDataPort; request.commands[10].inOrOut = 0; @@ -1921,7 +1615,7 @@ bool ApplePS2SynapticsTouchPad::getTouchPadData(UInt8 dataSelector, UInt8 buf3[] request.commands[11].inOrOut = 0; request.commands[12].command = kPS2C_ReadDataPort; request.commands[12].inOrOut = 0; - request.commands[13].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[13].command = kPS2C_SendCommandAndCompareAck; request.commands[13].inOrOut = kDP_SetDefaultsAndDisable; request.commandsCount = 14; assert(request.commandsCount <= countof(request.commands)); @@ -1948,7 +1642,9 @@ void ApplePS2SynapticsTouchPad::initTouchPad() _packetByteCount = 0; _ringBuffer.reset(); - _clickbuttons = 0; + _clickpad_pressed = 0; + _lastPassthruButtons = 0; + _lastExtendedButtons = 0; tracksecondary=false; // clear state of control key cache @@ -1960,7 +1656,7 @@ void ApplePS2SynapticsTouchPad::initTouchPad() // Also touchpad is enabled as side effect // - setTouchpadModeByte(); + enterAdvancedGestureMode(); // // Set LED state as it is lost after sleep @@ -1968,15 +1664,11 @@ void ApplePS2SynapticsTouchPad::initTouchPad() updateTouchpadLED(); } -bool ApplePS2SynapticsTouchPad::setTouchpadModeByte() -{ - _touchPadModeByte = _extendedwmodeSupported ? _touchPadModeByte | (1<<2) : _touchPadModeByte & ~(1<<2); - _extendedwmode = _extendedwmodeSupported; - return setTouchPadModeByte(_touchPadModeByte); -} - -bool ApplePS2SynapticsTouchPad::setTouchPadModeByte(UInt8 modeByteValue) +bool ApplePS2SynapticsTouchPad::enterAdvancedGestureMode() { + int i = 0; + TPS2Request<> request; + // make sure we are not early in the initialization... if (!_device) return false; @@ -2016,62 +1708,7 @@ bool ApplePS2SynapticsTouchPad::setTouchPadModeByte(UInt8 modeByteValue) // for a PS2Request is 30. So don't add any. Break it into multiple // requests! - int i; - TPS2Request<> request; - -#ifdef SET_STREAM_MODE - // This was another attempt to solve wake from sleep problems. Not needed. - i = 0; - request.commands[i++].inOrOut = kDP_SetMouseStreamMode; // EA - for (int x = 0; x < i; x++) - request.commands[x].command = kPS2C_SendMouseCommandAndCompareAck; - request.commandsCount = i; - assert(request.commandsCount <= countof(request.commands)); - DEBUG_LOG("VoodooPS2Trackpad: sending kDP_SetMouseStreamMode $EA\n"); - _device->submitRequestAndBlock(&request); - if (i != request.commandsCount) - DEBUG_LOG("VoodooPS2Trackpad: sending $EA failed: %d\n", request.commandsCount); -#endif - -#ifdef UNDOCUMENTED_INIT_SEQUENCE_PRE - // Also another attempt to solve wake from sleep problems. Probably not needed. - i = 0; - // From chiby's latest post... to take care of wakup issues? - request.commands[i++].inOrOut = kDP_SetMouseScaling2To1; // E7 - request.commands[i++].inOrOut = kDP_SetMouseScaling1To1; // E6 - request.commands[i++].inOrOut = kDP_Enable; // F4 - for (int x = 0; x < i; x++) - request.commands[x].command = kPS2C_SendMouseCommandAndCompareAck; - request.commandsCount = i; - DEBUG_LOG("VoodooPS2Trackpad: sending undoc pre\n"); - assert(request.commandsCount <= countof(request.commands)); - _device->submitRequestAndBlock(&request); - if (i != request.commandsCount) - DEBUG_LOG("VoodooPS2Trackpad: sending undoc pre failed: %d\n", request.commandsCount); -#endif - - // Disable stream mode before the command sequence. - i = 0; - request.commands[i++].inOrOut = kDP_SetDefaultsAndDisable; // F5 - request.commands[i++].inOrOut = kDP_SetDefaultsAndDisable; // F5 - request.commands[i++].inOrOut = kDP_SetMouseScaling1To1; // E6 - request.commands[i++].inOrOut = kDP_SetMouseScaling1To1; // E6 - - // 4 set resolution commands, each encode 2 data bits. - request.commands[i++].inOrOut = kDP_SetMouseResolution; // E8 - request.commands[i++].inOrOut = (modeByteValue >> 6) & 0x3; // 0x (depends on mode byte) - request.commands[i++].inOrOut = kDP_SetMouseResolution; // E8 - request.commands[i++].inOrOut = (modeByteValue >> 4) & 0x3; // 0x (depends on mode byte) - request.commands[i++].inOrOut = kDP_SetMouseResolution; // E8 - request.commands[i++].inOrOut = (modeByteValue >> 2) & 0x3; // 0x (depends on mode byte) - request.commands[i++].inOrOut = kDP_SetMouseResolution; // E8 - request.commands[i++].inOrOut = (modeByteValue >> 0) & 0x3; // 0x (depends on mode byte) - - // Set sample rate 20 to set mode byte 2. Older pads have 4 mode - // bytes (0,1,2,3), but only mode byte 2 remain in modern pads. - request.commands[i++].inOrOut = kDP_SetMouseSampleRate; // F3 - request.commands[i++].inOrOut = 20; // 14 - request.commands[i++].inOrOut = kDP_SetMouseScaling1To1; // E6 + setModeByte(false); #ifdef UNDOCUMENTED_INIT_SEQUENCE_POST // maybe this is commit? @@ -2095,7 +1732,7 @@ bool ApplePS2SynapticsTouchPad::setTouchPadModeByte(UInt8 modeByteValue) // all these commands are "send mouse" and "compare ack" for (int x = 0; x < i; x++) - request.commands[x].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[x].command = kPS2C_SendCommandAndCompareAck; request.commandsCount = i; assert(request.commandsCount <= countof(request.commands)); _device->submitRequestAndBlock(&request); @@ -2105,33 +1742,22 @@ bool ApplePS2SynapticsTouchPad::setTouchPadModeByte(UInt8 modeByteValue) return i == request.commandsCount; } - -void ApplePS2SynapticsTouchPad::setClickButtons(UInt32 clickButtons) -{ - UInt32 oldClickButtons = _clickbuttons; - _clickbuttons = clickButtons; - - if (!!oldClickButtons != !!clickButtons) - setModeByte(); -} - -bool ApplePS2SynapticsTouchPad::setModeByte() -{ - if (!_extendedwmodeSupported) - return false; - - _touchPadModeByte = _clickbuttons ? _touchPadModeByte | (1<<2) : _touchPadModeByte & ~(1<<2); - _extendedwmode = _clickbuttons; - - return setModeByte(_touchPadModeByte); -} - // simplified setModeByte for switching between normal mode and EW mode -bool ApplePS2SynapticsTouchPad::setModeByte(UInt8 modeByteValue) +bool ApplePS2SynapticsTouchPad::setModeByte(bool sleep) { // make sure we are not early in the initialization... if (!_device) return false; + + uint8_t modeByte = SYNA_MODE_ABSOLUTE | SYNA_MODE_W_MODE | SYNA_MODE_HIGH_RATE; + + if (_capabilities.extended_w_supported || + // Linux checks these bits too + _cont_caps.advanced_gestures || _cont_caps.reports_v) + modeByte |= SYNA_MODE_EXT_W; + + if (sleep) + modeByte |= SYNA_MODE_SLEEP; int i; TPS2Request<> request; @@ -2145,13 +1771,13 @@ bool ApplePS2SynapticsTouchPad::setModeByte(UInt8 modeByteValue) // 4 set resolution commands, each encode 2 data bits. request.commands[i++].inOrOut = kDP_SetMouseResolution; // E8 - request.commands[i++].inOrOut = (modeByteValue >> 6) & 0x3; // 0x (depends on mode byte) + request.commands[i++].inOrOut = (modeByte >> 6) & 0x3; // 0x (depends on mode byte) request.commands[i++].inOrOut = kDP_SetMouseResolution; // E8 - request.commands[i++].inOrOut = (modeByteValue >> 4) & 0x3; // 0x (depends on mode byte) + request.commands[i++].inOrOut = (modeByte >> 4) & 0x3; // 0x mode_byte request.commands[i++].inOrOut = kDP_SetMouseResolution; // E8 - request.commands[i++].inOrOut = (modeByteValue >> 2) & 0x3; // 0x (depends on mode byte) + request.commands[i++].inOrOut = (modeByte >> 2) & 0x3; // 0x (depends on mode byte) request.commands[i++].inOrOut = kDP_SetMouseResolution; // E8 - request.commands[i++].inOrOut = (modeByteValue >> 0) & 0x3; // 0x (depends on mode byte) + request.commands[i++].inOrOut = (modeByte >> 0) & 0x3; // 0x (depends on mode byte) // Set sample rate 20 to set mode byte 2. Older pads have 4 mode // bytes (0,1,2,3), but only mode byte 2 remain in modern pads. @@ -2159,12 +1785,9 @@ bool ApplePS2SynapticsTouchPad::setModeByte(UInt8 modeByteValue) request.commands[i++].inOrOut = 20; // 14 request.commands[i++].inOrOut = kDP_SetMouseScaling1To1; // E6 - // enable trackpad - request.commands[i++].inOrOut = kDP_Enable; // F4 - // all these commands are "send mouse" and "compare ack" for (int x = 0; x < i; x++) - request.commands[x].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[x].command = kPS2C_SendCommandAndCompareAck; request.commandsCount = i; assert(request.commandsCount <= countof(request.commands)); _device->submitRequestAndBlock(&request); @@ -2176,7 +1799,7 @@ bool ApplePS2SynapticsTouchPad::setModeByte(UInt8 modeByteValue) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void ApplePS2SynapticsTouchPad::setParamPropertiesGated(OSDictionary * config) +void ApplePS2SynapticsTouchPad::setPropertiesGated(OSDictionary * config) { if (NULL == config) return; @@ -2184,17 +1807,19 @@ void ApplePS2SynapticsTouchPad::setParamPropertiesGated(OSDictionary * config) const struct {const char *name; int *var;} int32vars[]={ {"FingerZ", &z_finger}, {"WakeDelay", &wakedelay}, - {"Resolution", &_resolution}, - {"ScrollResolution", &_scrollresolution}, - {"ButtonCount", &_buttonCount}, {"MinLogicalXOverride", &minXOverride}, {"MinLogicalYOverride", &minYOverride}, {"MaxLogicalXOverride", &maxXOverride}, {"MaxLogicalYOverride", &maxYOverride}, - {"TrackpointScrollXMultiplier", &thinkpadNubScrollXMultiplier}, - {"TrackpointScrollYMultiplier", &thinkpadNubScrollYMultiplier}, - {"MouseMultiplierX", &mousemultiplierx}, - {"MouseMultiplierY", &mousemultipliery}, + {"TrackpointDeadzone", &_deadzone}, + {"TrackpointScrollXMultiplier", &_scrollMultiplierX}, + {"TrackpointScrollYMultiplier", &_scrollMultiplierY}, + {"TrackpointScrollXDivisor", &_scrollDivisorX}, + {"TrackpointScrollYDivisor", &_scrollDivisorY}, + {"MouseMultiplierX", &_mouseMultiplierX}, + {"MouseMultiplierY", &_mouseMultiplierY}, + {"MouseDivisorX", &_mouseDivisorX}, + {"MouseDivisorY", &_mouseDivisorY}, {"ForceTouchMode", (int*)&_forceTouchMode}, // 0 - disable, 1 - left button, 2 - pressure threshold, 3 - pass pressure value {"ForceTouchPressureThreshold", &_forceTouchPressureThreshold}, // used in mode 2 {"SpecialKeyForQuietTime", &specialKey}, @@ -2204,35 +1829,20 @@ void ApplePS2SynapticsTouchPad::setParamPropertiesGated(OSDictionary * config) }; const struct {const char *name; int *var;} boolvars[]={ {"DisableLEDUpdate", &noled}, - {"SkipPassThrough", &skippassthru}, - {"ForcePassThrough", &forcepassthru}, - {"Thinkpad", &isthinkpad}, {"HWResetOnStart", &hwresetonstart}, - {"FakeMiddleButton", &_fakemiddlebutton}, {"ProcessUSBMouseStopsTrackpad", &_processusbmouse}, {"ProcessBluetoothMouseStopsTrackpad", &_processbluetoothmouse}, }; const struct {const char* name; bool* var;} lowbitvars[]={ {"USBMouseStopsTrackpad", &usb_mouse_stops_trackpad}, + {"DisableDeepSleep", &disableDeepSleep} }; const struct {const char* name; uint64_t* var; } int64vars[]={ {"QuietTimeAfterTyping", &maxaftertyping}, - {"QuietTimeAfterSpecialKey", &maxafterspecialtyping}, - {"MiddleClickTime", &_maxmiddleclicktime}, + {"QuietTimeAfterSpecialKey", &maxafterspecialtyping} }; - uint8_t oldmode = _touchPadModeByte; - - // highrate? OSBoolean *bl; - if ((bl=OSDynamicCast (OSBoolean, config->getObject ("UseHighRate")))) - { - if (bl->isTrue()) - _touchPadModeByte |= 1<<6; - else - _touchPadModeByte &= ~(1<<6); - setProperty("UseHighRate", bl->isTrue()); - } OSNumber *num; // 64-bit config items @@ -2272,18 +1882,6 @@ void ApplePS2SynapticsTouchPad::setParamPropertiesGated(OSDictionary * config) } } - // this driver assumes wmode is available (6-byte packets) - _touchPadModeByte |= 1<<0; - // extendedwmode is optional, used automatically - _touchPadModeByte = _extendedwmodeSupported ? _touchPadModeByte | (1<<2) : _touchPadModeByte & ~(1<<2); - // if changed, setup touchpad mode - if (_touchPadModeByte != oldmode) - { - setTouchpadModeByte(); - _packetByteCount=0; - _ringBuffer.reset(); - } - // disable trackpad when USB mouse is plugged in and this functionality is requested if (attachedHIDPointerDevices && attachedHIDPointerDevices->getCount() > 0) { ignoreall = usb_mouse_stops_trackpad; @@ -2299,20 +1897,12 @@ void ApplePS2SynapticsTouchPad::setParamPropertiesGated(OSDictionary * config) PE_parse_boot_argn("auth-root-dmg", val, sizeof(val))) _forceTouchMode = FORCE_TOUCH_DISABLED; } -} - -IOReturn ApplePS2SynapticsTouchPad::setParamProperties(OSDictionary* dict) -{ - ////IOReturn result = super::IOHIDevice::setParamProperties(dict); - if (_cmdGate) - { - // syncronize through workloop... - ////_cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2SynapticsTouchPad::setParamPropertiesGated), dict); - setParamPropertiesGated(dict); - } - return super::setParamProperties(dict); - ////return result; + setTrackpointProperties(); + if (voodooInputInstance != nullptr) { + super::messageClient(kIOMessageVoodooTrackpointUpdatePropertiesNotification, + voodooInputInstance, 0, 0); + } } IOReturn ApplePS2SynapticsTouchPad::setProperties(OSObject *props) @@ -2321,7 +1911,7 @@ IOReturn ApplePS2SynapticsTouchPad::setProperties(OSObject *props) if (dict && _cmdGate) { // synchronize through workloop... - _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2SynapticsTouchPad::setParamPropertiesGated), dict); + _cmdGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &ApplePS2SynapticsTouchPad::setPropertiesGated), dict); } return super::setProperties(props); @@ -2329,6 +1919,30 @@ IOReturn ApplePS2SynapticsTouchPad::setProperties(OSObject *props) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ApplePS2SynapticsTouchPad::setTrackpointProperties() +{ + // Trackpoint information for VoodooInput + OSDictionary *trackpoint = OSDictionary::withCapacity(10); + if (trackpoint == nullptr) + return; + + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_DEADZONE, _deadzone); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_BTN_CNT, 3); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_MOUSE_MULT_X, _mouseMultiplierX); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_MOUSE_MULT_Y, _mouseMultiplierY); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_MOUSE_DIV_X, _mouseDivisorX); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_MOUSE_DIV_Y, _mouseDivisorY); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_SCROLL_MULT_X, _scrollMultiplierX); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_SCROLL_MULT_Y, _scrollMultiplierY); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_SCROLL_DIV_X, _scrollDivisorX); + PS2DictSetNumber(trackpoint, VOODOO_TRACKPOINT_SCROLL_DIV_Y, _scrollDivisorY); + + setProperty(VOODOO_TRACKPOINT_KEY, trackpoint); + OSSafeReleaseNULL(trackpoint); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + void ApplePS2SynapticsTouchPad::setDevicePowerState( UInt32 whatToDo ) { switch ( whatToDo ) @@ -2339,8 +1953,10 @@ void ApplePS2SynapticsTouchPad::setDevicePowerState( UInt32 whatToDo ) // setTouchPadEnable( false ); // Disable stream mode - _touchPadModeByte |= 1 << 3; - setModeByte(_touchPadModeByte); // Enable sleep + + if (!disableDeepSleep) { + setModeByte(true); // Enable sleep + } break; case kPS2C_EnableDevice: @@ -2349,13 +1965,18 @@ void ApplePS2SynapticsTouchPad::setDevicePowerState( UInt32 whatToDo ) // completed its power-on self-test and calibration. // - IOSleep(wakedelay); - _touchPadModeByte &= ~(1 << 3); // Wake from sleep - setModeByte(_touchPadModeByte); + if (!disableDeepSleep) { + IOSleep(wakedelay); + setModeByte(false); + } IOSleep(wakedelay); // Reset and enable the touchpad. initTouchPad(); + + // Send extra kDP_Enable command + IOSleep(wakedelay); + setTouchPadEnable(true); break; } } @@ -2396,6 +2017,20 @@ IOReturn ApplePS2SynapticsTouchPad::message(UInt32 type, IOService* provider, vo break; } + case kPS2M_resetTouchpad: + { + int* reqCode = (int*)argument; + IOLog("VoodooPS2SynapticsTouchPad::kPS2M_resetTouchpad reqCode: %d\n", *reqCode); + if (*reqCode == 1) + { + ignoreall = false; + initTouchPad(); + IOSleep(wakedelay); + setTouchPadEnable(true); // Send extra kDP_Enable + updateTouchpadLED(); + } + break; + } case kPS2M_notifyKeyPressed: { // just remember last time key pressed... this can be used in @@ -2521,7 +2156,7 @@ IOReturn ApplePS2SynapticsTouchPad::message(UInt32 type, IOService* provider, vo void ApplePS2SynapticsTouchPad::updateTouchpadLED() { - if (ledpresent && !noled) + if (_extended_id.has_leds && !noled) setTouchpadLED(ignoreall ? 0x88 : 0x10); // if PS2M implements "TPDN" then, we can notify it of changes to LED state @@ -2564,7 +2199,7 @@ bool ApplePS2SynapticsTouchPad::setTouchpadLED(UInt8 touchLED) // all these commands are "send mouse" and "compare ack" for (int x = 0; x < request.commandsCount; x++) - request.commands[x].command = kPS2C_SendMouseCommandAndCompareAck; + request.commands[x].command = kPS2C_SendCommandAndCompareAck; _device->submitRequestAndBlock(&request); return 12 == request.commandsCount; diff --git a/VoodooPS2Trackpad/VoodooPS2SynapticsTouchPad.h b/VoodooPS2Trackpad/VoodooPS2SynapticsTouchPad.h index c550bab2..061a9981 100644 --- a/VoodooPS2Trackpad/VoodooPS2SynapticsTouchPad.h +++ b/VoodooPS2Trackpad/VoodooPS2SynapticsTouchPad.h @@ -24,141 +24,10 @@ #define _APPLEPS2SYNAPTICSTOUCHPAD_H #include "../VoodooPS2Controller/ApplePS2MouseDevice.h" -#include "LegacyIOHIPointing.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winconsistent-missing-override" #include #include -#pragma clang diagnostic pop - #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; - } -}; +#include "VoodooPS2TrackpadCommon.h" struct synaptics_hw_state { int x; @@ -174,7 +43,7 @@ struct synaptics_hw_state { transducers заполняются каждый раз с номером виртуального пальца столько, сколько надо. Будут ли при этом отжиматься отпущенные пальцы? */ -struct virtual_finger_state { +struct synaptics_virtual_finger_state { SimpleAverage x_avg; SimpleAverage y_avg; uint8_t pressure; @@ -184,14 +53,135 @@ struct virtual_finger_state { MT2FingerType fingerType; }; -typedef enum { - FORCE_TOUCH_DISABLED = 0, - FORCE_TOUCH_BUTTON = 1, - FORCE_TOUCH_THRESHOLD = 2, - FORCE_TOUCH_VALUE = 3, - FORCE_TOUCH_CUSTOM = 4 -} ForceTouchMode; +#pragma pack(push) +#pragma pack(1) +#define SYNAPTICS_IDENTIFY_QUERY 0x00 +struct synaptics_identify_trackpad { + uint8_t minor_ver; + uint8_t synaptics_const; + uint8_t major_ver : 4; + uint8_t model_code : 4; // Unused field +}; +static_assert(sizeof(synaptics_identify_trackpad) == 3, "Invalid Identity packet size"); + +#define SYNA_MODEL_QUERY 0x01 +struct synaptics_model { + uint8_t guest_present: 1; + uint8_t more_extended_caps: 1; + uint16_t model_number: 14; + uint8_t mode_byte; +}; +static_assert(sizeof(synaptics_model) == 3, "Invalid Model packet size"); + +#define SYNA_CAPABILITIES_QUERY 0x02 +struct synaptics_capabilities { + // Byte 0 + uint8_t _reserved0: 2; + uint8_t middle_btn: 1; + uint8_t _reserved1: 1; + uint8_t extended_queries: 3; + uint8_t has_extended_queries: 1; + // Byte 1 + uint8_t model_sub_num; + // Byte 2 + uint8_t palm_detect: 1; + uint8_t multi_finger: 1; + uint8_t ballistics: 1; + uint8_t sleep: 1; + uint8_t four_buttons: 1; // Currently unsupported + uint8_t extended_w_supported: 1; + uint8_t low_power: 1; + uint8_t passthrough: 1; +}; +static_assert(sizeof(synaptics_capabilities) == 3, "Invalid Capabilities packet size"); + +#define SYNA_SCALE_QUERY 0x08 +struct synaptics_scale { + uint8_t xupmm; + uint8_t reserved; + uint8_t yupmm; +}; +static_assert(sizeof(synaptics_scale) == 3, "Invalid Scale packet size"); + +#define SYNA_EXTENDED_ID_QUERY 0x09 +struct synaptics_extended_id { + // Byte 0 + uint8_t vert_scroll_zone: 1; + uint8_t horiz_scroll_zone: 1; + uint8_t extended_w_supported: 1; + uint8_t vertical_wheel: 1; + uint8_t transparent_passthru: 1; + uint8_t peak_detection: 1; + uint8_t has_leds: 1; + uint8_t reserved0: 1; + // Byte 1 + uint8_t reserved1: 2; + uint8_t info_sensor: 2; + uint8_t extended_btns: 4; + // Byte 2 + uint8_t product_id; +}; +static_assert(sizeof(synaptics_extended_id) == 3, "Invalid Extended ID packet size"); + +#define SYNA_CONT_CAPS_QUERY 0x0C +struct synaptics_cont_capabilities { + // Byte 0 + uint8_t adj_button_threshold: 1; + uint8_t reports_max: 1; + uint8_t is_clearpad: 1; + uint8_t advanced_gestures: 1; + uint8_t one_btn_clickpad: 1; + uint8_t multifinger_mode: 2; + uint8_t covered_pad_gesture: 1; + // Byte 1 + uint8_t two_btn_clickpad: 1; + uint8_t deluxe_leds: 1; + uint8_t no_abs_pos_filter: 1; + uint8_t reports_v: 1; + uint8_t uniform_clickpad: 1; + uint8_t reports_min: 1; + uint8_t intertouch: 1; + uint8_t reserved: 1; + // Byte 2 + uint8_t intertouch_addr; +}; +static_assert(sizeof(synaptics_cont_capabilities) == 3, "Invalid continued capabilities packet size"); + +#define SYNA_LOGIC_MAX_QUERY 0x0D +#define SYNA_LOGIC_MIN_QUERY 0x0F +#define SYNA_LOGIC_X(x) ((x.x_high << 5) | (x.x_low << 1)) +#define SYNA_LOGIC_Y(x) (x.y << 1) +struct synaptics_logic_min_max { + uint8_t x_high; + uint8_t x_low: 4; + uint16_t y: 12; +}; +static_assert(sizeof(synaptics_logic_min_max) == 3, "Invalid logic packet size"); + +#define SYNA_SECUREPAD_QUERY 0x10 +struct synaptics_securepad_id { + uint8_t trackstick_btns: 1; + uint8_t is_securepad: 1; + uint8_t unused: 6; + uint8_t securepad_width; + uint8_t securepad_height; +}; +static_assert(sizeof(synaptics_securepad_id) == 3, "Invalid securepad packet size"); + +#define SYNA_MODE_ABSOLUTE 0x80 +#define SYNA_MODE_HIGH_RATE 0x40 +#define SYNA_MODE_SLEEP 0x08 +#define SYNA_MODE_EXT_W 0x04 +#define SYNA_MODE_W_MODE 0x01 + +// W Values for packet types +#define SYNA_W_EXTENDED 0x02 +#define SYNA_W_PASSTHRU 0x03 + +#pragma pack(pop) + +#define SYNAPTICS_MAX_EXT_BTNS 8 #define SYNAPTICS_MAX_FINGERS 5 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -214,9 +204,9 @@ typedef enum { #define kPacketLength 6 -class EXPORT ApplePS2SynapticsTouchPad : public IOHIPointing +class EXPORT ApplePS2SynapticsTouchPad : public IOService { - typedef IOHIPointing super; + typedef IOService super; OSDeclareDefaultStructors(ApplePS2SynapticsTouchPad); private: @@ -227,18 +217,22 @@ class EXPORT ApplePS2SynapticsTouchPad : public IOHIPointing RingBuffer _ringBuffer {}; UInt32 _packetByteCount {0}; UInt8 _lastdata {0}; - UInt16 _touchPadVersion {0}; - UInt8 _touchPadType {0}; // from identify: either 0x46 or 0x47 - UInt8 _touchPadModeByte {0x80}; //default: absolute, low-rate, no w-mode + + synaptics_identify_trackpad _identity {0}; + synaptics_capabilities _capabilities {0}; + synaptics_extended_id _extended_id {0}; + synaptics_securepad_id _securepad {0}; + synaptics_scale _scale {0}; + synaptics_cont_capabilities _cont_caps {0}; IOCommandGate* _cmdGate {nullptr}; IOACPIPlatformDevice*_provider {nullptr}; VoodooInputEvent inputEvent {}; + TrackpointReport trackpointReport {}; // buttons and scroll wheel - bool left {false}; - bool right {false}; + bool _clickpad_pressed { false }; int margin_size_x {0}, margin_size_y {0}; uint32_t logical_max_x {0}; @@ -249,20 +243,27 @@ class EXPORT ApplePS2SynapticsTouchPad : public IOHIPointing uint32_t physical_max_x {0}; uint32_t physical_max_y {0}; - synaptics_hw_state fingerStates[SYNAPTICS_MAX_FINGERS] {}; - virtual_finger_state virtualFingerStates[SYNAPTICS_MAX_FINGERS] {}; - bool freeFingerTypes[kMT2FingerTypeCount]; + synaptics_hw_state fingerStates[SYNAPTICS_MAX_FINGERS] {}; + synaptics_virtual_finger_state virtualFingerStates[SYNAPTICS_MAX_FINGERS] {}; + bool freeFingerTypes[kMT2FingerTypeCount]; - static_assert(SYNAPTICS_MAX_FINGERS <= kMT2FingerTypeLittleFinger, "Too many fingers for one hand"); + bool disableDeepSleep {false}; + + static_assert(SYNAPTICS_MAX_FINGERS <= kMT2FingerTypeLittleFinger, "Too many fingers for one hand"); void assignVirtualFinger(int physicalFinger); - void assignFingerType(virtual_finger_state &vf); + void assignFingerType(synaptics_virtual_finger_state &vf); int lastFingerCount; int lastSentFingerCount; bool hadLiftFinger; int upperFingerIndex() const; const synaptics_hw_state& upperFinger() const; void swapFingers(int dst, int src); + + void synaptics_parse_normal_packet(const UInt8 buf[], const int w); + void synaptics_parse_agm_packet(const UInt8 buf[]); + void synaptics_parse_passthru(const UInt8 buf[], const UInt32 buttons); + int synaptics_parse_ext_btns(const UInt8 buf[], const int w); void synaptics_parse_hw_state(const UInt8 buf[]); /// Translates physical fingers into virtual fingers so that host software doesn't see 'jumps' and has coordinates for all fingers. @@ -284,53 +285,46 @@ class EXPORT ApplePS2SynapticsTouchPad : public IOHIPointing bool wasSkipped {false}; int z_finger {45}; int zlimit {0}; - int noled {0}; + int noled {0}; uint64_t maxaftertyping {500000000}; uint64_t maxafterspecialtyping {0}; int specialKey {0x80}; int wakedelay {1000}; - int skippassthru {0}; - int forcepassthru {0}; int hwresetonstart {0}; int diszl {0}, diszr {0}, diszt {0}, diszb {0}; - int _resolution {2300}, _scrollresolution {2300}; - int _buttonCount {2}; int minXOverride {-1}, minYOverride {-1}, maxXOverride {-1}, maxYOverride {-1}; - //vars for clickpad and middleButton support (thanks jakibaki) - int isthinkpad {0}; - int thinkpadButtonState {0}; - int thinkpadNubScrollXMultiplier {1}; - int thinkpadNubScrollYMultiplier {1}; - bool thinkpadMiddleScrolled {false}; - bool thinkpadMiddleButtonPressed {false}; - int mousemultiplierx {1}; - int mousemultipliery {1}; - + int _lastExtendedButtons {0}; + int _lastPassthruButtons {0}; + + // Trackpoint information + int _scrollMultiplierX {1}; + int _scrollMultiplierY {1}; + int _scrollDivisorX {1}; + int _scrollDivisorY {1}; + int _mouseMultiplierX {1}; + int _mouseMultiplierY {1}; + int _mouseDivisorX {1}; + int _mouseDivisorY {1}; + int _deadzone {1}; // state related to secondary packets/extendedwmode bool tracksecondary {false}; - bool _extendedwmode {false}, _extendedwmodeSupported {false}; - + // normal state - UInt32 passbuttons {0}; - UInt32 lastbuttons {0}; uint64_t keytime {0}; UInt16 keycode {0}; bool ignoreall {false}; #ifdef SIMULATE_PASSTHRU UInt32 trackbuttons {0}; #endif - bool passthru {false}; - bool ledpresent {false}; - bool _reportsv {false}; - int clickpadtype {0}; //0=not, 1=1button, 2=2button, 3=reserved - UInt32 _clickbuttons {0}; //clickbuttons to merge into buttons bool usb_mouse_stops_trackpad {true}; int _processusbmouse {true}; int _processbluetoothmouse {true}; + const OSSymbol* _smbusCompanion {nullptr}; + OSSet* attachedHIDPointerDevices {nullptr}; IONotifier* usb_hid_publish_notify {nullptr}; // Notification when an USB mouse HID device is connected @@ -341,27 +335,6 @@ class EXPORT ApplePS2SynapticsTouchPad : public IOHIPointing int _modifierdown {0}; // state of left+right control keys - // for scaling x/y values - int xupmm {50}, yupmm {50}; // 50 is just arbitrary, but same - - // for middle button simulation - enum mbuttonstate - { - STATE_NOBUTTONS, - STATE_MIDDLE, - STATE_WAIT4TWO, - STATE_WAIT4NONE, - STATE_NOOP, - } _mbuttonstate {STATE_NOBUTTONS}; - - UInt32 _pendingbuttons {0}; - uint64_t _buttontime {0}; - IOTimerEventSource* _buttonTimer {nullptr}; - uint64_t _maxmiddleclicktime {100000000}; - int _fakemiddlebutton {true}; - - void setClickButtons(UInt32 clickButtons); - inline bool isInDisableZone(int x, int y) { return x > diszl && x < diszr && y > diszb && y < diszt; } @@ -372,62 +345,42 @@ class EXPORT ApplePS2SynapticsTouchPad : public IOHIPointing virtual void setTouchPadEnable( bool enable ); virtual bool getTouchPadData( UInt8 dataSelector, UInt8 buf3[] ); virtual bool getTouchPadStatus( UInt8 buf3[] ); - virtual bool setTouchPadModeByte(UInt8 modeByteValue); virtual PS2InterruptResult interruptOccurred(UInt8 data); virtual void packetReady(); virtual void setDevicePowerState(UInt32 whatToDo); void updateTouchpadLED(); bool setTouchpadLED(UInt8 touchLED); - bool setTouchpadModeByte(); // set based on state void initTouchPad(); - bool setModeByte(UInt8 modeByteValue); - bool setModeByte(); // set based on state + bool enterAdvancedGestureMode(); + bool setModeByte(bool sleep); inline bool isFingerTouch(int z) { return z>z_finger && zsetTimeout(*(AbsoluteTime*)&time); } - inline void cancelTimer(IOTimerEventSource* timer) - { timer->cancelTimeout(); } - + public: bool init( OSDictionary * properties ) override; - ApplePS2SynapticsTouchPad * probe( IOService * provider, - SInt32 * score ) override; + IOService * 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; diff --git a/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist b/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist index 883e74f6..ecf1cfe6 100644 --- a/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist +++ b/VoodooPS2Trackpad/VoodooPS2Trackpad-Info.plist @@ -36,8 +36,57 @@ Default + Darwin 16+ + + ApplePreferenceCapability + + ApplePreferenceIdentifier + com.apple.AppleMultitouchTrackpad + MT Built-in + + MTHIDDevice + + SupportsGestureScrolling + + TrackpadEmbedded + + TrackpadFourFingerGestures + + TrackpadSecondaryClickCorners + + TrackpadThreeFingerDrag + + DisableDevice + DragLockTempMask + 1048592 + FingerZ + 1 + ForceTouchCustomDownThreshold + 90 + ForceTouchCustomPower + 8 + ForceTouchCustomUpThreshold + 20 + ForceTouchMode + 0 + ForceTouchPressureThreshold + 100 + ProcessBluetoothMouseStopsTrackpad + + ProcessUSBMouseStopsTrackpad + + QuietTimeAfterTyping + 100000000 + USBMouseStopsTrackpad + 0 + UnitsPerMMX + 50 + UnitsPerMMY + 50 + WakeDelay + 1000 HPQOEM @@ -84,6 +133,86 @@ ProBook + ProductID + 547 + RM,deliverNotifications + + VendorID + 1452 + + 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 + + TrackpointDividerX + 120 + TrackpointDividerY + 120 + TrackpointMultiplierX + 120 + TrackpointMultiplierY + 120 + USBMouseStopsTrackpad + 0 + UseHighRate + + WakeDelay + 1000 + + + RM,deliverNotifications + Native Multitouch Engine @@ -163,14 +292,6 @@ CFBundleIdentifier as.acidanthera.voodoo.driver.PS2Trackpad - HIDPointerAccelerationTable - AACAAFVTQioABwAAAAAAAgAEAAAABAAAABAAAAAQAAAAACAAAA0AAIAAAACAAAABQAAAAYAAAAIAAAAC4AAAAwAAAATgAAAEAAAAB0AAAAUAAAAKAAAABgAAAA1AAAAIAAAAFgAAAArAAAAjAAAADQAAAC8AAAAOwAAAOMAAABBAAABBAAAAEcAAAEjAAAAAUAAADwAAgAAAAIAAAAEAAAABQAAAAYAAAAJAAAACAAAAA4AAAAKAAAAE4AAAAwAAAAZgAAAEAAAACgAAAAUAAAAOQAAABgAAABNAAAAIAAAAHsAAAArAAAAuwAAADQAAADyAAAAOwAAARwAAABBAAABPwAAAEcAAAFiAAAAAgAAADwAAgAAAAIAAAAEAAAABYAAAAYAAAAKgAAACAAAABEAAAAKAAAAGAAAAAwAAAAgAAAAEAAAADQAAAAUAAAASwAAABgAAABkAAAAIAAAAKAAAAArAAAA7wAAADQAAAEuAAAAOwAAAV0AAABBAAABgQAAAEcAAAGkAAAAAsAAADwAAgAAAAIAAAAEAAAABoAAAAYAAAAMAAAACAAAABQAAAAKAAAAHQAAAAwAAAAnAAAAEAAAAEEAAAAUAAAAXgAAABgAAAB/AAAAIAAAAMgAAAArAAABKAAAADQAAAFyAAAAOwAAAaQAAABBAAABywAAAEcAAAHrAAAAA4AAADwAAgAAAAKAAAAEAAAABwAAAAYAAAANgAAACAAAABeAAAAKAAAAIoAAAAwAAAAvAAAAEAAAAE8AAAAUAAAAdQAAABgAAACfAAAAIAAAAPcAAAArAAABZAAAADQAAAG3AAAAOwAAAe0AAABBAAACFQAAAEcAAAIxAAAABAAAADwAAgAAAAMAAAAEAAAACAAAAAYAAAAPgAAACAAAABsAAAAKAAAAKQAAAAwAAAA5gAAAEAAAAGMAAAAUAAAAkwAAABgAAADLAAAAIAAAATUAAAArAAABugAAADQAAAIMAAAAOwAAAj0AAABBAAACXAAAAEcAAAJxAAA== - HIDPointerAccelerationType - HIDTrackpadAcceleration - HIDScrollAccelerationTable - AACAAFVTQioABwAAAAAAAQABAAAAAQAAAAAgAAAQAABxOwAATOMAAwAAAAdgAAAEwAAADoAAAAbxSgAX6V4ACVeCACMQWgALZ6EALBF7AA2N1AA03ToAD36aADvQuAASWKAARl01ABUAAABO2dgAF8AAAFXK7QAas+UAW2FQAB2QAABe0qoAIL75AGEgywAkLXsAYnXvACewAABjRl8AAIAAABMAAHE7AABWfwABAAAAAuAAAAIAAAAJYAAAAwAAABIAAAAEwAAAIMAAAAaAAAAwgAAACGp5AEH9tgAK7bUAV4ZuAA0B2ABrPTkADv1/AIEEcAAQy5gAkdRWABJouQCe3VAAE+c/AKikCgAXAucAtn2SABot3ADARE4AHVjQAMX4OgAg35IAytuYACQ4bgDO7mUAJ6CXANIwowAAsAAAFAAAcTsAAGFOAADAAAABwAAAAQAAAANgAAACAAAADCAAAAMAAAAW4AAABMAAACnAAAAGQAAAOsAAAAfOggBP7OcACgWAAGo8rgAL658AhMCkAA2RCwCfR+EADzjIALWDbwAQ/k8AxqbEABLNUQDUeCMAFepmAOTKxwAZoVYA8qFmAB1CvwD7fXwAIQuCAQFpdAAkS20BBVKIACegAAEIK4wAAOAAABQAAHE7AABtdwAAwAAAAeAAAAEAAAAD4AAAAgAAAA6AAAADAAAAHEAAAATAAAA0AAAABgAAAEdAAAAHN4wAXL7hAAkAAACAwAAACoAAAKGAAAALz7IAvU7UAA01yQDadikADvtPAPKF4gARLu4BCSWiABUlDwEfV8QAGT9oASzRKgAdChkBNQrGACDz5QE6cR4AJEARAT3XdgAnn1YBQMHWAABQAAATAABxOwAAS7AAAQAAAAJgAAACAAAABuAAAAMAAAAMYAAABMAAABcAAAAGwAAAJAAAAAkAAAAyAAAACz1uAEFngAANcE8AUMGhAA+sTQBgSTMAEagdAG5NTAATT9YAd9WQABTQfgB+26MAF3odAIbYXQAagocAjp+lAB2J6QCTvnkAIN/yAJdcWwAkO0IAmYu9ACegOACacdcAAQAAABUAAHE7AABWfwAAwAAAAiAAAAEAAAAEgAAAAYAAAAqAAAACAAAAEaAAAAMAAAAhgAAABMAAAD6AAAAFwAAAVEAAAAbAAABrAAAACAAAAIxAAAAJAAAAqYAAAApAAADKwAAAC8AAAPTAAAANwAABJAAAABAAAAFEn9IAFEAAAV49zAAZAAABbrsYABzQAAFz78AAIOAAAXhvwAAkIAABem/AACegAAF8JGg= - HIDScrollAccelerationType - HIDTrackpadScrollAcceleration IOClass ApplePS2SynapticsTouchPad IOProbeScore @@ -181,36 +302,11 @@ Default - ButtonCount - 3 - Darwin 16+ - - ApplePreferenceCapability - - ApplePreferenceIdentifier - com.apple.AppleMultitouchTrackpad - MT Built-in - - MTHIDDevice - - SupportsGestureScrolling - - TrackpadEmbedded - - TrackpadFourFingerGestures - - TrackpadSecondaryClickCorners - - TrackpadThreeFingerDrag - - DisableDevice DisableLEDUpdating - FakeMiddleButton - - ForceTouchMode + TrackpointDeadzone 1 ForceTouchPressureThreshold 100 @@ -220,26 +316,16 @@ 20 ForceTouchCustomPower 8 - MiddleClickTime - 100000000 - MouseMiddleScroll - ProcessBluetoothMouseStopsTrackpad ProcessUSBMouseStopsTrackpad QuietTimeAfterTyping 500000000 - Resolution - 400 - ScrollResolution - 400 SkipPassThrough USBMouseStopsTrackpad 0 - UseHighRate - WakeDelay 1000 @@ -297,39 +383,25 @@ Thinkpad_ClickPad Thinkpad_ClickPad - FakeMiddleButton - FingerZ 30 HWResetOnStart - MouseMultiplierX + TrackpointMultiplierX 2 - MouseMultiplierY + TrackpointMultiplierY 2 - MouseScrollMultiplierX + TrackpointScrollMultiplierX 2 - MouseScrollMultiplierY + TrackpointScrollMultiplierY 2 - ScrollResolution - 800 - Thinkpad - Thinkpad_TrackPad - FakeMiddleButton - FingerZ 47 HWResetOnStart - Resolution - 3200 - ScrollResolution - 800 - Thinkpad - X1CG3 Thinkpad_ClickPad diff --git a/VoodooPS2Trackpad/VoodooPS2TrackpadCommon.h b/VoodooPS2Trackpad/VoodooPS2TrackpadCommon.h new file mode 100644 index 00000000..cb4fd34f --- /dev/null +++ b/VoodooPS2Trackpad/VoodooPS2TrackpadCommon.h @@ -0,0 +1,160 @@ +// +// VoodooPS2TrackpadCommon.h +// VoodooPS2Trackpad +// +// Created by Le Bao Hiep on 27/09/2020. +// Copyright © 2020 Acidanthera. All rights reserved. +// + +#ifndef VoodooPS2TrackpadCommon_h +#define VoodooPS2TrackpadCommon_h + +#define TEST_BIT(x, y) ((x >> y) & 0x1) + +void inline PS2DictSetNumber(OSDictionary *dict, const char *key, unsigned int num) { + OSNumber *val = OSNumber::withNumber(num, 32); + if (val != nullptr) { + dict->setObject(key, val); + val->release(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// 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; + } +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Force Touch Modes +// + +typedef enum { + FORCE_TOUCH_DISABLED = 0, + FORCE_TOUCH_BUTTON = 1, + FORCE_TOUCH_THRESHOLD = 2, + FORCE_TOUCH_VALUE = 3, + FORCE_TOUCH_CUSTOM = 4 +} ForceTouchMode; + +#endif /* VoodooPS2TrackpadCommon_h */