├── .github └── workflows │ ├── main.yml │ └── release.yml ├── .gitignore ├── Changelog.md ├── Frameworks └── OSD.framework │ ├── Headers │ ├── OSDManager.h │ └── OSDUIHelperProtocol.h │ └── Resources ├── LICENSE ├── README.md ├── YogaSMC.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── YogaSMC ├── Alter-Info.plist ├── Info.plist ├── KeyImplementations.cpp ├── KeyImplementations.hpp ├── SSDTSample │ ├── SSDT-ECRW.dsl │ ├── SSDT-RCSM.dsl │ ├── SSDT-THINK.dsl │ ├── SSDT-WMIS.dsl │ └── SSDT-YVPC.dsl ├── WMI.cpp ├── WMI.h ├── YogaBaseService.cpp ├── YogaBaseService.hpp ├── YogaSMC.cpp ├── YogaSMC.hpp ├── YogaSMC │ ├── DYSMC.cpp │ ├── DYSMC.hpp │ ├── IdeaSMC.cpp │ ├── IdeaSMC.hpp │ ├── ThinkSMC.cpp │ └── ThinkSMC.hpp ├── YogaSMCUserClient.cpp ├── YogaSMCUserClient.h ├── YogaSMCUserClientPrivate.hpp ├── YogaVPC.cpp ├── YogaVPC.hpp ├── YogaVPC │ ├── DYTC.h │ ├── DYVPC.cpp │ ├── DYVPC.hpp │ ├── IdeaVPC.cpp │ ├── IdeaVPC.hpp │ ├── ThinkEvents.h │ ├── ThinkVPC.cpp │ ├── ThinkVPC.hpp │ ├── YogaHIDD.cpp │ └── YogaHIDD.hpp ├── YogaWMI.cpp ├── YogaWMI.hpp ├── YogaWMI │ ├── DYWMI.cpp │ ├── DYWMI.hpp │ ├── IdeaWMI.cpp │ └── IdeaWMI.hpp ├── bmfdec.c ├── bmfparser.cpp ├── bmfparser.hpp ├── common.h └── message.h ├── YogaSMCNC ├── AppDelegate.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── Main.storyboard ├── DualFan.png ├── Info.plist ├── NotificationHandler.swift ├── Resources │ ├── AirplaneMode.pdf │ ├── Antenna.pdf │ ├── BacklightHigh.pdf │ ├── BacklightLow.pdf │ ├── BacklightOff.pdf │ ├── Bluetooth.pdf │ ├── Camera.pdf │ ├── CapslockOff.pdf │ ├── CapslockOn.pdf │ ├── Dock.pdf │ ├── FunctionKey.pdf │ ├── FunctionKeyOff.pdf │ ├── FunctionKeyOn.pdf │ ├── Keyboard.pdf │ ├── KeyboardOff.pdf │ ├── Mic.pdf │ ├── MicOff.pdf │ ├── SecondDisplay.pdf │ ├── Sleep.pdf │ ├── Star.pdf │ ├── Undock.pdf │ ├── Wifi.pdf │ ├── WifiOff.pdf │ └── YogaSMCNC.icns └── YogaSMCNC.entitlements ├── YogaSMCNCHelper ├── Info.plist └── main.swift ├── YogaSMCPane ├── Base.lproj │ └── YogaSMCPane.xib ├── General.png ├── GeneralSMCPane.swift ├── IdeaSMCPane.swift ├── Info.plist ├── ThinkSMCPane.swift ├── YogaSMCPane.icns └── YogaSMCPane.swift ├── YogaSMCUtils ├── AudioHelper.swift ├── Configuration.swift ├── DateHelper.swift ├── OSDHelper.swift ├── PropertyHelper.swift ├── RFHelper.swift ├── ScriptHelper.swift ├── SystemPreferences.swift ├── ThinkFanHelper.swift └── YogaSMC-Bridging-Header.h └── en.lproj └── Localizable.strings /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | workflow_dispatch: 14 | 15 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 16 | jobs: 17 | # This workflow contains a single job called "build" 18 | build: 19 | # The type of runner that the job will run on 20 | runs-on: macos-latest 21 | 22 | # Steps represent a sequence of tasks that will be executed as part of the job 23 | steps: 24 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 25 | - uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 0 28 | - name: Download MacKernelSDK 29 | run: git clone --depth 1 https://github.com/acidanthera/MacKernelSDK.git 30 | - name: Download Lilu / VirtualSMC SDK (script from hieplpvip/AsusSMC (Acidanthera/Lilu)) 31 | run: src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/hieplpvip/AsusSMC/master/Scripts/bootstrap.sh) && eval "$src" || exit 1 32 | - name: Xcodebuild Debug 33 | uses: sersoft-gmbh/xcodebuild-action@v3.2.0 34 | with: 35 | project: YogaSMC.xcodeproj 36 | # scheme: # optional 37 | # destination: # optional 38 | configuration: Debug 39 | build-settings: ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES=NO MACOSX_DEPLOYMENT_TARGET=10.14.4 40 | target: BuildAll 41 | action: build 42 | - name: Xcodebuild Release 43 | uses: sersoft-gmbh/xcodebuild-action@v3.2.0 44 | with: 45 | project: YogaSMC.xcodeproj 46 | # destination: # optional 47 | configuration: Release 48 | build-settings: ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES=NO MACOSX_DEPLOYMENT_TARGET=10.14.4 49 | target: BuildAll 50 | action: build 51 | - name: Prepare release image 52 | run: | 53 | mkdir build/Release-App 54 | mv build/Release/YogaSMCNC.app build/Release-App/ 55 | mv build/Release/YogaSMCPane.prefPane build/Release-App/ 56 | cp LICENSE build/Release-App/ 57 | ln -s /Applications build/Release-App/ 58 | cp -r YogaSMC/SSDTSample build/Release/ 59 | - name: Prepare debug image 60 | run: | 61 | mkdir build/Debug-App 62 | mv build/Debug/YogaSMCNC.app build/Debug-App/ 63 | mv build/Debug/YogaSMCPane.prefPane build/Debug-App/ 64 | cp LICENSE build/Debug-App/ 65 | ln -s /Applications build/Debug-App/ 66 | cp -r YogaSMC/SSDTSample build/Debug/ 67 | - name: Upload debug kext 68 | uses: actions/upload-artifact@v4 69 | with: 70 | name: YogaSMC-Debug 71 | path: | 72 | build/Debug/*.kext 73 | build/Debug/LICENSE 74 | build/Debug/SSDTSample 75 | - name: Upload release kext 76 | uses: actions/upload-artifact@v4 77 | with: 78 | name: YogaSMC-Release 79 | path: | 80 | build/Release/*.kext 81 | build/Release/LICENSE 82 | build/Release/SSDTSample 83 | - name: Create DMG image 84 | run: | 85 | brew install create-dmg 86 | create-dmg --no-internet-enable --sandbox-safe --window-size 450 450 build/YogaSMC-App-Debug.dmg build/Debug-App/ 87 | create-dmg --no-internet-enable --sandbox-safe --window-size 450 450 build/YogaSMC-App-Release.dmg build/Release-App/ 88 | - name: Upload debug app 89 | uses: actions/upload-artifact@v4 90 | with: 91 | name: YogaSMC-App-Debug 92 | path: build/YogaSMC-App-Debug.dmg 93 | - name: Upload release app 94 | uses: actions/upload-artifact@v4 95 | with: 96 | name: YogaSMC-App-Release 97 | path: build/YogaSMC-App-Release.dmg 98 | - name: Upload release dSYM 99 | uses: actions/upload-artifact@v4 100 | with: 101 | name: YogaSMC-Release-dSYM 102 | path: | 103 | build/Release/*.dSYM 104 | build/Release/LICENSE 105 | analyze: 106 | runs-on: macos-12 107 | steps: 108 | - uses: actions/checkout@v4 109 | with: 110 | fetch-depth: 0 111 | - name: Download MacKernelSDK 112 | run: git clone --depth 1 https://github.com/acidanthera/MacKernelSDK.git 113 | - name: Download Lilu / VirtualSMC SDK (script from hieplpvip/AsusSMC (Acidanthera/Lilu)) 114 | run: src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/hieplpvip/AsusSMC/master/Scripts/bootstrap.sh) && eval "$src" || exit 1 115 | - name: Xcodebuild Debug Analyze 116 | uses: sersoft-gmbh/xcodebuild-action@v3.2.0 117 | with: 118 | project: YogaSMC.xcodeproj 119 | configuration: Debug 120 | target: BuildAll 121 | action: analyze 122 | - name: Xcodebuild Release Analyze 123 | uses: sersoft-gmbh/xcodebuild-action@v3.2.0 124 | with: 125 | project: YogaSMC.xcodeproj 126 | configuration: Release 127 | target: BuildAll 128 | action: analyze 129 | - name: Prepare release image 130 | run: | 131 | mkdir build/Release-App 132 | mv build/Release/YogaSMCNC.app build/Release-App/ 133 | mv build/Release/YogaSMCPane.prefPane build/Release-App/ 134 | cp LICENSE build/Release-App/ 135 | ln -s /Applications build/Release-App/ 136 | cp -r YogaSMC/SSDTSample build/Release/ 137 | - name: Prepare debug image 138 | run: | 139 | mkdir build/Debug-App 140 | mv build/Debug/YogaSMCNC.app build/Debug-App/ 141 | mv build/Debug/YogaSMCPane.prefPane build/Debug-App/ 142 | cp LICENSE build/Debug-App/ 143 | ln -s /Applications build/Debug-App/ 144 | cp -r YogaSMC/SSDTSample build/Debug/ 145 | - name: Upload debug kext 146 | uses: actions/upload-artifact@v4 147 | with: 148 | name: YogaSMC-Debug-12 149 | path: | 150 | build/Debug/*.kext 151 | build/Debug/LICENSE 152 | build/Debug/SSDTSample 153 | - name: Upload release kext 154 | uses: actions/upload-artifact@v4 155 | with: 156 | name: YogaSMC-Release-12 157 | path: | 158 | build/Release/*.kext 159 | build/Release/LICENSE 160 | build/Release/SSDTSample 161 | - name: Create DMG image 162 | run: | 163 | brew install create-dmg 164 | create-dmg --no-internet-enable --sandbox-safe --window-size 450 450 build/YogaSMC-App-Debug-12.dmg build/Debug-App/ 165 | create-dmg --no-internet-enable --sandbox-safe --window-size 450 450 build/YogaSMC-App-Release-12.dmg build/Release-App/ 166 | - name: Upload debug app 167 | uses: actions/upload-artifact@v4 168 | with: 169 | name: YogaSMC-App-Debug-12 170 | path: build/YogaSMC-App-Debug-12.dmg 171 | - name: Upload release app 172 | uses: actions/upload-artifact@v4 173 | with: 174 | name: YogaSMC-App-Release-12 175 | path: build/YogaSMC-App-Release-12.dmg 176 | - name: Upload release dSYM 177 | uses: actions/upload-artifact@v4 178 | with: 179 | name: YogaSMC-Release-dSYM-12 180 | path: | 181 | build/Release/*.dSYM 182 | build/Release/LICENSE 183 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - "*" 6 | 7 | jobs: 8 | build: 9 | runs-on: macos-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | with: 13 | fetch-depth: 0 14 | - name: Download MacKernelSDK 15 | run: git clone --depth 1 https://github.com/acidanthera/MacKernelSDK.git 16 | - name: Download Lilu / VirtualSMC SDK (script from hieplpvip/AsusSMC (Acidanthera/Lilu)) 17 | run: src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/hieplpvip/AsusSMC/master/Scripts/bootstrap.sh) && eval "$src" || exit 1 18 | - name: Xcodebuild Debug 19 | uses: sersoft-gmbh/xcodebuild-action@v1 20 | with: 21 | project: YogaSMC.xcodeproj 22 | # scheme: # optional 23 | # destination: # optional 24 | configuration: Debug 25 | build-settings: -target BuildAll 26 | action: build 27 | - name: Xcodebuild Release 28 | uses: sersoft-gmbh/xcodebuild-action@v1 29 | with: 30 | project: YogaSMC.xcodeproj 31 | # destination: # optional 32 | configuration: Release 33 | build-settings: -target BuildAll 34 | action: build 35 | - name: Prepare debug image 36 | run: mkdir build/Debug-App; mv build/Debug/YogaSMCNC.app build/Debug-App/; mv build/Debug/YogaSMCPane.prefPane build/Debug-App/; cp LICENSE build/Debug-App/; ln -s /Applications build/Debug-App/; cp -r YogaSMC/SSDTSample build/Debug/ 37 | - name: Prepare release image 38 | run: mkdir build/Release-App; mv build/Release/YogaSMCNC.app build/Release-App/; mv build/Release/YogaSMCPane.prefPane build/Release-App/; cp LICENSE build/Release-App/; ln -s /Applications build/Release-App/; cp -r YogaSMC/SSDTSample build/Release/ 39 | - name: Prepare debug kext 40 | run: cd build/Debug; zip -X -r YogaSMC-Debug.zip *.kext LICENSE SSDTSample 41 | - name: Prepare release kext 42 | run: cd build/Release; zip -X -r YogaSMC-Release.zip *.kext LICENSE SSDTSample 43 | - name: Install create-dmg 44 | run: brew install --build-from-source create-dmg 45 | - name: Create debug DMG image 46 | uses: nick-invision/retry@v2.2.0 47 | with: 48 | timeout_minutes: 2 49 | max_attempts: 5 50 | retry_on: error 51 | command: create-dmg --window-size 450 450 build/YogaSMC-App-Debug.dmg build/Debug-App/ 52 | - name: Create release DMG image 53 | uses: nick-invision/retry@v2.2.0 54 | with: 55 | timeout_minutes: 2 56 | max_attempts: 5 57 | retry_on: error 58 | command: create-dmg --window-size 450 450 build/YogaSMC-App-Release.dmg build/Release-App/ 59 | - name: Create Release 60 | id: create_release 61 | uses: actions/create-release@v1 62 | env: 63 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 64 | with: 65 | tag_name: ${{ github.ref }} 66 | release_name: ${{ github.ref }} 67 | draft: false 68 | prerelease: false 69 | - name: Upload debug kext 70 | uses: actions/upload-release-asset@v1 71 | env: 72 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 73 | with: 74 | upload_url: ${{ steps.create_release.outputs.upload_url }} 75 | asset_name: YogaSMC-Debug.zip 76 | asset_path: build/Debug/YogaSMC-Debug.zip 77 | asset_content_type: application/zip 78 | - name: Upload release kext 79 | uses: actions/upload-release-asset@v1 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 82 | with: 83 | upload_url: ${{ steps.create_release.outputs.upload_url }} 84 | asset_name: YogaSMC-Release.zip 85 | asset_path: build/Release/YogaSMC-Release.zip 86 | asset_content_type: application/zip 87 | - name: Upload debug app 88 | uses: actions/upload-release-asset@v1 89 | env: 90 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 91 | with: 92 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 93 | asset_name: YogaSMC-App-Debug.dmg 94 | asset_path: build/YogaSMC-App-Debug.dmg 95 | asset_content_type: application/octet-stream 96 | - name: Upload release app 97 | uses: actions/upload-release-asset@v1 98 | env: 99 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 100 | with: 101 | upload_url: ${{ steps.create_release.outputs.upload_url }} 102 | asset_name: YogaSMC-App-Release.dmg 103 | asset_path: build/YogaSMC-App-Release.dmg 104 | asset_content_type: application/octet-stream 105 | - name: Prepare release dSYM 106 | run: cd build/Release; zip -X -r YogaSMC-Release-dSYM.zip *.dSYM LICENSE 107 | - name: Upload release dSYM 108 | uses: actions/upload-release-asset@v1 109 | env: 110 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 111 | with: 112 | upload_url: ${{ steps.create_release.outputs.upload_url }} 113 | asset_name: YogaSMC-Release-dSYM.zip 114 | asset_path: build/Release/YogaSMC-Release-dSYM.zip 115 | asset_content_type: application/zip 116 | 117 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ._* 3 | *.aml 4 | *.kext 5 | xcuserdata/ 6 | xcshareddata/ 7 | build/ 8 | /MacKernelSDK 9 | /Index 10 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | YogaSMC Changelog 2 | ============================ 3 | #### v1.5.2 4 | - YogaVPC: query available DYTC functions 5 | - YogaVPC: fix updateAll call hierarchy, thx @antoniomcr96 6 | - ThinkVPC: support SCPF method (DEBUG) 7 | - Pane: adjust text color for Idea variant capability 8 | - ThinkVPC: don't disable top case when Yoga mode changes with clamshell mode enabled, thx @antoniomcr96 9 | - YogaBaseService: publish VersionInfo in alter version, thx @marianopela 10 | - ThinkVPC: remove deprecated methods 11 | - YogaBaseService: rework findPNP; minor fixes 12 | - DYVPC: correct comments for command type 13 | - IdeaWMI: support an extension of Yoga mode control device 14 | - YogaWMI: simplify WMI parsing 15 | - YogaVPC: fix battery notification 16 | - IdeaVPC: simplify capabilities update 17 | - Project: lower executable target to 10.10 (drop console log) 18 | - YogaVPC: fix notification delivery 19 | - ThinkVPC: filter invalid value 20 | - YogaVPC: use evaluateIntegerParam to call methods with only 1 parameter 21 | - ThinkVPC: implement setFnLock as an override function 22 | - IdeaVPC: ignore return of SBMC and SALS 23 | - IdeaVPC: fix conservation backlight update in release build 24 | - YogaVPC: support DYTC PSC function, thx @Someone52 @1Revenger1 25 | - ThinkVPC: skip Yoga mode notification when Clamshell mode is enabled, thx @antoniomcr96 26 | - YogaWMI: temporary fix for missing qualifiers in methods 27 | - IdeaWMI: support super resolution device and assign to FnLock 28 | - YogaWMI: fix clang analysis during mof decompress 29 | - YogaBMF: return OSString from parse_string; fix remaining memory issues 30 | - YogaBMF: unify log format; add more data typpr 31 | - YogaBMF: simplify method handling; remove debug properties 32 | - YogaSMCUtils: fix broken header; rename deprecated APIs 33 | - DYVPC: validate input data size; expose WMIQuery (DEBUG) 34 | - IdeaVPC: check adapter status to isolate keyboard backlight event 35 | - YogaVPC: support setting timeout on keyboard backlight 36 | 37 | #### v1.5.1 38 | - YogaSMC: refactor updateEC 39 | - YogaWMI: fix panic in release build, thx @Chris2fourlaw 40 | - YogaWMI: postpone late start to 2s 41 | - YogaSMC: fix kernel panic when setPowerState is called before poller initialized, thx @Chris2fourlaw 42 | - IdeaVPC: add workaround to extract battery info, thx @Chris2fourlaw @SukkaW 43 | 44 | #### v1.5.0 45 | - IdeaVPC: wait for EC region init 46 | - IdeaVPC: set conservationModeLock via Battery flag; update keyboard and battery capability via reset 47 | - ThinkVPC: only turn off mic mute led during init 48 | - YogaWMI: skip TB interface by _UID 49 | - ThinkVPC: evaluate raw tablet mode, thx @antoniomcr96 50 | - ThinkVPC: support basic fan speed control through setProperties, thx @wrobeljakub 51 | - YogaBaseService: rework findPNP 52 | - YogaWMI: various improvements 53 | - YogaSMC: store preset with struct 54 | - DYVPC: initial event support 55 | - DYWMI: initial event support 56 | - DYSMC: support sensor reading, thx @jqqqqqqqqqq 57 | - YogaWMI: remove intermediate class 58 | - YogaWMI: fix duplicated loading 59 | - Project: adjust deployment target 60 | - YogaBaseService: fix logging internal name 61 | - YogaSMC: validate EC availability 62 | - YogaSMC: support read internal status 63 | - DYSMC: fix conflict with DirectECKey, thx @jqqqqqqqqqq 64 | - NC: Add Docking Station Notification Events, thx @Sniki 65 | - NC: add icons for dock / undock 66 | - DYSMC: add more sensor names 67 | - YogaWMI: replace checkEvent wth registerEvent 68 | - YogaWMI: locate BMF blob with GUID 69 | - YogaWMI: fix probe timeout on WMI-based VPC 70 | - DYSMC: validate wmis presence 71 | - DYWMI: BIOS event support 72 | - BaseService: simplify PM support check 73 | - IdeaWMI: Game Zone sensor support 74 | - YogaSMC: simplify message handling 75 | - BaseService: adjust log prefix 76 | - YogaVPC: skip tb interface with GUID 77 | - YogaVPC: optimize WMI handling 78 | - YogaWMI: switch to evaluateMethod and queryBlock 79 | - IdeaVPC: show raw battery info (DEBUG) 80 | 81 | #### v1.4.3 82 | - YogaWMI: fix BMF parsing 83 | 84 | #### v1.4.2 85 | - YogaWMI: complete event handling 86 | - NC: use timer for distinguish input method switch, thx @vnln 87 | - NC: simplify RFHelper handling 88 | - IdeaVPC: support toggle Always On USB 89 | - Project: cleanup 90 | - YogaWMI: seperate BMF validation 91 | - NC: update holiday list 92 | - ThinkVPC: debug: support keyboard locale 93 | - IdeaVPC: notify battery on conservation mode change 94 | - NC: support launchbundle, thx @simprecicchiani 95 | - Pane: display threshold for all three battery types, thx @antoniomcr96 96 | 97 | #### v1.4.1 98 | - ThinkVPC: fix LEDSupport evaluation, thx @tylernguyen 99 | - NC: add AudioHelper workaround 100 | - NC: fix holiday date, thx @1Revenger1 101 | 102 | #### v1.4.0 103 | - Pane: debug: enable RapidChargeMode checkbox 104 | - ThinkVPC: fix forgotten break in switch case, thx @junaedahmed 105 | - NC: update presets, thx @antoniomcr96 @Ab2774 106 | - NC: support localization 107 | - ThinkVPC: check LED availability 108 | - NC: support hideText for actions with customized OSD 109 | - Build: switch ARCHS to x86_64 110 | - ThinkVPC: support yoga mode detection 111 | - NC: support DisableFan option 112 | - YogaHIDD: fix support for devices without `_DSM` 113 | - YogaVPC: validate clamshellCap and updateBacklight support 114 | - YogaWMI: fix getNotifyID 115 | - IdeaWMI: fix getBatteryInfo retain count 116 | - NC: hide selected text which status is revealed by image 117 | - YogaVPC: notify timestamp for key press 118 | - NC: fix easter egg and enable it by default 119 | - YogaWMI: evaluate all possible BMF names 120 | - YogaSMC: support atomicSpDeciKelvinKey 121 | - Docs: merge SSDT samples for ThinkSMC; add suggestions for LNUX 122 | - Pane: separate different views and update when reopen 123 | - YogaSMC: support atomicSpDeciKelvinKey 124 | - NC: update resources, thx @hexart 125 | - NC: support multiple instance, specifically YogaHIDD 126 | - NC: support modifier 127 | - NC: support Caps Lock monitor 128 | - ThinkVPC: evaluate GMKS for FnLock state 129 | - NC: add launchapp action 130 | - YogaBaseService: add identifier for Alter version 131 | - NC: fix ECCap detection, thanks @buyddy 132 | - Pane: support ClamshellMode 133 | - Pane: fix battery detection 134 | - NC: improve audio status handling 135 | - NC: support SaveFanLevel 136 | - IdeaVPC: workaround for buggy event evaluation 137 | - NC: improve config readability, backup your customizations first! 138 | 139 | #### v1.3.0 140 | - Pane: fix loading prior to 10.14.4 with embedded swift runtime, thx @Charlyo 141 | - NC: update presets, thx @junaedahmed @Sniki @tylernguyen 142 | - NC: support dual fan speed reading and control for think variant, thx @1Revenger1 143 | - Pane: fix LED Automation, thx @junaedahmed 144 | - Build: fix short version display 145 | - YogaSMC: pause polling during sleep 146 | - UserClient: recover read by name and write support 147 | - Build: use commit hash as CFBundleVersion 148 | - Build: adapt to Lilu kern_version 149 | - NC: add easter egg for menubar icon 150 | - Pane: support customized menubar title 151 | - YogaVPC: handle WMI probing 152 | - NC: optimize menu handling 153 | - NC: override unknown events with updated presets 154 | - BaseService: support extended EC operation 155 | - YogaSMC: add constants for fan support 156 | - NC: Using cached wireless status, thx @H15teve 157 | - YogaHIDD: experimental support for Intel HID event & 5 button array driver 158 | - BaseService: support sending key to primary keyboard and palm rejection 159 | - IdeaVPC: poll hotkey status with buggy EC notification with `-ideabr`, thx @moutorde 160 | - Pane: phase out muteCheck for release branch 161 | - ThinkVPC: fix legacy hotkey mask update, thx @junaedahmed 162 | 163 | Change of earlier versions may be tracked in [commits](https://github.com/zhen-zen/YogaSMC/commits/master) and [releases](https://github.com/zhen-zen/YogaSMC/releases). 164 | -------------------------------------------------------------------------------- /Frameworks/OSD.framework/Headers/OSDManager.h: -------------------------------------------------------------------------------- 1 | #import "OSDUIHelperProtocol.h" 2 | 3 | @class NSXPCConnection; 4 | 5 | @interface OSDManager : NSObject 6 | { 7 | id _proxyObject; 8 | NSXPCConnection *connection; 9 | } 10 | 11 | + (id)sharedManager; 12 | @property(retain) NSXPCConnection *connection; // @synthesize connection; 13 | - (void)showFullScreenImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecToAnimate:(unsigned int)arg4; 14 | - (void)fadeClassicImageOnDisplay:(unsigned int)arg1; 15 | - (void)showImageAtPath:(id)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(id)arg5; 16 | - (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 filledChiclets:(unsigned int)arg5 totalChiclets:(unsigned int)arg6 locked:(BOOL)arg7; 17 | - (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(id)arg5; 18 | - (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4; 19 | @property(readonly) id remoteObjectProxy; // @dynamic remoteObjectProxy; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Frameworks/OSD.framework/Headers/OSDUIHelperProtocol.h: -------------------------------------------------------------------------------- 1 | @class NSString; 2 | 3 | @protocol OSDUIHelperProtocol 4 | - (void)showFullScreenImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecToAnimate:(unsigned int)arg4; 5 | - (void)fadeClassicImageOnDisplay:(unsigned int)arg1; 6 | - (void)showImageAtPath:(NSString *)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(NSString *)arg5; 7 | - (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 filledChiclets:(unsigned int)arg5 totalChiclets:(unsigned int)arg6 locked:(BOOL)arg7; 8 | - (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(NSString *)arg5; 9 | - (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4; 10 | @end 11 | 12 | -------------------------------------------------------------------------------- /Frameworks/OSD.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YogaSMC ![CI](https://github.com/zhen-zen/YogaSMC/workflows/CI/badge.svg) [![Join the chat at https://gitter.im/YogaSMC/community](https://badges.gitter.im/YogaSMC/community.svg)](https://gitter.im/YogaSMC/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | 3 | This driver consists of YogaSMC, YogaWMI and YogaVPC. 4 | 5 | Each component can be derived for different targets. Currently ThinkPad and IdeaPad series (all other consumer brands) are supported. Support for generic Intel HID event & 5 button array, and HP system (both laptop and desktop, requires vanilla EC) is experimental. 6 | 7 | Command to driver can be sent with [ioio](https://github.com/RehabMan/OS-X-ioio), e.g. `ioio -s IdeaVPC ConservationMode true`. 8 | 9 | The driver will update the status in ioreg, while details are available in system log, e.g. `log stream --predicate 'senderImagePath contains "YogaSMC"'`. 10 | 11 | Companion userspace apps, YogaSMCPane and YogaSMCNC are also available with GUI configuration and notification service. 12 | 13 | ## YogaSMC 14 | Allow syncing SMC keys like sensors reading and battery conservation mode. 15 | 16 | Based on [acidanthera/VirtualSMC](https://github.com/acidanthera/VirtualSMC/) 17 | 18 | ### Customized sensor reading 19 | The EC field name for corresponding SMC key is read from Info.plist. If there's no `FieldUnit` object at desired offset, you can add an `OperationRegion` like `SSDT-THINK.dsl` in `SSDTSample`. 20 | 21 | | Variant | IdeaSMC | IdeaSMC (Game Zone) | DYSMC | 22 | | ---- | ---- | ---- | ---- | 23 | | Fan reading | Need testing | ✅ | ✅ | 24 | | Fan control | ☑️ | TBD | TBD | 25 | | Sensor reading | Generic | ✅ | ✅ | 26 | 27 | ## YogaWMI 28 | Support for parsing WMI devices and properties. On some devices, it could act as YogaVPC with access to extensive device control method. 29 | 30 | (For Thunderbolt WMI interface, see [al3xtjames/ThunderboltPkg](https://github.com/al3xtjames/ThunderboltPkg) instead.) 31 | 32 | Based on [the-darkvoid/macOS-IOElectrify](https://github.com/the-darkvoid/macOS-IOElectrify/) ([Dolnor/IOWMIFamily](https://github.com/Dolnor/IOWMIFamily/)) and [bmfparser](https://github.com/zhen-zen/bmfparser) ([pali/bmfdec](https://github.com/pali/bmfdec)) 33 | 34 | ### DYWMI 35 | - `WMIS` Sensor reading, check `SSDT-WMIS.dsl` in `SSDTSample` if the system is affected 36 | - `WMIV` Event driver, see DYVPC 37 | 38 | ### IdeaWMI 39 | - `GZFD` Game Zone control center, see header for available functions 40 | - `WBAT` Extra battery information (requires patching related methods like battery ones) 41 | - `WMI2` Fn+esc (obsolete paper looking function), currently assigned to Fn mode toggle 42 | - `WMIS` Fn+S (super resolution function), currently assigned to Fn mode toggle 43 | - `WMIY` (`AMW1`) Yoga Mode detection and disabling keyboard/touchpad when flipped 44 | 45 | ### ThinkWMI (WIP) 46 | ~~Based on [lenovo/thinklmi](https://github.com/lenovo/thinklmi) ([iksaif/thinkpad-wmi](https://github.com/iksaif/thinkpad-wmi))~~ 47 | 48 | ## YogaVPC 49 | Intercepting events on vendor-specific Virtual Power Controller (VPC) devices and sync states, some instructions are on [project boards](https://github.com/zhen-zen/YogaSMC/projects/). 50 | 51 | Currently available functions: 52 | - EC reading 53 | - DYTC setting (available for idea/think, might need appropriate OS version for XOSI) 54 | - Automatic backlight and LED control 55 | - Clamshell mode (need additional patch on `_LID` like `SSDT-RCSM.dsl` in `SSDTSample`) 56 | 57 | | Variant | IdeaVPC | ThinkVPC | YogaHIDD | DYSMC | 58 | | ---- | ---- | ---- | ---- | ---- | 59 | | `_HID` | `VPC2004` | `LEN0268`
`LEN0068` | `INT33D5`
`INTC1051` | (`WMIV`) | 60 | | Reference | [ideapad-laptop](https://github.com/torvalds/linux/blob/master/drivers/platform/x86/ideapad-laptop.c) | [thinkpad_acpi](https://github.com/torvalds/linux/blob/master/drivers/platform/x86/thinkpad_acpi.c) | [intel-hid](https://github.com/torvalds/linux/blob/master/drivers/platform/x86/intel-hid.c) | [hp-wmi](https://github.com/torvalds/linux/blob/master/drivers/platform/x86/hp-wmi.c) | 61 | | Hotkey polling | ✅ | ✅ | ✅ | ☑️ | 62 | | Conservation mode | ✅ | ✅ | N/A | TBD | 63 | | Battery threshold | Not supported | ✅ | N/A | TBD | 64 | | Charging control | Need testing | Need testing | N/A | TBD | 65 | | DYTC | ✅ | ✅ | N/A | N/A | 66 | | Fan reading | Need testing | ✅ | N/A | SMC | 67 | | Fan control | Need testing | ✅ | N/A | TBD | 68 | | Fn lock mode | ✅ | Native | N/A | TBD | 69 | | LED control | Not supported | ✅ | N/A | TBD | 70 | | Keyboard backlight | ✅ | ✅ | N/A | TBD | 71 | 72 | ### EC reading: 73 | When [Rehabman's](https://www.tonymacx86.com/threads/guide-how-to-patch-dsdt-for-working-battery-status.116102/) battery patching method `RE1B` `RECB` present (or `SSDT-ECRW.dsl` in `SSDTSample`), desired EC fields can be read using following commands: 74 | 75 | - One byte at specific offset: `ioio -s YogaVPC ReadECOffset 0xA4` for field at offset `0xA4` 76 | - Bulk reading: `ioio -s YogaVPC ReadECOffset 0x1006` for `0x10` bytes at offset `0x06` (add total bytes to read before offset) 77 | - Dump whole EC area: `ioio -s YogaVPC ReadECOffset 0x10000` 78 | - Known EC field name: `ioio -s YogaVPC ReadECName B1CY` (no larger than 1 byte due to OS constraint) 79 | 80 | ## YogaSMCPane 81 | The preference pane provides a graphical user interface for basic information and settings, such as battery conservation mode and backlight. 82 | 83 | 84 | 85 | ## YogaSMCNC 86 | The notification application receives EC events and displays them on OSD. Corresonding actions will also be triggered for function keys. The configuration can be customized at `~/Library/Preferences/org.zhen.YogaSMC.plist` after closing the app. 87 | 88 | 89 | 90 | Only a few models support dual fan reading and control, which could be enabled manually via debug prefpane or `SecondThinkFan` in preference plist. 91 | 92 | For unknown events in preset, feel free to submit a PR like [#40](https://github.com/zhen-zen/YogaSMC/pull/40). 93 | 94 | If you want to add new actions, the easiest approach is to use the `script` action and fill the AppleScript in `option` field. [be295da](https://github.com/zhen-zen/YogaSMC/commit/be295dad333866cf23466d7e068354bc4c1f02ea) is a good example to add it as a built-in action, which may be replaced with native one later. 95 | 96 | ## Installation 97 | The kext should work out-of-the-box. If you have modified `_QXX` methods before, please remove the patches. 98 | 99 | Some features may rely on methods accessing EC, please consider [ECEnabler](https://github.com/1Revenger1/ECEnabler) for EC fields larger than 8-bits. 100 | 101 | The `YogaSMCAlter.kext` is a variant without SMC keys support and the dependencies of `Lilu` and `VirtualSMC`. It's designed for quick loading / unloading without reboot when debugging. 102 | 103 | ## Building 104 | 1. Copy latest debug version of [Lilu.kext](https://github.com/acidanthera/Lilu/releases/latest) and [VirtualSMC.kext](https://github.com/acidanthera/VirtualSMC/releases/latest) into the folder 105 | 2. `git clone --depth 1 https://github.com/acidanthera/MacKernelSDK` 106 | 3. In Xcode, Select build target on upper left and click the button on the left 107 | 108 | ## Credits 109 | - [Apple](https://www.apple.com) for macOS 110 | - [Linux](https://www.linux.org) for [ideapad-laptop](https://github.com/torvalds/linux/blob/master/drivers/platform/x86/ideapad-laptop.c) and [thinkpad-acpi](https://github.com/torvalds/linux/blob/master/drivers/platform/x86/thinkpad_acpi.c) kernel module 111 | - [RehabMan](https://github.com/RehabMan) for [OS-X-Voodoo-PS2-Controller](https://github.com/RehabMan/OS-X-Voodoo-PS2-Controller), [OS-X-ACPI-Debug](https://github.com/RehabMan/OS-X-ACPI-Debug), [OS-X-ioio](https://github.com/RehabMan/OS-X-ioio) and DSDT patches 112 | - [vit9696](https://github.com/vit9696) for [VirtualSMC](https://github.com/acidanthera/VirtualSMC) 113 | - [the-darkvoid](https://github.com/the-darkvoid) for [macOS-IOElectrify](https://github.com/the-darkvoid/macOS-IOElectrify) 114 | - [pali](https://github.com/pali) for [bmfdec](https://github.com/pali/bmfdec) 115 | - [benbender](https://github.com/benbender), [1Revenger1](https://github.com/1Revenger1) and other contributors for testing and feedback 116 | -------------------------------------------------------------------------------- /YogaSMC.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YogaSMC/Alter-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(MODULE_VERSION) 19 | CFBundleVersion 20 | $(MODULE_VERSION) 21 | IOKitPersonalities 22 | 23 | DYVPC 24 | 25 | CFBundleIdentifier 26 | $(PRODUCT_BUNDLE_IDENTIFIER) 27 | IOClass 28 | DYVPC 29 | IONameMatch 30 | PNP0C14 31 | IOProbeScore 32 | 200 33 | IOProviderClass 34 | IOACPIPlatformDevice 35 | IOUserClientClass 36 | YogaSMCUserClient 37 | 38 | IdeaVPC 39 | 40 | CFBundleIdentifier 41 | $(PRODUCT_BUNDLE_IDENTIFIER) 42 | IOClass 43 | IdeaVPC 44 | IONameMatch 45 | VPC2004 46 | IOProbeScore 47 | 200 48 | IOProviderClass 49 | IOACPIPlatformDevice 50 | IOUserClientClass 51 | YogaSMCUserClient 52 | 53 | ThinkVPC 54 | 55 | CFBundleIdentifier 56 | $(PRODUCT_BUNDLE_IDENTIFIER) 57 | IOClass 58 | ThinkVPC 59 | IOProbeScore 60 | 200 61 | IOPropertyMatch 62 | 63 | 64 | name 65 | LEN0268 66 | 67 | 68 | name 69 | LEN0068 70 | 71 | 72 | IOProviderClass 73 | IOACPIPlatformDevice 74 | IOUserClientClass 75 | YogaSMCUserClient 76 | 77 | YogaHIDD 78 | 79 | CFBundleIdentifier 80 | $(PRODUCT_BUNDLE_IDENTIFIER) 81 | IOClass 82 | YogaHIDD 83 | IOProbeScore 84 | 200 85 | IOPropertyMatch 86 | 87 | 88 | name 89 | INT33D5 90 | 91 | 92 | name 93 | INTC1051 94 | 95 | 96 | name 97 | INTC1054 98 | 99 | 100 | IOProviderClass 101 | IOACPIPlatformDevice 102 | IOUserClientClass 103 | YogaSMCUserClient 104 | 105 | YogaVPC 106 | 107 | CFBundleIdentifier 108 | $(PRODUCT_BUNDLE_IDENTIFIER) 109 | IOClass 110 | YogaVPC 111 | IONameMatch 112 | VPC0000 113 | IOProbeScore 114 | 200 115 | IOProviderClass 116 | IOACPIPlatformDevice 117 | IOUserClientClass 118 | YogaSMCUserClient 119 | 120 | YogaWMI 121 | 122 | CFBundleIdentifier 123 | $(PRODUCT_BUNDLE_IDENTIFIER) 124 | IOClass 125 | YogaWMI 126 | IONameMatch 127 | PNP0C14 128 | IOProbeScore 129 | 100 130 | IOProviderClass 131 | IOACPIPlatformDevice 132 | 133 | 134 | NSHumanReadableCopyright 135 | Copyright © 2020 Zhen. All rights reserved. 136 | OSBundleLibraries 137 | 138 | com.apple.iokit.IOACPIFamily 139 | 1.0.0d1 140 | com.apple.kpi.bsd 141 | 8.0.0 142 | com.apple.kpi.iokit 143 | 8.0.0 144 | com.apple.kpi.libkern 145 | 8.0.0 146 | com.apple.kpi.mach 147 | 8.0.0 148 | com.apple.kpi.unsupported 149 | 8.0.0 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /YogaSMC/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(MODULE_VERSION) 19 | CFBundleVersion 20 | $(MODULE_VERSION) 21 | IOKitPersonalities 22 | 23 | DYVPC 24 | 25 | CFBundleIdentifier 26 | $(PRODUCT_BUNDLE_IDENTIFIER) 27 | IOClass 28 | DYVPC 29 | IONameMatch 30 | PNP0C14 31 | IOProbeScore 32 | 200 33 | IOProviderClass 34 | IOACPIPlatformDevice 35 | IOUserClientClass 36 | YogaSMCUserClient 37 | 38 | DYSMC 39 | 40 | CFBundleIdentifier 41 | $(PRODUCT_BUNDLE_IDENTIFIER) 42 | IOClass 43 | DYSMC 44 | IOProbeScore 45 | 100 46 | IOProviderClass 47 | DYVPC 48 | Sensors 49 | 50 | Airflow Left 51 | 52 | Airflow Right 53 | 54 | Battery 55 | 56 | Battery Sensor 1 57 | 58 | Battery Sensor 2 59 | 60 | CPU Core PECI 61 | 62 | CPU System Agent Core 63 | 64 | Memory Proximity 65 | 66 | Platform Controller Hub Die 67 | 68 | SO-DIMM 1 Proximity 69 | 70 | SO-DIMM 2 Proximity 71 | 72 | SO-DIMM 3 Proximity 73 | 74 | SO-DIMM 4 Proximity 75 | 76 | 77 | 78 | IdeaVPC 79 | 80 | CFBundleIdentifier 81 | $(PRODUCT_BUNDLE_IDENTIFIER) 82 | IOClass 83 | IdeaVPC 84 | IONameMatch 85 | VPC2004 86 | IOProbeScore 87 | 200 88 | IOProviderClass 89 | IOACPIPlatformDevice 90 | IOUserClientClass 91 | YogaSMCUserClient 92 | 93 | IdeaSMC 94 | 95 | CFBundleIdentifier 96 | $(PRODUCT_BUNDLE_IDENTIFIER) 97 | IOClass 98 | IdeaSMC 99 | IOProbeScore 100 | 100 101 | IOProviderClass 102 | IdeaVPC 103 | Sensors 104 | 105 | Airflow Left 106 | 107 | Airflow Right 108 | 109 | Battery 110 | 111 | Battery Sensor 1 112 | TBAT 113 | Battery Sensor 2 114 | 115 | CPU Core PECI 116 | CPEC 117 | CPU System Agent Core 118 | CTMP 119 | Memory Proximity 120 | MEM1 121 | Platform Controller Hub Die 122 | RSEN 123 | SO-DIMM 1 Proximity 124 | MEM1 125 | SO-DIMM 2 Proximity 126 | MEM2 127 | SO-DIMM 3 Proximity 128 | 129 | SO-DIMM 4 Proximity 130 | 131 | 132 | 133 | ThinkVPC 134 | 135 | CFBundleIdentifier 136 | $(PRODUCT_BUNDLE_IDENTIFIER) 137 | IOClass 138 | ThinkVPC 139 | IOProbeScore 140 | 200 141 | IOPropertyMatch 142 | 143 | 144 | name 145 | LEN0268 146 | 147 | 148 | name 149 | LEN0068 150 | 151 | 152 | IOProviderClass 153 | IOACPIPlatformDevice 154 | IOUserClientClass 155 | YogaSMCUserClient 156 | 157 | ThinkSMC 158 | 159 | CFBundleIdentifier 160 | $(PRODUCT_BUNDLE_IDENTIFIER) 161 | IOClass 162 | ThinkSMC 163 | IOProbeScore 164 | 100 165 | IOProviderClass 166 | ThinkVPC 167 | Sensors 168 | 169 | Airflow Left 170 | 171 | Airflow Right 172 | 173 | Battery 174 | 175 | Battery Sensor 1 176 | 177 | Battery Sensor 2 178 | 179 | CPU Core PECI 180 | 181 | CPU System Agent Core 182 | TMP0 183 | Memory Proximity 184 | 185 | Platform Controller Hub Die 186 | ESTA 187 | SO-DIMM 1 Proximity 188 | 189 | SO-DIMM 2 Proximity 190 | 191 | SO-DIMM 3 Proximity 192 | 193 | SO-DIMM 4 Proximity 194 | 195 | 196 | 197 | YogaHIDD 198 | 199 | CFBundleIdentifier 200 | $(PRODUCT_BUNDLE_IDENTIFIER) 201 | IOClass 202 | YogaHIDD 203 | IOProbeScore 204 | 200 205 | IOPropertyMatch 206 | 207 | 208 | name 209 | INT33D5 210 | 211 | 212 | name 213 | INTC1051 214 | 215 | 216 | name 217 | INTC1054 218 | 219 | 220 | IOProviderClass 221 | IOACPIPlatformDevice 222 | IOUserClientClass 223 | YogaSMCUserClient 224 | 225 | YogaVPC 226 | 227 | CFBundleIdentifier 228 | $(PRODUCT_BUNDLE_IDENTIFIER) 229 | IOClass 230 | YogaVPC 231 | IONameMatch 232 | VPC0000 233 | IOProbeScore 234 | 200 235 | IOProviderClass 236 | IOACPIPlatformDevice 237 | IOUserClientClass 238 | YogaSMCUserClient 239 | 240 | YogaWMI 241 | 242 | CFBundleIdentifier 243 | $(PRODUCT_BUNDLE_IDENTIFIER) 244 | IOClass 245 | YogaWMI 246 | IONameMatch 247 | PNP0C14 248 | IOProbeScore 249 | 100 250 | IOProviderClass 251 | IOACPIPlatformDevice 252 | 253 | 254 | NSHumanReadableCopyright 255 | Copyright © 2020 Zhen. All rights reserved. 256 | OSBundleLibraries 257 | 258 | as.vit9696.Lilu 259 | 1.4.5 260 | as.vit9696.VirtualSMC 261 | 1.1.4 262 | com.apple.iokit.IOACPIFamily 263 | 1.0.0d1 264 | com.apple.kpi.bsd 265 | 8.0.0 266 | com.apple.kpi.iokit 267 | 8.0.0 268 | com.apple.kpi.libkern 269 | 8.0.0 270 | com.apple.kpi.mach 271 | 8.0.0 272 | com.apple.kpi.unsupported 273 | 8.0.0 274 | 275 | 276 | 277 | -------------------------------------------------------------------------------- /YogaSMC/KeyImplementations.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // KeyImplementations.cpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 7/28/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | #include "KeyImplementations.hpp" 10 | 11 | SMC_RESULT BDVT::writeAccess() { 12 | if (!drv) 13 | return SmcNotWritable; 14 | return SmcSuccess; 15 | } 16 | 17 | SMC_RESULT BDVT::update(const SMC_DATA *src) { 18 | bool newValue = *(reinterpret_cast(src)); 19 | bool *oldValue = reinterpret_cast(data); 20 | DBGLOG("vpckey", "BDVT update %d -> %d", *oldValue, newValue); 21 | drv->dispatchMessage(kSMC_setConservation, &newValue); 22 | *oldValue = newValue; 23 | return SmcSuccess; 24 | } 25 | 26 | SMC_RESULT CH0B::writeAccess() { 27 | // TODO: determine whether charger control is supported 28 | return SmcSuccess; 29 | } 30 | 31 | SMC_RESULT CH0B::update(const SMC_DATA *src) { 32 | DBGLOG("vpckey", "CH0B update 0x%x -> 0x%x", data[0], src[0]); 33 | lilu_os_memcpy(data, src, 1); 34 | return SmcSuccess; 35 | } 36 | 37 | SMC_RESULT atomicSpKey::readAccess() { 38 | uint32_t value = atomic_load_explicit(currentSensor, memory_order_acquire); 39 | *reinterpret_cast(data) = VirtualSMCAPI::encodeSp(type, value); 40 | return SmcSuccess; 41 | } 42 | 43 | SMC_RESULT atomicSpDeciKelvinKey::readAccess() { 44 | uint32_t value = atomic_load_explicit(currentSensor, memory_order_acquire); 45 | *reinterpret_cast(data) = VirtualSMCAPI::encodeSp(type, ((double)value - 2731)/10); 46 | return SmcSuccess; 47 | } 48 | 49 | SMC_RESULT atomicFltKey::readAccess() { 50 | uint32_t value = atomic_load_explicit(currentSensor, memory_order_acquire); 51 | *reinterpret_cast(data) = VirtualSMCAPI::encodeFlt(value); 52 | return SmcSuccess; 53 | } 54 | 55 | SMC_RESULT atomicFpKey::readAccess() { 56 | uint16_t value = atomic_load_explicit(currentSensor, memory_order_acquire); 57 | *reinterpret_cast(data) = VirtualSMCAPI::encodeIntFp(type, value); 58 | return SmcSuccess; 59 | } 60 | -------------------------------------------------------------------------------- /YogaSMC/KeyImplementations.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // KeyImplementations.hpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 7/28/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef KeyImplementations_hpp 10 | #define KeyImplementations_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include "YogaBaseService.hpp" 16 | 17 | #define addECKeySp(key, name, type) \ 18 | do { \ 19 | if (sensorCount < MAX_SENSOR && (method = OSDynamicCast(OSString, conf->getObject(name))) && (method->getLength() == 4)) { \ 20 | if (ec->validateObject(method->getCStringNoCopy()) == kIOReturnSuccess) { \ 21 | atomic_init(¤tSensor[sensorCount], 0); \ 22 | VirtualSMCAPI::addKey(key, vsmcPlugin.data, VirtualSMCAPI::valueWithSp(0, SmcKeyTypeSp78, new type(¤tSensor[sensorCount]))); \ 23 | sensorMethods[sensorCount++] = method->getCStringNoCopy(); \ 24 | status->setObject(name, kOSBooleanTrue); \ 25 | } else { \ 26 | status->setObject(name, kOSBooleanFalse); \ 27 | } \ 28 | } \ 29 | } while (0) 30 | 31 | struct sensorPair { 32 | const SMC_KEY key; 33 | const char *name; 34 | }; 35 | 36 | static constexpr const char *KeyIndexes = "0123456789ABCDEF"; 37 | 38 | static constexpr SMC_KEY KeyBDVT = SMC_MAKE_IDENTIFIER('B','D','V','T'); 39 | static constexpr SMC_KEY KeyCH0B = SMC_MAKE_IDENTIFIER('C','H','0','B'); 40 | static constexpr SMC_KEY KeyTA0P(size_t i) { return SMC_MAKE_IDENTIFIER('T','A',KeyIndexes[i],'P');}; 41 | static constexpr SMC_KEY KeyTB0T(size_t i) { return SMC_MAKE_IDENTIFIER('T','B',KeyIndexes[i],'T'); } 42 | static constexpr SMC_KEY KeyTCGC = SMC_MAKE_IDENTIFIER('T','C','G','C'); 43 | static constexpr SMC_KEY KeyTCHP = SMC_MAKE_IDENTIFIER('T','C','H','P'); 44 | static constexpr SMC_KEY KeyTCSA = SMC_MAKE_IDENTIFIER('T','C','S','A'); 45 | static constexpr SMC_KEY KeyTCXC = SMC_MAKE_IDENTIFIER('T','C','X','C'); 46 | static constexpr SMC_KEY KeyTG0P(size_t i) { return SMC_MAKE_IDENTIFIER('T','G',KeyIndexes[i],'P');}; 47 | static constexpr SMC_KEY KeyTH0P(size_t i) { return SMC_MAKE_IDENTIFIER('T','H',KeyIndexes[i],'P');}; 48 | static constexpr SMC_KEY KeyTH0a = SMC_MAKE_IDENTIFIER('T','H','0','a'); 49 | static constexpr SMC_KEY KeyTH0b = SMC_MAKE_IDENTIFIER('T','H','0','b'); 50 | static constexpr SMC_KEY KeyTM0P = SMC_MAKE_IDENTIFIER('T','M','0','P'); 51 | static constexpr SMC_KEY KeyTM0p(size_t i) { return SMC_MAKE_IDENTIFIER('T','M',KeyIndexes[i],'p'); } 52 | static constexpr SMC_KEY KeyTPCD = SMC_MAKE_IDENTIFIER('T','P','C','D'); 53 | static constexpr SMC_KEY KeyTTRD = SMC_MAKE_IDENTIFIER('T','T','R','D'); 54 | static constexpr SMC_KEY KeyTW0P = SMC_MAKE_IDENTIFIER('T','W','0','P'); 55 | static constexpr SMC_KEY KeyTaLC = SMC_MAKE_IDENTIFIER('T','a','L','C'); 56 | static constexpr SMC_KEY KeyTaRC = SMC_MAKE_IDENTIFIER('T','a','R','C'); 57 | static constexpr SMC_KEY KeyTh0H(size_t i) { return SMC_MAKE_IDENTIFIER('T','h',KeyIndexes[i],'H'); } 58 | static constexpr SMC_KEY KeyTs0P(size_t i) { return SMC_MAKE_IDENTIFIER('T','s',KeyIndexes[i],'P'); } 59 | 60 | // Fan related keys, from SMCDellSensors 61 | 62 | static constexpr SMC_KEY KeyFNum = SMC_MAKE_IDENTIFIER('F','N','u','m'); // Number of supported fans 63 | static constexpr SMC_KEY KeyF0Ac(size_t i) { return SMC_MAKE_IDENTIFIER('F',KeyIndexes[i],'A','c'); } // Actual RPM 64 | static constexpr SMC_KEY KeyF0ID(size_t i) { return SMC_MAKE_IDENTIFIER('F',KeyIndexes[i],'I','D'); } // Description 65 | static constexpr SMC_KEY KeyF0Md(size_t i) { return SMC_MAKE_IDENTIFIER('F',KeyIndexes[i],'M','d'); } // Manual Mode (New) 66 | static constexpr SMC_KEY KeyF0Mn(size_t i) { return SMC_MAKE_IDENTIFIER('F',KeyIndexes[i],'M','n'); } // Minimum RPM 67 | static constexpr SMC_KEY KeyF0Mx(size_t i) { return SMC_MAKE_IDENTIFIER('F',KeyIndexes[i],'M','x'); } // Maximum RPM 68 | static constexpr SMC_KEY KeyF0Sf(size_t i) { return SMC_MAKE_IDENTIFIER('F',KeyIndexes[i],'S','f'); } // Safe RPM 69 | static constexpr SMC_KEY KeyF0Tg(size_t i) { return SMC_MAKE_IDENTIFIER('F',KeyIndexes[i],'T','g'); } // Target speed 70 | static constexpr SMC_KEY KeyFS__ = SMC_MAKE_IDENTIFIER('F','S','!',' '); // Fan force bits. FS![15:0] Setting bit to 1 allows for external control over fan speed target and prevents thermal manager from actively overidding value set via key access. 71 | 72 | typedef enum { FAN_PWM_TACH, FAN_RPM, PUMP_PWM, PUMP_RPM, FAN_PWM_NOTACH, EMPTY_PLACEHOLDER } FanType; 73 | 74 | typedef enum { 75 | LEFT_LOWER_FRONT, CENTER_LOWER_FRONT, RIGHT_LOWER_FRONT, 76 | LEFT_MID_FRONT, CENTER_MID_FRONT, RIGHT_MID_FRONT, 77 | LEFT_UPPER_FRONT, CENTER_UPPER_FRONT, RIGHT_UPPER_FRONT, 78 | LEFT_LOWER_REAR, CENTER_LOWER_REAR, RIGHT_LOWER_REAR, 79 | LEFT_MID_REAR, CENTER_MID_REAR, RIGHT_MID_REAR, 80 | LEFT_UPPER_REAR, CENTER_UPPER_REAR, RIGHT_UPPER_REAR 81 | } LocationType; 82 | 83 | static constexpr int32_t DiagFunctionStrLen = 12; 84 | 85 | typedef struct fanTypeDescStruct { 86 | UInt8 type {FAN_PWM_TACH}; 87 | UInt8 ui8Zone {1}; 88 | UInt8 location {LEFT_MID_REAR}; 89 | UInt8 rsvd {0}; // padding to get us to 16 bytes 90 | char strFunction[DiagFunctionStrLen]; 91 | } FanTypeDescStruct; 92 | 93 | class atomicSpKey : public VirtualSMCValue { 94 | _Atomic(uint32_t) *currentSensor; 95 | protected: 96 | SMC_RESULT readAccess() override; 97 | public: 98 | atomicSpKey(_Atomic(uint32_t) *currentSensor) : currentSensor(currentSensor) {}; 99 | }; 100 | 101 | class atomicSpDeciKelvinKey : public VirtualSMCValue { 102 | _Atomic(uint32_t) *currentSensor; 103 | protected: 104 | SMC_RESULT readAccess() override; 105 | public: 106 | atomicSpDeciKelvinKey(_Atomic(uint32_t) *currentSensor) : currentSensor(currentSensor) {}; 107 | }; 108 | 109 | class atomicFltKey : public VirtualSMCValue { 110 | _Atomic(uint32_t) *currentSensor; 111 | protected: 112 | SMC_RESULT readAccess() override; 113 | public: 114 | atomicFltKey(_Atomic(uint32_t) *currentSensor) : currentSensor(currentSensor) {}; 115 | }; 116 | 117 | class atomicFpKey : public VirtualSMCValue { 118 | _Atomic(uint32_t) *currentSensor; 119 | protected: 120 | SMC_RESULT readAccess() override; 121 | public: 122 | atomicFpKey(_Atomic(uint32_t) *currentSensor) : currentSensor(currentSensor) {}; 123 | }; 124 | 125 | class messageKey : public VirtualSMCValue { 126 | protected: 127 | YogaBaseService *drv; 128 | public: 129 | messageKey(YogaBaseService *src=nullptr) : drv(src) {}; 130 | }; 131 | 132 | class BDVT : public messageKey { using messageKey::messageKey; protected: SMC_RESULT writeAccess() override; SMC_RESULT update(const SMC_DATA *src) override;}; 133 | class CH0B : public VirtualSMCValue { protected: SMC_RESULT writeAccess() override; SMC_RESULT update(const SMC_DATA *src) override;}; 134 | 135 | #endif /* KeyImplementations_hpp */ 136 | -------------------------------------------------------------------------------- /YogaSMC/SSDTSample/SSDT-ECRW.dsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Sample SSDT for EC read / write access 3 | * 4 | * Check for conflict if you have patched battery reading 5 | * Taken from RehabMan's battery patch guide: 6 | * https://www.tonymacx86.com/threads/guide-how-to-patch-dsdt-for-working-battery-status.116102/ 7 | */ 8 | DefinitionBlock ("", "SSDT", 2, "hack", "ECRW", 0x00000000) 9 | { 10 | External (_SB_.PCI0.LPCB.H_EC, DeviceObj) // EC path 11 | External (_SB_.PCI0.LPCB.H_EC.BAT1, DeviceObj) // Battery path 12 | 13 | Scope (_SB.PCI0.LPCB.H_EC) 14 | { 15 | Method (RE1B, 1, NotSerialized) 16 | { 17 | OperationRegion (ERAM, EmbeddedControl, Arg0, One) 18 | Field (ERAM, ByteAcc, NoLock, Preserve) 19 | { 20 | BYTE, 8 21 | } 22 | 23 | Return (BYTE) 24 | } 25 | 26 | Method (RECB, 2, Serialized) 27 | { 28 | Arg1 = ((Arg1 + 0x07) >> 0x03) 29 | Name (TEMP, Buffer (Arg1) {}) 30 | Arg1 += Arg0 31 | Local0 = Zero 32 | While ((Arg0 < Arg1)) 33 | { 34 | TEMP [Local0] = RE1B (Arg0) 35 | Arg0++ 36 | Local0++ 37 | } 38 | 39 | Return (TEMP) 40 | } 41 | 42 | Method (WE1B, 2, NotSerialized) 43 | { 44 | OperationRegion (ERAM, EmbeddedControl, Arg0, One) 45 | Field (ERAM, ByteAcc, NoLock, Preserve) 46 | { 47 | BYTE, 8 48 | } 49 | 50 | BYTE = Arg1 51 | } 52 | 53 | Method (WECB, 3, Serialized) 54 | { 55 | Arg1 = ((Arg1 + 0x07) >> 0x03) 56 | Name (TEMP, Buffer (Arg1) {}) 57 | TEMP = Arg2 58 | Arg1 += Arg0 59 | Local0 = Zero 60 | While ((Arg0 < Arg1)) 61 | { 62 | WE1B (Arg0, DerefOf (TEMP [Local0])) 63 | Arg0++ 64 | Local0++ 65 | } 66 | } 67 | 68 | /* 69 | * Optional: Notify battery on conservation mode change 70 | */ 71 | Method (NBAT, 0, Serialized) 72 | { 73 | If (CondRefOf (BAT1)) 74 | { 75 | Notify (BAT1, 0x80) 76 | } 77 | } 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /YogaSMC/SSDTSample/SSDT-RCSM.dsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Sample SSDT to disable lid open 3 | */ 4 | DefinitionBlock ("", "SSDT", 2, "hack", "RCSM", 0x00000000) 5 | { 6 | External (_SB_.PCI0.LPCB.H_EC, DeviceObj) // EC path 7 | External (_SB_.PCI0.LPCB.H_EC.VPC0, DeviceObj) // VPC path 8 | External (_SB_.PCI0.LPCB.H_EC.XQ0D, MethodObj) // Use ACPIDebug to find your lid open EC query and rename that 9 | External (RMDT.XLID, IntObj) 10 | 11 | Scope (_SB.PCI0.LPCB.H_EC) 12 | { 13 | Name (RCSM, Zero) 14 | Scope (VPC0) 15 | { 16 | Method (GCSM, 0, NotSerialized) 17 | { 18 | Return (RCSM) 19 | } 20 | 21 | Method (SCSM, 1, NotSerialized) 22 | { 23 | If ((Arg0 == Zero)) 24 | { 25 | RCSM = Zero 26 | } 27 | Else 28 | { 29 | RCSM = One 30 | } 31 | 32 | Return (Zero) 33 | } 34 | } 35 | 36 | Method (_Q0D, 0, NotSerialized) // _QXX method for lid open 37 | { 38 | If ((RCSM == Zero)) 39 | { 40 | XQ0D () // Renamed _QXX method for lid open 41 | } 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /YogaSMC/SSDTSample/SSDT-THINK.dsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Sample SSDT for ThinkSMC 3 | */ 4 | DefinitionBlock ("", "SSDT", 2, "hack", "Think", 0x00000000) 5 | { 6 | External (_SB.PCI0.LPCB.EC, DeviceObj) // EC path 7 | External (_SB.PCI0.LPCB.EC.HKEY, DeviceObj) // HKEY path 8 | External (_SB.PCI0.LPCB.EC.HFSP, FieldUnitObj) // Fan control register 9 | External (_SB.PCI0.LPCB.EC.HFNI, FieldUnitObj) // Fan control register 10 | External (_SB.PCI0.LPCB.EC.VRST, FieldUnitObj) // Second fan switch register 11 | External (_SI._SST, MethodObj) // Indicator 12 | External (LNUX, IntObj) // Variable set with "Linux" or "FreeBSD" 13 | External (WNTF, IntObj) // Variable set with "Windows 2001" or "Microsoft Windows NT" 14 | 15 | Scope (\) 16 | { 17 | If (_OSI ("Darwin")) 18 | { 19 | // Initialze mute button mode like Linux when it's broken, may be combined with MuteLEDFixup in prefpane. 20 | LNUX = 0x01 21 | // Enable DYTC thermal-management on newer Thinkpads. Please check \_SB.PCI0.LPCB.EC.HKEY.DYTC() 22 | WNTF = 0x01 23 | } 24 | } 25 | 26 | /* 27 | * Optional: Route to customized LED pattern or origin _SI._SST if differ from built in pattern. 28 | */ 29 | Scope (\_SB.PCI0.LPCB.EC.HKEY) 30 | { 31 | // Proxy-method to interface with \_SI._SST in YogaSMC 32 | Method (CSSI, 1, NotSerialized) 33 | { 34 | \_SI._SST (Arg0) 35 | } 36 | } 37 | 38 | /* 39 | * Optional: Sensor access 40 | * 41 | * Double check name of FieldUnit for collision 42 | * Registers return 0x00 for non-implemented, 43 | * and return 0x80 when not available. 44 | */ 45 | Scope (_SB.PCI0.LPCB.EC) 46 | { 47 | OperationRegion (ESEN, EmbeddedControl, Zero, 0x0100) 48 | Field (ESEN, ByteAcc, Lock, Preserve) 49 | { 50 | // TP_EC_THERMAL_TMP0 51 | Offset (0x78), 52 | EST0, 8, // CPU 53 | EST1, 8, 54 | EST2, 8, 55 | EST3, 8, // GPU ? 56 | EST4, 8, // Battery ? 57 | EST5, 8, // Battery ? 58 | EST6, 8, // Battery ? 59 | EST7, 8, // Battery ? 60 | 61 | // TP_EC_THERMAL_TMP8 62 | Offset (0xC0), 63 | EST8, 8, 64 | EST9, 8, 65 | ESTA, 8, 66 | ESTB, 8, 67 | ESTC, 8, 68 | ESTD, 8, 69 | ESTE, 8, 70 | ESTF, 8 71 | } 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /YogaSMC/SSDTSample/SSDT-WMIS.dsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Sample SSDT to fix sensor return 3 | * 4 | * Certain models forget to return result from ThermalZone: 5 | * 6 | * Method (WQBI, 1, NotSerialized) 7 | * { 8 | * \_TZ.WQBI (Arg0) 9 | * } 10 | * 11 | * So we have to patch it for correct reporting. 12 | * Rename Method (WQBI, 1, N) to XQBI 13 | * (ThermalZone one usually has Serialized type) 14 | * 15 | * Find: 57514249 01 // WQBI 16 | * Repl: 58514249 01 // XQBI 17 | * 18 | * MethodFlags := 19 | * bit 0-2: ArgCount (0-7) 20 | * bit 3: SerializeFlag 21 | * 0 NotSerialized 22 | * 1 Serialized 23 | */ 24 | DefinitionBlock ("", "SSDT", 2, "hack", "WMIS", 0x00000000) 25 | { 26 | External (_TZ.WQBI, MethodObj) // Method in ThermalZone 27 | 28 | Method (_SB.WMIS.WQBI, 1, NotSerialized) 29 | { 30 | Return (\_TZ.WQBI (Arg0)) 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /YogaSMC/SSDTSample/SSDT-YVPC.dsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Sample SSDT for bogus VPC device 3 | */ 4 | DefinitionBlock ("", "SSDT", 2, "hack", "YVPC", 0x00000000) 5 | { 6 | External (_SB_.PCI0.LPCB.H_EC, DeviceObj) // EC path 7 | 8 | Scope (_SB.PCI0.LPCB.H_EC) 9 | { 10 | Device (YVPC) 11 | { 12 | Name (_HID, "VPC0000") 13 | } 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /YogaSMC/WMI.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Released under "The GNU General Public License (GPL-2.0)" 3 | * 4 | * This program is free software; you can redistribute it and/or modify it 5 | * under the terms of the GNU General Public License as published by the 6 | * Free Software Foundation; either version 2 of the License, or (at your 7 | * option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, but 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 | * for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | * 18 | */ 19 | 20 | #ifndef WMI_h 21 | #define WMI_h 22 | 23 | #include 24 | 25 | #define kWMIGuid "guid" 26 | #define kWMIObjectId "object-id" 27 | #define kWMINotifyId "notify-id" 28 | #define kWMIInstanceCount "instance-count" 29 | #define kWMIFlags "flags" 30 | #define kWMIFlagsText "flags-text" 31 | 32 | #define TBT_WMI_GUID "86ccfd48-205e-4a77-9c48-2021cbede341" 33 | 34 | /* 35 | * If the GUID data block is marked as expensive, we must enable and 36 | * explicitily disable data collection. 37 | */ 38 | 39 | enum { 40 | ACPI_WMI_EXPENSIVE = 0x1, 41 | ACPI_WMI_METHOD = 0x2, 42 | ACPI_WMI_STRING = 0x4, 43 | ACPI_WMI_EVENT = 0x8 44 | }; 45 | 46 | class WMI 47 | { 48 | IOACPIPlatformDevice* mDevice {nullptr}; 49 | OSDictionary* mData = {nullptr}; 50 | OSDictionary* mEvent = {nullptr}; 51 | const char* iname; 52 | 53 | public: 54 | // Constructor 55 | WMI(IOService *provider) : mDevice(OSDynamicCast(IOACPIPlatformDevice, provider)) {}; 56 | // Destructor 57 | ~WMI(); 58 | 59 | bool initialize(); 60 | void start(); 61 | inline const char *getName() {return mDevice->getName();} 62 | 63 | bool hasMethod(const char * guid, UInt8 flg = ACPI_WMI_METHOD); 64 | bool enableEvent(const char * guid, bool enable); 65 | bool executeMethod(const char * guid, OSObject ** result = 0, OSObject * params[] = 0, IOItemCount paramCount = 0, bool mute = false); 66 | bool executeInteger(const char * guid, UInt32 * result, OSObject * params[] = 0, IOItemCount paramCount = 0, bool mute = false); 67 | inline IOACPIPlatformDevice* getACPIDevice() { return mDevice; } 68 | inline OSDictionary* getEvent() { return mEvent; } 69 | bool getEventData(UInt32 event, OSObject ** result); 70 | UInt8 getInstanceCount(const char * guid); 71 | 72 | IOReturn evaluateMethod(const char * guid, UInt8 instance, UInt32 methodId, OSObject ** result = 0, OSObject * input = 0, bool mute = false); 73 | IOReturn evaluateInteger(const char * guid, UInt8 instance, UInt32 methodId, UInt32 * result, OSObject * input = 0, bool mute = false); 74 | IOReturn queryBlock(const char * guid, UInt8 instance, OSObject ** result, bool mute = false); 75 | 76 | private: 77 | OSDictionary* getMethod(const char * guid, UInt8 flg = 0, bool mute = false); 78 | 79 | bool extractData(); 80 | void parseWDGEntry(struct WMI_DATA * block); 81 | 82 | bool parseBMF(); 83 | }; 84 | 85 | 86 | #endif /* WMI_h */ 87 | -------------------------------------------------------------------------------- /YogaSMC/YogaBaseService.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // YogaBaseService.hpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 10/17/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef YogaBaseService_hpp 10 | #define YogaBaseService_hpp 11 | 12 | #ifndef ALTER 13 | #include 14 | #endif 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "common.h" 20 | #include "message.h" 21 | #include "WMI.h" 22 | 23 | class YogaBaseService : public IOService { 24 | typedef IOService super; 25 | OSDeclareDefaultStructors(YogaBaseService); 26 | 27 | private: 28 | void dispatchMessageGated(int* message, void* data); 29 | 30 | bool notificationHandler(void * refCon, IOService * newService, IONotifier * notifier); 31 | void notificationHandlerGated(IOService * newService, IONotifier * notifier); 32 | 33 | IONotifier* _publishNotify {nullptr}; 34 | IONotifier* _terminateNotify {nullptr}; 35 | OSSet* _notificationServices {nullptr}; 36 | const OSSymbol* _deliverNotification {nullptr}; 37 | 38 | protected: 39 | const char* iname {nullptr}; 40 | 41 | IOWorkLoop *workLoop {nullptr}; 42 | IOCommandGate *commandGate {nullptr}; 43 | 44 | /** 45 | * UserClient 46 | */ 47 | // YogaSMCUserClient *client {nullptr}; 48 | 49 | /** 50 | * WMI device, in place of provider and direct ACPI evaluations 51 | */ 52 | WMI* YWMI {nullptr}; 53 | 54 | /** 55 | * Iterate over IOACPIPlane for PNP device 56 | * 57 | * @param id PNP name 58 | * @param device target ACPI device 59 | * 60 | * @return true if VPC is available 61 | */ 62 | bool findPNP(const char *id, IOACPIPlatformDevice **device); 63 | 64 | /** 65 | * Current Keyboard status 66 | */ 67 | bool Keyboardenabled {true}; 68 | 69 | /** 70 | * Current TouchPad status 71 | */ 72 | bool TouchPadenabled {true}; 73 | 74 | /** 75 | * Switch touchpad status 76 | */ 77 | void toggleTouchpad(); 78 | 79 | /** 80 | * Switch keyboard status 81 | */ 82 | void toggleKeyboard(); 83 | 84 | /** 85 | * Set keyboard and touchpad status 86 | * 87 | * @param enable desired status 88 | */ 89 | void setTopCase(bool enable); 90 | 91 | /** 92 | * Update keyboard and touchpad status 93 | * 94 | * @return false if keyboard and touchpad status mismatch 95 | */ 96 | bool updateTopCase(); 97 | 98 | /** 99 | * Power management support 100 | */ 101 | bool isPMsupported {false}; 102 | 103 | /** 104 | * Related ACPI methods 105 | */ 106 | static constexpr const char *readECOneByte = "RE1B"; 107 | static constexpr const char *readECBytes = "RECB"; 108 | static constexpr const char *writeECOneByte = "WE1B"; 109 | static constexpr const char *writeECBytes = "WECB"; 110 | 111 | /** 112 | * EC device 113 | */ 114 | IOACPIPlatformDevice *ec {nullptr}; 115 | 116 | /** 117 | * Test EC capability 118 | */ 119 | void validateEC(); 120 | 121 | /** 122 | * EC access capability, will be update on init 123 | * BIT 0 Read 124 | * BIT 1 Write 125 | */ 126 | UInt8 ECAccessCap {0}; 127 | 128 | /** 129 | * Wrapper for RE1B 130 | * 131 | * @param offset EC field offset 132 | * @param result EC field value 133 | * 134 | * @return kIOReturnSuccess on success 135 | */ 136 | IOReturn method_re1b(UInt32 offset, UInt8 *result); 137 | 138 | /** 139 | * Wrapper for RECB 140 | * 141 | * @param offset EC field offset 142 | * @param size EC field length in bytes 143 | * @param data EC field value 144 | * 145 | * @return kIOReturnSuccess on success 146 | */ 147 | IOReturn method_recb(UInt32 offset, UInt32 size, OSData **data); 148 | 149 | /** 150 | * Wrapper for WE1B 151 | * 152 | * @param offset EC field offset 153 | * @param value EC field value 154 | * 155 | * @return kIOReturnSuccess on success 156 | */ 157 | IOReturn method_we1b(UInt32 offset, UInt8 value); 158 | 159 | /** 160 | * Read custom field 161 | * 162 | * @param name EC field name 163 | * @param result EC field value 164 | * 165 | * @return kIOReturnSuccess on success 166 | */ 167 | IOReturn readECName(const char* name, UInt32 *result); 168 | 169 | /** 170 | * Send key event through VoodooPS2 171 | * @param keyCode event 172 | * @param goingDown pressed 173 | * @param time notify key time 174 | */ 175 | void dispatchKeyEvent(UInt16 keyCode, bool goingDown, bool time=true); 176 | 177 | public: 178 | virtual bool init(OSDictionary *dictionary) APPLE_KEXT_OVERRIDE; 179 | virtual IOService *probe(IOService *provider, SInt32 *score) APPLE_KEXT_OVERRIDE; 180 | 181 | virtual bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 182 | virtual void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 183 | 184 | void dispatchMessage(int message, void* data); 185 | 186 | virtual IOReturn setPowerState(unsigned long powerStateOrdinal, IOService * whatDevice) APPLE_KEXT_OVERRIDE; 187 | 188 | friend class YogaSMCUserClient; 189 | }; 190 | 191 | /** 192 | * Check if previous event is Active 193 | * 194 | * @param start timestamp of previous event 195 | * @param cd cool down time in ns 196 | * 197 | * @return false if within cool down time 198 | */ 199 | inline bool isActive(UInt64 *start, UInt64 cd = NSEC_PER_SEC) { 200 | UInt64 end; 201 | UInt64 delta; 202 | 203 | if (*start == 0) 204 | return false; 205 | 206 | clock_get_uptime(&end); 207 | absolutetime_to_nanoseconds(end - *start, &delta); 208 | 209 | *start = 0; 210 | 211 | if (delta < cd) 212 | return true; 213 | 214 | return false; 215 | }; 216 | 217 | #endif /* YogaBaseService_hpp */ 218 | -------------------------------------------------------------------------------- /YogaSMC/YogaSMC.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // YogaSMC.cpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 7/29/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | #include "YogaSMC.hpp" 10 | 11 | OSDefineMetaClassAndStructors(YogaSMC, YogaBaseService); 12 | 13 | bool ADDPR(debugEnabled) = false; 14 | uint32_t ADDPR(debugPrintDelay) = 0; 15 | 16 | static const struct sensorPair presetTemperatureDeciKelvin[] = { 17 | {KeyTB0T(0), "Battery"}, 18 | {KeyTB0T(1), "Battery Sensor 1"}, 19 | {KeyTB0T(2), "Battery Sensor 2"} 20 | }; 21 | 22 | static const struct sensorPair presetTemperature[] = { 23 | {KeyTCSA, "CPU System Agent Core"}, 24 | {KeyTCXC, "CPU Core PECI"}, 25 | // Laptops only have 1 key for both channel 26 | {KeyTM0P, "Memory Proximity"}, 27 | // Desktops 28 | {KeyTM0p(0), "SO-DIMM 1 Proximity"}, 29 | {KeyTM0p(1), "SO-DIMM 2 Proximity"}, 30 | {KeyTM0p(2), "SO-DIMM 3 Proximity"}, 31 | {KeyTM0p(3), "SO-DIMM 4 Proximity"}, 32 | {KeyTPCD, "Platform Controller Hub Die"}, 33 | {KeyTW0P, "Airport Proximity"}, 34 | {KeyTaLC, "Airflow Left"}, 35 | {KeyTaRC, "Airflow Right"}, 36 | {KeyTh0H(1), "Fin Stack Proximity Right"}, 37 | {KeyTh0H(2), "Fin Stack Proximity Left"}, 38 | {KeyTs0P(0), "Palm Rest"}, 39 | {KeyTs0P(1), "Trackpad Actuator"} 40 | }; 41 | 42 | void YogaSMC::addVSMCKey() { 43 | // ACPI-based 44 | if (!conf || !ec) 45 | return; 46 | 47 | ECSensorBase = sensorCount; 48 | 49 | OSDictionary *status = OSDictionary::withCapacity(1); 50 | OSString *method; 51 | 52 | for (auto &pair : presetTemperatureDeciKelvin) 53 | addECKeySp(pair.key, pair.name, atomicSpDeciKelvinKey); 54 | 55 | for (auto &pair : presetTemperature) 56 | addECKeySp(pair.key, pair.name, atomicSpKey); 57 | 58 | setProperty("DirectECKey", status); 59 | status->release(); 60 | } 61 | 62 | bool YogaSMC::start(IOService *provider) { 63 | isPMsupported = true; 64 | 65 | if (!super::start(provider)) 66 | return false; 67 | 68 | DebugLog("Starting"); 69 | 70 | validateEC(); 71 | 72 | poller = IOTimerEventSource::timerEventSource(this, [](OSObject *object, IOTimerEventSource *sender) { 73 | auto smc = OSDynamicCast(YogaSMC, object); 74 | if (smc) smc->updateEC(); 75 | }); 76 | 77 | if (!poller || (workLoop->addEventSource(poller) != kIOReturnSuccess)) { 78 | AlwaysLog("Failed to add poller"); 79 | return false; 80 | } 81 | 82 | // WARNING: watch out, key addition is sorted here! 83 | addVSMCKey(); 84 | qsort(const_cast(vsmcPlugin.data.data()), vsmcPlugin.data.size(), sizeof(VirtualSMCKeyValue), VirtualSMCKeyValue::compare); 85 | setProperty("Key Submitted", vsmcPlugin.data.size(), 32); 86 | vsmcNotifier = VirtualSMCAPI::registerHandler(vsmcNotificationHandler, this); 87 | 88 | awake = true; 89 | ready = true; 90 | poller->setTimeoutMS(POLLING_INTERVAL); 91 | poller->enable(); 92 | registerService(); 93 | return true; 94 | } 95 | 96 | void YogaSMC::stop(IOService *provider) 97 | { 98 | DebugLog("Stopping"); 99 | 100 | poller->disable(); 101 | workLoop->removeEventSource(poller); 102 | OSSafeReleaseNULL(poller); 103 | 104 | terminate(); 105 | 106 | super::stop(provider); 107 | } 108 | 109 | bool YogaSMC::vsmcNotificationHandler(void *sensors, void *refCon, IOService *vsmc, IONotifier *notifier) { 110 | auto self = OSDynamicCast(YogaSMC, reinterpret_cast(sensors)); 111 | if (sensors && vsmc) { 112 | DBGLOG("yogasmc", "got vsmc notification"); 113 | auto &plugin = self->vsmcPlugin; 114 | auto ret = vsmc->callPlatformFunction(VirtualSMCAPI::SubmitPlugin, true, sensors, &plugin, nullptr, nullptr); 115 | if (ret == kIOReturnSuccess) { 116 | DBGLOG("yogasmc", "submitted plugin"); 117 | return true; 118 | } else if (ret != kIOReturnUnsupported) { 119 | SYSLOG("yogasmc", "plugin submission failure %X", ret); 120 | } else { 121 | DBGLOG("yogasmc", "plugin submission to non vsmc"); 122 | } 123 | } else { 124 | SYSLOG("yogasmc", "got null vsmc notification"); 125 | } 126 | return false; 127 | } 128 | 129 | IOService* YogaSMC::probe(IOService *provider, SInt32 *score) { 130 | ec = OSDynamicCast(IOACPIPlatformDevice, provider->getProperty("ECDevice")); 131 | if (!ec) { 132 | DebugLog("EC not found"); 133 | return nullptr; 134 | } 135 | iname = ec->getName(); 136 | 137 | conf = OSDynamicCast(OSDictionary, getProperty("Sensors")); 138 | if (!conf) { 139 | DebugLog("conf not found"); 140 | return nullptr; 141 | } 142 | getWMISensor(provider); 143 | return this; 144 | } 145 | 146 | void YogaSMC::updateEC() { 147 | if (!awake) 148 | return; 149 | 150 | updateECVendor(); 151 | 152 | UInt32 result = 0; 153 | for (UInt8 i = ECSensorBase; i < sensorCount; i++) 154 | if (ec->evaluateInteger(sensorMethods[i], &result) == kIOReturnSuccess && result != 0) 155 | atomic_store_explicit(¤tSensor[i], result, memory_order_release); 156 | poller->setTimeoutMS(POLLING_INTERVAL); 157 | } 158 | 159 | IOReturn YogaSMC::setPowerState(unsigned long powerStateOrdinal, IOService * whatDevice) { 160 | if (super::setPowerState(powerStateOrdinal, whatDevice) != kIOPMAckImplied) 161 | return kIOReturnInvalid; 162 | 163 | if (powerStateOrdinal == 0) { 164 | if (awake) { 165 | poller->disable(); 166 | workLoop->removeEventSource(poller); 167 | awake = false; 168 | DebugLog("Going to sleep"); 169 | } 170 | } else { 171 | if (!awake && ready) { 172 | awake = true; 173 | workLoop->addEventSource(poller); 174 | poller->setTimeoutMS(POLLING_INTERVAL); 175 | poller->enable(); 176 | DebugLog("Woke up"); 177 | } 178 | } 179 | return kIOPMAckImplied; 180 | } 181 | 182 | void YogaSMC::setPropertiesGated(OSObject* props) { 183 | OSDictionary* dict = OSDynamicCast(OSDictionary, props); 184 | if (!dict) 185 | return; 186 | 187 | OSCollectionIterator* i = OSCollectionIterator::withCollection(dict); 188 | 189 | if (i) { 190 | while (OSString* key = OSDynamicCast(OSString, i->getNextObject())) { 191 | if (key->isEqualTo(readECPrompt)) { 192 | if (!ec) 193 | continue; 194 | OSNumber *value; 195 | getPropertyNumber(readECPrompt); 196 | if (value->unsigned8BitValue() >= sensorCount) { 197 | AlwaysLog(valueInvalid, readECPrompt); 198 | continue; 199 | } 200 | uint32_t raw = atomic_load_explicit(¤tSensor[value->unsigned8BitValue()], memory_order_acquire); 201 | AlwaysLog(updateSuccess, readECPrompt, raw); 202 | } else { 203 | AlwaysLog("Unknown property %s", key->getCStringNoCopy()); 204 | } 205 | } 206 | i->release(); 207 | } 208 | 209 | return; 210 | } 211 | 212 | IOReturn YogaSMC::setProperties(OSObject *props) { 213 | commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &YogaSMC::setPropertiesGated), props); 214 | return kIOReturnSuccess; 215 | } 216 | 217 | EXPORT extern "C" kern_return_t ADDPR(kern_start)(kmod_info_t *, void *) { 218 | // Report success but actually do not start and let I/O Kit unload us. 219 | // This works better and increases boot speed in some cases. 220 | PE_parse_boot_argn("liludelay", &ADDPR(debugPrintDelay), sizeof(ADDPR(debugPrintDelay))); 221 | ADDPR(debugEnabled) = checkKernelArgument("-vsmcdbg"); 222 | return KERN_SUCCESS; 223 | } 224 | 225 | EXPORT extern "C" kern_return_t ADDPR(kern_stop)(kmod_info_t *, void *) { 226 | // It is not safe to unload VirtualSMC plugins! 227 | return KERN_FAILURE; 228 | } 229 | -------------------------------------------------------------------------------- /YogaSMC/YogaSMC.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // YogaSMC.hpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 7/29/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef YogaSMC_hpp 10 | #define YogaSMC_hpp 11 | 12 | #include 13 | #include "KeyImplementations.hpp" 14 | #include "YogaBaseService.hpp" 15 | 16 | #define MAX_SENSOR 0x10 17 | #define POLLING_INTERVAL 2000 18 | 19 | class YogaSMC : public YogaBaseService 20 | { 21 | typedef YogaBaseService super; 22 | OSDeclareDefaultStructors(YogaSMC) 23 | 24 | protected: 25 | bool awake {false}; 26 | bool ready {false}; 27 | 28 | IOTimerEventSource* poller {nullptr}; 29 | 30 | /** 31 | * VirtualSMC service registration notifier 32 | */ 33 | IONotifier *vsmcNotifier {nullptr}; 34 | 35 | /** 36 | * Registered plugin instance 37 | */ 38 | VirtualSMCAPI::Plugin vsmcPlugin { 39 | xStringify(PRODUCT_NAME), 40 | parseModuleVersion(xStringify(MODULE_VERSION)), 41 | VirtualSMCAPI::Version, 42 | }; 43 | 44 | /** 45 | * Sensors configuration 46 | */ 47 | OSDictionary* conf {nullptr}; 48 | 49 | /** 50 | * Add available SMC keys 51 | */ 52 | virtual void addVSMCKey(); 53 | 54 | /** 55 | * ACPI method for sensors 56 | */ 57 | const char *sensorMethods[MAX_SENSOR]; 58 | 59 | /** 60 | * Enabled sensor count 61 | */ 62 | UInt8 sensorCount {0}; 63 | 64 | /** 65 | * EC sensor base 66 | */ 67 | UInt8 ECSensorBase {0}; 68 | 69 | /** 70 | * Current sensor reading obtained from ACPI 71 | */ 72 | _Atomic(uint32_t) currentSensor[MAX_SENSOR]; 73 | 74 | /** 75 | * Poll EC field for sensor data 76 | */ 77 | void updateEC(); 78 | virtual inline void updateECVendor() {}; 79 | 80 | virtual void setPropertiesGated(OSObject* props); 81 | 82 | inline virtual void getWMISensor(IOService* provider) {}; 83 | 84 | public: 85 | virtual IOService *probe(IOService *provider, SInt32 *score) APPLE_KEXT_OVERRIDE; 86 | virtual bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 87 | virtual void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 88 | 89 | virtual IOReturn setPowerState(unsigned long powerStateOrdinal, IOService * whatDevice) APPLE_KEXT_OVERRIDE; 90 | virtual IOReturn setProperties(OSObject* props) APPLE_KEXT_OVERRIDE; 91 | 92 | /** 93 | * Submit the keys to received VirtualSMC service. 94 | * 95 | * @param sensors SMCBatteryManager service 96 | * @param refCon reference 97 | * @param vsmc VirtualSMC service 98 | * @param notifier created notifier 99 | */ 100 | static bool vsmcNotificationHandler(void *sensors, void *refCon, IOService *vsmc, IONotifier *notifier); 101 | }; 102 | 103 | #endif /* YogaSMC_hpp */ 104 | -------------------------------------------------------------------------------- /YogaSMC/YogaSMC/DYSMC.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // DYSMC.hpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 4/28/21. 6 | // Copyright © 2021 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef DYSMC_hpp 10 | #define DYSMC_hpp 11 | 12 | #include "DYWMI.hpp" 13 | #include "YogaSMC.hpp" 14 | 15 | #define SENSOR_TYPE_TEMPERATURE 2 16 | #define SENSOR_TYPE_AIR_FLOW 12 17 | 18 | /** 19 | * BIOS Numeric Sensor (index start from 1) 20 | * 21 | * 0x1: Name 22 | * 0x2: Description 23 | * 0x3: SensorType 24 | * 0: Unknown 25 | * 1: Other 26 | * 2: Temperature 27 | * 5: Tachometer 28 | * 11: Presence 29 | * 12: Air Flow 30 | * 0x4: OtherSensorType 31 | * 0x5: OperationalStatus 32 | * 0: Unknown 33 | * 1: Other 34 | * 2: OK 35 | * 3: Degraded 36 | * 5: Predictive Failure 37 | * 6: Error 38 | * 10: Stopped 39 | * 12: No Contact 40 | * 0x6: Size 41 | * 0x7: PossibleStates[Size] 42 | * 0x8: CurrentState 43 | * 44 | * 0x9: BaseUnits 45 | * 1: Other 46 | * 2: Degrees C 47 | * 3: RPM 48 | * 0xa: UnitModifier (sint32) 49 | * 0xb: CurrentReading 50 | * 0xc: RateUnits 51 | * 0: None 52 | * 1: Per Microsecond 53 | * 2: Per Millisecond 54 | * 3: Per Second 55 | * 4: Per Minute 56 | * 5: Per Hour 57 | * 6: Per Day 58 | * 7: Per Week 59 | * 8: Per Month 60 | * 9: Per Year 61 | */ 62 | 63 | class DYSMC : public YogaSMC 64 | { 65 | typedef YogaSMC super; 66 | OSDeclareDefaultStructors(DYSMC) 67 | 68 | private: 69 | /** 70 | * WMI device, in place of provider and direct ACPI evaluations 71 | */ 72 | DYWMI *wmis {nullptr}; 73 | 74 | /** 75 | * Corresponding sensor index 76 | */ 77 | UInt8 sensorIndex[MAX_SENSOR]; 78 | 79 | bool addTachometerKey(OSString *name); 80 | bool addTemperatureKey(OSString *name); 81 | 82 | void addVSMCKey() APPLE_KEXT_OVERRIDE; 83 | void updateECVendor() APPLE_KEXT_OVERRIDE; 84 | 85 | void getWMISensor(IOService* provider) APPLE_KEXT_OVERRIDE; 86 | }; 87 | 88 | #endif /* DYSMC_hpp */ 89 | -------------------------------------------------------------------------------- /YogaSMC/YogaSMC/IdeaSMC.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // IdeaSMC.cpp 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 8/25/20. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | #include "IdeaSMC.hpp" 11 | OSDefineMetaClassAndStructors(IdeaSMC, YogaSMC); 12 | 13 | void IdeaSMC::addVSMCKey() { 14 | // Add message-based key 15 | VirtualSMCAPI::addKey(KeyBDVT, vsmcPlugin.data, VirtualSMCAPI::valueWithFlag(true, new BDVT(this), SMC_KEY_ATTRIBUTE_READ | SMC_KEY_ATTRIBUTE_WRITE)); 16 | VirtualSMCAPI::addKey(KeyCH0B, vsmcPlugin.data, VirtualSMCAPI::valueWithData(nullptr, 1, SmcKeyTypeHex, new CH0B, SMC_KEY_ATTRIBUTE_READ | SMC_KEY_ATTRIBUTE_WRITE)); 17 | 18 | if (!wmig) { 19 | super::addVSMCKey(); 20 | return; 21 | } 22 | 23 | UInt32 value; 24 | if (wmig->getGamzeZoneData(GAME_ZONE_WMI_GET_FAN_NUM, &value) && value) { 25 | VirtualSMCAPI::addKey(KeyF0Ac(0), vsmcPlugin.data, VirtualSMCAPI::valueWithFp(0, SmcKeyTypeFpe2, new atomicFpKey(¤tSensor[sensorCount]))); 26 | sensorIndex[sensorCount++] = GAME_ZONE_WMI_GET_FAN_ONE; 27 | 28 | if (value > 1) { 29 | VirtualSMCAPI::addKey(KeyF0Ac(1), vsmcPlugin.data, VirtualSMCAPI::valueWithFp(0, SmcKeyTypeFpe2, new atomicFpKey(¤tSensor[sensorCount]))); 30 | sensorIndex[sensorCount++] = GAME_ZONE_WMI_GET_FAN_TWO; 31 | } 32 | 33 | VirtualSMCAPI::addKey(KeyFNum, vsmcPlugin.data, VirtualSMCAPI::valueWithUint8(sensorCount, nullptr, SMC_KEY_ATTRIBUTE_CONST | SMC_KEY_ATTRIBUTE_READ)); 34 | 35 | if (wmig->getGamzeZoneData(GAME_ZONE_WMI_GET_FAN_MAX, &value)) { 36 | VirtualSMCAPI::addKey(KeyF0Mx(0), vsmcPlugin.data, VirtualSMCAPI::valueWithFp(value, SmcKeyTypeFpe2, nullptr, SMC_KEY_ATTRIBUTE_WRITE | SMC_KEY_ATTRIBUTE_READ)); 37 | if (sensorCount == 2) 38 | VirtualSMCAPI::addKey(KeyF0Mx(1), vsmcPlugin.data, VirtualSMCAPI::valueWithFp(value, SmcKeyTypeFpe2, nullptr, SMC_KEY_ATTRIBUTE_WRITE | SMC_KEY_ATTRIBUTE_READ)); 39 | } 40 | } 41 | 42 | if (wmig->getGamzeZoneData(GAME_ZONE_WMI_GET_TMP_IR, &value) && value) { 43 | VirtualSMCAPI::addKey(KeyTPCD, vsmcPlugin.data, VirtualSMCAPI::valueWithSp(0, SmcKeyTypeSp78, new atomicSpKey(¤tSensor[sensorCount]))); 44 | sensorIndex[sensorCount++] = GAME_ZONE_WMI_GET_TMP_IR; 45 | } 46 | 47 | if (wmig->getGamzeZoneData(GAME_ZONE_WMI_GET_TMP_CPU, &value) && value) { 48 | VirtualSMCAPI::addKey(KeyTCXC, vsmcPlugin.data, VirtualSMCAPI::valueWithSp(0, SmcKeyTypeSp78, new atomicSpKey(¤tSensor[sensorCount]))); 49 | sensorIndex[sensorCount++] = GAME_ZONE_WMI_GET_TMP_CPU; 50 | } 51 | 52 | if (wmig->getGamzeZoneData(GAME_ZONE_WMI_GET_TMP_GPU, &value) && value) { 53 | VirtualSMCAPI::addKey(KeyTCGC, vsmcPlugin.data, VirtualSMCAPI::valueWithSp(0, SmcKeyTypeSp78, new atomicSpKey(¤tSensor[sensorCount]))); 54 | sensorIndex[sensorCount++] = GAME_ZONE_WMI_GET_TMP_GPU; 55 | } 56 | 57 | super::addVSMCKey(); 58 | } 59 | 60 | void IdeaSMC::updateECVendor() { 61 | UInt32 value; 62 | for (UInt8 index = 0; index < ECSensorBase; ++index) 63 | if (wmig->getGamzeZoneData(sensorIndex[index], &value)) 64 | atomic_store_explicit(¤tSensor[index], value, memory_order_release); 65 | } 66 | 67 | void IdeaSMC::getWMISensor(IOService *provder) { 68 | wmig = OSDynamicCast(IdeaWMIGameZone, provder->getProperty("Game Zone")); 69 | } 70 | -------------------------------------------------------------------------------- /YogaSMC/YogaSMC/IdeaSMC.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // IdeaSMC.hpp 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 8/25/20. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | #ifndef IdeaSMC_hpp 11 | #define IdeaSMC_hpp 12 | 13 | #include "IdeaWMI.hpp" 14 | #include "YogaSMC.hpp" 15 | 16 | class IdeaSMC : public YogaSMC 17 | { 18 | typedef YogaSMC super; 19 | OSDeclareDefaultStructors(IdeaSMC) 20 | 21 | private: 22 | /** 23 | * WMI device, in place of provider and direct ACPI evaluations 24 | */ 25 | IdeaWMIGameZone *wmig {nullptr}; 26 | 27 | /** 28 | * Corresponding sensor index 29 | */ 30 | UInt8 sensorIndex[MAX_SENSOR]; 31 | 32 | void addVSMCKey() APPLE_KEXT_OVERRIDE; 33 | void updateECVendor() APPLE_KEXT_OVERRIDE; 34 | 35 | void getWMISensor(IOService* provider) APPLE_KEXT_OVERRIDE; 36 | }; 37 | 38 | #endif /* IdeaSMC_hpp */ 39 | -------------------------------------------------------------------------------- /YogaSMC/YogaSMC/ThinkSMC.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // ThinkSMC.cpp 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 8/30/20. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | #include "ThinkSMC.hpp" 11 | 12 | OSDefineMetaClassAndStructors(ThinkSMC, YogaSMC); 13 | 14 | void ThinkSMC::addVSMCKey() { 15 | // Add message-based key 16 | VirtualSMCAPI::addKey(KeyBDVT, vsmcPlugin.data, VirtualSMCAPI::valueWithFlag(true, new BDVT(this), SMC_KEY_ATTRIBUTE_READ | SMC_KEY_ATTRIBUTE_WRITE)); 17 | VirtualSMCAPI::addKey(KeyCH0B, vsmcPlugin.data, VirtualSMCAPI::valueWithData(nullptr, 1, SmcKeyTypeHex, new CH0B, SMC_KEY_ATTRIBUTE_READ | SMC_KEY_ATTRIBUTE_WRITE)); 18 | 19 | VirtualSMCAPI::addKey(KeyFNum, vsmcPlugin.data, VirtualSMCAPI::valueWithUint8(dualFan ? 2 : 1, nullptr, SMC_KEY_ATTRIBUTE_CONST | SMC_KEY_ATTRIBUTE_READ)); 20 | VirtualSMCAPI::addKey(KeyF0Ac(0), vsmcPlugin.data, VirtualSMCAPI::valueWithFp(0, SmcKeyTypeFpe2, new atomicFpKey(¤tSensor[0]))); 21 | ++sensorCount; 22 | if (dualFan) { 23 | VirtualSMCAPI::addKey(KeyF0Ac(1), vsmcPlugin.data, VirtualSMCAPI::valueWithFp(0, SmcKeyTypeFpe2, new atomicFpKey(¤tSensor[1]))); 24 | ++sensorCount; 25 | } 26 | setProperty("Dual fan", dualFan); 27 | super::addVSMCKey(); 28 | } 29 | 30 | void ThinkSMC::updateECVendor() { 31 | if (!(ECAccessCap & ECReadCap)) 32 | return; 33 | 34 | UInt8 lo, hi; 35 | if (method_re1b(0x84, &lo) == kIOReturnSuccess && 36 | method_re1b(0x85, &hi) == kIOReturnSuccess) { 37 | UInt16 result = (hi << 8) | lo; 38 | atomic_store_explicit(¤tSensor[0], result, memory_order_release); 39 | } 40 | 41 | if (!(ECAccessCap & ECWriteCap) || !dualFan) 42 | return; 43 | 44 | UInt8 select; 45 | if (method_re1b(0x84, &select) == kIOReturnSuccess) { 46 | if ((select & 0x1) != 0) 47 | select &= 0xfe; 48 | else 49 | select |= 0x1; 50 | if (method_we1b(0x31, select) == kIOReturnSuccess && 51 | method_re1b(0x84, &lo) == kIOReturnSuccess && 52 | method_re1b(0x85, &hi) == kIOReturnSuccess) { 53 | UInt16 result = (hi << 8) | lo; 54 | atomic_store_explicit(¤tSensor[1], result, memory_order_release); 55 | if ((select & 0x1) != 0) 56 | select &= 0xfe; 57 | else 58 | select |= 0x1; 59 | if (method_we1b(0x31, select) != kIOReturnSuccess) 60 | dualFan = false; 61 | } else { 62 | dualFan = false; 63 | } 64 | } else { 65 | dualFan = false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /YogaSMC/YogaSMC/ThinkSMC.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // ThinkSMC.hpp 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 8/30/20. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | #ifndef ThinkSMC_hpp 11 | #define ThinkSMC_hpp 12 | 13 | #include "YogaSMC.hpp" 14 | 15 | class ThinkSMC : public YogaSMC 16 | { 17 | typedef YogaSMC super; 18 | OSDeclareDefaultStructors(ThinkSMC) 19 | 20 | private: 21 | void addVSMCKey() APPLE_KEXT_OVERRIDE; 22 | void updateECVendor() APPLE_KEXT_OVERRIDE; 23 | 24 | bool dualFan {false}; 25 | 26 | }; 27 | 28 | #endif /* ThinkSMC_hpp */ 29 | -------------------------------------------------------------------------------- /YogaSMC/YogaSMCUserClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // YogaSMCUserClient.h 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 10/9/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef YogaSMCUserClient_h 10 | #define YogaSMCUserClient_h 11 | 12 | #include 13 | #include 14 | 15 | #ifndef IOKIT 16 | typedef uint64_t io_user_reference_t; 17 | #endif 18 | 19 | enum { 20 | kYSMCUCOpen, // ScalarIScalarO 21 | kYSMCUCClose, // ScalarIScalarO 22 | kYSMCUCReadEC, // ScalarIStructO 23 | kYSMCUCWriteEC, // ScalarIStructI 24 | kYSMCUCReadECName, // ScalarIScalarO 25 | kYSMCUCNumMethods 26 | }; 27 | 28 | typedef struct { 29 | mach_msg_header_t header; 30 | UInt32 event; 31 | UInt32 data; 32 | io_user_reference_t ref; 33 | } SMCNotificationMessage; 34 | 35 | #endif /* YogaSMCUserClient_h */ 36 | -------------------------------------------------------------------------------- /YogaSMC/YogaSMCUserClientPrivate.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // YogaSMCUserClientPrivate.hpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 10/7/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef YogaSMCUserClientPrivate_hpp 10 | #define YogaSMCUserClientPrivate_hpp 11 | 12 | #include 13 | #include "common.h" 14 | #include "YogaSMCUserClient.h" 15 | #include "YogaVPC.hpp" 16 | 17 | class YogaVPC; 18 | class YogaSMCUserClient : public IOUserClient { 19 | typedef IOUserClient super; 20 | OSDeclareDefaultStructors(YogaSMCUserClient) 21 | 22 | protected: 23 | YogaVPC* fProvider; 24 | const char* iname; 25 | task_t fTask; 26 | mach_port_t m_notificationPort; 27 | SMCNotificationMessage notification; 28 | 29 | public: 30 | virtual bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 31 | virtual void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 32 | 33 | virtual IOExternalMethod *getTargetAndMethodForIndex(IOService **target, UInt32 Index) APPLE_KEXT_OVERRIDE; 34 | IOReturn registerNotificationPort(mach_port_t port, UInt32 type, io_user_reference_t refCon) APPLE_KEXT_OVERRIDE; 35 | 36 | virtual IOReturn clientClose(void) APPLE_KEXT_OVERRIDE; 37 | virtual IOReturn clientDied(void) APPLE_KEXT_OVERRIDE; 38 | 39 | IOReturn sendNotification(UInt32 event, UInt32 data=0); 40 | 41 | // Externally accessible methods 42 | IOReturn userClientOpen(void); 43 | IOReturn userClientClose(void); 44 | IOReturn readEC(UInt64 offset, UInt8 *output, IOByteCount *outputSizeP); 45 | IOReturn writeEC(UInt64 offset, UInt8 *input, IOByteCount inputSizeP); 46 | IOReturn readECName(char* name, UInt8* output); 47 | 48 | }; 49 | #endif /* YogaSMCUserClientPrivate_hpp */ 50 | -------------------------------------------------------------------------------- /YogaSMC/YogaVPC/DYTC.h: -------------------------------------------------------------------------------- 1 | // 2 | // DYTC.h 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 8/26/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef DYTC_h 10 | #define DYTC_h 11 | 12 | #include 13 | 14 | // based on ibm-acpi-devel 15 | 16 | enum { 17 | DYTC_CMD_QUERY = 0, /* Get DYTC Version */ 18 | DYTC_CMD_SET = 1, /* Set current IC function and mode */ 19 | DYTC_CMD_GET = 2, /* Get current IC function and mode */ 20 | DYTC_CMD_FCAP = 3, /* Get available IC functions */ 21 | /* 4-7, 0x0100 unknown yet, capability? */ 22 | DYTC_CMD_MMC_GET = 8, /* Get MMC mode, only available on 6+ */ 23 | DYTC_CMD_RESET = 0x1ff, /* Reset current IC function and mode */ 24 | }; 25 | 26 | // Functions 27 | #define DYTC_FUNCTION_STD 0 /* standard mode */ 28 | #define DYTC_FUNCTION_CQL 1 /* lap mode */ 29 | #define DYTC_FUNCTION_MMC 0xB /* desk mode, priority 5 */ 30 | 31 | // Functions spotted 32 | #define DYTC_FUNCTION_MYH 3 /* priority 7 */ 33 | #define DYTC_FUNCTION_STP 4 /* priority 1 */ 34 | #define DYTC_FUNCTION_DMC 8 35 | #define DYTC_FUNCTION_IFC 0xA /* priority 6, aka AAA */ 36 | #define DYTC_FUNCTION_MSC 0xC /* priority 4 */ 37 | #define DYTC_FUNCTION_PSC 0xD /* priority 3 */ 38 | 39 | // Functions deduced 40 | #define DYTC_FUNCTION_TIO 2 /* priority 0, aka FBC */ 41 | #define DYTC_FUNCTION_CQH 5 /* aka APM */ 42 | #define DYTC_FUNCTION_DCC 6 /* aka AQM */ 43 | #define DYTC_FUNCTION_SFN 7 /* priority 2 */ 44 | #define DYTC_FUNCTION_FHP 9 45 | #define DYTC_FUNCTION_CSC 0xE 46 | 47 | // Availeble mode 48 | #define DYTC_MODE_PERFORM 2 /* High power mode aka performance */ 49 | #define DYTC_MODE_QUIET 3 /* low power mode aka quiet */ 50 | #define DYTC_MODE_BALANCE 0xF /* default mode aka balance */ 51 | 52 | // Error code 53 | #define DYTC_EXCEPTION 0 54 | #define DYTC_SUCCESS 1 55 | #define DYTC_FUNC_INVALID 1 << 1 56 | #define DYTC_CMD_INVALID 2 << 1 57 | #define DYTC_DPTF_UNAVAILABLE 3 << 1 58 | #define DYTC_UNSUPPORTED 4 << 1 59 | #define DYTC_MODE_INVALID 5 << 1 60 | 61 | // Deprecated constants 62 | 63 | //#define DYTC_QUERY_ENABLE_BIT 8 /* Bit 8 - 0 = disabled, 1 = enabled */ 64 | //#define DYTC_QUERY_SUBREV_BIT 16 /* Bits 16 - 27 - sub revisision */ 65 | //#define DYTC_QUERY_REV_BIT 28 /* Bits 28 - 31 - revision */ 66 | 67 | //#define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */ 68 | //#define DYTC_GET_MODE_BIT 12 /* Bits 12-15 - mode setting */ 69 | //#define DYTC_GET_LAPMODE_BIT 17 /* Bit 17 - lapmode. Set when on lap */ 70 | 71 | //#define DYTC_SET_FUNCTION_BIT 12 /* Bits 12-15 - funct setting */ 72 | //#define DYTC_SET_MODE_BIT 16 /* Bits 16-19 - mode setting */ 73 | //#define DYTC_SET_VALID_BIT 20 /* Bit 20 - 1 = on, 0 = off */ 74 | 75 | union DYTC_CMD { 76 | UInt32 raw; 77 | struct { 78 | union { 79 | UInt16 command; 80 | struct __attribute__((packed)) { 81 | UInt8 command_lo; 82 | UInt command_hi: 1; 83 | UInt padding: 3; 84 | UInt ICFunc: 4; 85 | }; 86 | }; 87 | UInt ICMode: 4; 88 | UInt validF: 1; 89 | }; 90 | }; 91 | 92 | static const union DYTC_CMD dytc_query_cmd = {.command = DYTC_CMD_QUERY}; 93 | static const union DYTC_CMD dytc_set_cmd = {.command = DYTC_CMD_SET}; 94 | static const union DYTC_CMD dytc_get_cmd = {.command = DYTC_CMD_GET}; 95 | static const union DYTC_CMD dytc_func_cap_cmd = {.command = DYTC_CMD_FCAP}; 96 | static const union DYTC_CMD dytc_reset_cmd = {.command = DYTC_CMD_RESET}; 97 | 98 | #define DYTC_SET_CMD(func, mode, enable) \ 99 | .command = DYTC_CMD_SET, .ICFunc = func, \ 100 | .ICMode = mode, .validF = enable 101 | 102 | typedef union { 103 | UInt32 raw; 104 | struct { 105 | UInt8 errorcode; 106 | union { 107 | struct __attribute__((packed)) { 108 | bool enable; 109 | union { 110 | UInt16 version; 111 | struct __attribute__((packed)) { 112 | UInt8 subrev_lo; 113 | UInt subrev_hi: 4; 114 | UInt rev: 4; 115 | }; 116 | }; 117 | } query; 118 | struct __attribute__((packed)) { 119 | UInt funcmode: 4; 120 | UInt perfmode: 4; 121 | UInt16 func_sta; 122 | } get; 123 | struct __attribute__((packed)) { 124 | UInt8 padding; 125 | UInt16 bitmap; 126 | } func_cap; 127 | struct __attribute__((packed)) { 128 | UInt funcmode: 4; 129 | } mmc_get; 130 | }; 131 | }; 132 | } DYTC_RESULT; 133 | 134 | #endif /* DYTC_h */ 135 | -------------------------------------------------------------------------------- /YogaSMC/YogaVPC/DYVPC.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // DYVPC.hpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 1/7/21. 6 | // Copyright © 2021 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef DYVPC_hpp 10 | #define DYVPC_hpp 11 | 12 | #include "YogaVPC.hpp" 13 | 14 | // from linux/drivers/platform/x86/hp-wmi.c 15 | 16 | #define INPUT_WMI_EVENT "95f24279-4d7b-4334-9387-accdc67ef61c" 17 | #define BIOS_QUERY_WMI_METHOD "5fb7f034-2c63-45e9-be91-3d44e2c707e4" 18 | 19 | enum hp_wmi_radio { 20 | HPWMI_WIFI = 0x0, 21 | HPWMI_BLUETOOTH = 0x1, 22 | HPWMI_WWAN = 0x2, 23 | HPWMI_GPS = 0x3, 24 | }; 25 | 26 | // hpqBEvnt 27 | 28 | enum hp_wmi_event_ids { 29 | HPWMI_DOCK_EVENT = 0x01, 30 | HPWMI_PARK_HDD = 0x02, 31 | HPWMI_SMART_ADAPTER = 0x03, // _PSR 32 | HPWMI_BEZEL_BUTTON = 0x04, 33 | HPWMI_WIRELESS = 0x05, // _L62 / HWWB 34 | HPWMI_CPU_BATTERY_THROTTLE = 0x06, 35 | HPWMI_LOCK_SWITCH = 0x07, 36 | HPWMI_LID_SWITCH = 0x08, 37 | HPWMI_SCREEN_ROTATION = 0x09, 38 | HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A, 39 | HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B, 40 | HPWMI_PROXIMITY_SENSOR = 0x0C, 41 | HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D, 42 | HPWMI_PEAKSHIFT_PERIOD = 0x0F, // HVWP / _Q20 43 | HPWMI_BATTERY_CHARGE_PERIOD = 0x10, // HVWP / _Q20 44 | // 0x14 // _Q24 / _Q25 / _Q26 45 | // 0x18 // _L62 46 | // 0x19 // HWAK / SSLC / HVWP 47 | // 0x00020001 // _Q07 48 | // 0x00020002 // _Q01 / _Q02 / _Q10 / _Q11 / _Q12 / _Q13 / _Q15 / _Q16 49 | }; 50 | 51 | // hpqBIntM 52 | 53 | struct bios_args { // hpqBIOSInt 0 / 4 / 128 / 1024 / 4096 54 | UInt32 signature; // 0x1, SNIN, Sign, should be uint8 * 4, 0x55434553 (UCES) 55 | UInt32 command; // 0x2, COMD, Command, uint32 56 | UInt32 commandtype; // 0x3, CMTP, CommandType, uint32 57 | UInt32 datasize; // 0x4, DASI, Size, uint32 58 | UInt8 data[128]; // 0x5, PVWB, hpqBData, uint8 * Size 59 | }; 60 | 61 | enum hp_wmi_commandtype { 62 | HPWMI_DISPLAY_QUERY = 0x01, 63 | HPWMI_HDDTEMP_QUERY = 0x02, 64 | HPWMI_ALS_QUERY = 0x03, 65 | HPWMI_HARDWARE_QUERY = 0x04, // r:WGDS 66 | HPWMI_WIRELESS_QUERY = 0x05, 67 | // 0x06 // w:SBBC, Arg3 68 | HPWMI_BATTERY_QUERY = 0x07, // r:WGBI, ToInteger (DerefOf (Arg3 [Zero])) 69 | HPWMI_BIOS_QUERY = 0x09, // r:GHKS, w:SHKS, Arg3 70 | // 0x0a // r:GHKF, w:SHKF, Arg3 71 | HPWMI_FEATURE_QUERY = 0x0b, 72 | HPWMI_HOTKEY_QUERY = 0x0c, 73 | HPWMI_FEATURE2_QUERY = 0x0d, 74 | // 0x0f // r:GSAS 75 | HPWMI_WIRELESS2_QUERY = 0x1b, // r:WGWS; w:GVWE 76 | // 0x25 // w:CPMC 77 | // 0x28 // r:GTMS; w:STMM, Arg3; Get/SetThermalStatus 78 | // 0x29 // w:HWWB 79 | HPWMI_POSTCODEERROR_QUERY = 0x2a, 80 | // 0x2b // r:WGBC; w:WSBC, Arg1, Arg2, Arg3; Get/SetBatteryControl 81 | // 0x2c // w:STCS, Arg3 82 | // 0x30 // r:GEID 83 | // HPWMI_PEAKSHIFT_QUERY = 0x36 // w:GVWE 84 | // HPWMI_BATTERY_CHARGE_QUERY = 0x37 // w:GVWE 85 | // HPWMI_PEAKSHIFT_QUERY = 0x38 // w:GVWE 86 | // 0x3e // r:GPPS, w:SPPA, Arg3 87 | // 0x3f // w:STPB, Arg3 88 | // 0x42 // r:GAAT 89 | // 0x45 // r:GECP, Arg3; w:SECP, Arg3 90 | // 0x48 // r:EC01; w:EC02, Arg3 91 | HPWMI_THERMAL_POLICY_QUERY = 0x4c, 92 | }; 93 | 94 | enum hp_wmi_command { 95 | HPWMI_READ = 0x01, // VRBC 96 | HPWMI_WRITE = 0x02, // VWBC 97 | HPWMI_ODM = 0x03, // ? 98 | // 0x00020006 // FBCD 99 | // 0x0002000B // USBTypeC Fw 100 | // 0x00020011 // ? 101 | }; 102 | 103 | enum hp_wmi_hardware_mask { 104 | HPWMI_DOCK_MASK = 0x01, 105 | HPWMI_TABLET_MASK = 0x04, 106 | }; 107 | 108 | struct bios_return { // hpqBDataOut 0 / 4 / 128 / 1024 / 4096 109 | UInt32 sigpass; // 0x1, SNOU, Sign, should be uint8 * 4, 0x4C494146 (LIAF) / 0x53534150 (SSAP) 110 | UInt32 return_code; // 0x2, RTCD, rwReturnCode, uint32 111 | // UInt8 data[]; // 0x3, HVWA, Data, uint8 * 0 / 4 / 128 / 1024 / 4096 112 | }; 113 | 114 | enum hp_return_value { 115 | HPWMI_RET_WRONG_SIGNATURE = 0x02, 116 | HPWMI_RET_UNKNOWN_COMMAND = 0x03, 117 | HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, 118 | HPWMI_RET_INVALID_PARAMETERS = 0x05, 119 | }; 120 | 121 | enum hp_wireless2_bits { 122 | HPWMI_POWER_STATE = 0x01, 123 | HPWMI_POWER_SOFT = 0x02, 124 | HPWMI_POWER_BIOS = 0x04, 125 | HPWMI_POWER_HARD = 0x08, 126 | HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD, 127 | }; 128 | 129 | #define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW) 130 | #define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) 131 | 132 | struct bios_rfkill2_device_state { 133 | UInt8 radio_type; 134 | UInt8 bus_type; 135 | UInt16 vendor_id; 136 | UInt16 product_id; 137 | UInt16 subsys_vendor_id; 138 | UInt16 subsys_product_id; 139 | UInt8 rfkill_id; 140 | UInt8 power; 141 | UInt8 unknown[4]; 142 | }; 143 | 144 | /* 7 devices fit into the 128 byte buffer */ 145 | #define HPWMI_MAX_RFKILL2_DEVICES 7 146 | 147 | struct bios_rfkill2_state { 148 | UInt8 unknown[7]; 149 | UInt8 count; 150 | UInt8 pad[8]; 151 | struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; 152 | }; 153 | 154 | class DYVPC : public YogaVPC 155 | { 156 | typedef YogaVPC super; 157 | OSDeclareDefaultStructors(DYVPC) 158 | 159 | private: 160 | bool probeVPC(IOService *provider) APPLE_KEXT_OVERRIDE; 161 | bool initVPC() APPLE_KEXT_OVERRIDE; 162 | void setPropertiesGated(OSObject* props) APPLE_KEXT_OVERRIDE; 163 | // void updateAll() APPLE_KEXT_OVERRIDE; 164 | void updateVPC(UInt32 event) APPLE_KEXT_OVERRIDE; 165 | bool exitVPC() APPLE_KEXT_OVERRIDE; 166 | 167 | /** 168 | * Initialize VPC EC status 169 | * 170 | * @return true if success 171 | */ 172 | bool initEC(); 173 | 174 | IOService* initWMI(WMI *instance) APPLE_KEXT_OVERRIDE; 175 | bool examineWMI(IOService *provider) APPLE_KEXT_OVERRIDE; 176 | 177 | /** 178 | * input capability 179 | */ 180 | bool inputCap {false}; 181 | 182 | /** 183 | * BIOS setting capability 184 | */ 185 | bool BIOSCap {false}; 186 | 187 | /** 188 | * size of read command data 189 | */ 190 | OSArray *readCommandDateSize {nullptr}; 191 | 192 | /** 193 | * size of write command data 194 | */ 195 | OSArray *writeCommandDateSize {nullptr}; 196 | 197 | /** 198 | * BIOS setting capability 199 | * 200 | * @param query commandtype 201 | * @param buffer result 202 | * @param command hp_wmi_command 203 | * @param insize input size 204 | * @param outsize output size 205 | * 206 | * @return true on success 207 | */ 208 | bool WMIQuery(UInt32 query, void *buffer, enum hp_wmi_command command = HPWMI_READ, UInt32 insize = sizeof(UInt32), UInt32 outsize = sizeof(UInt32)); 209 | 210 | public: 211 | IOReturn message(UInt32 type, IOService *provider, void *argument) APPLE_KEXT_OVERRIDE; 212 | }; 213 | #endif /* DYVPC_hpp */ 214 | -------------------------------------------------------------------------------- /YogaSMC/YogaVPC/ThinkEvents.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // ThinkEvents.h 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 10/15/20. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | #ifndef ThinkEvents_h 11 | #define ThinkEvents_h 12 | 13 | #include 14 | 15 | // mostly from linux/drivers/platform/x86/thinkpad_acpi.c 16 | 17 | /* HKEY events */ 18 | enum tpacpi_hkey_event_t : UInt32 { 19 | /* Hotkey-related */ 20 | TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* First hotkey (FN+F1, _Q10) */ 21 | TP_HKEY_EV_SLEEP = 0x1004, /* Sleep button (F4, _Q13) */ 22 | TP_HKEY_EV_NETWORK = 0x1005, /* Network (F8, _Q64) */ 23 | TP_HKEY_EV_DISPLAY = 0x1007, /* Dual Display (F7, _Q16/_Q19) */ 24 | TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up (F6, _Q14/_Q1C) */ 25 | TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down (F5, _Q15/_Q1D) */ 26 | TP_HKEY_EV_KBD_LIGHT = 0x1012, /* Thinklight/kbd backlight (Fn+Space, _Q1F) */ 27 | TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */ 28 | TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ 29 | TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ 30 | TP_HKEY_EV_MIC_MUTE = 0x101B, /* Microphone Mute (F4, _Q6A) */ 31 | TP_HKEY_EV_SETTING = 0x101D, /* Settings (F9, _Q66) */ 32 | TP_HKEY_EV_SEARCH = 0x101E, /* Search (F10, _Q67) */ 33 | TP_HKEY_EV_MISSION = 0x101F, /* All open apps/Mission Control (F11, _Q68) */ 34 | TP_HKEY_EV_APPS = 0x1020, /* All programs/Launchpad (F12, _Q69) */ 35 | 36 | /* Hotkey-related (preset masks) */ 37 | TP_HKEY_EV_STAR = 0x1311, /* Star (F12, _Q62) */ 38 | TP_HKEY_EV_BLUETOOTH = 0x1314, /* Bluetooth (F10, _Q60) */ 39 | TP_HKEY_EV_KEYBOARD = 0x1315, /* Keyboard (F11, _Q61) */ 40 | 41 | /* Reasons for waking up from S3/S4 */ 42 | TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */ 43 | TP_HKEY_EV_WKUP_S4_UNDOCK = 0x2404, /* undock requested, S4 */ 44 | TP_HKEY_EV_WKUP_S3_BAYEJ = 0x2305, /* bay ejection req, S3 */ 45 | TP_HKEY_EV_WKUP_S4_BAYEJ = 0x2405, /* bay ejection req, S4 */ 46 | TP_HKEY_EV_WKUP_S3_BATLOW = 0x2313, /* battery empty, S3 */ 47 | TP_HKEY_EV_WKUP_S4_BATLOW = 0x2413, /* battery empty, S4 */ 48 | 49 | /* Auto-sleep after eject request */ 50 | TP_HKEY_EV_BAYEJ_ACK = 0x3003, /* bay ejection complete */ 51 | TP_HKEY_EV_UNDOCK_ACK = 0x4003, /* undock complete */ 52 | 53 | /* Misc bay events */ 54 | TP_HKEY_EV_OPTDRV_EJ = 0x3006, /* opt. drive tray ejected */ 55 | TP_HKEY_EV_HOTPLUG_DOCK = 0x4010, /* docked into hotplug dock or port replicator */ 56 | TP_HKEY_EV_HOTPLUG_UNDOCK = 0x4011, /* undocked from hotplug dock or port replicator */ 57 | 58 | /* User-interface events */ 59 | TP_HKEY_EV_LID_CLOSE = 0x5001, /* laptop lid closed */ 60 | TP_HKEY_EV_LID_OPEN = 0x5002, /* laptop lid opened */ 61 | TP_HKEY_EV_TABLET_TABLET = 0x5009, /* tablet swivel up */ 62 | TP_HKEY_EV_TABLET_NOTEBOOK = 0x500A, /* tablet swivel down */ 63 | TP_HKEY_EV_TABLET_CHANGED = 0x60C0, /* X1 Yoga (2016): 64 | * enter/leave tablet mode 65 | */ 66 | TP_HKEY_EV_PEN_INSERTED = 0x500B, /* tablet pen inserted */ 67 | TP_HKEY_EV_PEN_REMOVED = 0x500C, /* tablet pen removed */ 68 | TP_HKEY_EV_BRGHT_CHANGED = 0x5010, /* backlight control event */ 69 | 70 | /* Key-related user-interface events */ 71 | TP_HKEY_EV_KEY_NUMLOCK = 0x6000, /* NumLock key pressed */ 72 | TP_HKEY_EV_KEY_FN = 0x6005, /* Fn key pressed? E420 */ 73 | TP_HKEY_EV_KEY_FN_ESC = 0x6060, /* Fn+Esc key pressed X240 */ 74 | 75 | 76 | /* Thermal events */ 77 | TP_HKEY_EV_ALARM_BAT_HOT = 0x6011, /* battery too hot */ 78 | TP_HKEY_EV_ALARM_BAT_XHOT = 0x6012, /* battery critically hot */ 79 | TP_HKEY_EV_ALARM_SENSOR_HOT = 0x6021, /* sensor too hot */ 80 | TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */ 81 | TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* windows; thermal table changed */ 82 | TP_HKEY_EV_THM_CSM_COMPLETED = 0x6032, /* windows; thermal control set 83 | * command completed. Related to 84 | * AML DYTC */ 85 | TP_HKEY_EV_BACKLIGHT_CHANGED = 0x6050, /* backlight changed */ 86 | TP_HKEY_EV_LID_STATUS_CHANGED = 0x60D0, /* lid status changed */ 87 | TP_HKEY_EV_THM_TRANSFM_CHANGED = 0x60F0, /* windows; thermal transformation 88 | * changed. Related to AML GMTS */ 89 | 90 | /* AC-related events */ 91 | TP_HKEY_EV_AC_CHANGED = 0x6040, /* AC status changed */ 92 | 93 | /* Further user-interface events */ 94 | TP_HKEY_EV_PALM_DETECTED = 0x60B0, /* palm hoveres keyboard */ 95 | TP_HKEY_EV_PALM_UNDETECTED = 0x60B1, /* palm removed */ 96 | 97 | /* Misc */ 98 | TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */ 99 | }; 100 | 101 | #endif /* ThinkEvents_h */ 102 | -------------------------------------------------------------------------------- /YogaSMC/YogaVPC/YogaHIDD.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // YogaHIDD.hpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 11/4/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef YogaHIDD_hpp 10 | #define YogaHIDD_hpp 11 | 12 | #include "YogaVPC.hpp" 13 | 14 | // from linux/drivers/platform/x86/intel-hid.c 15 | 16 | #define HID_EVENT_FILTER_UUID "eeec56b3-4442-408f-a792-4edd4d758054" 17 | #define HIDD_DSM_REVISION 1 18 | 19 | enum intel_hid_dsm_fn_codes { 20 | INTEL_HID_DSM_FN_INVALID, 21 | INTEL_HID_DSM_BTNL_FN, 22 | INTEL_HID_DSM_HDMM_FN, 23 | INTEL_HID_DSM_HDSM_FN, 24 | INTEL_HID_DSM_HDEM_FN, 25 | INTEL_HID_DSM_BTNS_FN, 26 | INTEL_HID_DSM_BTNE_FN, 27 | INTEL_HID_DSM_HEBC_V1_FN, 28 | INTEL_HID_DSM_VGBS_FN, 29 | INTEL_HID_DSM_HEBC_V2_FN, 30 | INTEL_HID_DSM_FN_MAX 31 | }; 32 | 33 | static const char *intel_hid_dsm_fn_to_method[INTEL_HID_DSM_FN_MAX] = { 34 | NULL, 35 | "BTNL", 36 | "HDMM", 37 | "HDSM", 38 | "HDEM", 39 | "BTNS", 40 | "BTNE", 41 | "HEBC", 42 | "VGBS", 43 | "HEEC" 44 | }; 45 | 46 | class YogaHIDD : public YogaVPC 47 | { 48 | typedef YogaVPC super; 49 | OSDeclareDefaultStructors(YogaHIDD) 50 | 51 | bool initVPC() APPLE_KEXT_OVERRIDE; 52 | bool exitVPC() APPLE_KEXT_OVERRIDE; 53 | 54 | bool initDSM(); 55 | 56 | /** 57 | * Evaluate _DSM for specific GUID and function index. 58 | * @param index Function index 59 | * @param result The return is a buffer containing one bit for each function index if Function Index is zero, otherwise could be any data object (See 9.1.1 _DSM (Device Specific Method) in ACPI Specification, Version 6.3) 60 | * @param arg argument array 61 | * @param uuid Human-readable GUID string (big-endian) 62 | * @param revision _DSM revision 63 | * 64 | * @return *kIOReturnSuccess* upon a successfull *_DSM* parse, otherwise failed when executing *evaluateObject*. 65 | */ 66 | IOReturn evaluateDSM(UInt32 index, OSObject **result, OSArray *arg=nullptr, const char *uuid=HID_EVENT_FILTER_UUID, UInt32 revision=HIDD_DSM_REVISION); 67 | 68 | /** 69 | * Evaluate _DSM for specific GUID and function index. 70 | * @param index Function index 71 | * @param value pointer to value 72 | * @param arg argument 73 | * 74 | * @return *kIOReturnSuccess* upon a successfull *_DSM* parse, otherwise failed when executing *evaluateObject*. 75 | */ 76 | IOReturn evaluateHIDD(intel_hid_dsm_fn_codes index, UInt64 *value, SInt64 arg=-1); 77 | 78 | /** 79 | * Fn mask 80 | */ 81 | UInt64 fn_mask {0}; 82 | 83 | /** 84 | * 5 button array or v2 power button capability, will be update on init 85 | */ 86 | UInt64 arrayCap {0}; 87 | 88 | /** 89 | * Initialize 5 button array or v2 power button 90 | */ 91 | void initButtonArray(); 92 | 93 | public: 94 | IOReturn message(UInt32 type, IOService *provider, void *argument) APPLE_KEXT_OVERRIDE; 95 | IOReturn setPowerState(unsigned long powerStateOrdinal, IOService * whatDevice) APPLE_KEXT_OVERRIDE; 96 | }; 97 | 98 | #endif /* YogaHIDD_hpp */ 99 | -------------------------------------------------------------------------------- /YogaSMC/YogaWMI.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // YogaWMI.cpp 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 2020/5/21. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | #include "YogaWMI.hpp" 11 | 12 | OSDefineMetaClassAndStructors(YogaWMI, YogaBaseService) 13 | 14 | IOService *YogaWMI::probe(IOService *provider, SInt32 *score) 15 | { 16 | if (!super::probe(provider, score)) 17 | return nullptr; 18 | 19 | DebugLog("Probing"); 20 | 21 | OSObject *uid = nullptr; 22 | IOACPIPlatformDevice *dev = OSDynamicCast(IOACPIPlatformDevice, provider); 23 | if (dev && dev->evaluateObject("_UID", &uid) == kIOReturnSuccess) { 24 | OSString *str = OSDynamicCast(OSString, uid); 25 | if (str && str->isEqualTo("TBFP")) { 26 | DebugLog("Skip Thunderbolt interface"); 27 | return nullptr; 28 | } 29 | } 30 | OSSafeReleaseNULL(uid); 31 | 32 | return this; 33 | } 34 | 35 | void YogaWMI::getNotifyID(const char *key) { 36 | OSDictionary *item = OSDynamicCast(OSDictionary, Event->getObject(key)); 37 | if (!item) { 38 | AlwaysLog("found unparsed notify id %s", key); 39 | return; 40 | } 41 | 42 | OSNumber *num = OSDynamicCast(OSNumber, item->getObject(kWMINotifyId)); 43 | if (num == nullptr) { 44 | AlwaysLog("found invalid notify id %s", key); 45 | return; 46 | } 47 | 48 | UInt32 id; 49 | sscanf(key, "%2x", &id); 50 | if (id != num->unsigned8BitValue()) 51 | AlwaysLog("notify id %s mismatch %x", key, num->unsigned8BitValue()); 52 | 53 | const char *rname = nullptr; 54 | OSString *guid = OSDynamicCast(OSString, item->getObject("guid")); 55 | if (guid) 56 | rname = registerEvent(guid, id); 57 | 58 | OSDictionary *mof = OSDynamicCast(OSDictionary, item->getObject("MOF")); 59 | if (!mof) { 60 | AlwaysLog("found %s notify id 0x%x with no description", rname ? rname : "unknown", id); 61 | return; 62 | } 63 | 64 | OSString *cname = OSDynamicCast(OSString, mof->getObject("__CLASS")); 65 | if (!cname) { 66 | AlwaysLog("found %s notify id 0x%x with no __CLASS", rname ? rname : "unknown", id); 67 | return; 68 | } 69 | 70 | AlwaysLog("found %s notify id 0x%x for %s", rname ? rname : "unknown", id, cname->getCStringNoCopy()); 71 | // TODO: Event Enable and Disable WExx; Data Collection Enable and Disable WCxx 72 | } 73 | 74 | bool YogaWMI::start(IOService *provider) 75 | { 76 | if (!YWMI) { 77 | auto key = OSSymbol::withCString("YogaWMISupported"); 78 | auto dict = IOService::propertyMatching(key, kOSBooleanTrue); 79 | key->release(); 80 | if (!dict) { 81 | DebugLog("Failed to create matching dictionary"); 82 | return false; 83 | } 84 | 85 | auto vpc = IOService::waitForMatchingService(dict, 2000000000); 86 | dict->release(); 87 | if (vpc) { 88 | vpc->release(); 89 | DebugLog("YogaWMI variant available, exiting"); 90 | return false; 91 | } 92 | } 93 | 94 | if (!super::start(provider)) 95 | return false; 96 | 97 | DebugLog("Starting"); 98 | 99 | if (!YWMI) { 100 | YWMI = new WMI(provider); 101 | YWMI->initialize(); 102 | } 103 | YWMI->start(); 104 | processWMI(); 105 | 106 | Event = YWMI->getEvent(); 107 | if (Event) { 108 | setProperty("Event", Event); 109 | OSCollectionIterator* i = OSCollectionIterator::withCollection(Event); 110 | 111 | if (i) { 112 | while (OSString* key = OSDynamicCast(OSString, i->getNextObject())) 113 | getNotifyID(key->getCStringNoCopy()); 114 | i->release(); 115 | } 116 | } 117 | 118 | return true; 119 | } 120 | 121 | void YogaWMI::stop(IOService *provider) 122 | { 123 | DebugLog("Stopping"); 124 | 125 | if (YWMI) 126 | delete YWMI; 127 | 128 | super::stop(provider); 129 | } 130 | 131 | void YogaWMI::ACPIEvent(UInt32 argument) { 132 | AlwaysLog("message: Unknown ACPI Notification 0x%02X", argument); 133 | } 134 | 135 | IOReturn YogaWMI::message(UInt32 type, IOService *provider, void *argument) { 136 | switch (type) 137 | { 138 | case kIOACPIMessageDeviceNotification: 139 | if (argument) 140 | ACPIEvent(*(reinterpret_cast(argument))); 141 | else 142 | AlwaysLog("message: ACPI provider=%s, unknown argument", provider->getName()); 143 | break; 144 | 145 | default: 146 | if (argument) 147 | AlwaysLog("message: type=%x, provider=%s, argument=0x%04x", type, provider->getName(), *(reinterpret_cast(argument))); 148 | else 149 | AlwaysLog("message: type=%x, provider=%s, unknown argument", type, provider->getName()); 150 | } 151 | return kIOReturnSuccess; 152 | } 153 | -------------------------------------------------------------------------------- /YogaSMC/YogaWMI.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // YogaWMI.hpp 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 2020/5/21. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | #ifndef YogaWMI_hpp 11 | #define YogaWMI_hpp 12 | 13 | #include "YogaBaseService.hpp" 14 | 15 | class YogaWMI : public YogaBaseService 16 | { 17 | typedef YogaBaseService super; 18 | OSDeclareDefaultStructors(YogaWMI) 19 | 20 | protected: 21 | /** 22 | * Find notify id and other properties of an event 23 | * 24 | * @param key name of event dictionary 25 | */ 26 | void getNotifyID(const char *key); 27 | 28 | /** 29 | * Vendor specific WMI analyze 30 | */ 31 | inline virtual void processWMI() {}; 32 | 33 | OSDictionary *Event {nullptr}; 34 | 35 | /** 36 | * Register WMI event id 37 | * 38 | * @param guid WMI GUID 39 | * @param id WMI event id 40 | * 41 | * @return event name if available 42 | */ 43 | inline virtual const char* registerEvent(OSString *guid, UInt32 id) {return nullptr;}; 44 | 45 | /** 46 | * Corresponding event to trigger after receiving a message 47 | * 48 | * @param argument argument of message 49 | */ 50 | virtual void ACPIEvent(UInt32 argument); 51 | 52 | public: 53 | virtual IOService *probe(IOService *provider, SInt32 *score) APPLE_KEXT_OVERRIDE; 54 | 55 | virtual bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 56 | virtual void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 57 | 58 | virtual IOReturn message(UInt32 type, IOService *provider, void *argument) APPLE_KEXT_OVERRIDE; 59 | static YogaWMI *withIdeaWMI(WMI *provider); 60 | static YogaWMI *withDYWMI(WMI *provider); 61 | }; 62 | 63 | #endif /* YogaWMI_hpp */ 64 | -------------------------------------------------------------------------------- /YogaSMC/YogaWMI/DYWMI.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // DYWMI.cpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 4/24/21. 6 | // Copyright © 2021 Zhen. All rights reserved. 7 | // 8 | 9 | #include "DYWMI.hpp" 10 | 11 | extern "C" 12 | { 13 | #include 14 | } 15 | 16 | OSDefineMetaClassAndStructors(DYWMI, YogaWMI); 17 | 18 | YogaWMI* YogaWMI::withDYWMI(WMI *provider) { 19 | YogaWMI* dev = nullptr; 20 | 21 | if (provider->hasMethod(SENSOR_DATA_WMI_ARRAY, 0)) 22 | dev = OSTypeAlloc(DYWMI); 23 | else 24 | dev = OSTypeAlloc(YogaWMI); 25 | 26 | OSDictionary *dictionary = OSDictionary::withCapacity(1); 27 | 28 | dev->iname = provider->getName(); 29 | dev->YWMI = provider; 30 | 31 | if (!dev->init(dictionary)) 32 | OSSafeReleaseNULL(dev); 33 | 34 | dictionary->release(); 35 | return dev; 36 | } 37 | 38 | IOReturn DYWMI::setProperties(OSObject *props) { 39 | commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &DYWMI::setPropertiesGated), props); 40 | return kIOReturnSuccess; 41 | } 42 | 43 | void DYWMI::setPropertiesGated(OSObject* props) { 44 | OSDictionary* dict = OSDynamicCast(OSDictionary, props); 45 | if (!dict) 46 | return; 47 | 48 | OSCollectionIterator* i = OSCollectionIterator::withCollection(dict); 49 | if (i) { 50 | while (OSString* key = OSDynamicCast(OSString, i->getNextObject())) { 51 | if (key->isEqualTo(updateSensorPrompt)) { 52 | OSNumber *value; 53 | getPropertyNumber(updateSensorPrompt); 54 | 55 | if (sensorRange <= value->unsigned8BitValue()) { 56 | AlwaysLog(valueMatched, updateSensorPrompt, sensorRange); 57 | continue; 58 | } 59 | 60 | OSObject *result; 61 | if (getSensorInfo(value->unsigned8BitValue(), &result)) { 62 | char sensorName[10]; 63 | snprintf(sensorName, 10, "Sensor %d", value->unsigned8BitValue()); 64 | if (result) { 65 | setProperty(sensorName, result); 66 | DebugLog(toggleSuccess, updateSensorPrompt, value->unsigned8BitValue(), "see ioreg"); 67 | } else { 68 | AlwaysLog("%s empty result, please check the method return", sensorName); 69 | } 70 | } else { 71 | AlwaysLog(toggleFailure, updateSensorPrompt); 72 | } 73 | OSSafeReleaseNULL(result); 74 | } else { 75 | AlwaysLog("Unknown property %s", key->getCStringNoCopy()); 76 | } 77 | } 78 | i->release(); 79 | } 80 | 81 | return; 82 | } 83 | 84 | void DYWMI::processWMI() { 85 | setProperty("Feature", feature); 86 | sensorRange = YWMI->getInstanceCount(SENSOR_DATA_WMI_ARRAY); 87 | registerService(); 88 | } 89 | 90 | bool DYWMI::getSensorInfo(UInt8 index, OSObject **result) { 91 | IOReturn ret; 92 | 93 | ret = YWMI->queryBlock(SENSOR_DATA_WMI_ARRAY, index, result, true); 94 | if (ret != kIOReturnSuccess) 95 | AlwaysLog("Sensor %d evaluation failed", index); 96 | 97 | return (ret == kIOReturnSuccess); 98 | } 99 | 100 | const char* DYWMI::registerEvent(OSString *guid, UInt32 id) { 101 | if (guid->isEqualTo(SENSOR_EVENT_WMI_METHOD)) { 102 | sensorEvent = id; 103 | setProperty("Sensor Event", true); 104 | return feature; 105 | } 106 | return nullptr; 107 | } 108 | 109 | bool DisplayUserNotification(OSString *name, OSString *desc, unsigned int flags) { 110 | kern_return_t notificationError; 111 | char header[20]; 112 | strlcpy(header, name->getCStringNoCopy(), sizeof(header)); 113 | char message[50]; 114 | strlcpy(message, desc->getCStringNoCopy(), sizeof(message)); 115 | notificationError = KUNCUserNotificationDisplayNotice(flags < kKUNCPlainAlertLevel ? 0 : 2 , 116 | flags, 117 | (char *) "", 118 | (char *) "", 119 | (char *) "", 120 | header, 121 | message, 122 | (char *) "OK"); 123 | return (notificationError == kIOReturnSuccess); 124 | } 125 | 126 | void DYWMI::processBIOSEvent(OSObject *result) { 127 | OSArray *arr = OSDynamicCast(OSArray, result); 128 | if (!arr) { 129 | DebugLog("Unknown event"); 130 | return; 131 | } 132 | 133 | OSString *name = OSDynamicCast(OSString, arr->getObject(0)); 134 | if (!name) { 135 | DebugLog("Unknown event name"); 136 | return; 137 | } 138 | 139 | OSString *desc = OSDynamicCast(OSString, arr->getObject(1)); 140 | if (!desc) { 141 | DebugLog("Unknown event description"); 142 | return; 143 | } 144 | 145 | OSNumber *severity = OSDynamicCast(OSNumber, arr->getObject(3)); 146 | if (severity == nullptr) { 147 | DebugLog("Unknown event severity"); 148 | return; 149 | } 150 | 151 | unsigned int flags = 0; 152 | switch (severity->unsigned8BitValue()) { 153 | case EVENT_SEVERITY_UNKNOWN: 154 | flags = kKUNCCautionAlertLevel; 155 | break; 156 | 157 | case EVENT_SEVERITY_OK: 158 | flags = kKUNCPlainAlertLevel; 159 | break; 160 | 161 | case EVENT_SEVERITY_WARNING: 162 | flags = kKUNCNoteAlertLevel; 163 | break; 164 | 165 | default: 166 | flags = kKUNCStopAlertLevel; 167 | break; 168 | } 169 | #ifndef DEBUG 170 | if (flags < kKUNCPlainAlertLevel) { 171 | #endif 172 | AlwaysLog("BIOS Event: %s - %s - %d", name->getCStringNoCopy(), desc->getCStringNoCopy(), severity->unsigned8BitValue()); 173 | if (!DisplayUserNotification(name, desc, flags)) 174 | AlwaysLog("Failed to send BIOS Event"); 175 | #ifndef DEBUG 176 | } 177 | #endif 178 | } 179 | 180 | void DYWMI::ACPIEvent(UInt32 argument) { 181 | if (argument != sensorEvent) { 182 | super::ACPIEvent(argument); 183 | return; 184 | } 185 | 186 | OSObject *result; 187 | if (!YWMI->getEventData(argument, &result)) 188 | AlwaysLog("message: Unknown ACPI notification 0x%04X", argument); 189 | else 190 | processBIOSEvent(result); 191 | OSSafeReleaseNULL(result); 192 | } 193 | -------------------------------------------------------------------------------- /YogaSMC/YogaWMI/DYWMI.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // DYWMI.hpp 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 4/24/21. 6 | // Copyright © 2021 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef DYWMI_hpp 10 | #define DYWMI_hpp 11 | 12 | #include "YogaWMI.hpp" 13 | 14 | #define SENSOR_DATA_WMI_ARRAY "8f1f6435-9f42-42c8-badc-0e9424f20c9a" 15 | #define SENSOR_EVENT_WMI_METHOD "2b814318-4be8-4707-9d84-a190a859b5d0" 16 | 17 | #define kIOACPIMessageDYSensor 0xd0 18 | 19 | #define EVENT_SEVERITY_UNKNOWN 0 20 | #define EVENT_SEVERITY_OK 5 21 | #define EVENT_SEVERITY_WARNING 10 22 | 23 | /** 24 | * BIOS Event (index start from 1) 25 | * 26 | * 0x1: Name 27 | * 0x2: Description 28 | * 0x3: Category 29 | * 0: Unknown 30 | * 1: Configuration Change 31 | * 2: Button Pressed 32 | * 3: Sensor 33 | * 4: BIOS Settings 34 | * 0x4: Severity 35 | * 0: Unknown 36 | * 5: OK 37 | * 10: Degraded/Warning 38 | * 15: Minor Failure 39 | * 20: Major Failure 40 | * 25: Critical Failure 41 | * 30: Non-recoverable Error 42 | * 0x5: Status 43 | * 0: Unknown 44 | * 1: Other 45 | * 2: OK 46 | * 4: Stressed 47 | * 5: Predictive Failure 48 | * 10: Stopped 49 | * 12: Aborted 50 | */ 51 | 52 | class DYWMI : public YogaWMI 53 | { 54 | typedef YogaWMI super; 55 | OSDeclareDefaultStructors(DYWMI) 56 | 57 | private: 58 | static constexpr const char *feature = "Sensor"; 59 | UInt32 sensorEvent {0xa0}; 60 | 61 | void processBIOSEvent(OSObject *result); 62 | 63 | UInt8 sensorRange {0}; 64 | 65 | /** 66 | * Parse Sensor Info 67 | * 68 | * @param index sensor info to be executed 69 | * @param result sensor info 70 | * 71 | * @return true if success 72 | */ 73 | bool getSensorInfo (UInt8 index, OSObject **result); 74 | 75 | virtual void setPropertiesGated(OSObject* props); 76 | 77 | #ifndef ALTER 78 | friend class DYSMC; 79 | #endif 80 | 81 | void processWMI() APPLE_KEXT_OVERRIDE; 82 | void ACPIEvent(UInt32 argument) APPLE_KEXT_OVERRIDE; 83 | const char* registerEvent(OSString *guid, UInt32 id) APPLE_KEXT_OVERRIDE; 84 | 85 | public: 86 | virtual IOReturn setProperties(OSObject* props) APPLE_KEXT_OVERRIDE; 87 | }; 88 | #endif /* DYWMI_hpp */ 89 | -------------------------------------------------------------------------------- /YogaSMC/YogaWMI/IdeaWMI.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // IdeaWMI.hpp 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 2020/6/23. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | #ifndef IdeaWMI_hpp 11 | #define IdeaWMI_hpp 12 | 13 | #include "YogaWMI.hpp" 14 | 15 | #define WBAT_BAT0_BatMaker 0 16 | #define WBAT_BAT0_HwId 1 17 | #define WBAT_BAT0_MfgDate 2 18 | #define WBAT_BAT1_BatMaker 3 19 | #define WBAT_BAT1_HwId 4 20 | #define WBAT_BAT1_MfgDate 5 21 | 22 | #define BAT_INFO_WMI_STRING "c3a03776-51ac-49aa-ad0f-f2f7d62c3f3c" 23 | #define GSENSOR_DATA_WMI_METHOD "09b0ee6e-c3fd-4243-8da1-7911ff80bb8c" 24 | #define GSENSOR_WMI_EVENT "06129d99-6083-4164-81ad-f092f9d773a6" 25 | #define GSENSOR_DATA_WMI_METHOD_EXT "abbc0f6f-8ea1-11d1-00a0-c90629100000" 26 | #define GSENSOR_WMI_EVENT_EXT "abbc0f72-8ea1-11d1-00a0-c90629100000" 27 | #define PAPER_LOOKING_WMI_EVENT "56322276-8493-4ce8-a783-98c991274f5e" 28 | #define GAME_ZONE_DATA_WMI_METHOD "887b54e3-dddc-4b2c-8b88-68a26a8835d0" 29 | #define GAME_ZONE_TEMP_WMI_EVENT "bfd42481-aee3-4501-a107-afb68425c5f8" 30 | #define GAME_ZONE_OC_WMI_EVENT "d062906b-12d4-4510-999d-4831ee80e985" 31 | #define GAME_ZONE_GPU_WMI_EVENT "bfd42481-aee3-4502-a107-afb68425c5f8" 32 | #define GAME_ZONE_FAN_WMI_EVENT "bc72a435-e8c1-4275-b3e2-d8b8074aba59" 33 | #define GAME_ZONE_KEY_WMI_EVENT "10afc6d9-ea8b-4590-a2e7-1cd3c84bb4b1" 34 | #define SUPER_RES_DATA_WMI_METHOD "77e614ed-f19e-46d6-a613-a8669fee1ff0" 35 | #define SUPER_RES_WMI_EVENT "95d1df76-d6c0-4e16-9193-7b2a849f3df2" 36 | 37 | enum { 38 | GAME_ZONE_WMI_GET_TMP_IR = 0x01, 39 | GAME_ZONE_WMI_GET_THERMAL_TABLE = 0x02, 40 | GAME_ZONE_WMI_SET_THERMAL_TABLE = 0x03, 41 | GAME_ZONE_WMI_CAP_GPU_OC = 0x04, 42 | GAME_ZONE_WMI_GET_GPU_GPS = 0x05, 43 | GAME_ZONE_WMI_SET_GPU_GPS = 0x06, 44 | GAME_ZONE_WMI_GET_FAN_NUM = 0x07, 45 | GAME_ZONE_WMI_GET_FAN_ONE = 0x08, 46 | GAME_ZONE_WMI_GET_FAN_TWO = 0x09, 47 | GAME_ZONE_WMI_GET_FAN_MAX = 0x0A, 48 | GAME_ZONE_WMI_GET_VERSION = 0x0B, 49 | GAME_ZONE_WMI_CAP_FAN_COOLING = 0x0C, 50 | GAME_ZONE_WMI_SET_FAN_COOLING = 0x0D, 51 | GAME_ZONE_WMI_CAP_CPU_OC = 0x0E, 52 | GAME_ZONE_WMI_CAP_BIOS_OC = 0x0F, 53 | GAME_ZONE_WMI_SET_BIOS_OC = 0x10, 54 | GAME_ZONE_WMI_GET_TMP_TRIGGER = 0x11, 55 | GAME_ZONE_WMI_GET_TMP_CPU = 0x12, 56 | GAME_ZONE_WMI_GET_TMP_GPU = 0x13, 57 | GAME_ZONE_WMI_GET_FAN_STA = 0x14, 58 | GAME_ZONE_WMI_CAP_WIN_KEY = 0x15, 59 | GAME_ZONE_WMI_SET_WIN_KEY = 0x16, 60 | GAME_ZONE_WMI_GET_WIN_KEY = 0x17, 61 | GAME_ZONE_WMI_CAP_TOUCHPAD = 0x18, 62 | GAME_ZONE_WMI_SET_TOUCHPAD = 0x19, 63 | GAME_ZONE_WMI_GET_TOUCHPAD = 0x1A, 64 | GAME_ZONE_WMI_GET_GPU_NORM_MAX = 0x1B, 65 | GAME_ZONE_WMI_GET_GPU_OC_MAX = 0x1C, 66 | GAME_ZONE_WMI_GET_GPU_OC_TYPE = 0x1D, 67 | GAME_ZONE_WMI_CAP_KEYBOARD = 0x1E, 68 | GAME_ZONE_WMI_CAP_MEM_OC_INFO = 0x1F, 69 | GAME_ZONE_WMI_CAP_WATER_COOLING = 0x20, 70 | GAME_ZONE_WMI_SET_WATER_COOLING = 0x21, 71 | GAME_ZONE_WMI_GET_WATER_COOLING = 0x22, 72 | GAME_ZONE_WMI_CAP_LIGHTING = 0x23, 73 | GAME_ZONE_WMI_SET_LIGHTING = 0x24, 74 | GAME_ZONE_WMI_GET_LIGHTING = 0x25, 75 | GAME_ZONE_WMI_GET_MARCOKEY_CODE = 0x26, 76 | GAME_ZONE_WMI_GET_MARCOKEY_CNT = 0x27, 77 | GAME_ZONE_WMI_CAP_GSYNC = 0x28, 78 | GAME_ZONE_WMI_GET_GSYNC = 0x29, 79 | GAME_ZONE_WMI_SET_GSYNC = 0x2A, 80 | GAME_ZONE_WMI_CAP_SMARTFAN = 0x2B, 81 | GAME_ZONE_WMI_SET_SMARTFAN_MODE = 0x2C, 82 | GAME_ZONE_WMI_GET_SMARTFAN_MODE = 0x2D, 83 | GAME_ZONE_WMI_GET_SMARTFAN_STA = 0x2F, 84 | GAME_ZONE_WMI_GET_CHARGE_MODE = 0x30, 85 | GAME_ZONE_WMI_CAP_OVER_DRIVE = 0x31, 86 | GAME_ZONE_WMI_GET_OVER_DRIVE = 0x32, 87 | GAME_ZONE_WMI_SET_OVER_DRIVE = 0x33, 88 | GAME_ZONE_WMI_SET_LIGHT_CTL = 0x34, 89 | GAME_ZONE_WMI_SET_DDS_CTL = 0x35, 90 | GAME_ZONE_WMI_RET_OC_VAL = 0x36, 91 | GAME_ZONE_WMI_GET_THERMAL_MODE = 0x37 92 | }; 93 | 94 | enum 95 | { 96 | GSENSOR_DATA_WMI_UASGE_MODE = 1, 97 | GSENSOR_DATA_WMI_AXIS_X = 2, 98 | GSENSOR_DATA_WMI_AXIS_Y = 3, 99 | GSENSOR_DATA_WMI_AXIS_Z = 4, 100 | GSENSOR_DATA_WMI_ANGLE_4 = 5, 101 | GSENSOR_DATA_WMI_ANGLE_5 = 6, 102 | GSENSOR_DATA_WMI_ANGLE_6 = 7 103 | }; 104 | 105 | enum 106 | { 107 | kYogaMode_laptop = 1, // 0-90 degree 108 | kYogaMode_tablet = 2, // 0/360 degree 109 | kYogaMode_stand = 3, // 180-360 degree, ∠ , screen face up 110 | kYogaMode_tent = 4 // 180-360 degree, ∧ , screen upside down, trigger rotation? 111 | } kYogaMode; 112 | 113 | enum 114 | { 115 | SUPER_RES_DATA_WMI_VALUE = 1, 116 | SUPER_RES_DATA_WMI_START = 2, 117 | SUPER_RES_DATA_WMI_STOP = 3, 118 | SUPER_RES_DATA_WMI_CAP = 4, 119 | SUPER_RES_DATA_WMI_RESERVED1 = 5, 120 | SUPER_RES_DATA_WMI_RESERVED2 = 6 121 | }; 122 | 123 | class IdeaWMIYoga : public YogaWMI 124 | { 125 | typedef YogaWMI super; 126 | OSDeclareDefaultStructors(IdeaWMIYoga) 127 | 128 | static constexpr const char *feature = "Yoga Mode Control"; 129 | UInt32 YogaEvent {0xd0}; 130 | bool extension {false}; 131 | /** 132 | * Current Yoga Mode, see kYogaMode 133 | */ 134 | int YogaMode {kYogaMode_laptop}; 135 | 136 | /** 137 | * Update Yoga Mode 138 | */ 139 | void updateYogaMode(); 140 | 141 | void processWMI() APPLE_KEXT_OVERRIDE; 142 | void ACPIEvent(UInt32 argument) APPLE_KEXT_OVERRIDE; 143 | const char* registerEvent(OSString *guid, UInt32 id) APPLE_KEXT_OVERRIDE; 144 | 145 | public: 146 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 147 | 148 | IOReturn setPowerState(unsigned long powerStateOrdinal, IOService * whatDevice) APPLE_KEXT_OVERRIDE; 149 | }; 150 | 151 | class IdeaWMIPaper : public YogaWMI 152 | { 153 | typedef YogaWMI super; 154 | OSDeclareDefaultStructors(IdeaWMIPaper) 155 | 156 | static constexpr const char *feature = "Paper Display"; 157 | UInt32 paperEvent {0x80}; 158 | 159 | void processWMI() APPLE_KEXT_OVERRIDE; 160 | void ACPIEvent(UInt32 argument) APPLE_KEXT_OVERRIDE; 161 | const char* registerEvent(OSString *guid, UInt32 id) APPLE_KEXT_OVERRIDE; 162 | }; 163 | 164 | class IdeaWMISuperRes : public YogaWMI 165 | { 166 | typedef YogaWMI super; 167 | OSDeclareDefaultStructors(IdeaWMISuperRes) 168 | 169 | static constexpr const char *feature = "Super Resolution"; 170 | UInt32 SREvent {0xd0}; 171 | virtual void setPropertiesGated(OSObject* props); 172 | 173 | void processWMI() APPLE_KEXT_OVERRIDE; 174 | void ACPIEvent(UInt32 argument) APPLE_KEXT_OVERRIDE; 175 | const char* registerEvent(OSString *guid, UInt32 id) APPLE_KEXT_OVERRIDE; 176 | 177 | public: 178 | virtual IOReturn setProperties(OSObject* props) APPLE_KEXT_OVERRIDE; 179 | }; 180 | 181 | class IdeaWMIBattery : public YogaWMI 182 | { 183 | typedef YogaWMI super; 184 | OSDeclareDefaultStructors(IdeaWMIBattery) 185 | 186 | /** 187 | * Parse Battery Info 188 | * 189 | * @param method method name to be executed 190 | * @param bat array for status 191 | * 192 | * @return true if success 193 | */ 194 | bool getBatteryInfo (UInt32 index, OSArray *bat); 195 | 196 | void processWMI() APPLE_KEXT_OVERRIDE; 197 | }; 198 | 199 | class IdeaWMIGameZone : public YogaWMI 200 | { 201 | typedef YogaWMI super; 202 | OSDeclareDefaultStructors(IdeaWMIGameZone) 203 | 204 | static constexpr const char *feature = "Game Zone"; 205 | UInt32 tempEvent {0xd0}; 206 | UInt32 OCEvent {0xd1}; 207 | UInt32 GPUEvent {0xe0}; 208 | UInt32 fanEvent {0xe1}; 209 | UInt32 keyEvent {0xe2}; 210 | 211 | bool getGamzeZoneData(UInt32 query, UInt32 *result); 212 | 213 | #ifndef ALTER 214 | friend class IdeaSMC; 215 | #endif 216 | 217 | void processWMI() APPLE_KEXT_OVERRIDE; 218 | void ACPIEvent(UInt32 argument) APPLE_KEXT_OVERRIDE; 219 | const char* registerEvent(OSString *guid, UInt32 id) APPLE_KEXT_OVERRIDE; 220 | }; 221 | 222 | #endif /* IdeaWMI_hpp */ 223 | -------------------------------------------------------------------------------- /YogaSMC/bmfparser.hpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // bmfparser.hpp 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 2020/5/24. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | //#include "common.h" 11 | 12 | #ifndef bmfparser_hpp 13 | #define bmfparser_hpp 14 | 15 | #include 16 | 17 | #define BMF_DATA_WMI_BUFFER "05901221-d566-11d1-b2f0-00a0c9062910" 18 | 19 | enum mof_offset_type { 20 | MOF_OFFSET_UNKNOWN, 21 | MOF_OFFSET_BOOLEAN = 0x01, 22 | MOF_OFFSET_OBJECT = 0x02, 23 | MOF_OFFSET_STRING = 0x03, 24 | MOF_OFFSET_SINT32 = 0x11, 25 | }; 26 | 27 | enum mof_data_type { 28 | MOF_UNKNOWN, 29 | MOF_SINT16 = 0x02, // Unused 30 | MOF_SINT32 = 0x03, 31 | MOF_REAL64 = 0x04, // Unused 32 | MOF_REAL32 = 0x05, // Unused 33 | MOF_STRING = 0x08, 34 | MOF_BOOLEAN = 0x0B, 35 | MOF_OBJECT = 0x0D, 36 | MOF_SINT8 = 0x10, // Unused 37 | MOF_UINT8 = 0x11, // Unused 38 | MOF_UINT16 = 0x12, // Unused 39 | MOF_UINT32 = 0x13, // Unused 40 | MOF_SINT64 = 0x14, // Unused 41 | MOF_UINT64 = 0x15, // Unused 42 | MOF_DATETIME = 0x65, // Unused 43 | MOF_CHAR16 = 0x67, // Unused 44 | }; 45 | 46 | class MOF { 47 | 48 | public: 49 | MOF(char *data, uint32_t size, OSDictionary *mData, const char* name) : buf(data), size(size), mData(mData), wmi_name(name) {}; 50 | MOF(); 51 | OSObject* parse_bmf(); 52 | bool parsed {true}; 53 | private: 54 | const char* wmi_name; 55 | OSString *parse_string(char *buf, uint32_t size); 56 | uint16_t parse_valuemap(uint16_t *buf, bool map, uint32_t i); 57 | uint32_t parse_valuemap(int32_t *buf, bool map, uint32_t i); 58 | 59 | OSDictionary* parse_class(uint32_t *buf); 60 | OSDictionary* parse_method(uint32_t *buf, uint32_t verify = 0); 61 | 62 | int indent {0}; 63 | 64 | char * buf; 65 | uint32_t size; 66 | OSDictionary *mData; 67 | 68 | OSArray* valuemap {nullptr}; 69 | OSDictionary *vmap {nullptr}; 70 | }; 71 | 72 | #endif /* bmfparser_hpp */ 73 | -------------------------------------------------------------------------------- /YogaSMC/common.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // common.h 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 8/23/20. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | #ifndef common_h 11 | #define common_h 12 | 13 | #include 14 | 15 | #ifndef __ACIDANTHERA_MAC_SDK 16 | #error "This kext SDK is unsupported. Download from https://github.com/acidanthera/MacKernelSDK" 17 | #endif 18 | 19 | #ifdef DEBUG 20 | #define DebugLog(str, ...) do { IOLog("YSMC - Debug: %s::%s " str "\n", iname ? iname : "(null)", getName(), ## __VA_ARGS__); } while (0) 21 | #else 22 | #define DebugLog(str, ...) do { } while (0) 23 | #endif 24 | #define AlwaysLog(str, ...) do { IOLog("YSMC - Info: %s::%s " str "\n", iname ? iname : "(null)", getName(), ## __VA_ARGS__); } while (0) 25 | 26 | #define BIT(nr) (1U << (nr)) 27 | 28 | #define ECReadCap BIT(0) 29 | #define ECWriteCap BIT(1) 30 | 31 | #define getPropertyBoolean(prompt) \ 32 | do { \ 33 | value = OSDynamicCast(OSBoolean, dict->getObject(prompt)); \ 34 | if (value == nullptr) { \ 35 | AlwaysLog(valueInvalid, prompt); \ 36 | continue; \ 37 | } \ 38 | } while (0) 39 | 40 | #define getPropertyNumber(prompt) \ 41 | do { \ 42 | value = OSDynamicCast(OSNumber, dict->getObject(prompt)); \ 43 | if (value == nullptr) { \ 44 | AlwaysLog(valueInvalid, prompt); \ 45 | continue; \ 46 | } \ 47 | } while (0) 48 | 49 | #define getPropertyString(prompt) \ 50 | do { \ 51 | value = OSDynamicCast(OSString, dict->getObject(prompt)); \ 52 | if (value == nullptr) { \ 53 | AlwaysLog(valueInvalid, prompt); \ 54 | continue; \ 55 | } \ 56 | } while (0) 57 | 58 | #define setPropertyBoolean(dict, name, boolean) \ 59 | do { dict->setObject(name, boolean ? kOSBooleanTrue : kOSBooleanFalse); } while (0) 60 | 61 | // define a OSNumber(OSObject) *value before use 62 | #define setPropertyNumber(dict, name, number, bits) \ 63 | do { \ 64 | value = OSNumber::withNumber(number, bits); \ 65 | if (value != nullptr) { \ 66 | dict->setObject(name, value); \ 67 | value->release(); \ 68 | } \ 69 | } while (0) 70 | 71 | #define setPropertyString(dict, name, str) \ 72 | do { \ 73 | value = OSString::withCString(str); \ 74 | if (value != nullptr) { \ 75 | dict->setObject(name, value); \ 76 | value->release(); \ 77 | } \ 78 | } while (0) 79 | 80 | #define setPropertyBytes(dict, name, data, len) \ 81 | do { \ 82 | value = OSData::withBytes(data, len); \ 83 | if (value != nullptr) { \ 84 | dict->setObject(name, value); \ 85 | value->release(); \ 86 | } \ 87 | } while (0) 88 | 89 | #define setPropertyObject(dict, name, obj) \ 90 | do { \ 91 | value = obj; \ 92 | if (value != nullptr) { \ 93 | dict->setObject(name, value); \ 94 | value->release(); \ 95 | } \ 96 | } while (0) 97 | 98 | #endif /* common_h */ 99 | -------------------------------------------------------------------------------- /YogaSMC/message.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // 3 | // message.h 4 | // YogaSMC 5 | // 6 | // Created by Zhen on 7/26/20. 7 | // Copyright © 2020 Zhen. All rights reserved. 8 | // 9 | 10 | #ifndef message_h 11 | #define message_h 12 | 13 | #define kIOACPIMessageReserved 0x80 14 | 15 | #define PnpDeviceIdEC "PNP0C09" 16 | #define PnpDeviceIdWMI "PNP0C14" 17 | 18 | #define kIOPMNumberPowerStates 2 19 | 20 | static IOPMPowerState IOPMPowerStates[kIOPMNumberPowerStates] = { 21 | {1, kIOServicePowerCapabilityOff, kIOServicePowerCapabilityOff, kIOServicePowerCapabilityOff, 0, 0, 0, 0, 0, 0, 0, 0}, 22 | {1, kIOServicePowerCapabilityOn, kIOServicePowerCapabilityOn, kIOServicePowerCapabilityOn, 0, 0, 0, 0, 0, 0, 0, 0} 23 | }; 24 | 25 | #define kDeliverNotifications "RM,deliverNotifications" 26 | 27 | #define alwaysOnUSBPrompt "AlwaysOnUSBMode" 28 | #define autoBacklightPrompt "AutoBacklight" 29 | #define batteryPrompt "Battery" 30 | #define backlightPrompt "BacklightLevel" 31 | #define backlightTimeoutPrompt "BacklightTimeout" 32 | #define beepPrompt "Beep" 33 | #define SSTPrompt "SST" 34 | #define conservationPrompt "ConservationMode" 35 | #define clamshellPrompt "ClamshellMode" 36 | #define DYTCPrompt "DYTCMode" 37 | #define DYTCFuncPrompt "DYTCFuncMode" 38 | #define DYTCPerfPrompt "DYTCPerfMode" 39 | #define DYTCPSCPrompt "DYTCPSCMode" 40 | #define ECLockPrompt "ECLock" 41 | #define FnKeyPrompt "FnlockMode" 42 | #define fanControlPrompt "FanControl" 43 | #define fanSpeedPrompt "FanSpeed" 44 | #define keyboardPrompt "KeyboardMode" 45 | #define HotKeyPrompt "HotKey" 46 | #define LEDPrompt "LED" 47 | #define localePrompt "KeyboardLocale" 48 | #define mutePrompt "Mute" 49 | #define muteLEDPrompt "MuteLED" 50 | #define muteSupportPrompt "MuteSupport" 51 | #define micMuteLEDPrompt "MicMuteLED" 52 | #define VPCPrompt "VPCconfig" 53 | #define rapidChargePrompt "RapidChargeMode" 54 | #define readECPrompt "ReadEC" 55 | #define resetPrompt "reset" 56 | #define writeECPrompt "WriteEC" 57 | #define updatePrompt "Update" 58 | #define updateSensorPrompt "UpdateSensor" 59 | 60 | #define initFailure "%s evaluation failed, exiting" 61 | #define updateFailure "%s evaluation failed" 62 | #define updateSuccess "%s 0x%x" 63 | #define toggleError "%s toggle error code: 0x%x" 64 | #define toggleFailure "%s toggle failed" 65 | #define toggleSuccess "%s set to 0x%x: %s" 66 | 67 | #define notSupported "%s not supported" 68 | #define valueMatched "%s already %x" 69 | #define valueInvalid "Invalid value for %s" 70 | #define valueUnknown "Unknown value for %s: %d" 71 | 72 | #define timeoutPrompt "%s timeout 0x%x" 73 | 74 | enum 75 | { 76 | // from keyboard to mouse/touchpad 77 | kSMC_setDisableTouchpad = iokit_vendor_specific_msg(100), // set disable/enable touchpad (data is bool*) 78 | kSMC_getDisableTouchpad = iokit_vendor_specific_msg(101), // get disable/enable touchpad (data is bool*) 79 | 80 | // from sensor to keyboard 81 | kSMC_setKeyboardStatus = iokit_vendor_specific_msg(200), // set disable/enable keyboard (data is bool*) 82 | kSMC_getKeyboardStatus = iokit_vendor_specific_msg(201), // get disable/enable keyboard (data is bool*) 83 | kSMC_notifyKeystroke = iokit_vendor_specific_msg(202), // notify of key press (data is PS2KeyInfo*) 84 | 85 | // SMC message types 86 | kSMC_VPCType = iokit_vendor_specific_msg(500), // set loaded VPC type (data is UInt32*) 87 | kSMC_YogaEvent = iokit_vendor_specific_msg(501), // set Yoga mode (data is UInt32*) 88 | kSMC_FnlockEvent = iokit_vendor_specific_msg(502), // notify Fnlock event 89 | kSMC_getConservation = iokit_vendor_specific_msg(503), // get conservation mode (data is bool*) 90 | kSMC_setConservation = iokit_vendor_specific_msg(504) // set conservation mode (data is bool*) 91 | }; 92 | 93 | // from VoodooPS2 94 | enum 95 | { 96 | // from keyboard to mouse/touchpad 97 | kPS2M_notifyKeyPressed = iokit_vendor_specific_msg(102), // notify of time key pressed (data is PS2KeyInfo*) 98 | 99 | kPS2M_notifyKeyTime = iokit_vendor_specific_msg(110), // notify of timestamp a non-modifier key was pressed (data is uint64_t*) 100 | 101 | kPS2M_resetTouchpad = iokit_vendor_specific_msg(151), // Force touchpad reset (data is int*) 102 | }; 103 | 104 | typedef struct PS2KeyInfo 105 | { 106 | uint64_t time; 107 | UInt16 adbKeyCode; 108 | bool goingDown; 109 | bool eatKey; 110 | } PS2KeyInfo; 111 | 112 | enum hid_adb_codes { 113 | ADB_PLAY_PAUSE = 0x34, 114 | ADB_LEFT_META = 0x37, 115 | ADB_NUM_LOCK = 0x47, 116 | ADB_VOLUME_UP = 0x48, 117 | ADB_VOLUME_DOWN = 0x49, 118 | ADB_MUTE = 0x4a, 119 | ADB_BRIGHTNESS_DOWN = 0x6b, 120 | ADB_BRIGHTNESS_UP = 0x71, 121 | ADB_HOME = 0x73, 122 | ADB_PAGE_UP = 0x74, 123 | ADB_END = 0x77, 124 | ADB_PAGE_DOWN = 0x79, 125 | ADB_POWER = 0x7f, 126 | }; 127 | 128 | #endif /* message_h */ 129 | -------------------------------------------------------------------------------- /YogaSMCNC/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /YogaSMCNC/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /YogaSMCNC/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /YogaSMCNC/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /YogaSMCNC/DualFan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/DualFan.png -------------------------------------------------------------------------------- /YogaSMCNC/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | YogaSMCNC.icns 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | $(CURRENT_PROJECT_VERSION) 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | LSApplicationCategoryType 24 | public.app-category.utilities 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSAppleEventsUsageDescription 30 | 31 | NSHumanReadableCopyright 32 | Copyright © 2022 Zhen. All rights reserved. 33 | NSMainStoryboardFile 34 | Main 35 | NSPrincipalClass 36 | YogaSMCNC.VolumeObserver 37 | 38 | 39 | -------------------------------------------------------------------------------- /YogaSMCNC/Resources/AirplaneMode.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/AirplaneMode.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/Antenna.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/Antenna.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/BacklightHigh.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/BacklightHigh.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/BacklightLow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/BacklightLow.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/BacklightOff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/BacklightOff.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/Bluetooth.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/Bluetooth.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/Camera.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/Camera.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/CapslockOff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/CapslockOff.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/CapslockOn.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/CapslockOn.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/Dock.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/Dock.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/FunctionKey.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/FunctionKey.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/FunctionKeyOff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/FunctionKeyOff.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/FunctionKeyOn.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/FunctionKeyOn.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/Keyboard.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/Keyboard.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/KeyboardOff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/KeyboardOff.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/Mic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/Mic.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/MicOff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/MicOff.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/SecondDisplay.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/SecondDisplay.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/Sleep.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/Sleep.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/Star.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/Star.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/Undock.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/Undock.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/Wifi.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/Wifi.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/WifiOff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/WifiOff.pdf -------------------------------------------------------------------------------- /YogaSMCNC/Resources/YogaSMCNC.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCNC/Resources/YogaSMCNC.icns -------------------------------------------------------------------------------- /YogaSMCNC/YogaSMCNC.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.automation.apple-events 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /YogaSMCNCHelper/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | $(CURRENT_PROJECT_VERSION) 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | LSApplicationCategoryType 24 | public.app-category.utilities 25 | LSBackgroundOnly 26 | 27 | LSMinimumSystemVersion 28 | $(MACOSX_DEPLOYMENT_TARGET) 29 | NSHumanReadableCopyright 30 | Copyright © 2020 zhen. All rights reserved. 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /YogaSMCNCHelper/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // YogaSMCNCHelper 4 | // 5 | // Created by Zhen on 10/12/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | 11 | class AppDelegate: NSObject, NSApplicationDelegate { 12 | func applicationDidFinishLaunching(_ aNotification: Notification) { 13 | let mainBundleID = Bundle.main.bundleIdentifier!.replacingOccurrences(of: "Helper", with: "") 14 | let bundlePath = Bundle.main.bundlePath as NSString 15 | 16 | guard NSRunningApplication.runningApplications(withBundleIdentifier: mainBundleID).isEmpty else { 17 | return NSApp.terminate(self) 18 | } 19 | 20 | let pathComponents = bundlePath.pathComponents 21 | let path = NSString.path(withComponents: Array(pathComponents[0 ..< (pathComponents.count - 4)])) 22 | 23 | if !NSWorkspace.shared.launchApplication(path) { 24 | let alert = NSAlert() 25 | alert.messageText = "Failed to open \(path)" 26 | alert.alertStyle = .warning 27 | alert.addButton(withTitle: "OK") 28 | alert.runModal() 29 | } 30 | NSApp.terminate(nil) 31 | } 32 | 33 | func applicationWillTerminate(_ aNotification: Notification) {} 34 | } 35 | 36 | let app = NSApplication.shared 37 | let delegate = AppDelegate() 38 | app.delegate = delegate 39 | app.run() 40 | -------------------------------------------------------------------------------- /YogaSMCPane/General.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCPane/General.png -------------------------------------------------------------------------------- /YogaSMCPane/GeneralSMCPane.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneralSMCPane.swift 3 | // YogaSMCPane 4 | // 5 | // Created by Zhen on 12/21/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | import Foundation 11 | 12 | extension YogaSMCPane { 13 | @IBAction func DYTCset(_ sender: NSSlider) { 14 | if DYTCSlider.maxValue == 2 { 15 | _ = sendString("DYTCMode", DYTCCommand[DYTCSlider.integerValue], service) 16 | } else { 17 | _ = sendNumber("DYTCPSCMode", (DYTCSlider.integerValue != 0) ? DYTCSlider.integerValue : 0xf, service) 18 | } 19 | if let dict = getDictionary("DYTC", service) { 20 | updateDYTC(dict) 21 | } 22 | } 23 | 24 | @IBAction func backlightSet(_ sender: NSSlider) { 25 | if !sendNumber("BacklightLevel", backlightSlider.integerValue, service) { 26 | let backlightLevel = getNumber("BacklightLevel", service) 27 | if backlightLevel != -1 { 28 | backlightSlider.integerValue = backlightLevel 29 | } else { 30 | backlightSlider.isEnabled = false 31 | } 32 | } 33 | } 34 | 35 | @IBAction func backlightTimeoutSet(_ sender: NSTextField) { 36 | _ = sendNumber("BacklightTimeout", sender.integerValue, service) 37 | } 38 | 39 | @IBAction func vClamshellModeSet(_ sender: NSButton) { 40 | _ = sendBoolean("ClamshellMode", (sender.state == .on), service) 41 | } 42 | 43 | @IBAction func autoBacklightSet(_ sender: NSButton) { 44 | let val = ((autoSleepCheck.state == .on) ? 1 << 0 : 0) + 45 | ((yogaModeCheck.state == .on) ? 1 << 1 : 0) + 46 | ((indicatorCheck.state == .on) ? 1 << 2 : 0) + 47 | ((muteCheck.state == .on) ? 1 << 3 : 0) + 48 | ((micMuteCheck.state == .on) ? 1 << 4 : 0) 49 | if !sendNumber("AutoBacklight", val, service) { 50 | let autoBacklight = getNumber("AutoBacklight", service) 51 | if autoBacklight != -1 { 52 | autoSleepCheck.state = ((autoBacklight & (1 << 0)) != 0) ? .on : .off 53 | yogaModeCheck.state = ((autoBacklight & (1 << 1)) != 0) ? .on : .off 54 | indicatorCheck.state = ((autoBacklight & (1 << 2)) != 0) ? .on : .off 55 | muteCheck.state = ((autoBacklight & (1 << 3)) != 0) ? .on : .off 56 | micMuteCheck.state = ((autoBacklight & (1 << 4)) != 0) ? .on : .off 57 | } else { 58 | autoSleepCheck.isEnabled = false 59 | yogaModeCheck.isEnabled = false 60 | indicatorCheck.isEnabled = false 61 | muteCheck.isEnabled = false 62 | micMuteCheck.isEnabled = false 63 | } 64 | } 65 | } 66 | 67 | @IBAction func DYTCPSCSupport(_ sender: NSButton) { 68 | defaults.setValue(sender.state == .on, forKey: "DYTCPSC") 69 | if sender.state == .on { 70 | DYTCSlider.numberOfTickMarks = 9 71 | DYTCSlider.maxValue = 8 72 | DYTCSlider.integerValue = 0 73 | } else { 74 | DYTCSlider.numberOfTickMarks = 3 75 | DYTCSlider.maxValue = 2 76 | DYTCSlider.integerValue = 1 77 | } 78 | } 79 | 80 | func updateDYTC(_ dict: NSDictionary) { 81 | if let ver = dict["Revision"] as? NSNumber, 82 | let subver = dict["SubRevision"] as? NSNumber { 83 | vDYTCRevision.stringValue = "\(ver.intValue).\(subver.intValue)" 84 | } else { 85 | vDYTCRevision.stringValue = "Unknown" 86 | } 87 | if let funcMode = dict["FuncMode"] as? String { 88 | vDYTCFuncMode.stringValue = funcMode 89 | } else { 90 | vDYTCFuncMode.stringValue = "Unknown" 91 | } 92 | if let capabilities = dict["Function Status"] as? NSDictionary { 93 | let hasMMC = (capabilities["MMC"] != nil) 94 | let hasPSC = (capabilities["PSC"] != nil) 95 | DYTCPSCCheck.isHidden = !(hasPSC && hasMMC) 96 | DYTCSlider.isEnabled = hasPSC || hasMMC 97 | if hasPSC, !hasMMC { 98 | DYTCSlider.numberOfTickMarks = 9 99 | DYTCSlider.maxValue = 8 100 | DYTCSlider.integerValue = 0 101 | } 102 | if !DYTCPSCCheck.isHidden { 103 | DYTCPSCCheck.state = defaults.bool(forKey: "DYTCPSC") ? .on : .off 104 | DYTCPSCSupport(DYTCPSCCheck) 105 | } 106 | } 107 | if let perfMode = dict["PerfMode"] as? String { 108 | DYTCSlider.toolTip = perfMode 109 | switch perfMode { 110 | case "Quiet": 111 | DYTCSlider.integerValue = 0 112 | case "Balance": 113 | if DYTCSlider.maxValue == 2 { 114 | DYTCSlider.integerValue = 1 115 | } else { 116 | DYTCSlider.integerValue = 0 117 | } 118 | case "Performance": 119 | DYTCSlider.integerValue = 2 120 | case "Performance (Reduced as lapmode active)": 121 | DYTCSlider.integerValue = 2 122 | default: 123 | DYTCSlider.isEnabled = false 124 | } 125 | } else if let perfMode = dict["PerfMode"] as? NSNumber { 126 | DYTCSlider.toolTip = "\(perfMode.intValue)" 127 | DYTCSlider.integerValue = perfMode.intValue 128 | } else { 129 | DYTCSlider.isEnabled = false 130 | } 131 | } 132 | 133 | func updateMain(_ props: NSDictionary) { 134 | if let val = props["AutoBacklight"] as? NSNumber { 135 | let autoBacklight = val.intValue 136 | autoSleepCheck.state = ((autoBacklight & (1 << 0)) != 0) ? .on : .off 137 | yogaModeCheck.state = ((autoBacklight & (1 << 1)) != 0) ? .on : .off 138 | indicatorCheck.state = ((autoBacklight & (1 << 2)) != 0) ? .on : .off 139 | muteCheck.state = ((autoBacklight & (1 << 3)) != 0) ? .on : .off 140 | micMuteCheck.state = ((autoBacklight & (1 << 4)) != 0) ? .on : .off 141 | } else { 142 | autoSleepCheck.isEnabled = false 143 | yogaModeCheck.isEnabled = false 144 | indicatorCheck.isEnabled = false 145 | micMuteCheck.isEnabled = false 146 | } 147 | #if !DEBUG 148 | muteCheck.isEnabled = false 149 | if muteCheck.state == .on { 150 | muteCheck.state = .off 151 | autoBacklightSet(muteCheck) 152 | } 153 | #endif 154 | 155 | if let val = props["BacklightLevel"] as? NSNumber { 156 | backlightSlider.integerValue = val.intValue 157 | } else { 158 | backlightSlider.isEnabled = false 159 | } 160 | 161 | if let val = props["BacklightTimeout"] as? NSNumber { 162 | vBackLightTimeout.integerValue = val.intValue 163 | } else { 164 | vBackLightTimeout.isEnabled = false 165 | } 166 | 167 | if let dict = props["DYTC"] as? NSDictionary { 168 | updateDYTC(dict) 169 | } else { 170 | vDYTCRevision.stringValue = "Unsupported" 171 | } 172 | 173 | if defaults.bool(forKey: "HideIcon") { 174 | vHideMenubarIcon.state = .on 175 | } else { 176 | vHideMenubarIcon.state = .off 177 | vMenubarIcon.isEnabled = true 178 | } 179 | vMenubarIcon.stringValue = defaults.string(forKey: "Title") ?? "" 180 | 181 | vHideCapsLock.state = defaults.bool(forKey: "HideCapsLock") ? .on : .off 182 | 183 | if let val = props["ClamshellMode"] as? Bool { 184 | vClamshellMode.isEnabled = true 185 | vClamshellMode.state = val ? .on : .off 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /YogaSMCPane/IdeaSMCPane.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IdeaSMCPane.swift 3 | // YogaSMCPane 4 | // 5 | // Created by Zhen on 12/9/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | import Foundation 11 | 12 | extension YogaSMCPane { 13 | @IBAction func vFnKeySet(_ sender: NSButton) { 14 | if !sendBoolean("FnlockMode", vFnKeyRadio.state == .on, service) { 15 | vFnKeyRadio.state = getBoolean("FnlockMode", service) ? .on : .off 16 | } 17 | } 18 | 19 | @IBAction func vAlwaysOnUSBModeSet(_ sender: NSButton) { 20 | if !sendBoolean("AlwaysOnUSBMode", vAlwaysOnUSBMode.state == .on, service) { 21 | vAlwaysOnUSBMode.state = getBoolean("AlwaysOnUSBMode", service) ? .on : .off 22 | } 23 | } 24 | 25 | @IBAction func vConservationModeSet(_ sender: NSButton) { 26 | if !sendBoolean("ConservationMode", vConservationMode.state == .on, service) { 27 | vConservationMode.state = getBoolean("ConservationMode", service) ? .on : .off 28 | } 29 | } 30 | 31 | func updateIdeaBattery(_ dict: NSDictionary) { 32 | vBatteryID.stringValue = dict["ID"] as? String ?? "Unknown" 33 | vCycleCount.stringValue = dict["Cycle count"] as? String ?? "Unknown" 34 | vBatteryTemperature.stringValue = dict["Temperature"] as? String ?? "Unknown" 35 | vMfgDate.stringValue = dict["Manufacture date"] as? String ?? "Unknown" 36 | } 37 | 38 | func updateIdeaCap(_ dict: NSDictionary) { 39 | if let val = dict["Camera"] as? Bool { 40 | vCamera.textColor = val ? NSColor.systemGreen : NSColor.systemGray 41 | } 42 | if let val = dict["Bluetooth"] as? Bool { 43 | vBluetooth.textColor = val ? NSColor.systemGreen : NSColor.systemGray 44 | } 45 | if let val = dict["Wireless"] as? Bool { 46 | vWireless.textColor = val ? NSColor.systemGreen : NSColor.systemGray 47 | } 48 | if let val = dict["3G"] as? Bool { 49 | vWWAN.textColor = val ? NSColor.systemGreen : NSColor.systemGray 50 | } 51 | if let val = dict["Graphics"] as? NSString { 52 | vGraphics.toolTip = val as String 53 | switch val { 54 | case "Intel": 55 | vGraphics.textColor = NSColor(red: 0/0xff, green: 0x71/0xff, blue: 0xc5/0xff, alpha: 1) 56 | case "ATI", "Intel and ATI": 57 | vGraphics.textColor = NSColor(red: 0x97/0xff, green: 0x0a/0xff, blue: 0x1b/0xff, alpha: 1) 58 | case "Nvidia", "Intel and Nvidia": 59 | vGraphics.textColor = NSColor(red: 0x76/0xff, green: 0xb9/0xff, blue: 0/0xff, alpha: 1) 60 | default: break 61 | } 62 | } 63 | } 64 | 65 | func updateIdea(_ props: NSDictionary) { 66 | if let val = props["PrimeKeyType"] as? NSString { 67 | vFnKeyRadio.title = val as String 68 | if let val = props["FnlockMode"] as? Bool { 69 | vFnKeyRadio.isEnabled = true 70 | vFxKeyRadio.isEnabled = true 71 | if val { 72 | vFnKeyRadio.state = .on 73 | } else { 74 | vFxKeyRadio.state = .on 75 | } 76 | } 77 | } else { 78 | vFnKeyRadio.title = "Unknown" 79 | } 80 | 81 | if let val = props["AlwaysOnUSBMode"] as? Bool { 82 | vAlwaysOnUSBMode.state = val ? .on : .off 83 | vAlwaysOnUSBMode.isEnabled = true 84 | } 85 | 86 | if let val = props["ConservationMode"] as? Bool { 87 | vConservationMode.state = val ? .on : .off 88 | vConservationMode.isEnabled = true 89 | } 90 | 91 | if let val = props["RapidChargeMode"] as? Bool { 92 | vRapidChargeMode.state = val ? .on : .off 93 | vRapidChargeMode.isEnabled = true 94 | } 95 | 96 | if let dict = props["Battery 0"] as? NSDictionary { 97 | updateIdeaBattery(dict) 98 | } 99 | 100 | if let dict = props["Capability"] as? NSDictionary { 101 | updateIdeaCap(dict) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /YogaSMCPane/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(CURRENT_PROJECT_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2022 Zhen. All rights reserved. 23 | NSMainNibFile 24 | YogaSMCPane 25 | NSPrefPaneIconFile 26 | YogaSMCPane.icns 27 | NSPrefPaneIconLabel 28 | YogaSMCPane 29 | NSPrincipalClass 30 | YogaSMCPane 31 | 32 | 33 | -------------------------------------------------------------------------------- /YogaSMCPane/ThinkSMCPane.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThinkSMCPane.swift 3 | // YogaSMCPane 4 | // 5 | // Created by Zhen on 12/9/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | import Foundation 11 | import os.log 12 | 13 | extension YogaSMCPane { 14 | @IBAction func vChargeThresholdStartSet(_ sender: NSTextField) { 15 | let target = sender.integerValue 16 | #if DEBUG 17 | showOSD(String(format: "Start %d", target)) 18 | #endif 19 | 20 | if thinkBatteryNumber != sender.tag, !updateThinkBatteryIndex(sender.tag) { 21 | if #available(macOS 10.12, *) { 22 | os_log("Failed to update battery %d", type: .error, sender.tag) 23 | } 24 | sender.isEnabled = false 25 | return 26 | } 27 | 28 | if target == sender.integerValue { return } 29 | _ = sendNumber("setCMstart", target, service) 30 | sender.integerValue = target 31 | } 32 | 33 | @IBAction func vChargeThresholdStopSet(_ sender: NSTextField) { 34 | let target = sender.integerValue 35 | 36 | if thinkBatteryNumber != sender.tag, !updateThinkBatteryIndex(sender.tag) { 37 | if #available(macOS 10.12, *) { 38 | os_log("Failed to update battery %d", type: .error, sender.tag) 39 | } 40 | return 41 | } 42 | 43 | if target == sender.integerValue { return } 44 | #if DEBUG 45 | showOSD(String(format: "Stop %d", target)) 46 | #endif 47 | _ = sendNumber("setCMstop", target == 100 ? 0 : target, service) 48 | sender.integerValue = target 49 | } 50 | 51 | @IBAction func vPowerLEDSet(_ sender: NSSlider) { 52 | if !sendNumber("LED", thinkLEDCommand[vPowerLEDSlider.integerValue] + 0x00, service) { 53 | return 54 | } 55 | } 56 | @IBAction func vStandbyLEDSet(_ sender: NSSlider) { 57 | if !sendNumber("LED", thinkLEDCommand[vStandbyLEDSlider.integerValue] + 0x07, service) { 58 | return 59 | } 60 | } 61 | 62 | @IBAction func vThinkDotSet(_ sender: NSSlider) { 63 | if !sendNumber("LED", thinkLEDCommand[vThinkDotSlider.integerValue] + 0x0A, service) { 64 | return 65 | } 66 | } 67 | 68 | @IBAction func vCustomLEDSet(_ sender: NSSlider) { 69 | let value = thinkLEDCommand[vCustomLEDSlider.integerValue] + vCustomLEDList.indexOfSelectedItem 70 | #if DEBUG 71 | showOSD(String(format: "LED 0x%02X", value)) 72 | #endif 73 | if !sendNumber("LED", value, service) { 74 | return 75 | } 76 | } 77 | 78 | @IBAction func vSecondFanSet(_ sender: NSButton) { 79 | defaults.setValue((vSecondFan.state == .on), forKey: "SecondThinkFan") 80 | _ = scriptHelper(reloadAS, "Reload YogaSMCNC") 81 | } 82 | @IBAction func vFanStopSet(_ sender: NSButton) { 83 | defaults.setValue((vFanStop.state == .on), forKey: "AllowFanStop") 84 | _ = scriptHelper(reloadAS, "Reload YogaSMCNC") 85 | } 86 | @IBAction func vDisableFanSet(_ sender: NSButton) { 87 | defaults.setValue((vDisableFan.state == .on), forKey: "DisableFan") 88 | _ = scriptHelper(reloadAS, "Reload YogaSMCNC") 89 | } 90 | 91 | @IBAction func vSaveFanLevelSet(_ sender: NSButton) { 92 | defaults.setValue((vSaveFanLevel.state == .on), forKey: "SaveFanLevel") 93 | _ = scriptHelper(reloadAS, "Reload YogaSMCNC") 94 | } 95 | 96 | @IBAction func vMuteLEDFixupSet(_ sender: NSButton) { 97 | defaults.setValue((vMuteLEDFixup.state == .on), forKey: "ThinkMuteLEDFixup") 98 | _ = scriptHelper(reloadAS, "Reload YogaSMCNC") 99 | } 100 | 101 | func updateThinkBatteryIndex(_ index: Int) -> Bool { 102 | if !sendNumber("Battery", index, service) { return false } 103 | 104 | switch index { 105 | case 0: 106 | return updateThinkBattery(vChargeThresholdStart, vChargeThresholdStop) 107 | case 1: 108 | return updateThinkBattery(vPrimaryChargeThresholdStart, vPrimaryChargeThresholdStop) 109 | case 2: 110 | return updateThinkBattery(vSecondaryChargeThresholdStart, vSecondaryChargeThresholdStop) 111 | default: 112 | return false 113 | } 114 | } 115 | 116 | func updateThinkBattery(_ startThreshold: NSTextField, _ stopThreshold: NSTextField) -> Bool { 117 | guard startThreshold.tag == stopThreshold.tag else { return false} 118 | let index = startThreshold.tag 119 | 120 | guard let dict = getDictionary(thinkBatteryName[index], service), 121 | let vStart = dict["BCTG"] as? NSNumber, 122 | let vStop = dict["BCSG"] as? NSNumber, 123 | vStart.int32Value >= 0, 124 | vStart.int32Value & 0xff < 100, 125 | vStop.int32Value >= 0, 126 | vStop.int32Value & 0xff < 100 else { return false } 127 | 128 | startThreshold.isEnabled = true 129 | stopThreshold.isEnabled = true 130 | startThreshold.integerValue = vStart.intValue & 0xff 131 | stopThreshold.integerValue = (vStop.intValue & 0xff) == 0 ? 100 : vStop.intValue & 0xff 132 | 133 | thinkBatteryNumber = index 134 | return true 135 | } 136 | 137 | func updateThink(_ props: NSDictionary) { 138 | _ = updateThinkBatteryIndex(0) 139 | _ = updateThinkBatteryIndex(1) 140 | _ = updateThinkBatteryIndex(2) 141 | 142 | if let val = props["FnlockMode"] as? Bool { 143 | vFnKeyRadio.title = "FnKey" 144 | vFnKeyRadio.isEnabled = true 145 | vFxKeyRadio.isEnabled = true 146 | if val { 147 | vFnKeyRadio.state = .on 148 | } else { 149 | vFxKeyRadio.state = .on 150 | } 151 | } 152 | #if DEBUG 153 | if let val = props["Dual fan"] as? Bool, 154 | val == true { 155 | vSecondFan.isEnabled = true 156 | vSecondFan.state = defaults.bool(forKey: "SecondThinkFan") ? .on : .off 157 | } 158 | #endif 159 | vFanStop.state = defaults.bool(forKey: "AllowFanStop") ? .on : .off 160 | if let val = props["LEDSupport"] as? Bool, 161 | val == true { 162 | vPowerLEDSlider.isEnabled = true 163 | vStandbyLEDSlider.isEnabled = true 164 | vThinkDotSlider.isEnabled = true 165 | vCustomLEDSlider.isEnabled = true 166 | } 167 | vDisableFan.state = defaults.bool(forKey: "DisableFan") ? .on : .off 168 | vSaveFanLevel.state = defaults.bool(forKey: "SaveFanLevel") ? .on : .off 169 | vMuteLEDFixup.state = defaults.bool(forKey: "ThinkMuteLEDFixup") ? .on : .off 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /YogaSMCPane/YogaSMCPane.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhen-zen/YogaSMC/299907b359c4d323e8c45ecc91e88c2a87b6082d/YogaSMCPane/YogaSMCPane.icns -------------------------------------------------------------------------------- /YogaSMCPane/YogaSMCPane.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YogaSMCPane.swift 3 | // YogaSMCPane 4 | // 5 | // Created by Zhen on 9/17/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | import Foundation 11 | import IOKit 12 | import PreferencePanes 13 | import os.log 14 | 15 | let DYTCCommand = ["L", "M", "H"] 16 | let thinkLEDCommand = [0, 0x80, 0xA0, 0xC0] 17 | let thinkBatteryName = ["BAT_ANY", "BAT_PRIMARY", "BAT_SECONDARY"] 18 | 19 | class YogaSMCPane: NSPreferencePane { 20 | let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("YogaVPC")) 21 | let defaults = UserDefaults(suiteName: "org.zhen.YogaSMC")! 22 | 23 | var thinkBatteryNumber = -1 24 | 25 | @IBOutlet weak var vVersion: NSTextField! 26 | @IBOutlet weak var vClass: NSTextField! 27 | @IBOutlet weak var vECRead: NSTextField! 28 | @IBOutlet weak var vHideMenubarIcon: NSButton! 29 | @IBAction func toggleMenubarIcon(_ sender: NSButton) { 30 | if vHideMenubarIcon.state == .on { 31 | defaults.setValue(true, forKey: "HideIcon") 32 | vMenubarIcon.isEnabled = false 33 | } else { 34 | defaults.setValue(false, forKey: "HideIcon") 35 | vMenubarIcon.isEnabled = true 36 | } 37 | _ = scriptHelper(reloadAS, "Reload YogaSMCNC") 38 | } 39 | 40 | @IBOutlet weak var vMenubarIcon: NSTextField! 41 | @IBAction func setMenubarIcon(_ sender: NSTextField) { 42 | if vMenubarIcon.stringValue == "" { 43 | guard defaults.value(forKey: "Title") != nil else { return } 44 | defaults.removeObject(forKey: "Title") 45 | } else { 46 | if defaults.string(forKey: "Title") == vMenubarIcon.stringValue { return } 47 | defaults.setValue(vMenubarIcon.stringValue, forKey: "Title") 48 | } 49 | _ = scriptHelper(reloadAS, "Reload YogaSMCNC") 50 | } 51 | 52 | @IBOutlet weak var vHideCapsLock: NSButton! 53 | @IBAction func toggleHideCapsLock(_ sender: NSButton) { 54 | defaults.setValue(vHideCapsLock.state == .on, forKey: "HideCapsLock") 55 | _ = scriptHelper(reloadAS, "Reload YogaSMCNC") 56 | } 57 | 58 | @IBAction func vClearEvents(_ sender: NSButton) { 59 | _ = scriptHelper(stopAS, "Stop YogaSMCNC") 60 | defaults.removeObject(forKey: "Events") 61 | _ = scriptHelper(startAS, "Start YogaSMCNC") 62 | } 63 | 64 | // Idea 65 | @IBOutlet weak var vFnKeyRadio: NSButton! 66 | @IBOutlet weak var vFxKeyRadio: NSButton! 67 | 68 | @IBOutlet weak var vBatteryID: NSTextField! 69 | @IBOutlet weak var vBatteryTemperature: NSTextField! 70 | @IBOutlet weak var vCycleCount: NSTextField! 71 | @IBOutlet weak var vMfgDate: NSTextField! 72 | 73 | @IBOutlet weak var vAlwaysOnUSBMode: NSButton! 74 | @IBOutlet weak var vConservationMode: NSButton! 75 | @IBOutlet weak var vRapidChargeMode: NSButton! 76 | 77 | @IBOutlet weak var vCamera: NSTextField! 78 | @IBOutlet weak var vBluetooth: NSTextField! 79 | @IBOutlet weak var vWireless: NSTextField! 80 | @IBOutlet weak var vWWAN: NSTextField! 81 | @IBOutlet weak var vGraphics: NSTextField! 82 | 83 | // Think 84 | 85 | @IBOutlet weak var vChargeThresholdStart: NSTextField! 86 | @IBOutlet weak var vChargeThresholdStop: NSTextField! 87 | @IBOutlet weak var vPrimaryChargeThresholdStart: NSTextField! 88 | @IBOutlet weak var vPrimaryChargeThresholdStop: NSTextField! 89 | @IBOutlet weak var vSecondaryChargeThresholdStart: NSTextField! 90 | @IBOutlet weak var vSecondaryChargeThresholdStop: NSTextField! 91 | 92 | @IBOutlet weak var vPowerLEDSlider: NSSlider! 93 | @IBOutlet weak var vStandbyLEDSlider: NSSlider! 94 | @IBOutlet weak var vThinkDotSlider: NSSliderCell! 95 | @IBOutlet weak var vCustomLEDSlider: NSSlider! 96 | @IBOutlet weak var vCustomLEDList: NSPopUpButton! 97 | 98 | @IBOutlet weak var vSecondFan: NSButton! 99 | @IBOutlet weak var vFanStop: NSButton! 100 | @IBOutlet weak var vDisableFan: NSButton! 101 | @IBOutlet weak var vSaveFanLevel: NSButton! 102 | 103 | @IBOutlet weak var vMuteLEDFixup: NSButton! 104 | 105 | // Main 106 | 107 | @IBOutlet weak var mainTabView: NSTabView! 108 | @IBOutlet weak var ideaViewItem: NSTabViewItem! 109 | @IBOutlet weak var thinkViewItem: NSTabViewItem! 110 | 111 | @IBOutlet weak var vDYTCRevision: NSTextField! 112 | @IBOutlet weak var vDYTCFuncMode: NSTextField! 113 | @IBOutlet weak var DYTCSlider: NSSlider! 114 | @IBOutlet weak var DYTCPSCCheck: NSButton! 115 | 116 | @IBOutlet weak var backlightSlider: NSSlider! 117 | @IBOutlet weak var vBackLightTimeout: NSTextField! 118 | 119 | @IBOutlet weak var autoSleepCheck: NSButton! 120 | @IBOutlet weak var yogaModeCheck: NSButton! 121 | @IBOutlet weak var indicatorCheck: NSButton! 122 | @IBOutlet weak var muteCheck: NSButton! 123 | @IBOutlet weak var micMuteCheck: NSButton! 124 | 125 | @IBOutlet weak var vClamshellMode: NSButton! 126 | 127 | override func mainViewDidLoad() { 128 | super.mainViewDidLoad() 129 | if #available(macOS 10.12, *) { 130 | os_log(#function, type: .info) 131 | } 132 | // nothing 133 | } 134 | 135 | override func willSelect() { 136 | guard service != 0, sendBoolean("Update", true, service) else { return } 137 | 138 | guard let props = getProperties(service) else { 139 | if #available(macOS 10.12, *) { 140 | os_log("Unable to acquire driver properties!", type: .fault) 141 | } 142 | return 143 | } 144 | 145 | if let val = props["VersionInfo"] as? NSString { 146 | vVersion.stringValue = val as String 147 | } else { 148 | if #available(macOS 10.12, *) { 149 | os_log("Unable to identify driver version!", type: .fault) 150 | } 151 | return 152 | } 153 | 154 | if let val = props["EC Capability"] as? NSString { 155 | vECRead.stringValue = val as String 156 | } else { 157 | if #available(macOS 10.12, *) { 158 | os_log("Unable to identify EC capability!", type: .fault) 159 | } 160 | return 161 | } 162 | 163 | updateMain(props) 164 | 165 | switch props["IOClass"] as? NSString { 166 | case "IdeaVPC": 167 | vClass.stringValue = "Idea" 168 | updateIdea(props) 169 | #if !DEBUG 170 | mainTabView.removeTabViewItem(thinkViewItem) 171 | #endif 172 | case "ThinkVPC": 173 | vClass.stringValue = "Think" 174 | updateThink(props) 175 | #if !DEBUG 176 | mainTabView.removeTabViewItem(ideaViewItem) 177 | #endif 178 | case "YogaHIDD": 179 | vClass.stringValue = "HIDD" 180 | mainTabView.removeTabViewItem(ideaViewItem) 181 | mainTabView.removeTabViewItem(thinkViewItem) 182 | default: 183 | vClass.stringValue = "Unsupported" 184 | mainTabView.removeTabViewItem(ideaViewItem) 185 | mainTabView.removeTabViewItem(thinkViewItem) 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /YogaSMCUtils/DateHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateHelper.swift 3 | // YogaSMCNC 4 | // 5 | // Created by Zhen on 10/31/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | 12 | // based on https://stackoverflow.com/questions/4311930/list-of-all-american-holidays-as-nsdates 13 | func setHolidayIcon(_ button: NSStatusItem) { 14 | button.title = "⎇" 15 | button.toolTip = nil 16 | 17 | if applyHolidayIconLunar(), setHolidayIconLunar(button) { return } 18 | 19 | let components = Calendar.current.dateComponents([.year, .month, .day, .weekday, .weekdayOrdinal], from: Date()) 20 | guard let year = components.year, 21 | let month = components.month, 22 | let day = components.day, 23 | let weekday = components.weekday, 24 | let weekdayOrdinal = components.weekdayOrdinal else { return } 25 | 26 | let easterDateComponents = dateComponentsForEaster(year: year) 27 | let easterMonth: Int = easterDateComponents?.month ?? -1 28 | let easterDay: Int = easterDateComponents?.day ?? -1 29 | 30 | switch (month, day, weekday, weekdayOrdinal) { 31 | case (1, 1, _, _): 32 | button.title = "🎍" 33 | button.toolTip = NSLocalizedString("New Year", comment: "") 34 | case (2, 14, _, _): 35 | button.title = "💝" 36 | button.toolTip = NSLocalizedString("Valentine's Day", comment: "") 37 | case (3, 17, _, _): 38 | button.title = "🍀" 39 | button.toolTip = NSLocalizedString("Saint Patrick's Day", comment: "") 40 | case (easterMonth, easterDay, _, _): 41 | button.title = "🥚" 42 | button.toolTip = NSLocalizedString("Easter", comment: "") 43 | case (10, 31, _, _): 44 | button.title = "🎃" 45 | button.toolTip = NSLocalizedString("Halloween", comment: "") 46 | case (11, _, 5, 4): 47 | button.title = "🦃" 48 | button.toolTip = NSLocalizedString("Thanksgiving", comment: "") 49 | case (12, 24, _, _): 50 | button.title = "🎄" 51 | button.toolTip = NSLocalizedString("Christmas Eve", comment: "") 52 | case (12, 25, _, _): 53 | button.title = "🎁" 54 | button.toolTip = NSLocalizedString("Christmas Day", comment: "") 55 | case (12, 31, _, _): 56 | button.title = "🎇" 57 | button.toolTip = NSLocalizedString("New Year's Eve", comment: "") 58 | default: 59 | break 60 | } 61 | } 62 | 63 | func dateComponentsForEaster(year: Int) -> DateComponents? { 64 | // Easter calculation from Anonymous Gregorian algorithm 65 | // AKA Meeus/Jones/Butcher algorithm 66 | let a = year % 19 67 | let b = Int(floor(Double(year) / 100)) 68 | let c = year % 100 69 | let d = Int(floor(Double(b) / 4)) 70 | let e = b % 4 71 | let f = Int(floor(Double(b+8) / 25)) 72 | let g = Int(floor(Double(b-f+1) / 3)) 73 | let h = (19*a + b - d - g + 15) % 30 74 | let i = Int(floor(Double(c) / 4)) 75 | let k = c % 4 76 | let L = (32 + 2*e + 2*i - h - k) % 7 77 | let m = Int(floor(Double(a + 11*h + 22*L) / 451)) 78 | var dateComponents = DateComponents() 79 | dateComponents.month = Int(floor(Double(h + L - 7*m + 114) / 31)) 80 | dateComponents.day = ((h + L - 7*m + 114) % 31) + 1 81 | dateComponents.year = year 82 | 83 | guard let easter = Calendar.current.date(from: dateComponents) else { return nil } 84 | // Convert to calculate weekday, weekdayOrdinal 85 | return Calendar.current.dateComponents([.year, .month, .day, .weekday, .weekdayOrdinal], from: easter) 86 | } 87 | 88 | func applyHolidayIconLunar() -> Bool { 89 | if let defaults = UserDefaults(suiteName: "org.zhen.YogaSMC"), 90 | let res = defaults.object(forKey: "Lunar") as? Bool { return res } 91 | 92 | for lang in Locale.preferredLanguages { 93 | if lang.hasPrefix("zh") { return true } 94 | } 95 | return false 96 | } 97 | 98 | func setHolidayIconLunar(_ button: NSStatusItem) -> Bool { 99 | let calendar: Calendar = Calendar(identifier: .chinese) 100 | let components = calendar.dateComponents([.year, .month, .day], from: Date()) 101 | guard let year = components.year, 102 | let month = components.month, 103 | let day = components.day else { return false } 104 | 105 | switch (month, day) { 106 | case (1, 1): 107 | button.title = iconForLunarYear(year: year) 108 | button.title = "🧨" 109 | button.toolTip = NSLocalizedString("Lunar New Year", comment: "") 110 | case (1, 15): 111 | button.title = "🏮" 112 | button.toolTip = NSLocalizedString("Lantern Festival", comment: "") 113 | case (5, 5): 114 | button.title = "🐲" 115 | button.toolTip = NSLocalizedString("Dragon Festival", comment: "") 116 | case (7, 7): 117 | button.title = "🎋" 118 | button.toolTip = NSLocalizedString("Qixi Festival", comment: "") 119 | case (7, 15): 120 | button.title = "🪔" 121 | button.toolTip = NSLocalizedString("Ghost Festival", comment: "") 122 | case (8, 15): 123 | button.title = "🥮" 124 | button.toolTip = NSLocalizedString("Mid-Autumn Festival", comment: "") 125 | case (9, 9): 126 | button.title = "🌼" 127 | button.toolTip = NSLocalizedString("Double Ninth Festival", comment: "") 128 | case (12, 8): 129 | button.title = "🥣" 130 | button.toolTip = NSLocalizedString("Laba Festival", comment: "") 131 | case (12, 30): 132 | button.title = "🎇" 133 | button.toolTip = NSLocalizedString("Lunar New Year's Eve", comment: "") 134 | default: 135 | let formatter = DateFormatter() 136 | formatter.calendar = calendar 137 | formatter.dateStyle = .medium 138 | button.toolTip = formatter.string(from: Date()) 139 | return false 140 | } 141 | return true 142 | } 143 | 144 | func iconForLunarYear(year: Int) -> String { 145 | switch year % 12 { 146 | case 1: 147 | return "🐀" 148 | case 2: 149 | return "🐂" 150 | case 3: 151 | return "🐅" 152 | case 4: 153 | return "🐇" 154 | case 5: 155 | return "🐉" 156 | case 6: 157 | return "🐍" 158 | case 7: 159 | return "🐎" 160 | case 8: 161 | return "🐑" 162 | case 9: 163 | return "🐒" 164 | case 10: 165 | return "🐓" 166 | case 11: 167 | return "🐕" 168 | case 0: 169 | return "🐖" 170 | default: 171 | return "🧨" 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /YogaSMCUtils/OSDHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OSDHelper.swift 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 10/14/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import os.log 11 | 12 | // from https://ffried.codes/2018/01/20/the-internals-of-the-macos-hud/ 13 | //@objc enum OSDImage: CLongLong { 14 | // case kBrightness = 1 15 | // case brightness2 = 2 16 | // case kVolume = 3 17 | // case kMute = 4 18 | // case volume5 = 5 19 | // case kEject = 6 20 | // case brightness7 = 7 21 | // case brightness8 = 8 22 | // case kAirportRange = 9 23 | // case wireless2Forbid = 10 24 | // case kBright = 11 25 | // case kBrightOff = 12 26 | // case kBright13 = 13 27 | // case kBrightOff14 = 14 28 | // case ajar = 15 29 | // case mute16 = 16 30 | // case volume17 = 17 31 | // case empty18 = 18 32 | // case kRemoteLinkedGeneric = 19 33 | // case kRemoteSleepGeneric = 20 // will put into sleep 34 | // case muteForbid = 21 35 | // case volumeForbid = 22 36 | // case volume23 = 23 37 | // case empty24 = 24 38 | // case kBright25 = 25 39 | // case kBrightOff26 = 26 40 | // case backlightonForbid = 27 41 | // case backlightoffForbid = 28 42 | // /* and more cases from 1 to 28 (except 18 and 24) */ 43 | //} 44 | 45 | let defaultImage: NSString = "/System/Library/CoreServices/OSDUIHelper.app/Contents/Resources/kBrightOff.pdf" 46 | 47 | // Bundled resources 48 | enum EventImage: String { 49 | case kAirplaneMode, kAntenna, kMic, kMicOff, kKeyboard, kKeyboardOff, kWifi, kWifiOff 50 | case kBacklightHigh, kBacklightLow, kBacklightOff, kCapslockOn, kCapslockOff, kDock, kUndock 51 | case kBluetooth, kCamera, kFunctionKey, kFunctionKeyOff, kFunctionKeyOn, kSecondDisplay, kSleep, kStar 52 | } 53 | 54 | // from https://github.com/alin23/Lunar/blob/master/Lunar/Data/Hotkeys.swift 55 | func showOSDRaw(_ prompt: String, _ img: NSString? = nil, duration: UInt32 = 1000, priority: UInt32 = 0x1f4) { 56 | guard let manager = OSDManager.sharedManager() as? OSDManager else { 57 | if #available(macOS 10.12, *) { 58 | os_log("OSDManager unavailable", type: .error) 59 | } 60 | return 61 | } 62 | 63 | manager.showImage( 64 | atPath: img ?? defaultImage, 65 | onDisplayID: CGMainDisplayID(), 66 | priority: priority, 67 | msecUntilFade: duration, 68 | withText: prompt as NSString) 69 | } 70 | 71 | func showOSD(_ prompt: String, _ img: NSString? = nil, duration: UInt32 = 1000, priority: UInt32 = 0x1f4) { 72 | showOSDRaw(NSLocalizedString(prompt, comment: "LocalizedString"), img, duration: duration, priority: priority) 73 | } 74 | 75 | func showOSDRes(_ prompt: String, _ image: EventImage, duration: UInt32 = 1000, priority: UInt32 = 0x1f4) { 76 | var img: NSString? 77 | if let path = Bundle.main.pathForImageResource(String(image.rawValue.dropFirst(1))), 78 | path.hasPrefix("/Applications") { 79 | img = path as NSString 80 | } 81 | showOSDRaw(NSLocalizedString(prompt, comment: "LocalizedString"), img, duration: duration, priority: priority) 82 | } 83 | 84 | func showOSDRes( 85 | _ prompt: String, 86 | _ status: String, 87 | _ image: EventImage, 88 | duration: UInt32 = 1000, 89 | priority: UInt32 = 0x1f4 90 | ) { 91 | var img: NSString? 92 | if let path = Bundle.main.pathForImageResource(String(image.rawValue.dropFirst(1))), 93 | path.hasPrefix("/Applications") { 94 | img = path as NSString 95 | } 96 | var alias = prompt 97 | if img != nil { 98 | alias = NSLocalizedString(prompt, comment: "") 99 | } 100 | if alias.isEmpty { 101 | showOSDRaw(alias, img, duration: duration, priority: priority) 102 | } else { 103 | alias += " " + NSLocalizedString(status, comment: "") 104 | showOSDRaw(alias, img, duration: duration, priority: priority) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /YogaSMCUtils/PropertyHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PropertyHelper.swift 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 10/14/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // from https://github.com/LinusHenze/Fugu/blob/master/USB/IOKitUSB.swift 12 | // IOIteratorNext, Swift Style 13 | func IOIteratorNextOptional(_ iterator: io_iterator_t) -> io_service_t? { 14 | let service = IOIteratorNext(iterator) 15 | return service != 0 ? service : nil 16 | } 17 | 18 | func getBoolean(_ key: String, _ service: io_service_t) -> Bool { 19 | guard let rvalue = IORegistryEntryCreateCFProperty(service, key as CFString, kCFAllocatorDefault, 0), 20 | let val = rvalue.takeRetainedValue() as? Bool else { 21 | return false 22 | } 23 | return val 24 | } 25 | 26 | func getNumber(_ key: String, _ service: io_service_t) -> Int { 27 | guard let rvalue = IORegistryEntryCreateCFProperty(service, key as CFString, kCFAllocatorDefault, 0), 28 | let val = rvalue.takeRetainedValue() as? Int else { 29 | return -1 30 | } 31 | return val 32 | } 33 | 34 | func getString(_ key: String, _ service: io_service_t) -> String? { 35 | guard let rvalue = IORegistryEntryCreateCFProperty(service, key as CFString, kCFAllocatorDefault, 0), 36 | let val = rvalue.takeRetainedValue() as? NSString else { 37 | return nil 38 | } 39 | return val as String 40 | } 41 | 42 | func getDictionary(_ key: String, _ service: io_service_t) -> NSDictionary? { 43 | guard let rvalue = IORegistryEntryCreateCFProperty(service, key as CFString, kCFAllocatorDefault, 0) else { 44 | return nil 45 | } 46 | return rvalue.takeRetainedValue() as? NSDictionary 47 | } 48 | 49 | func getProperties(_ service: io_service_t) -> NSDictionary? { 50 | var CFProps: Unmanaged? 51 | guard kIOReturnSuccess == IORegistryEntryCreateCFProperties(service, &CFProps, kCFAllocatorDefault, 0), 52 | CFProps != nil else { 53 | return nil 54 | } 55 | return CFProps?.takeRetainedValue() as NSDictionary? 56 | } 57 | 58 | func sendBoolean(_ key: String, _ value: Bool, _ service: io_service_t) -> Bool { 59 | return (kIOReturnSuccess == IORegistryEntrySetCFProperty(service, key as CFString, value as CFBoolean)) 60 | } 61 | 62 | func sendNumber(_ key: String, _ value: Int, _ service: io_service_t) -> Bool { 63 | return (kIOReturnSuccess == IORegistryEntrySetCFProperty(service, key as CFString, value as CFNumber)) 64 | } 65 | 66 | func sendString(_ key: String, _ value: String, _ service: io_service_t) -> Bool { 67 | return (kIOReturnSuccess == IORegistryEntrySetCFProperty(service, key as CFString, value as CFString)) 68 | } 69 | -------------------------------------------------------------------------------- /YogaSMCUtils/RFHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RFHelper.swift 3 | // YogaSMCNC 4 | // 5 | // Created by Zhen on 10/16/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import IOBluetooth 11 | import os.log 12 | import CoreWLAN 13 | 14 | func bluetoothHelper(_ name: String, _ display: Bool) { 15 | guard IOBluetoothPreferencesAvailable() != 0 else { 16 | showOSDRes("Bluetooth", "Unavailable", .kBluetooth) 17 | if #available(macOS 10.12, *) { 18 | os_log("Bluetooth unavailable!", type: .error) 19 | } 20 | return 21 | } 22 | let status = (IOBluetoothPreferenceGetControllerPowerState() == 0) 23 | IOBluetoothPreferenceSetControllerPowerState(status ? 1 : 0) 24 | if display { 25 | showOSDRes(name.isEmpty ? "Bluetooth" : name, status ? "On" : "Off", .kBluetooth) 26 | } 27 | } 28 | 29 | func bluetoothDiscoverableHelper(_ name: String, _ display: Bool) { 30 | guard IOBluetoothPreferencesAvailable() != 0 else { 31 | showOSDRes("Bluetooth", "Unavailable", .kBluetooth) 32 | if #available(macOS 10.12, *) { 33 | os_log("Bluetooth unavailable!", type: .error) 34 | } 35 | return 36 | } 37 | let status = (IOBluetoothPreferenceGetDiscoverableState() == 0) 38 | IOBluetoothPreferenceSetDiscoverableState(status ? 1 : 0) 39 | if display { 40 | showOSDRes(name.isEmpty ? "BT Discoverable" : name, status ? "On" : "Off", .kBluetooth) 41 | } 42 | } 43 | 44 | func wirelessHelper(_ name: String, _ display: Bool) { 45 | guard let iface = CWWiFiClient.shared().interface() else { 46 | showOSDRes("Wireless", "Unavailable", .kWifi) 47 | if #available(macOS 10.12, *) { 48 | os_log("Wireless unavailable!", type: .error) 49 | } 50 | return 51 | } 52 | let status = !iface.powerOn() 53 | do { 54 | try iface.setPower(status) 55 | if display { 56 | showOSDRes(name, status ? "On" : "Off", status ? .kWifi : .kWifiOff) 57 | } 58 | } catch { 59 | showOSDRes("Wireless", "Toggle failed", .kWifi) 60 | if #available(macOS 10.12, *) { 61 | os_log("Wireless toggle failed!", type: .error) 62 | } 63 | } 64 | } 65 | 66 | func airplaneModeHelper(_ name: String, _ display: Bool) { 67 | guard IOBluetoothPreferencesAvailable() != 0 else { 68 | showOSDRes("Bluetooth", "Unavailable", .kBluetooth) 69 | if #available(macOS 10.12, *) { 70 | os_log("Bluetooth unavailable!", type: .error) 71 | } 72 | return 73 | } 74 | guard let iface = CWWiFiClient.shared().interface() else { 75 | showOSDRes("Wireless", "Unavailable", .kWifi) 76 | if #available(macOS 10.12, *) { 77 | os_log("Wireless unavailable!", type: .error) 78 | } 79 | return 80 | } 81 | let status = (IOBluetoothPreferenceGetDiscoverableState() == 0 && !iface.powerOn()) 82 | do { 83 | try iface.setPower(status) 84 | IOBluetoothPreferenceSetControllerPowerState(status ? 1 : 0) 85 | if display { 86 | showOSDRes(name, status ? "Off" : "On", status ? .kAntenna : .kAirplaneMode) 87 | } 88 | } catch { 89 | showOSDRes("Wireless", "Toggle failed", .kWifi) 90 | if #available(macOS 10.12, *) { 91 | os_log("Wireless toggle failed!", type: .error) 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /YogaSMCUtils/ScriptHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScriptHelper.swift 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 10/14/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | import Foundation 11 | import ScriptingBridge 12 | import os.log 13 | 14 | let prefpaneAS = """ 15 | tell application "System Preferences" 16 | reveal pane "org.zhen.YogaSMCPane" 17 | activate 18 | end tell 19 | """ 20 | 21 | let sleepAS = "tell application \"System Events\" to sleep" 22 | 23 | let searchAS = "tell application \"System Events\" to keystroke space using {command down, option down}" 24 | let spotlightAS = "tell application \"System Events\" to keystroke space using command down" 25 | let siriAS = """ 26 | tell application "System Events" to tell the front menu bar of process "SystemUIServer" 27 | tell (first menu bar item whose description is "Siri") 28 | perform action "AXPress" 29 | end tell 30 | end tell 31 | """ 32 | let reloadAS = """ 33 | tell application "YogaSMCNC" to quit 34 | tell application "YogaSMCNC" to activate 35 | """ 36 | let stopAS = "tell application \"YogaSMCNC\" to quit" 37 | let startAS = "tell application \"YogaSMCNC\" to activate" 38 | 39 | let getAudioMutedAS = "output muted of (get volume settings)" 40 | let setAudioMuteAS = "set volume with output muted" 41 | let setAudioUnmuteAS = "set volume without output muted" 42 | 43 | let getMicVolumeAS = "input volume of (get volume settings)" 44 | let setMicVolumeAS = "set volume input volume %d" 45 | 46 | // based on https://medium.com/macoclock/1ba82537f7c3 47 | func scriptHelper(_ source: String, _ name: String, _ image: NSString? = nil) -> NSAppleEventDescriptor? { 48 | if let scpt = NSAppleScript(source: source) { 49 | var error: NSDictionary? 50 | let ret = scpt.executeAndReturnError(&error) 51 | if error == nil { 52 | if let img = image { 53 | showOSD(name, img) 54 | } 55 | return ret 56 | } 57 | } 58 | if #available(macOS 10.12, *) { 59 | os_log("%s: failed to execute script", type: .error, name) 60 | } 61 | return nil 62 | } 63 | 64 | func prefpaneHelper() { 65 | 66 | let homeDir = NSHomeDirectory() 67 | let path = "\(homeDir)/Library/PreferencePanes/" 68 | let path2 = "/Library/PreferencePanes/" 69 | let url = NSURL(fileURLWithPath: path) 70 | let url2 = NSURL(fileURLWithPath: path2) 71 | 72 | if let pathComponent = url.appendingPathComponent("YogaSMCPane.prefPane"), let pathComponent2 = url2.appendingPathComponent("YogaSMCPane.prefPane") { 73 | let filePath = pathComponent.path 74 | let filePath2 = pathComponent2.path 75 | let fileManager = FileManager.default 76 | 77 | if fileManager.fileExists(atPath: filePath ) || fileManager.fileExists(atPath: filePath2 ) { 78 | NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference")!) 79 | NSWorkspace.shared.open(URL(fileURLWithPath: "\(homeDir)/Library/PreferencePanes/YogaSMCPane.prefPane")) 80 | NSWorkspace.shared.open(URL(fileURLWithPath: "/Library/PreferencePanes/YogaSMCPane.prefPane")) 81 | return 82 | } else { 83 | let alert = NSAlert() 84 | alert.messageText = "Failed to open Preferences/Settings" 85 | alert.informativeText = "Please install YogaSMCPane" 86 | alert.alertStyle = .warning 87 | alert.addButton(withTitle: "OK") 88 | alert.runModal() 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /YogaSMCUtils/SystemPreferences.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SystemPreferences.swift 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 10/15/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | import ScriptingBridge 10 | 11 | @objc public protocol SBObjectProtocol: NSObjectProtocol { 12 | func get() -> Any! 13 | } 14 | 15 | @objc public protocol SBApplicationProtocol: SBObjectProtocol { 16 | func activate() 17 | var delegate: SBApplicationDelegate! { get set } 18 | var isRunning: Bool { get } 19 | } 20 | 21 | // MARK: SystemPreferencesSaveOptions 22 | @objc public enum SystemPreferencesSaveOptions: AEKeyword { 23 | case yes = 0x79657320 /* 'yes ' */ 24 | case no = 0x6e6f2020 /* 'no ' */ 25 | case ask = 0x61736b20 /* 'ask ' */ 26 | } 27 | 28 | // MARK: SystemPreferencesPrintingErrorHandling 29 | @objc public enum SystemPreferencesPrintingErrorHandling: AEKeyword { 30 | case standard = 0x6c777374 /* 'lwst' */ 31 | case detailed = 0x6c776474 /* 'lwdt' */ 32 | } 33 | 34 | // MARK: System 35 | @objc public protocol System { 36 | @objc optional func closeSaving(_ saving: SystemPreferencesSaveOptions, savingIn: Any!) // Close a document. 37 | @objc optional func printWithProperties(_ withProperties: Any!, printDialog: Any!) // Print a document. 38 | } 39 | 40 | // MARK: SystemPreferencesApplication 41 | @objc public protocol SystemPreferencesApplication: SBApplicationProtocol { 42 | @objc optional func documents() 43 | @objc optional func windows() -> SBElementArray 44 | @objc optional var name: Int { get } // The name of the application. 45 | @objc optional var frontmost: Int { get } // Is this the active application? 46 | @objc optional var version: Int { get } // The version number of the application. 47 | @objc optional func `open`(_ x: Any!) -> Any // Open a document. 48 | @objc optional func print(_ x: Any!, withProperties: Any!, printDialog: Any!) // Print a document. 49 | @objc optional func quitSaving(_ saving: SystemPreferencesSaveOptions) // Quit the application. 50 | @objc optional func exists(_ x: Any!) // Verify that an object exists. 51 | @objc optional func panes() -> SBElementArray 52 | @objc optional var currentPane: SystemPreferencesPane { get } // the currently selected pane 53 | @objc optional var preferencesWindow: SystemPreferencesWindow { get } // the main preferences window 54 | @objc optional var showAll: Int { get } // Is SystemPrefs in show all view. (Setting to false will do nothing) 55 | @objc optional func setCurrentPane(_ currentPane: SystemPreferencesPane!) // the currently selected pane 56 | @objc optional func setShowAll(_ showAll: Int) // Is SystemPrefs in show all view. (Setting to false will do nothing) 57 | } 58 | extension SBApplication: SystemPreferencesApplication {} 59 | 60 | // MARK: SystemPreferencesDocument 61 | @objc public protocol SystemPreferencesDocument: SBObjectProtocol { 62 | @objc optional var modified: Int { get } // Has it been modified since the last save? 63 | @objc optional var file: Int { get } // Its location on disk, if it has one. 64 | } 65 | extension SBObject: SystemPreferencesDocument {} 66 | 67 | // MARK: SystemPreferencesWindow 68 | @objc public protocol SystemPreferencesWindow: SBObjectProtocol { 69 | @objc optional var name: String { get } // The title of the window 70 | @objc optional func id() // The unique identifier of the window. 71 | @objc optional var index: Int { get } // The index of the window, ordered front to back. 72 | @objc optional var bounds: Int { get } // The bounding rectangle of the window. 73 | @objc optional var closeable: Int { get } // Does the window have a close button? 74 | @objc optional var miniaturizable: Int { get } // Does the window have a minimize button? 75 | @objc optional var miniaturized: Int { get } // Is the window minimized right now? 76 | @objc optional var resizable: Int { get } // Can the window be resized? 77 | @objc optional var visible: Int { get } // Is the window visible right now? 78 | @objc optional var zoomable: Int { get } // Does the window have a zoom button? 79 | @objc optional var zoomed: Int { get } // Is the window zoomed right now? 80 | @objc optional var document: SystemPreferencesDocument { get } // The document whose contents are displayed in the window. 81 | @objc optional func setIndex(_ index: Int) // The index of the window, ordered front to back. 82 | @objc optional func setBounds(_ bounds: Int) // The bounding rectangle of the window. 83 | @objc optional func setMiniaturized(_ miniaturized: Int) // Is the window minimized right now? 84 | @objc optional func setVisible(_ visible: Int) // Is the window visible right now? 85 | @objc optional func setZoomed(_ zoomed: Int) // Is the window zoomed right now? 86 | } 87 | extension SBObject: SystemPreferencesWindow {} 88 | 89 | // MARK: SystemPreferencesPane 90 | @objc public protocol SystemPreferencesPane: SBObjectProtocol { 91 | @objc optional func anchors() -> SBElementArray 92 | @objc optional func id() -> String // locale independent name of the preference pane; can refer to a pane using the expression: pane id "" 93 | @objc optional var localizedName: Int { get } // localized name of the preference pane 94 | @objc optional var name: String { get } // name of the preference pane as it appears in the title bar; can refer to a pane using the expression: pane "" 95 | @objc optional func reveal() -> Any // Reveals an anchor within a preference pane or preference pane itself 96 | @objc optional func authorize() -> SystemPreferencesPane // Prompt authorization for given preference pane 97 | } 98 | extension SBObject: SystemPreferencesPane {} 99 | 100 | // MARK: SystemPreferencesAnchor 101 | @objc public protocol SystemPreferencesAnchor: SBObjectProtocol { 102 | @objc optional var name: String { get } // name of the anchor within a preference pane 103 | @objc optional func reveal() -> Any // Reveals an anchor within a preference pane or preference pane itself 104 | } 105 | extension SBObject: SystemPreferencesAnchor {} 106 | -------------------------------------------------------------------------------- /YogaSMCUtils/YogaSMC-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // YogaSMC-Bridging-Header.h 3 | // YogaSMC 4 | // 5 | // Created by Zhen on 10/14/20. 6 | // Copyright © 2020 Zhen. All rights reserved. 7 | // 8 | 9 | #ifndef YogaSMC_Bridging_Header_h 10 | #define YogaSMC_Bridging_Header_h 11 | 12 | #import 13 | #import 14 | #import 15 | 16 | #import "../YogaSMC/YogaSMCUserClient.h" 17 | #import "../YogaSMC/YogaVPC/ThinkEvents.h" 18 | 19 | // from https://github.com/toy/blueutil/blob/master/blueutil.m#L44 20 | int IOBluetoothPreferencesAvailable(); 21 | 22 | int IOBluetoothPreferenceGetControllerPowerState(); 23 | void IOBluetoothPreferenceSetControllerPowerState(int state); 24 | 25 | int IOBluetoothPreferenceGetDiscoverableState(); 26 | void IOBluetoothPreferenceSetDiscoverableState(int state); 27 | 28 | // from https://github.com/koekeishiya/yabai/issues/147 29 | CG_EXTERN void CoreDockSendNotification(CFStringRef, void*); 30 | 31 | #endif /* YogaSMC_Bridging_Header_h */ 32 | -------------------------------------------------------------------------------- /en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | YogaSMC 4 | 5 | Created by Zhen on 11/17/20. 6 | Copyright © 2020 Zhen. All rights reserved. 7 | */ 8 | 9 | // OSD Warning 10 | "ConnectFail" = "Failed to connect \n to YogaSMC"; 11 | "AlreadyConnected" = "Another instance has \n connected to YogaSMC"; 12 | "YogaSMC Unavailable"; 13 | "Unknown Version"; 14 | "ECAccessUnavailable" = "EC access unavailable! \n See SSDT-ECRW.dsl"; 15 | "Unknown Class"; 16 | "MoveApp" = "Please move the app \n into Applications"; 17 | 18 | // Menu 19 | "ClassVar" = "Class: "; 20 | "Preferences"; 21 | "Start at Login"; 22 | 23 | // Event name 24 | // Idea 25 | "Special Button"; 26 | "Fn-Q Cooling"; 27 | "Keyboard Backlight"; 28 | "Screen Off"; 29 | "Screen On"; 30 | "TouchPad Off"; 31 | "TouchPad On"; 32 | "Switch Video"; 33 | "Camera"; 34 | "Mic Mute" = ""; 35 | "TouchPad On"; 36 | "Yoga Mode"; 37 | "Laptop Mode"; 38 | "Tablet Mode"; 39 | "Stand Mode"; 40 | "Tent Mode"; 41 | "FnKey" = ""; 42 | "Caps Lock" = ""; 43 | // Think 44 | "Sleep"; 45 | "Second Display"; 46 | "Brightness Up"; 47 | "Brightness Down"; 48 | "Settings"; 49 | "Search"; 50 | "Mission Control"; 51 | "Launchpad"; 52 | "Custom Hotkey"; 53 | "Bluetooth"; 54 | "Keyboard" = ""; 55 | "LID Close"; 56 | "LID Open"; 57 | "Thermal Table Change"; 58 | "Thermal Control Change"; 59 | "AC Status Change"; 60 | "Backlight Changed"; 61 | "FnLock"; 62 | "Palm Detected"; 63 | "Palm Undetected"; 64 | "Thermal Changed"; 65 | // HIDD 66 | "STOPCD"; 67 | "Rotate Lock"; 68 | "Convertible"; 69 | 70 | // OSD Res 71 | "On"; 72 | "Off"; 73 | "Low"; 74 | "High"; 75 | "Enabled"; 76 | "Disabled"; 77 | "Unavailable"; 78 | "Toggle failed"; 79 | 80 | "Backlight"; 81 | "EventVar" = "Event "; 82 | 83 | // Helper 84 | "OpenFail" = "Failed to open "; 85 | 86 | // Date 87 | "New Year"; 88 | "Valentine's Day"; 89 | "Saint Patrick's Day"; 90 | "Easter"; 91 | "Halloween"; 92 | "Thanksgiving"; 93 | "Christmas Eve"; 94 | "Christmas Day"; 95 | "New Year's Eve"; 96 | 97 | // RF 98 | "Bluetooth"; 99 | "BT Discoverable"; 100 | "Wireless" = ""; 101 | "Airplane Mode" = ""; 102 | 103 | // Audio 104 | "Mic"; 105 | "Mute"; 106 | "Unmute"; 107 | 108 | // Script 109 | "AppleEventAccess" = "Please grant access \n to Apple Event"; 110 | 111 | // ThinkFan 112 | "Auto"; 113 | "Main"; 114 | "ReadFanFail" = "Read fan level failed!"; 115 | "WriteFanFail" = "Write Fan level failed!"; 116 | "Main: %d rpm"; 117 | "Alt: %d rpm"; 118 | --------------------------------------------------------------------------------